diff --git a/packages/react/src/components/controlComponents.tsx b/packages/react/src/components/controlComponents.tsx index 1d4ff88703d..ed8dcf9f027 100644 --- a/packages/react/src/components/controlComponents.tsx +++ b/packages/react/src/components/controlComponents.tsx @@ -1,7 +1,8 @@ import { deprecated } from '@clerk/shared/deprecated'; import type { - Autocomplete, CheckAuthorizationWithCustomPermissions, + ClerkCustomFeatureKey, + ClerkCustomPlanKey, HandleOAuthCallbackParams, OrganizationCustomPermissionKey, OrganizationCustomRoleKey, @@ -83,7 +84,7 @@ export type ProtectProps = React.PropsWithChildren< condition?: never; role?: never; permission?: never; - feature: Autocomplete<`user:${string}` | `org:${string}`>; + feature: ClerkCustomFeatureKey; plan?: never; } | { @@ -91,7 +92,7 @@ export type ProtectProps = React.PropsWithChildren< role?: never; permission?: never; feature?: never; - plan: Autocomplete<`user:${string}` | `org:${string}`>; + plan: ClerkCustomPlanKey; } | { condition?: never; diff --git a/packages/types/src/organizationMembership.ts b/packages/types/src/organizationMembership.ts index d0350ed5def..035df542d55 100644 --- a/packages/types/src/organizationMembership.ts +++ b/packages/types/src/organizationMembership.ts @@ -7,13 +7,24 @@ import type { Autocomplete } from './utils'; interface Base { permission: string; role: string; + feature: string; + plan: string; } interface Placeholder { permission: unknown; role: unknown; + feature: unknown; + plan: unknown; } +// interface StructuredClerkAuthorization { +// permission?: `org:${string}`; +// role?: `org:${string}`; +// feature: `org:${string}` | `user:${string}`; +// plan: `org:${string}` | `user:${string}`; +// } + declare global { // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface ClerkAuthorization {} @@ -72,6 +83,56 @@ export type OrganizationCustomRoleKey = ClerkAuthorization extends Placeholder : Base['role'] : Base['role']; +// type ExtractAfterPrefix = T extends `${P}${infer Rest}` ? Rest : never; + +// type Util = T | ExtractAfterPrefix | ExtractAfterPrefix; + +// type ExtractAfterPrefix = T extends `${P}${infer Rest}` ? Rest : never; + +// type DistributeUnion = T extends any ? T : never; + +// type Util = DistributeUnion | ExtractAfterPrefix>; +// +// type Util = T | ExtractAfterPrefix | ExtractAfterPrefix; +// +// type InternalClerkCustomFeatureKey = ClerkAuthorization['feature'] + +// type ExtractAfterPrefix = T extends `${P}${infer Rest}` ? Rest : never; + +// type Util = T | ExtractAfterPrefix | ExtractAfterPrefix; + +// type InternalClerkCustomFeatureKey = Util; + +type ValidPrefix = 'user:' | 'org:'; +type ValidFeaturePattern = `${ValidPrefix}${string}`; + +type ExtractAfterPrefix = T extends `${P}${infer Rest}` ? Rest : never; + +type Util = T extends ValidFeaturePattern + ? T | ExtractAfterPrefix | ExtractAfterPrefix + : `Expected format: "user:" | "org:", but got "${T}"`; + +type ExtractMiddlePart = T extends `${'org:'}${infer Rest}:${string}` ? `org:${Rest}` : never; + +interface FakeClerkAuthorization { + // @ts-ignore + feature: Util | Util>; + // @ts-ignore + plan: Util; +} + +export type ClerkCustomFeatureKey = ClerkAuthorization extends Placeholder + ? ClerkAuthorization['feature'] extends string + ? FakeClerkAuthorization['feature'] + : Util + : Util; + +export type ClerkCustomPlanKey = ClerkAuthorization extends Placeholder + ? ClerkAuthorization['plan'] extends string + ? FakeClerkAuthorization['plan'] + : Util + : Util; + export type OrganizationSystemPermissionPrefix = 'org:sys_'; export type OrganizationSystemPermissionKey = | `${OrganizationSystemPermissionPrefix}domains:manage` diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 7642a9c64d3..aed568409f3 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -12,6 +12,8 @@ import type { } from './factors'; import type { ActClaim } from './jwtv2'; import type { + ClerkCustomFeatureKey, + ClerkCustomPlanKey, OrganizationCustomPermissionKey, OrganizationCustomRoleKey, OrganizationPermissionKey, @@ -26,7 +28,6 @@ import type { import type { SessionJSONSnapshot } from './snapshots'; import type { TokenResource } from './token'; import type { UserResource } from './user'; -import type { Autocomplete } from './utils'; /** * @inline @@ -70,14 +71,14 @@ export type CheckAuthorizationParamsWithCustomPermissions = WithReverification< | { role?: never; permission?: never; - feature: Autocomplete<`user:${string}` | `org:${string}`>; + feature: ClerkCustomFeatureKey; plan?: never; } | { role?: never; permission?: never; feature?: never; - plan: Autocomplete<`user:${string}` | `org:${string}`>; + plan: ClerkCustomPlanKey; } | { role?: never; permission?: never; feature?: never; plan?: never } >; @@ -100,14 +101,14 @@ type CheckAuthorizationParams = WithReverification< | { role?: never; permission?: never; - feature: Autocomplete<`user:${string}` | `org:${string}`>; + feature: ClerkCustomFeatureKey; plan?: never; } | { role?: never; permission?: never; feature?: never; - plan: Autocomplete<`user:${string}` | `org:${string}`>; + plan: ClerkCustomPlanKey; } | { role?: never; permission?: never; feature?: never; plan?: never } >; @@ -137,14 +138,14 @@ export type CheckAuthorizationParamsFromSessionClaims

; + feature: ClerkCustomFeatureKey; plan?: never; } | { role?: never; permission?: never; feature?: never; - plan: Autocomplete<`user:${string}` | `org:${string}`>; + plan: ClerkCustomPlanKey; } | { role?: never; permission?: never; feature?: never; plan?: never } >;