Skip to content

Commit

Permalink
feat: bump , upgrade to next 15+
Browse files Browse the repository at this point in the history
  • Loading branch information
hyperse-net committed Oct 29, 2024
1 parent 693a238 commit 8f6409a
Show file tree
Hide file tree
Showing 31 changed files with 279 additions and 567 deletions.
9 changes: 9 additions & 0 deletions .changeset/rich-monkeys-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@hyperse/next-graphql": patch
"@hyperse/next-auth": patch
"@hyperse/next-core": patch
"@hyperse/next-demo": patch
"@hyperse/next-env": patch
---

upgrade next to 15+, BREAKCHANGE
2 changes: 1 addition & 1 deletion packages/next-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
"peerDependencies": {
"@prisma/client": "^5.21.1",
"next": "*"
"next": "^15.0.1"
},
"peerDependenciesMeta": {
"@prisma/client": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-auth/src/handlers/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function logoutHandler(
await lucia.invalidateSession(session.token);

const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(
(await cookies()).set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
Expand Down
7 changes: 4 additions & 3 deletions packages/next-auth/src/providers/github/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export async function authorizeHandler(
}

const state = generateState();
const url = await provider.createAuthorizationURL(state);
const url = await provider.createAuthorizationURL(state, []);

cookies().set('github_oauth_state', state, {
(await cookies()).set('github_oauth_state', state, {
path: '/',
httpOnly: true,
maxAge: 60 * 10, // 10 minutes
Expand Down Expand Up @@ -61,7 +61,8 @@ export async function authorizeCallbackHandler(
const url = new URL(request.url);
const code = url.searchParams.get('code');
const state = url.searchParams.get('state');
const storedState = cookies().get('github_oauth_state')?.value ?? null;
const storedState =
(await cookies()).get('github_oauth_state')?.value ?? null;
if (!code || !state || !storedState || state !== storedState) {
return new NextResponse(null, {
status: 400,
Expand Down
25 changes: 18 additions & 7 deletions packages/next-auth/src/providers/github/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import { GitHub } from 'arctic';
import { type ProviderConfig } from '../providerType.js';
import { type OAuth2Provider, type ProviderConfig } from '../providerType.js';
import { authorizeCallbackHandler, authorizeHandler } from './handlers.js';

export const createGithubProvider = (config: ProviderConfig) => {
const { redirectUri, clientId, clientSecret, enterpriseDomain } = config;
return new GitHub(clientId, clientSecret, {
redirectURI: redirectUri,
enterpriseDomain: enterpriseDomain,
});
export const createGithubProvider = (
config: ProviderConfig
): OAuth2Provider => {
const { redirectUri, clientId, clientSecret } = config;
const provider = new GitHub(clientId, clientSecret, redirectUri || null);

return {
createAuthorizationURL: async (state: string, scopes?: string[]) => {
return provider.createAuthorizationURL(state, scopes || []);
},
validateAuthorizationCode: async (code: string) => {
return provider.validateAuthorizationCode(code);
},
refreshAccessToken: async (refreshToken: string) => {
return provider.refreshAccessToken(refreshToken);
},
};
};

export const githubProviderHandlers = {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-auth/src/providers/password/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function logout(lucia: Lucia) {
await lucia.invalidateSession(session.token);

const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(
(await cookies()).set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
Expand Down
7 changes: 4 additions & 3 deletions packages/next-auth/src/providers/password/validateRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export const validateRequest = async (
): Promise<
{ user: User; session: Session } | { user: null; session: null }
> => {
const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null;
const sessionId =
(await cookies()).get(lucia.sessionCookieName)?.value ?? null;
if (!sessionId) {
return {
user: null,
Expand All @@ -18,15 +19,15 @@ export const validateRequest = async (
try {
if (result.session && result.session.fresh) {
const sessionCookie = lucia.createSessionCookie(result.session.token);
cookies().set(
(await cookies()).set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
);
}
if (!result.session) {
const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(
(await cookies()).set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
Expand Down
16 changes: 5 additions & 11 deletions packages/next-auth/src/providers/providerType.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
export interface OAuth2Provider {
createAuthorizationURL(state: string): Promise<URL>;
validateAuthorizationCode(code: string): Promise<Tokens>;
refreshAccessToken?(refreshToken: string): Promise<Tokens>;
}
import type { OAuth2Tokens } from 'arctic';

export interface Tokens {
accessToken: string;
refreshToken?: string | null;
accessTokenExpiresAt?: Date;
refreshTokenExpiresAt?: Date | null;
idToken?: string;
export interface OAuth2Provider {
createAuthorizationURL(state: string, scopes?: string[]): Promise<URL>;
validateAuthorizationCode(code: string): Promise<OAuth2Tokens>;
refreshAccessToken?(refreshToken: string): Promise<OAuth2Tokens>;
}

export interface ProviderConfig {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-auth/src/utils/createSessionAndCookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { type Lucia } from '../lucia/index.js';
export const createSessionAndCookie = async (lucia: Lucia, userId: string) => {
const session = await lucia.createSession(userId, {});
const sessionCookie = lucia.createSessionCookie(session.token);
cookies().set(
(await cookies()).set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes
Expand Down
9 changes: 7 additions & 2 deletions packages/next-auth/src/utils/get-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ export type IGetSessionReturn = {
} | null;

export const getSession = async (lucia: Lucia): Promise<IGetSessionReturn> => {
const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null;
const sessionId =
(await cookies()).get(lucia.sessionCookieName)?.value ?? null;
if (!sessionId) return null;
const { session, user } = await lucia.validateSession(sessionId);
return { session, user };

return {
session,
user,
};
};
2 changes: 1 addition & 1 deletion packages/next-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"@tanstack/query-core": "^5.x",
"@tanstack/react-query": "^5.59.16",
"@trpc/server": "*",
"next": "*",
"next": "^15.0.1",
"react": ">=18.2.0"
},
"engines": {
Expand Down
4 changes: 2 additions & 2 deletions packages/next-core/src/trpc/local-storage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* This file makes sure that we can get a storage that is unique to the current request context
*/
import { requestAsyncStorage } from 'next/dist/client/components/request-async-storage.external.js';
import { getExpectedRequestStore } from 'next/dist/server/app-render/work-unit-async-storage.external.js';
import type { AsyncLocalStorage } from 'async_hooks';

// https://github.com/vercel/next.js/blob/canary/packages/next/client/components/request-async-storage.ts

const asyncStorage: AsyncLocalStorage<any> | object = requestAsyncStorage;
const asyncStorage: AsyncLocalStorage<any> | object = getExpectedRequestStore;

function throwError(msg: string) {
throw new Error(msg);
Expand Down
2 changes: 1 addition & 1 deletion packages/next-demo/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
30 changes: 13 additions & 17 deletions packages/next-demo/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import withNextIntl from 'next-intl/plugin';
import { z } from 'zod';
import { getNextConfig, getNextConfigEnv } from '@hyperse/next-env';
import bundleAnalyzer from '@next/bundle-analyzer';
import { envSchema } from './src/config/envSchema.mjs';

/**
* The order of plugins
Expand All @@ -15,21 +16,19 @@ plugins.push(
enabled: process.env.ANALYZE === 'true',
})
);
const env = envSchema.extend({
NEXT_BUILD_ENV_OUTPUT: z
.enum(['standalone', 'export'], {
description:
'For standalone mode: https://nextjs.org/docs/app/api-reference/next-config-js/output',
})
.optional(),
});

// We use a custom env to validate the build env
const buildEnv = getNextConfigEnv(
z.object({
NEXT_BUILD_ENV_OUTPUT: z
.enum(['standalone', 'export'], {
description:
'For standalone mode: https://nextjs.org/docs/app/api-reference/next-config-js/output',
})
.optional(),
}),
{
isProd: process.env.ANALYZE === 'production',
}
);
const buildEnv = getNextConfigEnv(env, {
isProd: process.env.ANALYZE === 'production',
});

/**
* Don't be scared of the generics here.
Expand All @@ -46,11 +45,8 @@ const config = {
allowedForwardedHosts: ['www.domain.com', 'localhost'],
allowedOrigins: ['www.domain.com', 'localhost'],
},
serverComponentsExternalPackages: [
'@node-rs/argon2',
'@node-rs/bcrypt-darwin-arm64',
],
},
serverExternalPackages: ['@node-rs/argon2', '@node-rs/bcrypt-darwin-arm64'],
/** We run eslint as a separate task in CI */
eslint: { ignoreDuringBuilds: !!process.env.CI },
// transpilePackages: [
Expand Down
69 changes: 69 additions & 0 deletions packages/next-demo/prisma/migrations/20241029085338_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Warnings:
- You are about to drop the `Authorized` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `Session` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE `Authorized` DROP FOREIGN KEY `Authorized_userId_fkey`;

-- DropForeignKey
ALTER TABLE `Session` DROP FOREIGN KEY `Session_userId_fkey`;

-- DropTable
DROP TABLE `Authorized`;

-- DropTable
DROP TABLE `Session`;

-- DropTable
DROP TABLE `User`;

-- CreateTable
CREATE TABLE `user` (
`id` VARCHAR(191) NOT NULL,
`username` VARCHAR(191) NOT NULL,
`email` VARCHAR(191) NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,

UNIQUE INDEX `user_username_key`(`username`),
UNIQUE INDEX `user_email_key`(`email`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- CreateTable
CREATE TABLE `authorized` (
`id` VARCHAR(191) NOT NULL,
`userId` VARCHAR(191) NOT NULL,
`hashedPassword` VARCHAR(191) NULL,
`providerId` VARCHAR(191) NULL,
`providerMethod` VARCHAR(191) NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,

INDEX `authorized_userId_idx`(`userId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- CreateTable
CREATE TABLE `session` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`token` VARCHAR(191) NOT NULL,
`userId` VARCHAR(191) NOT NULL,
`expiresAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,

UNIQUE INDEX `session_token_key`(`token`),
INDEX `session_userId_idx`(`userId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- AddForeignKey
ALTER TABLE `authorized` ADD CONSTRAINT `authorized_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE `session` ADD CONSTRAINT `session_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
7 changes: 4 additions & 3 deletions packages/next-demo/src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type PropsWithChildren } from 'react';
import { notFound } from 'next/navigation';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, unstable_setRequestLocale } from 'next-intl/server';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { PublicEnvScript } from 'next-runtime-env';
import { ClientProvider } from '@/common/client-trpc';
import { Navigation } from '@/components/Navigation/Navigation';
Expand All @@ -10,13 +10,14 @@ import './globals.css';

export default async function LocaleLayout({
children,
params: { locale },
params,
}: PropsWithChildren<PageProps>) {
const { locale } = await params;
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale as never)) notFound();

// https://next-intl-docs.vercel.app/blog/next-intl-3-0#static-rendering-of-server-components
unstable_setRequestLocale(locale);
setRequestLocale(locale);

// Receive messages provided in `i18n.ts`
const messages = await getMessages();
Expand Down
18 changes: 11 additions & 7 deletions packages/next-demo/src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Suspense, use } from 'react';
import { useTranslations } from 'next-intl';
import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
import { getTranslations, setRequestLocale } from 'next-intl/server';
import { CreatePostForm } from '@/app/[locale]/post/components/CreatePostForm';
import { PostList } from '@/app/[locale]/post/components/PostList';
import { PostListItem } from '@/app/[locale]/post/components/PostListItem';
Expand Down Expand Up @@ -34,17 +33,22 @@ function PostListRSC() {
);
}

export async function generateMetadata({ params: { locale } }: PageProps) {
const t = await getTranslations({ locale, namespace: 'IndexPage' });
export async function generateMetadata(props: PageProps) {
const params = await props.params;
const t = await getTranslations({
locale: params.locale,
namespace: 'IndexPage',
});
return {
title: t('title'),
};
}

export default function Page({ params: { locale } }: PageProps) {
unstable_setRequestLocale(locale);
export default async function Page(props: PageProps) {
const params = await props.params;
setRequestLocale(params.locale);

const t = useTranslations('IndexPage');
const t = await getTranslations('IndexPage');
// Once the request locale is set, you
// can call hooks from `next-intl`

Expand Down
Loading

0 comments on commit 8f6409a

Please sign in to comment.