Skip to content

Commit

Permalink
merges with main
Browse files Browse the repository at this point in the history
  • Loading branch information
BRKalow committed Oct 31, 2024
2 parents 41e5330 + 188bf40 commit d437635
Show file tree
Hide file tree
Showing 74 changed files with 1,027 additions and 423 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-bananas-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/clerk-react": patch
---

Fix `signOutOptions` prop usage in `<SignOutButton />` component
5 changes: 5 additions & 0 deletions .changeset/clever-lions-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/nextjs": minor
---

Bug fix: For next>=14 applications resolve `__unstable__onBeforeSetActive` once `invalidateCacheAction` resolves.
33 changes: 33 additions & 0 deletions .changeset/five-insects-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
"@clerk/nextjs": minor
"@clerk/clerk-react": minor
---

Introduce a new experimental hook called `useReverification` that makes it easy to handle reverification errors.
It returns a high order function (HOF) and allows developers to wrap any function that triggers a fetch request which might fail due to a user's session verification status.
When such error is returned, the recommended UX is to offer a way to the user to recover by re-verifying their credentials.
This helper will automatically handle this flow in the developer's behalf, by displaying a modal the end-user can interact with.
Upon completion, the original request that previously failed, will be retried (only once).

Example with clerk-js methods.
```tsx
import { __experimental_useReverification as useReverification } from '@clerk/nextjs';

function DeleteAccount() {
const { user } = useUser();
const [deleteUserAccount] = useReverification(() => {
if (!user) return;
return user.delete()
});

return <>
<button
onClick={async () => {
await deleteUserAccount();
}}>
Delete account
</button>
</>
}

```
5 changes: 5 additions & 0 deletions .changeset/nasty-meals-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/nextjs": minor
---

Replace `next/headers` with `ezheaders`
7 changes: 7 additions & 0 deletions .changeset/ninety-rabbits-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@clerk/shared": minor
---

Introduce experimental reverification error helpers.
- `reverificationMismatch` returns the error as an object which can later be used as a return value from a React Server Action.
- `reverificationMismatchResponse` returns a Response with the above object serialized. It can be used in any Backend Javascript frameworks that supports `Response`.
5 changes: 5 additions & 0 deletions .changeset/odd-peas-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/elements": minor
---

Added support for `__experimental_legalAccepted` field
6 changes: 6 additions & 0 deletions .changeset/rich-moles-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@clerk/nextjs": patch
"@clerk/clerk-react": patch
---

Updating peerDependencies for correct ranges
5 changes: 5 additions & 0 deletions .changeset/serious-poems-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/clerk-js": patch
---

Chore: Replace beforeEmit with an explicit call after `setActive`, inside the experimental UserVerification.
8 changes: 8 additions & 0 deletions .changeset/witty-meals-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@clerk/clerk-js": patch
"@clerk/types": patch
---

- Changed `__experimental_legalAccepted` checkbox Indicator element descriptor and element id
- Changed `__experimental_legalAccepted` checkbox Label element descriptor and element id
- Added two new element descriptors `formFieldCheckboxInput`, `formFieldCheckboxLabel`.
2 changes: 2 additions & 0 deletions .changeset/yellow-snails-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ jobs:
- name: Run Integration Tests
if: ${{ steps.task-status.outputs.affected == '1' }}
id: integration-tests
run: sudo --preserve-env npx turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS --only -- --project=${{ matrix.test-project }}
run: sudo --preserve-env npx turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS
env:
E2E_APP_CLERK_JS_DIR: ${{runner.temp}}
E2E_CLERK_VERSION: 'latest'
Expand Down
2 changes: 1 addition & 1 deletion integration/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const common: PlaywrightTestConfig = {
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
timeout: 90000,
timeout: 90_000,
maxFailures: process.env.CI ? 1 : undefined,
workers: process.env.CI ? '50%' : '70%',
reporter: process.env.CI ? 'line' : 'list',
Expand Down
8 changes: 8 additions & 0 deletions integration/presets/envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ const withCustomRoles = base
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('with-custom-roles').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-custom-roles').pk);

const withReverification = base
.clone()
.setId('withReverification')
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('with-reverification').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-reverification').pk)
.setEnvVariable('private', 'CLERK_ENCRYPTION_KEY', constants.E2E_CLERK_ENCRYPTION_KEY || 'a-key');

const withEmailCodesQuickstart = withEmailCodes
.clone()
.setEnvVariable('public', 'CLERK_SIGN_IN_URL', '')
Expand Down Expand Up @@ -106,6 +113,7 @@ export const envs = {
withEmailCodes_destroy_client,
withEmailLinks,
withCustomRoles,
withReverification,
withEmailCodesQuickstart,
withAPCore1ClerkLatest,
withAPCore1ClerkV4,
Expand Down
1 change: 1 addition & 0 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const createLongRunningApps = () => {
env: envs.withEmailCodes_destroy_client,
},
{ id: 'next.appRouter.withCustomRoles', config: next.appRouter, env: envs.withCustomRoles },
{ id: 'next.appRouter.withReverification', config: next.appRouter, env: envs.withReverification },
{ id: 'quickstart.next.appRouter', config: next.appRouterQuickstart, env: envs.withEmailCodesQuickstart },
{ id: 'elements.next.appRouter', config: elements.nextAppRouter, env: envs.withEmailCodes },
{ id: 'astro.node.withCustomRoles', config: astro.node, env: envs.withCustomRoles },
Expand Down
4 changes: 3 additions & 1 deletion integration/presets/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { applicationConfig } from '../models/applicationConfig.js';
import { templates } from '../templates/index.js';

const clerkNextjsLocal = `file:${process.cwd()}/packages/nextjs`;
const clerkSharedLocal = `file:${process.cwd()}/packages/shared`;
const appRouter = applicationConfig()
.setName('next-app-router')
.useTemplate(templates['next-app-router'])
Expand All @@ -14,7 +15,8 @@ const appRouter = applicationConfig()
.addDependency('next', constants.E2E_NEXTJS_VERSION)
.addDependency('react', constants.E2E_REACT_VERSION)
.addDependency('react-dom', constants.E2E_REACT_DOM_VERSION)
.addDependency('@clerk/nextjs', constants.E2E_CLERK_VERSION || clerkNextjsLocal);
.addDependency('@clerk/nextjs', constants.E2E_CLERK_VERSION || clerkNextjsLocal)
.addDependency('@clerk/shared', clerkSharedLocal);

const appRouterTurbo = appRouter
.clone()
Expand Down
3 changes: 3 additions & 0 deletions integration/templates/next-app-router/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
serverActions: true,
},
};

module.exports = nextConfig;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';
import { useState, useTransition } from 'react';
import { __experimental_useReverification as useReverification } from '@clerk/nextjs';
import { logUserIdActionReverification } from '@/app/(reverification)/actions';

function Page() {
const [logUserWithReverification] = useReverification(logUserIdActionReverification);
const [pending, startTransition] = useTransition();
const [res, setRes] = useState(null);

return (
<>
<button
disabled={pending}
onClick={() => {
startTransition(async () => {
await logUserWithReverification().then(e => {
setRes(e as any);
});
});
}}
>
LogUserId
</button>
<pre>{JSON.stringify(res)}</pre>
</>
);
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use server';

import { auth } from '@clerk/nextjs/server';
import { __experimental_reverificationMismatch as reverificationMismatch } from '@clerk/shared/authorization-errors';

const logUserIdActionReverification = async () => {
const { userId, has } = await auth.protect();

const config = {
level: 'secondFactor',
afterMinutes: 1,
} as const;

const userNeedsReverification = !has({
__experimental_reverification: config,
});

if (userNeedsReverification) {
return reverificationMismatch(config);
}

return {
userId,
};
};

export { logUserIdActionReverification };
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';
import { useState, useTransition } from 'react';

export function ButtonAction({ action }: { action: () => Promise<any> }) {
const [pending, startTransition] = useTransition();
const [res, setRes] = useState(null);

return (
<>
<button
disabled={pending}
onClick={() => {
startTransition(async () => {
await action().then(setRes);
});
}}
>
LogUserId
</button>
<pre>{JSON.stringify(res)}</pre>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { logUserIdActionReverification } from '../actions';
import { ButtonAction } from '../button-action';

function Page() {
return <ButtonAction action={logUserIdActionReverification} />;
}

export default Page;
2 changes: 2 additions & 0 deletions integration/testUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { createUserButtonPageObject } from './userButtonPageObject';
import { createUserProfileComponentPageObject } from './userProfilePageObject';
import type { FakeOrganization, FakeUser } from './usersService';
import { createUserService } from './usersService';
import { createUserVerificationComponentPageObject } from './userVerificationPageObject';

export type { FakeUser, FakeOrganization };
const createClerkClient = (app: Application) => {
Expand Down Expand Up @@ -91,6 +92,7 @@ export const createTestUtils = <
userProfile: createUserProfileComponentPageObject(testArgs),
organizationSwitcher: createOrganizationSwitcherComponentPageObject(testArgs),
userButton: createUserButtonPageObject(testArgs),
userVerification: createUserVerificationComponentPageObject(testArgs),
expect: createExpectPageObject(testArgs),
clerk: createClerkUtils(testArgs),
};
Expand Down
27 changes: 27 additions & 0 deletions integration/testUtils/userVerificationPageObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Browser, BrowserContext } from '@playwright/test';

import type { createAppPageObject } from './appPageObject';
import { common } from './commonPageObject';

export type EnchancedPage = ReturnType<typeof createAppPageObject>;
export type TestArgs = { page: EnchancedPage; context: BrowserContext; browser: Browser };

export const createUserVerificationComponentPageObject = (testArgs: TestArgs) => {
const { page } = testArgs;
const self = {
...common(testArgs),
waitForMounted: (selector = '.cl-userVerification-root') => {
return page.waitForSelector(selector, { state: 'attached' });
},
getUseAnotherMethodLink: () => {
return page.getByRole('link', { name: /use another method/i });
},
getAltMethodsEmailCodeButton: () => {
return page.getByRole('button', { name: /email code to/i });
},
getAltMethodsEmailLinkButton: () => {
return page.getByRole('button', { name: /email link to/i });
},
};
return self;
};
Loading

0 comments on commit d437635

Please sign in to comment.