diff --git a/.changeset/wet-foxes-attend.md b/.changeset/wet-foxes-attend.md new file mode 100644 index 00000000000..e8a1223f99f --- /dev/null +++ b/.changeset/wet-foxes-attend.md @@ -0,0 +1,8 @@ +--- +"@clerk/astro": patch +"@clerk/clerk-react": patch +"@clerk/types": patch +"@clerk/vue": patch +--- + +Extract internal `ProtectProps` type to shared types to eliminate duplication across SDKs diff --git a/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap b/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap index 1c3967fdf4a..3e9d7c11042 100644 --- a/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap +++ b/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap @@ -45,6 +45,7 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = ` "types/path-value.mdx", "types/pending-session-options.mdx", "types/pending-session-resource.mdx", + "types/protect-props.mdx", "types/record-to-path.mdx", "types/redirect-options.mdx", "types/reverification-config.mdx", diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index 2857d321413..ee7f46044a6 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -1,12 +1,9 @@ import type { - Autocomplete, - CheckAuthorizationWithCustomPermissions, Clerk, ClerkOptions, ClientResource, MultiDomainAndOrProxyPrimitives, - OrganizationCustomPermissionKey, - OrganizationCustomRoleKey, + ProtectProps, Without, } from '@clerk/types'; @@ -48,50 +45,6 @@ declare global { } } -type ProtectProps = - | { - condition?: never; - role: OrganizationCustomRoleKey; - permission?: never; - feature?: never; - plan?: never; - } - | { - condition?: never; - role?: never; - feature?: never; - plan?: never; - permission: OrganizationCustomPermissionKey; - } - | { - condition: (has: CheckAuthorizationWithCustomPermissions) => boolean; - role?: never; - permission?: never; - feature?: never; - plan?: never; - } - | { - condition?: never; - role?: never; - permission?: never; - feature: Autocomplete<`user:${string}` | `org:${string}`>; - plan?: never; - } - | { - condition?: never; - role?: never; - permission?: never; - feature?: never; - plan: Autocomplete<`user:${string}` | `org:${string}`>; - } - | { - condition?: never; - role?: never; - permission?: never; - feature?: never; - plan?: never; - }; - export type { AstroClerkUpdateOptions, AstroClerkIntegrationParams, AstroClerkCreateInstanceParams, ProtectProps }; export type ButtonProps = { diff --git a/packages/react/src/components/controlComponents.tsx b/packages/react/src/components/controlComponents.tsx index 818c824914e..933ff91daaf 100644 --- a/packages/react/src/components/controlComponents.tsx +++ b/packages/react/src/components/controlComponents.tsx @@ -1,12 +1,5 @@ import { deprecated } from '@clerk/shared/deprecated'; -import type { - Autocomplete, - CheckAuthorizationWithCustomPermissions, - HandleOAuthCallbackParams, - OrganizationCustomPermissionKey, - OrganizationCustomRoleKey, - PendingSessionOptions, -} from '@clerk/types'; +import type { HandleOAuthCallbackParams, PendingSessionOptions, ProtectProps as _ProtectProps } from '@clerk/types'; import React from 'react'; import { useIsomorphicClerkContext } from '../contexts/IsomorphicClerkContext'; @@ -77,50 +70,7 @@ export const ClerkDegraded = ({ children }: React.PropsWithChildren) => }; export type ProtectProps = React.PropsWithChildren< - ( - | { - condition?: never; - role: OrganizationCustomRoleKey; - permission?: never; - feature?: never; - plan?: never; - } - | { - condition?: never; - role?: never; - feature?: never; - plan?: never; - permission: OrganizationCustomPermissionKey; - } - | { - condition: (has: CheckAuthorizationWithCustomPermissions) => boolean; - role?: never; - permission?: never; - feature?: never; - plan?: never; - } - | { - condition?: never; - role?: never; - permission?: never; - feature: Autocomplete<`user:${string}` | `org:${string}`>; - plan?: never; - } - | { - condition?: never; - role?: never; - permission?: never; - feature?: never; - plan: Autocomplete<`user:${string}` | `org:${string}`>; - } - | { - condition?: never; - role?: never; - permission?: never; - feature?: never; - plan?: never; - } - ) & { + _ProtectProps & { fallback?: React.ReactNode; } & PendingSessionOptions >; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 7db0228bbe8..854394dbeba 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -37,6 +37,7 @@ export * from './organizationSuggestion'; export * from './passwords'; export * from './permission'; export * from './phoneNumber'; +export * from './protect'; export * from './redirects'; export * from './resource'; export * from './role'; diff --git a/packages/types/src/protect.ts b/packages/types/src/protect.ts new file mode 100644 index 00000000000..e96df803046 --- /dev/null +++ b/packages/types/src/protect.ts @@ -0,0 +1,70 @@ +import type { OrganizationCustomPermissionKey, OrganizationCustomRoleKey } from './organizationMembership'; +import type { CheckAuthorizationWithCustomPermissions } from './session'; +import type { Autocomplete } from './utils'; + +/** + * Props for the `` component, which restricts access to its children based on authentication and authorization. + * + * Use `ProtectProps` to specify the required role, permission, feature, or plan for access. + * + * @example + * ```tsx + * // Require a specific permission + * + * + * // Require a specific role + * + * + * // Use a custom condition callback + * has({ permission: "a_permission_key" })} /> + * + * // Require a specific feature + * + * + * // Require a specific plan + * + * ``` + */ +export type ProtectProps = + | { + condition?: never; + role: OrganizationCustomRoleKey; + permission?: never; + feature?: never; + plan?: never; + } + | { + condition?: never; + role?: never; + feature?: never; + plan?: never; + permission: OrganizationCustomPermissionKey; + } + | { + condition: (has: CheckAuthorizationWithCustomPermissions) => boolean; + role?: never; + permission?: never; + feature?: never; + plan?: never; + } + | { + condition?: never; + role?: never; + permission?: never; + feature: Autocomplete<`user:${string}` | `org:${string}`>; + plan?: never; + } + | { + condition?: never; + role?: never; + permission?: never; + feature?: never; + plan: Autocomplete<`user:${string}` | `org:${string}`>; + } + | { + condition?: never; + role?: never; + permission?: never; + feature?: never; + plan?: never; + }; diff --git a/packages/vue/src/components/controlComponents.ts b/packages/vue/src/components/controlComponents.ts index 2838de895ec..843254fe04c 100644 --- a/packages/vue/src/components/controlComponents.ts +++ b/packages/vue/src/components/controlComponents.ts @@ -1,11 +1,8 @@ import { deprecated } from '@clerk/shared/deprecated'; import type { - Autocomplete, - CheckAuthorizationWithCustomPermissions, HandleOAuthCallbackParams, - OrganizationCustomPermissionKey, - OrganizationCustomRoleKey, PendingSessionOptions, + ProtectProps as _ProtectProps, RedirectOptions, } from '@clerk/types'; import { defineComponent } from 'vue'; @@ -107,51 +104,7 @@ export const AuthenticateWithRedirectCallback = defineComponent((props: HandleOA return () => null; }); -export type ProtectProps = ( - | { - condition?: never; - role: OrganizationCustomRoleKey; - permission?: never; - feature?: never; - plan?: never; - } - | { - condition?: never; - role?: never; - feature?: never; - plan?: never; - permission: OrganizationCustomPermissionKey; - } - | { - condition: (has: CheckAuthorizationWithCustomPermissions) => boolean; - role?: never; - permission?: never; - feature?: never; - plan?: never; - } - | { - condition?: never; - role?: never; - permission?: never; - feature: Autocomplete<`user:${string}` | `org:${string}`>; - plan?: never; - } - | { - condition?: never; - role?: never; - permission?: never; - feature?: never; - plan: Autocomplete<`user:${string}` | `org:${string}`>; - } - | { - condition?: never; - role?: never; - permission?: never; - feature?: never; - plan?: never; - } -) & - PendingSessionOptions; +export type ProtectProps = _ProtectProps & PendingSessionOptions; export const Protect = defineComponent((props: ProtectProps, { slots }) => { const { isLoaded, has, userId } = useAuth({ treatPendingAsSignedOut: props.treatPendingAsSignedOut });