From 4a447b8f10a2e038f936c3fee3f3e5aa9754e029 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:35:08 -0700 Subject: [PATCH 01/51] Drop functions from property tables; add JSDoc comments --- .typedoc/custom-plugin.mjs | 5 ++ .typedoc/custom-theme.mjs | 119 ++++++++++++++++++++++++- packages/shared/src/types/apiKeys.ts | 48 ++++++++++ packages/shared/src/types/clerk.ts | 96 +++++++++++++++----- packages/shared/src/types/instance.ts | 3 + packages/shared/src/types/telemetry.ts | 3 + 6 files changed, 252 insertions(+), 22 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index d2f31f4270e..a4e5cbbb96f 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -99,6 +99,7 @@ const LINK_REPLACEMENTS = [ ['deleted-object-resource', '/docs/reference/types/deleted-object-resource'], ['checkout-flow-resource', '/docs/reference/hooks/use-checkout#checkout-flow-resource'], ['organization-creation-defaults-resource', '#organization-creation-defaults-resource'], + ['billing-namespace', '/docs/reference/objects/billing'], ]; /** @@ -127,6 +128,10 @@ function getRelativeLinkReplacements() { function getCatchAllReplacements() { return [ + { + pattern: /(?/g, replace: '[`Appearance`](/docs/guides/customizing-clerk/appearance-prop/overview)', diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index ec1c32fa2dd..68302fb4349 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1,5 +1,5 @@ // @ts-check -import { ReflectionKind, ReflectionType, UnionType } from 'typedoc'; +import { IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; /** @@ -48,6 +48,21 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, + /** + * Drop function-valued interface/class properties from the properties table (they remain TypeScript "Property" + * members because they use property syntax). Keeps data fields and object namespaces in the table. + */ + /** + * @param {import('typedoc').DeclarationReflection[]} model + * @param {Parameters[1]} [options] + */ + propertiesTable: (model, options) => { + if (!Array.isArray(model)) { + return superPartials.propertiesTable(/** @type {any} */ (model), options); + } + const filtered = model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)); + return superPartials.propertiesTable(filtered, options); + }, /** * This hides the "Type parameters" section and the signature title from the output (by default). Shows the signature title if the `@displayFunctionSignature` tag is present. * @param {import('typedoc').SignatureReflection} model @@ -637,3 +652,105 @@ function swap(arr, i, j) { arr[j] = t; return arr; } + +/** + * @param {import('typedoc').DeclarationReflection} prop + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + */ +function isCallableInterfaceProperty(prop, helpers) { + /** + * Use the declared value type for properties. `getDeclarationType` mirrors accessor/parameter behavior and can + * return the wrong node when TypeDoc attaches signatures to the property (same class of bug as TypeAlias + `decl.type`). + */ + const t = + (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type + ? prop.type + : helpers.getDeclarationType(prop); + return isCallablePropertyValueType(t, helpers, new Set()); +} + +/** + * True when the property's value type is callable (function type, union/intersection of callables, or reference to a + * type alias of a function type). Object types with properties (e.g. namespaces) stay false. + * + * @param {import('typedoc').Type | undefined} t + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + * @param {Set} seenReflectionIds + * @returns {boolean} + */ +function isCallablePropertyValueType(t, helpers, seenReflectionIds) { + if (!t) { + return false; + } + if (t.type === 'optional' && 'elementType' in t) { + return isCallablePropertyValueType( + /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType, + helpers, + seenReflectionIds, + ); + } + if (t instanceof UnionType) { + const nonNullish = t.types.filter( + u => !(u.type === 'intrinsic' && ['undefined', 'null'].includes(/** @type {{ name: string }} */ (u).name)), + ); + if (nonNullish.length === 0) { + return false; + } + return nonNullish.every(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); + } + if (t instanceof IntersectionType) { + return t.types.some(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); + } + if (t instanceof ReflectionType) { + const decl = t.declaration; + const callSigs = decl.signatures?.length ?? 0; + const hasProps = (decl.children?.length ?? 0) > 0; + const hasIndex = (decl.indexSignatures?.length ?? 0) > 0; + return callSigs > 0 && !hasProps && !hasIndex; + } + if (t instanceof ReferenceType) { + /** + * Unresolved reference (`reflection` missing): TypeDoc did not link the symbol (not in entry graph, external, + * filtered, etc.). We cannot tell a function alias from an interface, so we only treat a few **name** patterns as + * callable (`*Function`, `*Listener`). For anything else, ensure the type is part of the documented program so + * `reflection` resolves and the structural checks above apply — do not add one-off type names here. + * E.g. `CustomNavigation`, `RouterFn`, etc. + */ + if (!t.reflection && typeof t.name === 'string' && /(?:Function|Listener)$/.test(t.name)) { + return true; + } + const ref = t.reflection; + if (!ref) { + return false; + } + const refId = ref.id; + if (refId != null && seenReflectionIds.has(refId)) { + return false; + } + if (refId != null) { + seenReflectionIds.add(refId); + } + try { + const decl = /** @type {import('typedoc').DeclarationReflection} */ (ref); + /** + * For `type Fn = (a: T) => U`, TypeDoc may attach call signatures to the TypeAlias reflection. + * `getDeclarationType` then returns `signatures[0].type` (here `U`), not the full function type, so we + * mis-classify properties typed as that alias (e.g. `navigate: CustomNavigation`) as non-callable. + * Prefer `decl.type` (the full RHS) for type aliases. + */ + const typeToCheck = + decl.kind === ReflectionKind.TypeAlias && decl.type + ? decl.type + : (helpers.getDeclarationType(decl) ?? decl.type); + if (typeToCheck) { + return isCallablePropertyValueType(typeToCheck, helpers, seenReflectionIds); + } + } finally { + if (refId != null) { + seenReflectionIds.delete(refId); + } + } + return false; + } + return false; +} diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts index 09679504ecf..0cd1f6e8bc0 100644 --- a/packages/shared/src/types/apiKeys.ts +++ b/packages/shared/src/types/apiKeys.ts @@ -3,21 +3,69 @@ import type { ClerkPaginatedResponse } from './pagination'; import type { ClerkResource } from './resource'; export interface APIKeyResource extends ClerkResource { + /** + * A unique identifier for the API key. + */ id: string; + /** + * The type of the API key. + */ type: string; + /** + * The name of the API key. + */ name: string; + /** + * The user or organization ID that the API key is associated with. + */ subject: string; + /** + * An array of scopes that define what the API key can access. + */ scopes: string[]; + /** + * Custom claims associated with the API key, or `null` if none. + */ claims: Record | null; + /** + * Indicates whether the API key has been revoked. + */ revoked: boolean; + /** + * The reason the API key was revoked, or `null` if not revoked. + */ revocationReason: string | null; + /** + * Indicates whether the API key has expired. + */ expired: boolean; + /** + * The expiration date and time for the API key, or `null` if the key never expires. + */ expiration: Date | null; + /** + * The ID of the user that created the API key. + */ createdBy: string | null; + /** + * An optional description for the API key. + */ description: string | null; + /** + * The API key secret. **This property is only present in the response from [`create()`](https://clerk.com/docs/reference/objects/api-keys#create) and cannot be retrieved later.** + */ secret?: string; + /** + * The date and time when the API key was last used to authenticate a request, or `null` if it has never been used. + */ lastUsedAt: Date | null; + /** + * The date and time when the API key was created. + */ createdAt: Date; + /** + * The date and time when the API key was last updated. + */ updatedAt: Date; } diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 1a6921a6717..bb7584f5125 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -214,27 +214,27 @@ export type ClerkStatus = 'degraded' | 'error' | 'loading' | 'ready'; */ export interface Clerk { /** - * Clerk SDK version number. + * The Clerk SDK version number. */ version: string | undefined; /** * If present, contains information about the SDK that the host application is using. - * For example, if Clerk is loaded through `@clerk/nextjs`, this would be `{ name: '@clerk/nextjs', version: '1.0.0' }` + * For example, if Clerk is loaded through `@clerk/nextjs`, this would be `{ name: '@clerk/nextjs', version: '1.0.0' }`. You don't need to set this value yourself unless you're [developing an SDK](https://clerk.com/docs/guides/development/sdk-development/overview). */ sdkMetadata: SDKMetadata | undefined; /** - * If true the bootstrapping of Clerk.load() has completed successfully. + * Indicates if the `Clerk` object is ready for use. Set to `false` when the `status` is `"loading"`. Set to `true` when the `status` is `"ready"` or `"degraded"`. */ loaded: boolean; /** - * Describes the state the clerk singleton operates in: - * - `"error"`: Clerk failed to initialize. - * - `"loading"`: Clerk is still attempting to load. - * - `"ready"`: Clerk singleton is fully operational. - * - `"degraded"`: Clerk singleton is partially operational. + * The status of the `Clerk` instance. Possible values are: + * - `"error"`: Set when hotloading `clerk-js` or `Clerk.load()` failed. + * - `"loading"`: Set during initialization. + * - `"ready"`: Set when Clerk is fully operational. + * - `"degraded"`: Set when Clerk is partially operational. */ status: ClerkStatus; @@ -245,39 +245,42 @@ export interface Clerk { frontendApi: string; - /** Clerk Publishable Key string. */ + /** Your Clerk [Publishable Key](!publishable-key). */ publishableKey: string; - /** Clerk Proxy url string. */ + /** Your Clerk app's proxy URL. Required for applications that run behind a reverse proxy. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`). */ proxyUrl: string | undefined; - /** Clerk Satellite Frontend API string. */ + /** The current Clerk app's domain. Prefixed with `clerk.` on production if not already prefixed. Returns `""` when ran on the server. */ domain: string; - /** Clerk Flag for satellite apps. */ + /** Indicates if the instance is a satellite app. */ isSatellite: boolean; - /** Clerk Instance type is defined from the Publishable key */ + /** Indicates if the Clerk instance is running in a production or development environment. */ instanceType: InstanceType | undefined; - /** Clerk flag for loading Clerk in a standard browser setup */ + /** + * Indicates if the instance is being loaded in a standard browser environment. Set to `false` on native platforms where cookies cannot be set. When `undefined`, Clerk assumes a standard browser. + * @inline + */ isStandardBrowser: boolean | undefined; /** - * Indicates whether the current user has a valid signed-in client session + * Indicates whether the current user has a valid signed-in client session. */ isSignedIn: boolean; - /** Client handling most Clerk operations. */ + /** The `Client` object for the current window. */ client: ClientResource | undefined; - /** Current Session. */ + /** The currently active `Session`, which is guaranteed to be one of the sessions in `Client.sessions`. If there is no active session, this field will be `null`. If the session is loading, this field will be `undefined`. */ session: SignedInSessionResource | null | undefined; - /** Active Organization */ + /** A shortcut to the last active `Session.user.organizationMemberships` which holds an instance of a `Organization` object. If the session is `null` or `undefined`, the user field will match. */ organization: OrganizationResource | null | undefined; - /** Current User. */ + /** A shortcut to `Session.user` which holds the currently active `User` object. If the session is `null` or `undefined`, the user field will match. */ user: UserResource | null | undefined; /** @@ -291,17 +294,26 @@ export interface Clerk { * Entrypoint for Clerk's Signal API containing resource signals along with accessible versions of `computed()` and * `effect()` that can be used to subscribe to changes from Signals. * + * @internal * @experimental This experimental API is subject to change. */ __internal_state: State; /** + * The `Billing` object used for managing billing. + * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ billing: BillingNamespace; + /** + * [Telemetry](https://clerk.com/docs/guides/how-clerk-works/security/clerk-telemetry) configuration. + */ telemetry: TelemetryCollector | undefined; + /** + * @internal + */ __internal_country?: string | null; /** @@ -329,11 +341,13 @@ export interface Clerk { * Opens the Clerk Checkout component in a drawer. * * @param props - Optional checkout configuration parameters. + * @internal */ __internal_openCheckout: (props?: __internal_CheckoutProps) => void; /** * Closes the Clerk Checkout drawer. + * @internal */ __internal_closeCheckout: () => void; @@ -341,11 +355,13 @@ export interface Clerk { * Opens the Clerk PlanDetails drawer component in a drawer. * * @param props - `plan` or `planId` parameters are required. + * @internal */ __internal_openPlanDetails: (props: __internal_PlanDetailsProps) => void; /** * Closes the Clerk PlanDetails drawer. + * @internal */ __internal_closePlanDetails: () => void; @@ -353,11 +369,13 @@ export interface Clerk { * Opens the Clerk SubscriptionDetails drawer component in a drawer. * * @param props - Optional configuration parameters. + * @internal */ __internal_openSubscriptionDetails: (props?: __internal_SubscriptionDetailsProps) => void; /** * Closes the Clerk SubscriptionDetails drawer. + * @internal */ __internal_closeSubscriptionDetails: () => void; @@ -365,16 +383,19 @@ export interface Clerk { * Opens the Clerk UserVerification component in a modal. * * @param props - Optional user verification configuration parameters. + * @internal */ __internal_openReverification: (props?: __internal_UserVerificationModalProps) => void; /** * Closes the Clerk user verification modal. + * @internal */ __internal_closeReverification: () => void; /** * Attempts to enable a environment setting from a development instance, prompting if disabled. + * @internal */ __internal_attemptToEnableEnvironmentSetting: ( options: __internal_AttemptToEnableEnvironmentSettingParams, @@ -382,11 +403,13 @@ export interface Clerk { /** * Opens the Clerk Enable Organizations prompt for development instance + * @internal */ __internal_openEnableOrganizationsPrompt: (props: __internal_EnableOrganizationsPromptProps) => void; /** * Closes the Clerk Enable Organizations modal. + * @internal */ __internal_closeEnableOrganizationsPrompt: () => void; @@ -664,6 +687,7 @@ export interface Clerk { * * @param targetNode - Target node to mount the OAuth consent component. * @param oauthConsentProps - OAuth consent configuration parameters. + * @internal */ __internal_mountOAuthConsent: (targetNode: HTMLDivElement, oauthConsentProps?: __internal_OAuthConsentProps) => void; @@ -671,6 +695,7 @@ export interface Clerk { * Unmounts a OAuth consent component from the target element. * * @param targetNode - Target node to unmount the OAuth consent component from. + * @internal */ __internal_unmountOAuthConsent: (targetNode: HTMLDivElement) => void; @@ -779,7 +804,7 @@ export interface Clerk { setActive: SetActive; /** - * Function used to commit a navigation after certain steps in the Clerk processes. + * A function used to commit a navigation after certain steps in the Clerk processes. */ navigate: CustomNavigation; @@ -1019,11 +1044,13 @@ export interface Clerk { /** * Internal flag indicating whether a `setActive` call is in progress. Used to prevent navigations from being * initiated outside of the Clerk class. + * + * @internal */ __internal_setActiveInProgress: boolean; /** - * API Keys Object + * The `APIKeys` object used for managing API keys. */ apiKeys: APIKeysNamespace; @@ -1088,6 +1115,9 @@ export type HandleOAuthCallbackParams = TransferableOption & export type HandleSamlCallbackParams = HandleOAuthCallbackParams; +/** + * A function used to navigate to a given URL after certain steps in the Clerk processes. + */ export type CustomNavigation = (to: string, options?: NavigateOptions) => Promise | void; export type ClerkThemeOptions = DeepSnakeToCamel>; @@ -2118,19 +2148,43 @@ export type APIKeysProps = { }; export type GetAPIKeysParams = ClerkPaginationParams<{ + /** + * The user or organization ID to query API keys by. If not provided, defaults to the [Active Organization](!active-organization), then the current User. + */ subject?: string; + /** + * A search query to filter API keys by name. + */ query?: string; }>; export type CreateAPIKeyParams = { + /** + * The name of the API key. + */ name: string; + /** + * The user or organization ID to associate the API key with. If not provided, defaults to the [Active Organization](!active-organization), then the current User. + */ subject?: string; + /** + * The number of seconds until the API key expires. Set to `null` or omit to create a key that never expires. + */ secondsUntilExpiration?: number; + /** + * The description of the API key. + */ description?: string; }; export type RevokeAPIKeyParams = { + /** + * The ID of the API key to revoke. + */ apiKeyID: string; + /** + * The reason for revoking the API key. + */ revocationReason?: string; }; diff --git a/packages/shared/src/types/instance.ts b/packages/shared/src/types/instance.ts index 617799635de..921926dd885 100644 --- a/packages/shared/src/types/instance.ts +++ b/packages/shared/src/types/instance.ts @@ -1 +1,4 @@ +/** + * @inline + */ export type InstanceType = 'production' | 'development'; diff --git a/packages/shared/src/types/telemetry.ts b/packages/shared/src/types/telemetry.ts index bc518207818..14643c3ea65 100644 --- a/packages/shared/src/types/telemetry.ts +++ b/packages/shared/src/types/telemetry.ts @@ -57,6 +57,9 @@ export interface TelemetryLogEntry { readonly userId?: string; } +/** + * @inline + */ export interface TelemetryCollector { isEnabled: boolean; isDebug: boolean; From 91009769986f874cc9704e53bef62349faad783b Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:29:29 -0700 Subject: [PATCH 02/51] undo changes about removing functions from property tables --- .typedoc/custom-theme.mjs | 119 +------------------------------------- 1 file changed, 1 insertion(+), 118 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 68302fb4349..ec1c32fa2dd 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1,5 +1,5 @@ // @ts-check -import { IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; +import { ReflectionKind, ReflectionType, UnionType } from 'typedoc'; import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; /** @@ -48,21 +48,6 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, - /** - * Drop function-valued interface/class properties from the properties table (they remain TypeScript "Property" - * members because they use property syntax). Keeps data fields and object namespaces in the table. - */ - /** - * @param {import('typedoc').DeclarationReflection[]} model - * @param {Parameters[1]} [options] - */ - propertiesTable: (model, options) => { - if (!Array.isArray(model)) { - return superPartials.propertiesTable(/** @type {any} */ (model), options); - } - const filtered = model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)); - return superPartials.propertiesTable(filtered, options); - }, /** * This hides the "Type parameters" section and the signature title from the output (by default). Shows the signature title if the `@displayFunctionSignature` tag is present. * @param {import('typedoc').SignatureReflection} model @@ -652,105 +637,3 @@ function swap(arr, i, j) { arr[j] = t; return arr; } - -/** - * @param {import('typedoc').DeclarationReflection} prop - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers - */ -function isCallableInterfaceProperty(prop, helpers) { - /** - * Use the declared value type for properties. `getDeclarationType` mirrors accessor/parameter behavior and can - * return the wrong node when TypeDoc attaches signatures to the property (same class of bug as TypeAlias + `decl.type`). - */ - const t = - (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type - ? prop.type - : helpers.getDeclarationType(prop); - return isCallablePropertyValueType(t, helpers, new Set()); -} - -/** - * True when the property's value type is callable (function type, union/intersection of callables, or reference to a - * type alias of a function type). Object types with properties (e.g. namespaces) stay false. - * - * @param {import('typedoc').Type | undefined} t - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers - * @param {Set} seenReflectionIds - * @returns {boolean} - */ -function isCallablePropertyValueType(t, helpers, seenReflectionIds) { - if (!t) { - return false; - } - if (t.type === 'optional' && 'elementType' in t) { - return isCallablePropertyValueType( - /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType, - helpers, - seenReflectionIds, - ); - } - if (t instanceof UnionType) { - const nonNullish = t.types.filter( - u => !(u.type === 'intrinsic' && ['undefined', 'null'].includes(/** @type {{ name: string }} */ (u).name)), - ); - if (nonNullish.length === 0) { - return false; - } - return nonNullish.every(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); - } - if (t instanceof IntersectionType) { - return t.types.some(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); - } - if (t instanceof ReflectionType) { - const decl = t.declaration; - const callSigs = decl.signatures?.length ?? 0; - const hasProps = (decl.children?.length ?? 0) > 0; - const hasIndex = (decl.indexSignatures?.length ?? 0) > 0; - return callSigs > 0 && !hasProps && !hasIndex; - } - if (t instanceof ReferenceType) { - /** - * Unresolved reference (`reflection` missing): TypeDoc did not link the symbol (not in entry graph, external, - * filtered, etc.). We cannot tell a function alias from an interface, so we only treat a few **name** patterns as - * callable (`*Function`, `*Listener`). For anything else, ensure the type is part of the documented program so - * `reflection` resolves and the structural checks above apply — do not add one-off type names here. - * E.g. `CustomNavigation`, `RouterFn`, etc. - */ - if (!t.reflection && typeof t.name === 'string' && /(?:Function|Listener)$/.test(t.name)) { - return true; - } - const ref = t.reflection; - if (!ref) { - return false; - } - const refId = ref.id; - if (refId != null && seenReflectionIds.has(refId)) { - return false; - } - if (refId != null) { - seenReflectionIds.add(refId); - } - try { - const decl = /** @type {import('typedoc').DeclarationReflection} */ (ref); - /** - * For `type Fn = (a: T) => U`, TypeDoc may attach call signatures to the TypeAlias reflection. - * `getDeclarationType` then returns `signatures[0].type` (here `U`), not the full function type, so we - * mis-classify properties typed as that alias (e.g. `navigate: CustomNavigation`) as non-callable. - * Prefer `decl.type` (the full RHS) for type aliases. - */ - const typeToCheck = - decl.kind === ReflectionKind.TypeAlias && decl.type - ? decl.type - : (helpers.getDeclarationType(decl) ?? decl.type); - if (typeToCheck) { - return isCallablePropertyValueType(typeToCheck, helpers, seenReflectionIds); - } - } finally { - if (refId != null) { - seenReflectionIds.delete(refId); - } - } - return false; - } - return false; -} From ea3d2eb6f20f83cfc4ac8090380dbcb27aa6b6ab Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:34:22 -0700 Subject: [PATCH 03/51] Revert "undo changes about removing functions from property tables" This reverts commit 91009769986f874cc9704e53bef62349faad783b. --- .typedoc/custom-theme.mjs | 119 +++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index ec1c32fa2dd..68302fb4349 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1,5 +1,5 @@ // @ts-check -import { ReflectionKind, ReflectionType, UnionType } from 'typedoc'; +import { IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; /** @@ -48,6 +48,21 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, + /** + * Drop function-valued interface/class properties from the properties table (they remain TypeScript "Property" + * members because they use property syntax). Keeps data fields and object namespaces in the table. + */ + /** + * @param {import('typedoc').DeclarationReflection[]} model + * @param {Parameters[1]} [options] + */ + propertiesTable: (model, options) => { + if (!Array.isArray(model)) { + return superPartials.propertiesTable(/** @type {any} */ (model), options); + } + const filtered = model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)); + return superPartials.propertiesTable(filtered, options); + }, /** * This hides the "Type parameters" section and the signature title from the output (by default). Shows the signature title if the `@displayFunctionSignature` tag is present. * @param {import('typedoc').SignatureReflection} model @@ -637,3 +652,105 @@ function swap(arr, i, j) { arr[j] = t; return arr; } + +/** + * @param {import('typedoc').DeclarationReflection} prop + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + */ +function isCallableInterfaceProperty(prop, helpers) { + /** + * Use the declared value type for properties. `getDeclarationType` mirrors accessor/parameter behavior and can + * return the wrong node when TypeDoc attaches signatures to the property (same class of bug as TypeAlias + `decl.type`). + */ + const t = + (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type + ? prop.type + : helpers.getDeclarationType(prop); + return isCallablePropertyValueType(t, helpers, new Set()); +} + +/** + * True when the property's value type is callable (function type, union/intersection of callables, or reference to a + * type alias of a function type). Object types with properties (e.g. namespaces) stay false. + * + * @param {import('typedoc').Type | undefined} t + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + * @param {Set} seenReflectionIds + * @returns {boolean} + */ +function isCallablePropertyValueType(t, helpers, seenReflectionIds) { + if (!t) { + return false; + } + if (t.type === 'optional' && 'elementType' in t) { + return isCallablePropertyValueType( + /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType, + helpers, + seenReflectionIds, + ); + } + if (t instanceof UnionType) { + const nonNullish = t.types.filter( + u => !(u.type === 'intrinsic' && ['undefined', 'null'].includes(/** @type {{ name: string }} */ (u).name)), + ); + if (nonNullish.length === 0) { + return false; + } + return nonNullish.every(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); + } + if (t instanceof IntersectionType) { + return t.types.some(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); + } + if (t instanceof ReflectionType) { + const decl = t.declaration; + const callSigs = decl.signatures?.length ?? 0; + const hasProps = (decl.children?.length ?? 0) > 0; + const hasIndex = (decl.indexSignatures?.length ?? 0) > 0; + return callSigs > 0 && !hasProps && !hasIndex; + } + if (t instanceof ReferenceType) { + /** + * Unresolved reference (`reflection` missing): TypeDoc did not link the symbol (not in entry graph, external, + * filtered, etc.). We cannot tell a function alias from an interface, so we only treat a few **name** patterns as + * callable (`*Function`, `*Listener`). For anything else, ensure the type is part of the documented program so + * `reflection` resolves and the structural checks above apply — do not add one-off type names here. + * E.g. `CustomNavigation`, `RouterFn`, etc. + */ + if (!t.reflection && typeof t.name === 'string' && /(?:Function|Listener)$/.test(t.name)) { + return true; + } + const ref = t.reflection; + if (!ref) { + return false; + } + const refId = ref.id; + if (refId != null && seenReflectionIds.has(refId)) { + return false; + } + if (refId != null) { + seenReflectionIds.add(refId); + } + try { + const decl = /** @type {import('typedoc').DeclarationReflection} */ (ref); + /** + * For `type Fn = (a: T) => U`, TypeDoc may attach call signatures to the TypeAlias reflection. + * `getDeclarationType` then returns `signatures[0].type` (here `U`), not the full function type, so we + * mis-classify properties typed as that alias (e.g. `navigate: CustomNavigation`) as non-callable. + * Prefer `decl.type` (the full RHS) for type aliases. + */ + const typeToCheck = + decl.kind === ReflectionKind.TypeAlias && decl.type + ? decl.type + : (helpers.getDeclarationType(decl) ?? decl.type); + if (typeToCheck) { + return isCallablePropertyValueType(typeToCheck, helpers, seenReflectionIds); + } + } finally { + if (refId != null) { + seenReflectionIds.delete(refId); + } + } + return false; + } + return false; +} From 0292f5fec0b4be3de8a439d0cc9aac10372124fc Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:41:36 -0700 Subject: [PATCH 04/51] only remove functions from property tables if output page listed in allowlist --- .typedoc/custom-theme.mjs | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 68302fb4349..779dce24a3c 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -29,6 +29,28 @@ class ClerkMarkdownTheme extends MarkdownTheme { */ const unionCommentMap = new Map(); +/** + * Only for these output pages do we remove function-valued members from **property** tables. + * Match {@link import('typedoc-plugin-markdown').MarkdownPageEvent#url} as TypeDoc emits it (relative to `out`), + * e.g. `shared/clerk.mdx` — not the path after `cpy` to `.typedoc/docs` (that does not change `page.url`). + */ +const PROPERTY_TABLE_EXCLUDE_FUNCTIONS_ALLOWLIST = ['shared/clerk.mdx']; + +/** + * @param {string | undefined} pageUrl + * @param {readonly string[]} allowlist + */ +function pageMatchesPropertyTableFunctionFilterAllowlist(pageUrl, allowlist) { + if (!pageUrl) { + return false; + } + const normalized = pageUrl.replace(/\\/g, '/').replace(/^\.\//, ''); + return allowlist.some(entry => { + const e = entry.replace(/\\/g, '/').replace(/^\/+/, ''); + return normalized === e || normalized.endsWith(`/${e}`) || normalized.endsWith(e); + }); +} + /** * Our custom Clerk theme * @extends MarkdownThemeContext @@ -49,10 +71,9 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, /** - * Drop function-valued interface/class properties from the properties table (they remain TypeScript "Property" - * members because they use property syntax). Keeps data fields and object namespaces in the table. - */ - /** + * On allowlisted output pages only (see `PROPERTY_TABLE_EXCLUDE_FUNCTIONS_ALLOWLIST`): drop function-valued + * interface/class properties from property tables (property syntax with function types). Other pages unchanged. + * * @param {import('typedoc').DeclarationReflection[]} model * @param {Parameters[1]} [options] */ @@ -60,7 +81,11 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { if (!Array.isArray(model)) { return superPartials.propertiesTable(/** @type {any} */ (model), options); } - const filtered = model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)); + const allowlisted = pageMatchesPropertyTableFunctionFilterAllowlist( + this.page?.url, + PROPERTY_TABLE_EXCLUDE_FUNCTIONS_ALLOWLIST, + ); + const filtered = allowlisted ? model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)) : model; return superPartials.propertiesTable(filtered, options); }, /** From 8b9b1d443d596a6a3063f5fc771942ac4f963d7f Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:55:25 -0700 Subject: [PATCH 05/51] begin building extract-methods.mjs --- .typedoc/custom-plugin.mjs | 4 + .typedoc/custom-theme.mjs | 22 +- .typedoc/extract-methods.mjs | 578 +++++++++++++++++++++++ .typedoc/reference-objects.mjs | 15 + package.json | 2 +- packages/clerk-js/src/core/clerk.ts | 8 + packages/shared/src/types/clerk.ts | 373 +++++++++++---- packages/shared/src/types/client.ts | 81 +++- packages/shared/src/types/multiDomain.ts | 7 +- packages/shared/src/types/redirects.ts | 6 +- packages/shared/src/types/waitlist.ts | 3 + turbo.json | 8 +- 12 files changed, 1006 insertions(+), 101 deletions(-) create mode 100644 .typedoc/extract-methods.mjs create mode 100644 .typedoc/reference-objects.mjs diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index a4e5cbbb96f..90437859175 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -219,6 +219,10 @@ function getCatchAllReplacements() { replace: '[`OrganizationMembershipPublicMetadata`](/docs/reference/types/metadata#organization-membership-public-metadata)', }, + { + pattern: /`RedirectOptions`/g, + replace: '[`RedirectOptions`](/docs/reference/types/redirect-options)', + }, { pattern: /(? !isCallableInterfaceProperty(prop, this.helpers)) : model; return superPartials.propertiesTable(filtered, options); }, diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs new file mode 100644 index 00000000000..deff922d20c --- /dev/null +++ b/.typedoc/extract-methods.mjs @@ -0,0 +1,578 @@ +// @ts-check +/** + * For each entry in REFERENCE_OBJECTS_LIST, finds callable members on the mapped interface/class via TypeDoc + * and writes one .mdx per method (kebab file name) next to the main reference page output. + * + * Run after `typedoc` (same cwd as repo root). Uses a second TypeDoc convert pass to read reflections. + */ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { Application, Comment, ReflectionKind } from 'typedoc'; + +import typedocConfig from '../typedoc.config.mjs'; +import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_PAGE_SYMBOLS } from './reference-objects.mjs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * TypeDoc `code` display parts often already include backticks (same as {@link Comment.combineDisplayParts}). + * Wrapping again would produce `` `Client` `` in MDX. + * + * @param {string} text + */ +function codeDisplayPartToMarkdown(text) { + const trimmed = text.trim(); + if (trimmed.length >= 2 && trimmed.startsWith('`') && trimmed.endsWith('`')) { + return trimmed; + } + return `\`${text}\``; +} + +/** + * @param {import('typedoc').CommentDisplayPart[] | undefined} parts + */ +function displayPartsToString(parts) { + if (!parts?.length) { + return ''; + } + return parts + .map(p => { + if (p.kind === 'text') { + return p.text; + } + if (p.kind === 'code') { + return codeDisplayPartToMarkdown(p.text); + } + if (p.kind === 'inline-tag') { + return p.text; + } + if (p.kind === 'relative-link') { + return p.text; + } + return ''; + }) + .join(''); +} + +/** + * @param {import('typedoc').ProjectReflection} project + * @param {string} name + * @param {string} [sourcePathHint] e.g. `types/clerk` + */ +function findInterfaceOrClass(project, name, sourcePathHint) { + /** @type {import('typedoc').DeclarationReflection[]} */ + const candidates = []; + for (const r of Object.values(project.reflections)) { + if (r.name !== name) { + continue; + } + if (!r.kindOf(ReflectionKind.Interface) && !r.kindOf(ReflectionKind.Class)) { + continue; + } + candidates.push(/** @type {import('typedoc').DeclarationReflection} */ (r)); + } + if (candidates.length === 0) { + return undefined; + } + if (candidates.length === 1) { + return candidates[0]; + } + if (sourcePathHint) { + const hit = candidates.find(c => c.sources?.some(s => s.fileName.replace(/\\/g, '/').includes(sourcePathHint))); + if (hit) { + return hit; + } + } + return candidates[0]; +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + * @returns {import('typedoc').SignatureReflection | undefined} + */ +function getPrimaryCallSignature(decl) { + if (decl.signatures?.length) { + return decl.signatures[0]; + } + const t = decl.type; + if (t && 'declaration' in t && t.declaration?.signatures?.length) { + return t.declaration.signatures[0]; + } + return undefined; +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + */ +function isCallableMember(decl) { + if (decl.kind === ReflectionKind.Method) { + return true; + } + if (decl.kind === ReflectionKind.Property) { + return !!getPrimaryCallSignature(decl); + } + return false; +} + +/** + * @param {string} name + */ +function toKebabCase(name) { + return name + .replace(/([a-z\d])([A-Z])/g, '$1-$2') + .replace(/[\s_]+/g, '-') + .toLowerCase(); +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {string} memberName + */ +function formatTypeScriptSignature(sig, memberName) { + const typeParams = sig.typeParameters?.map(tp => tp.name).join(', ') ?? ''; + const typeParamStr = typeParams ? `<${typeParams}>` : ''; + const params = + sig.parameters?.map(p => { + const opt = p.flags.isOptional ? '?' : ''; + const rest = p.flags.isRest ? '...' : ''; + const typeStr = p.type ? p.type.toString() : 'unknown'; + return `${rest}${p.name}${opt}: ${typeStr}`; + }) ?? []; + const ret = sig.type ? sig.type.toString() : 'void'; + return `function ${memberName}${typeParamStr}(${params.join(', ')}): ${ret}`; +} + +/** + * `@returns - foo` is often stored with a leading dash, which renders as a bullet. Normalize to prose for + * "Returns …" lines. + * @param {string} body + */ +function normalizeReturnsBody(body) { + return body.replace(/^\s*[-*]\s+/, '').trim(); +} + +/** + * Lowercase the first character so the line reads "Returns an …" not "Returns An …". + * @param {string} body + */ +function lowercaseFirstCharacter(body) { + if (!body) { + return body; + } + return body.charAt(0).toLowerCase() + body.slice(1); +} + +/** + * @param {import('typedoc').CommentTag} tag + */ +function formatReturnsLineFromTag(tag) { + const raw = Comment.combineDisplayParts(tag.content).trim(); + if (!raw) { + return ''; + } + const body = lowercaseFirstCharacter(normalizeReturnsBody(raw)); + return `Returns ${body}`; +} + +/** + * @param {import('typedoc').Comment | undefined} comment + */ +function commentSummaryAndBody(comment) { + if (!comment) { + return ''; + } + const summary = displayPartsToString(comment.summary).trim(); + const block = comment.blockTags + ?.filter(t => !['@param', '@typeParam', '@returns'].includes(t.tag)) + .map(t => displayPartsToString(t.content).trim()) + .filter(Boolean) + .join('\n\n'); + const returnsLines = + comment.blockTags + ?.filter(t => t.tag === '@returns') + .map(t => formatReturnsLineFromTag(t)) + .filter(Boolean) ?? []; + return [summary, block, ...returnsLines].filter(Boolean).join('\n\n'); +} + +/** + * When `@returns` exists only on the call signature (not on the declaration), append it to the prose. + * @param {import('typedoc').Comment | undefined} declComment + * @param {import('typedoc').Comment | undefined} sigComment + */ +function appendSignatureOnlyReturns(declComment, sigComment) { + if (declComment?.getTag('@returns')?.content?.length) { + return ''; + } + const tag = sigComment?.getTag('@returns'); + if (!tag?.content?.length) { + return ''; + } + return formatReturnsLineFromTag(tag); +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {import('typedoc').ParameterReflection} param + * @param {import('typedoc').DeclarationReflection} decl + */ +function getParamDescription(sig, param, decl) { + if (param.comment?.summary?.length) { + return displayPartsToString(param.comment.summary).trim(); + } + const tag = + sig.comment?.getIdentifiedTag(param.name, '@param') ?? decl.comment?.getIdentifiedTag(param.name, '@param'); + if (tag?.content?.length) { + return Comment.combineDisplayParts(tag.content).trim(); + } + return ''; +} + +/** + * Object / type-literal declaration for a parameter type (reference, inlined reflection, intersection). + * TypeDoc applies `@param parent.prop` descriptions onto property reflections under this declaration. + * + * @param {import('typedoc').SomeType | undefined} t + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function resolveDeclarationWithObjectMembers(t) { + if (!t) { + return undefined; + } + if (t.type === 'reflection') { + const d = t.declaration; + if (d.children?.length) { + return d; + } + } + if (t.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + const r = ref.reflection; + if (r && 'type' in r) { + const decl = /** @type {import('typedoc').DeclarationReflection} */ (r); + if (decl.children?.length) { + return decl; + } + if (decl.type) { + return resolveDeclarationWithObjectMembers(decl.type); + } + } + } + if (t.type === 'intersection') { + for (const inner of /** @type {import('typedoc').IntersectionType} */ (t).types) { + const res = resolveDeclarationWithObjectMembers(inner); + if (res) { + return res; + } + } + } + if (t.type === 'optional') { + return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType); + } + return undefined; +} + +/** + * @param {string} baseName + * @param {string[]} pathSegments + * @param {boolean} parentOptional + */ +function formatNestedParamNameColumn(baseName, pathSegments, parentOptional) { + const chain = pathSegments.join('.'); + const inner = parentOptional ? `${baseName}?.${chain}` : `${baseName}.${chain}`; + return `\`${inner}\``; +} + +/** + * Rows for object properties that have documentation (including from `@param parent.prop` on the method), + * which TypeDoc stores on property reflections rather than leaving `@param` block tags on the signature. + * + * @param {import('typedoc').ParameterReflection} param + * @returns {string[]} + */ +function nestedParameterRowsFromDocumentedProperties(param) { + const holder = resolveDeclarationWithObjectMembers(param.type); + if (!holder?.children?.length) { + return []; + } + const props = holder.children.filter(c => c.kindOf(ReflectionKind.Property) && c.comment?.summary?.length); + props.sort((a, b) => a.name.localeCompare(b.name)); + /** @type {string[]} */ + const rows = []; + for (const child of props) { + const summary = child.comment?.summary; + if (!summary?.length) { + continue; + } + const nestedTypeRaw = child.type?.toString(); + const nestedTypeStr = nestedTypeRaw ? `\`${nestedTypeRaw.replace(/\|/g, '\\|')}\`` : '`unknown`'; + const nestedNameCol = formatNestedParamNameColumn(param.name, [child.name], param.flags.isOptional); + const nestedDesc = displayPartsToString(summary).trim() || '—'; + rows.push(`| ${nestedNameCol} | ${nestedTypeStr} | ${nestedDesc} |`); + } + return rows; +} + +/** + * Merged / external references sometimes leave {@link ReferenceType.reflection} unset; resolve by name. + * + * @param {import('typedoc').ProjectReflection} project + * @param {string} name + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function lookupInterfaceOrTypeAliasByName(project, name) { + /** @type {import('typedoc').DeclarationReflection[]} */ + const cands = []; + for (const r of Object.values(project.reflections)) { + if (r.name !== name) { + continue; + } + if (!r.kindOf(ReflectionKind.Interface) && !r.kindOf(ReflectionKind.TypeAlias)) { + continue; + } + cands.push(/** @type {import('typedoc').DeclarationReflection} */ (r)); + } + if (cands.length === 0) { + return undefined; + } + if (cands.length === 1) { + return cands[0]; + } + const withChildren = cands.find(c => c.children?.length); + return withChildren ?? cands[0]; +} + +/** + * Unwrap optional wrappers. When the parameter is a single named interface or type alias for an object + * shape, returns that name and the declaration holding object properties. + * + * @param {import('typedoc').SomeType | undefined} t + * @param {import('typedoc').ProjectReflection} project + * @returns {{ sectionTitle: string, holder: import('typedoc').DeclarationReflection, typeDecl: import('typedoc').DeclarationReflection } | undefined} + */ +function resolveNominalObjectTypeForSingleParam(t, project) { + if (!t) { + return undefined; + } + if (t.type === 'optional') { + return resolveNominalObjectTypeForSingleParam( + /** @type {import('typedoc').OptionalType} */ (t).elementType, + project, + ); + } + if (t.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + let typeDecl = + ref.reflection && 'kind' in ref.reflection + ? /** @type {import('typedoc').DeclarationReflection} */ (ref.reflection) + : lookupInterfaceOrTypeAliasByName(project, ref.name); + if (!typeDecl) { + return undefined; + } + if (typeDecl.kindOf(ReflectionKind.Interface)) { + if (!typeDecl.children?.length) { + return undefined; + } + return { sectionTitle: typeDecl.name, holder: typeDecl, typeDecl }; + } + if (typeDecl.kindOf(ReflectionKind.TypeAlias)) { + const holder = resolveDeclarationWithObjectMembers(typeDecl.type); + if (!holder?.children?.length) { + return undefined; + } + return { sectionTitle: typeDecl.name, holder, typeDecl }; + } + } + return undefined; +} + +/** + * @param {import('typedoc').DeclarationReflection} typeDecl + * @param {import('typedoc').DeclarationReflection[]} props + */ +function isNominalParamTypeDocumented(typeDecl, props) { + if (typeDecl.comment?.summary?.length) { + return true; + } + return props.some(p => p.comment?.summary?.length); +} + +/** + * @param {import('typedoc').DeclarationReflection} holder + * @returns {string[]} + */ +function propertyTableRowsForDeclaration(holder) { + const props = (holder.children ?? []).filter(c => c.kindOf(ReflectionKind.Property)); + props.sort((a, b) => a.name.localeCompare(b.name)); + /** @type {string[]} */ + const rows = []; + for (const child of props) { + const opt = child.flags.isOptional ? '?' : ''; + const nameCol = `\`${child.name}${opt}\``; + const nestedTypeRaw = child.type?.toString(); + const nestedTypeStr = nestedTypeRaw ? `\`${nestedTypeRaw.replace(/\|/g, '\\|')}\`` : '`unknown`'; + const nestedDesc = child.comment?.summary?.length ? displayPartsToString(child.comment.summary).trim() : '—'; + rows.push(`| ${nameCol} | ${nestedTypeStr} | ${nestedDesc} |`); + } + return rows; +} + +/** + * Single parameter that is a named object type (interface / type alias): one section titled after the type, + * table lists every property (not the outer `params` row). + * + * @param {import('typedoc').SignatureReflection} sig + * @returns {string | undefined} + */ +function trySingleNominalParameterTypeSection(sig) { + const params = sig.parameters ?? []; + if (params.length !== 1) { + return undefined; + } + const p = params[0]; + const nominal = resolveNominalObjectTypeForSingleParam(p.type, sig.project); + if (!nominal) { + return undefined; + } + const props = (nominal.holder.children ?? []).filter(c => c.kindOf(ReflectionKind.Property)); + if (props.length === 0) { + return undefined; + } + if (!isNominalParamTypeDocumented(nominal.typeDecl, props)) { + return undefined; + } + const rows = propertyTableRowsForDeclaration(nominal.holder); + if (rows.length === 0) { + return undefined; + } + return [`#### ${nominal.sectionTitle}`, '', '| Name | Type | Description |', '| --- | --- | --- |', ...rows, ''].join( + '\n', + ); +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {import('typedoc').DeclarationReflection} decl + */ +function parametersMarkdownTable(sig, decl) { + const params = sig.parameters ?? []; + if (params.length === 0) { + return ''; + } + + const singleNominal = trySingleNominalParameterTypeSection(sig); + if (singleNominal) { + return singleNominal; + } + + /** @type {string[]} */ + const rows = []; + for (const p of params) { + const typeStr = p.type ? `\`${p.type.toString().replace(/\|/g, '\\|')}\`` : '`unknown`'; + const opt = p.flags.isOptional ? '?' : ''; + const nameCol = `\`${p.name}${opt}\``; + const desc = getParamDescription(sig, p, decl); + rows.push(`| ${nameCol} | ${typeStr} | ${desc || '—'} |`); + rows.push(...nestedParameterRowsFromDocumentedProperties(p)); + } + + return ['#### Parameters', '', '| Name | Type | Description |', '| --- | --- | --- |', ...rows, ''].join('\n'); +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + */ +function buildMethodMdx(decl) { + const name = decl.name; + const sig = getPrimaryCallSignature(decl); + if (!sig) { + return ''; + } + const title = `### \`${name}()\``; + /** Prefer the declaration comment (property-style methods document `addListener` on the property, not the signature). */ + const comment = decl.comment ?? sig.comment; + let description = commentSummaryAndBody(comment); + const sigReturns = appendSignatureOnlyReturns(decl.comment, sig.comment); + if (sigReturns) { + description = [description, sigReturns].filter(Boolean).join('\n\n'); + } + const ts = ['```typescript', formatTypeScriptSignature(sig, name), '```'].join('\n'); + const paramsMd = parametersMarkdownTable(sig, decl); + + const parts = [title, '', description, '', ts, '']; + if (paramsMd) { + parts.push(paramsMd); + } + return parts.join('\n').trim() + '\n'; +} + +/** + * @param {string} pageUrl + * @param {import('typedoc').ProjectReflection} project + */ +function extractMethodsForPage(pageUrl, project) { + const symbol = /** @type {Record} */ (/** @type {unknown} */ (REFERENCE_OBJECT_PAGE_SYMBOLS))[ + pageUrl + ]; + if (!symbol) { + console.warn(`[extract-methods] No symbol mapping for ${pageUrl}, skipping`); + return 0; + } + + const hint = symbol === 'Clerk' ? 'types/clerk' : symbol === 'ClientResource' ? 'types/client' : undefined; + const decl = findInterfaceOrClass(project, symbol, hint); + if (!decl?.children) { + console.warn(`[extract-methods] Could not find interface/class "${symbol}"`); + return 0; + } + + const outDir = path.join(__dirname, 'temp-docs', path.dirname(pageUrl), `${path.basename(pageUrl, '.mdx')}-methods`); + fs.mkdirSync(outDir, { recursive: true }); + + let count = 0; + for (const child of decl.children) { + if (child.name.startsWith('__')) { + continue; + } + if (!isCallableMember(/** @type {import('typedoc').DeclarationReflection} */ (child))) { + continue; + } + const mdx = buildMethodMdx(/** @type {import('typedoc').DeclarationReflection} */ (child)); + if (!mdx) { + continue; + } + const fileName = `${toKebabCase(child.name)}.mdx`; + const filePath = path.join(outDir, fileName); + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } + return count; +} + +async function main() { + const app = await Application.bootstrapWithPlugins({ + ...typedocConfig, + // Avoid writing markdown twice; we only need reflections. + out: path.join(__dirname, 'temp-docs-unused'), + }); + + const project = await app.convert(); + if (!project) { + console.error('[extract-methods] TypeDoc conversion failed'); + process.exit(1); + } + + let total = 0; + for (const pageUrl of REFERENCE_OBJECTS_LIST) { + total += extractMethodsForPage(pageUrl, project); + } + console.log(`[extract-methods] Wrote ${total} method files total`); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs new file mode 100644 index 00000000000..db12e70bbb7 --- /dev/null +++ b/.typedoc/reference-objects.mjs @@ -0,0 +1,15 @@ +// @ts-check +/** + * Shared between the markdown theme and extract-methods.mjs. + * `page.url` values are relative to TypeDoc `out` (e.g. `.typedoc/temp-docs`). + */ + +export const REFERENCE_OBJECTS_LIST = ['shared/clerk.mdx', 'shared/client-resource.mdx']; + +/** + * Primary interface/class documented on each reference object page (used to resolve TypeDoc reflections). + */ +export const REFERENCE_OBJECT_PAGE_SYMBOLS = { + 'shared/clerk.mdx': 'Clerk', + 'shared/client-resource.mdx': 'ClientResource', +}; diff --git a/package.json b/package.json index 69259d945cb..01bca99d149 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "test:typedoc": "pnpm typedoc:generate && cd ./.typedoc && vitest run", "turbo:clean": "turbo daemon clean", "typedoc:generate": "pnpm build && pnpm typedoc:generate:skip-build", - "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && node .typedoc/extract-returns-and-params.mjs && rimraf .typedoc/docs && cpy '.typedoc/temp-docs/**' '.typedoc/docs' && rimraf .typedoc/temp-docs", + "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && node .typedoc/extract-returns-and-params.mjs && node .typedoc/extract-methods.mjs && rimraf .typedoc/docs && cpy '.typedoc/temp-docs/**' '.typedoc/docs' && rimraf .typedoc/temp-docs", "version-packages": "changeset version && pnpm install --lockfile-only --engine-strict=false", "version-packages:canary": "./scripts/canary.mjs", "version-packages:canary-core3": "./scripts/canary-core3.mjs", diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index e199021fa03..6ab532be38c 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -423,6 +423,14 @@ export class Clerk implements ClerkInterface { return !!this.session; } + /** + * Create an instance of the `Clerk` class with dedicated options. + * + * This method is only available when importing `Clerk` from `@clerk/clerk-js`, rather than using a Window script. + * + * @param key - Your Clerk [Publishable Key](!publishable-key). + * @param options - The satellite domain or reverse proxy URL used to connect to Clerk. + */ public constructor(key: string, options?: DomainOrProxyUrl) { key = (key || '').trim(); diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index bb7584f5125..ba992ec780b 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -134,7 +134,16 @@ export type SDKMetadata = { environment?: string; }; +/** + * A callback function that is called when Clerk resources change. + * @inline + */ export type ListenerCallback = (emission: Resources) => void; +/** + * Optional configuration for the `addListener()` method. + * @param skipInitialEmit - If `true`, the callback will not be called immediately after registration. Defaults to `false`. + * @inline + */ export type ListenerOptions = { skipInitialEmit?: boolean }; export type UnsubscribeCallback = () => void; @@ -755,27 +764,29 @@ export interface Clerk { __internal_loadStripeJs: () => Promise; /** - * Register a listener that triggers a callback each time important Clerk resources are changed. - * Allows to hook up at different steps in the sign up, sign in processes. + * Register a listener that triggers a callback whenever a change in the [`Client`](https://clerk.com/docs/reference/objects/client), [`Session`](https://clerk.com/docs/reference/objects/session), [`User`](https://clerk.com/docs/reference/objects/user), or [`Organization`](https://clerk.com/docs/reference/objects/organization) resources occurs. This method is primarily used to build frontend SDKs like [`@clerk/react`](https://www.npmjs.com/package/@clerk/react). + * + * Allows hooking up at different steps in the sign up, sign in processes. * * Some important checkpoints: - * When there is an active session, user === session.user. - * When there is no active session, user and session will both be null. - * When a session is loading, user and session will be undefined. + * - When there is an active session, `user === session.user`. + * - When there is no active session, user and session will both be `null`. + * - When a session is loading, user and session will be `undefined`. * - * @param callback - Callback function receiving the most updated Clerk resources after a change. - * @param options.skipInitialEmit - If true, the callback will not be called immediately after registration. - * @returns - Unsubscribe callback + * @param callback - The function to call when Clerk resources change. + * @param options - Optional configuration. + * @param options.skipInitialEmit - If `true`, the callback will not be called immediately after registration. Defaults to `false`. + * @returns - An `UnsubscribeCallback` function that can be used to remove the listener from the `Clerk` object. */ addListener: (callback: ListenerCallback, options?: ListenerOptions) => UnsubscribeCallback; /** * Registers an event handler for a specific Clerk event. * - * @param event - The event name to subscribe to - * @param handler - The callback function to execute when the event is dispatched - * @param opt - Optional configuration object - * @param opt.notify - If true and the event was previously dispatched, handler will be called immediately with the latest payload + * @param event - The event name to subscribe to. + * @param handler - The callback function to execute when the event is triggered. + * @param opt - An optional object to control the behavior of the event handler. If true, and the event was previously dispatched, handler will be called immediately with the latest payload. + * @param opt.notify - If `true` and the event was previously dispatched, the handler will be called immediately with the latest payload. */ on: OnEventListener; @@ -783,7 +794,7 @@ export interface Clerk { * Removes an event handler for a specific Clerk event. * * @param event - The event name to unsubscribe from - * @param handler - The callback function to remove + * @param handler - The callback function to remove. */ off: OffEventListener; @@ -796,7 +807,7 @@ export interface Clerk { __internal_addNavigationListener: (callback: () => void) => UnsubscribeCallback; /** - * Set the active session and Organization explicitly. + * A method used to set the current session and/or Organization for the client. Accepts a [`SetActiveParams`](https://clerk.com/docs/reference/types/set-active-params) object. * * If the session param is `null`, the active session is deleted. * In a similar fashion, if the organization param is `null`, the current organization is removed as active. @@ -804,148 +815,167 @@ export interface Clerk { setActive: SetActive; /** - * A function used to commit a navigation after certain steps in the Clerk processes. + * Helper method which will use the custom push navigation function of your application to navigate to the provided URL or relative path. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ navigate: CustomNavigation; /** - * Decorates the provided url with the auth token for development instances. + * Decorates the provided URL with the auth token for development instances. * - * @param to + * @param to - The route to create a URL towards. */ buildUrlWithAuth(to: string): string; /** - * Returns the configured url where `` is mounted or a custom sign-in page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/sign-in) is mounted or a custom sign-in page is rendered. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options used to control the redirect in the constructed URL. */ buildSignInUrl(opts?: RedirectOptions): string; /** - * Returns the configured url where `` is mounted or a custom sign-up page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/sign-up) is mounted or a custom sign-up page is rendered. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options used to control the redirect in the constructed URL. */ buildSignUpUrl(opts?: RedirectOptions): string; /** - * Returns the url where `` is mounted or a custom user-profile page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/user-profile) is mounted or a custom user-profile page is rendered. */ buildUserProfileUrl(): string; /** - * Returns the configured url where `` is mounted or a custom create-organization page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/organization/create-organization) is mounted or a custom create-organization page is rendered. */ buildCreateOrganizationUrl(): string; /** - * Returns the configured url where `` is mounted or a custom organization-profile page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/organization/organization-profile) is mounted or a custom organization-profile page is rendered. */ buildOrganizationProfileUrl(): string; /** - * Returns the configured url where tasks are mounted. + * Returns the configured URL where [session tasks](https://clerk.com/docs/guides/configure/session-tasks) are mounted. */ buildTasksUrl(): string; /** - * Returns the configured afterSignInUrl of the instance. + * Returns the configured `afterSignInUrl` of the instance. + * @param params - Optional query parameters to append to the URL. */ buildAfterSignInUrl({ params }?: { params?: URLSearchParams }): string; /** - * Returns the configured afterSignInUrl of the instance. + * Returns the configured `afterSignInUrl` of the instance. + * @param params - Optional query parameters to append to the URL. */ buildAfterSignUpUrl({ params }?: { params?: URLSearchParams }): string; /** - * Returns the configured afterSignOutUrl of the instance. + * Returns the configured `afterSignOutUrl` of the instance. */ buildAfterSignOutUrl(): string; /** - * Returns the configured newSubscriptionRedirectUrl of the instance. + * Returns the configured `newSubscriptionRedirectUrl` of the instance. */ buildNewSubscriptionRedirectUrl(): string; /** - * Returns the configured afterMultiSessionSingleSignOutUrl of the instance. + * Returns the configured `afterMultiSessionSingleSignOutUrl` of the instance. */ buildAfterMultiSessionSingleSignOutUrl(): string; /** - * Returns the configured url where `` is mounted or a custom waitlist page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/waitlist) is mounted or a custom waitlist page is rendered. */ buildWaitlistUrl(opts?: { initialValues?: Record }): string; /** * - * Redirects to the provided url after decorating it with the auth token for development instances. + * Redirects to the provided URL after appending authentication credentials. For development instances, this method decorates the URL with an auth token to maintain authentication state. For production instances, the standard session cookie is used. * - * @param to + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. + * + * @param to - The URL to redirect to. */ redirectWithAuth(to: string): Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the sign-in URL, as configured in your application's instance settings. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options to control the redirect. */ redirectToSignIn(opts?: SignInRedirectOptions): Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the sign-up URL, as configured in your application's instance settings. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. * - * @param opts - A {@link RedirectOptions} object + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. + * + * @param opts - Options to control the redirect. */ redirectToSignUp(opts?: SignUpRedirectOptions): Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/user/user-profile) is mounted. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ redirectToUserProfile: () => Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/organization/organization-profile) is mounted. This method uses the [`navigate()`](#navigate) method under the hood. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ redirectToOrganizationProfile: () => Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/organization/create-organization) is mounted. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ redirectToCreateOrganization: () => Promise; /** - * Redirects to the configured afterSignIn URL. + * Redirects to the configured `afterSignIn` URL. */ redirectToAfterSignIn: () => void; /** - * Redirects to the configured afterSignUp URL. + * Redirects to the configured `afterSignUp` URL. */ redirectToAfterSignUp: () => void; /** - * Redirects to the configured afterSignOut URL. + * Redirects to the configured `afterSignOut` URL. */ redirectToAfterSignOut: () => void; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/authentication/waitlist) is mounted. */ redirectToWaitlist: () => void; /** - * Redirects to the configured URL where tasks are mounted. + * Redirects to the configured URL where [session tasks](https://clerk.com/docs/reference/objects/session#currenttask) are mounted. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options to control the redirect (e.g. redirect URL after tasks are complete). */ redirectToTasks(opts?: TasksRedirectOptions): Promise; /** - * Completes a Google One Tap redirection flow started by - * {@link Clerk.authenticateWithGoogleOneTap} + * Completes a Google One Tap redirection flow started by [`authenticateWithGoogleOneTap()`](https://clerk.com/reference/objects/clerk#authenticate-with-google-one-tap). This method should be called after the user is redirected back from visiting the Google One Tap prompt. + * + * @param signInOrUp - The resource returned from the initial `authenticateWithGoogleOneTap()` call (before redirect). + * @param params - Additional props that define where the user will be redirected to at the end of a successful Google One Tap flow. + * @param customNavigate - A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. */ handleGoogleOneTapCallback: ( signInOrUp: SignInResource | SignUpResource, @@ -954,8 +984,10 @@ export interface Clerk { ) => Promise; /** - * Completes an OAuth or SAML redirection flow started by - * {@link Clerk.client.signIn.authenticateWithRedirect} or {@link Clerk.client.signUp.authenticateWithRedirect} + * Completes a custom OAuth or SAML redirect flow that was started by calling [`SignIn.authenticateWithRedirect(params)`](https://clerk.com/docs/reference/objects/sign-in) or [`SignUp.authenticateWithRedirect(params)`](https://clerk.com/docs/reference/objects/sign-up). + * + * @param params - Additional props that define where the user will be redirected to at the end of a successful OAuth or SAML flow. + * @param customNavigate - A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. */ handleRedirectCallback: ( params: HandleOAuthCallbackParams | HandleSamlCallbackParams, @@ -963,7 +995,9 @@ export interface Clerk { ) => Promise; /** - * Completes a Email Link flow started by {@link Clerk.client.signIn.createEmailLinkFlow} or {@link Clerk.client.signUp.createEmailLinkFlow} + * Completes an email link verification flow started by {@link Clerk.client.signIn.createEmailLinkFlow} or {@link Clerk.client.signUp.createEmailLinkFlow}, by processing the verification results from the redirect URL query parameters. This method should be called after the user is redirected back from visiting the verification link in their email. + * @param params - Allows you to define the URLs where the user should be redirected to on successful verification or pending/completed sign-up or sign-in attempts. If the email link is successfully verified on another device, there's a callback function parameter that allows custom code execution. + * @param customNavigate - A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. */ handleEmailLinkVerification: ( params: HandleEmailLinkVerificationParams, @@ -971,32 +1005,32 @@ export interface Clerk { ) => Promise; /** - * Authenticates user using their Metamask browser extension + * Starts a sign-in flow that uses the MetaMask browser extension to authenticate the user using their Metamask wallet address. */ authenticateWithMetamask: (params?: AuthenticateWithMetamaskParams) => Promise; /** - * Authenticates user using their Coinbase Smart Wallet and browser extension + * Starts a sign-in flow that uses the Coinbase Smart Wallet and browser extension to authenticate the user using their Coinbase wallet address. */ authenticateWithCoinbaseWallet: (params?: AuthenticateWithCoinbaseWalletParams) => Promise; /** - * Authenticates user using their OKX Wallet browser extension + * Starts a sign-in flow that uses the OKX Wallet browser extension to authenticate the user using their OKX wallet address. */ authenticateWithOKXWallet: (params?: AuthenticateWithOKXWalletParams) => Promise; /** - * Authenticates user using Base Account SDK + * Starts a sign-in flow that uses the Base Account SDK to authenticate the user using their Base wallet address. */ authenticateWithBase: (params?: AuthenticateWithBaseParams) => Promise; /** - * Authenticates user using their Solana supported Web3 wallet browser extension + * Starts a sign-in flow that uses a Solana supported Web3 wallet browser extension to authenticate the user using their Solana wallet address. */ authenticateWithSolana: (params: AuthenticateWithSolanaParams) => Promise; /** - * Authenticates user using their Web3 Wallet browser extension + * Starts a sign-in flow that uses a Web3 Wallet browser extension to authenticate the user using their Ethereum wallet address. */ authenticateWithWeb3: (params: ClerkAuthenticateWithWeb3Params) => Promise; @@ -1008,20 +1042,28 @@ export interface Clerk { ) => Promise; /** - * Creates an Organization, adding the current user as admin. + * Creates an Organization programmatically, adding the current user as admin. Returns an [`Organization`](https://clerk.com/docs/reference/objects/organization) object. + * + * > [!NOTE] + * > For React-based apps, consider using the [``](https://clerk.com/docs/reference/components/organization/create-organization) component. */ createOrganization: (params: CreateOrganizationParams) => Promise; /** - * Retrieves a single Organization by ID. + * Retrieves a single [Organization](https://clerk.com/docs/reference/objects/organization) by ID. + * + * @param organizationId - The ID of the Organization to retrieve. */ getOrganization: (organizationId: string) => Promise; /** - * Handles a 401 response from Frontend API by refreshing the client and session object accordingly + * Handles a 401 response from the Frontend API by refreshing the [`Client`](https://clerk.com/docs/reference/objects/client) and [`Session`](https://clerk.com/docs/reference/objects/session) object accordingly. */ handleUnauthenticated: () => Promise; + /** + * Create a new waitlist entry programmatically. Requires that you set your app's sign-up mode to [**Waitlist**](https://clerk.com/docs/guides/secure/restricting-access#waitlist) in the Clerk Dashboard. + */ joinWaitlist: (params: JoinWaitlistParams) => Promise; /** @@ -1063,44 +1105,42 @@ export interface Clerk { __experimental_checkout: __experimental_CheckoutFunction; } +/** @document */ export type HandleOAuthCallbackParams = TransferableOption & SignInForceRedirectUrl & SignInFallbackRedirectUrl & SignUpForceRedirectUrl & SignUpFallbackRedirectUrl & { /** - * Full URL or path where the SignIn component is mounted. + * The full URL or path where the [``](https://clerk.com/docs/reference/components/authentication/sign-in) component is mounted. */ signInUrl?: string; /** - * Full URL or path where the SignUp component is mounted. + * The full URL or path where the [``](https://clerk.com/docs/reference/components/authentication/sign-up) component is mounted. */ signUpUrl?: string; /** - * Full URL or path to navigate to during sign in, - * if identifier verification is required. + * The full URL or path to navigate to during sign in, if [first factor verification](!first-factor-verification) is required. */ firstFactorUrl?: string; /** - * Full URL or path to navigate to during sign in, - * if 2FA is enabled. + * The full URL or path to navigate to during sign in, if [multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) is enabled. */ secondFactorUrl?: string; /** - * Full URL or path to navigate to during sign in, - * if the user is required to reset their password. + * The full URL or path to navigate to during sign in, if the user is required to reset their password. */ resetPasswordUrl?: string; /** - * Full URL or path to navigate to after an incomplete sign up. + * The full URL or path to navigate to if the sign up requires additional information. */ continueSignUpUrl?: string | null; /** - * Full URL or path to navigate to after requesting email verification. + * The full URL or path to navigate to after requesting email verification. */ verifyEmailAddressUrl?: string | null; /** - * Full URL or path to navigate to after requesting phone verification. + * The full URL or path to navigate to after requesting phone verification. */ verifyPhoneNumberUrl?: string | null; /** @@ -1108,7 +1148,7 @@ export type HandleOAuthCallbackParams = TransferableOption & */ reloadResource?: 'signIn' | 'signUp'; /** - * Additional arbitrary metadata to be stored alongside the User object when a sign-up transfer occurs. + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. */ unsafeMetadata?: SignUpUnsafeMetadata; }; @@ -1117,6 +1157,12 @@ export type HandleSamlCallbackParams = HandleOAuthCallbackParams; /** * A function used to navigate to a given URL after certain steps in the Clerk processes. + * @inline + * @param to - The URL or relative path to navigate to. + * @param options - Optional configuration. `NavigateOptions` is an object that accepts the following properties: + * + * - `replace?` (boolean): If `true`, replace the current history entry instead of pushing a new one. + * - `metadata?` (RouterMetadata): Optional router metadata. */ export type CustomNavigation = (to: string, options?: NavigateOptions) => Promise | void; @@ -1320,6 +1366,9 @@ export interface NavigateOptions { metadata?: RouterMetadata; } +/** + * @inline + */ export interface Resources { client: ClientResource; session?: SignedInSessionResource | null; @@ -1476,6 +1525,7 @@ export type RoutingOptions = | { path: string | undefined; routing?: Extract } | { path?: never; routing?: Extract }; +/** @document */ export type SignInProps = RoutingOptions & { /** * Full URL or path to navigate to after successful sign in. @@ -1544,8 +1594,7 @@ export type SignInProps = RoutingOptions & { export interface TransferableOption { /** - * Indicates whether or not sign in attempts are transferable to the sign up flow. - * When set to false, prevents opaque sign ups when a user attempts to sign in via OAuth with an email that doesn't exist. + * Indicates whether or not sign-in attempts are transferable to the sign-up flow. Defaults to `true`. When set to `false`, prevents [opaque sign-ups](!opaque-sign-up) when a user attempts to sign in via OAuth with an email that doesn't exist. * * @default true */ @@ -1627,6 +1676,7 @@ export type __internal_AttemptToEnableEnvironmentSettingResult = { type GoogleOneTapRedirectUrlProps = SignInForceRedirectUrl & SignUpForceRedirectUrl; +/** @document */ export type GoogleOneTapProps = GoogleOneTapRedirectUrlProps & { /** * Whether to cancel the Google One Tap request if a user clicks outside the prompt. @@ -1652,6 +1702,7 @@ export type GoogleOneTapProps = GoogleOneTapRedirectUrlProps & { appearance?: ClerkAppearanceTheme; }; +/** @document */ export type SignUpProps = RoutingOptions & { /** * Full URL or path to navigate to after successful sign up. @@ -1718,6 +1769,7 @@ export type SignUpModalProps = WithoutRouting & { getContainer?: () => HTMLElement | null; }; +/** @document */ export type UserProfileProps = RoutingOptions & { /** * Customisation options to fully match the Clerk components to your own brand. @@ -1767,6 +1819,7 @@ export type UserProfileModalProps = WithoutRouting & { getContainer?: () => HTMLElement | null; }; +/** @document */ export type OrganizationProfileProps = RoutingOptions & { /** * Full URL or path to navigate to after the user leaves the currently Active Organization. @@ -1817,6 +1870,7 @@ export type OrganizationProfileModalProps = WithoutRouting HTMLElement | null; }; +/** @document */ export type CreateOrganizationProps = RoutingOptions & { /** * Full URL or path to navigate to after creating a new Organization. @@ -1841,6 +1895,7 @@ export type CreateOrganizationProps = RoutingOptions & { appearance?: ClerkAppearanceTheme; }; +/** @document */ export type CreateOrganizationModalProps = WithoutRouting & { /** * Function that returns the container element where portals should be rendered. @@ -1850,7 +1905,10 @@ export type CreateOrganizationModalProps = WithoutRouting HTMLElement | null; }; +/** @inline */ type UserProfileMode = 'modal' | 'navigation'; + +/** @document */ type UserButtonProfileMode = | { userProfileUrl?: never; @@ -1929,6 +1987,7 @@ type CreateOrganizationMode = | { createOrganizationUrl: string; createOrganizationMode?: 'navigation' } | { createOrganizationUrl?: never; createOrganizationMode?: 'modal' }; +/** @document */ export type OrganizationSwitcherProps = CreateOrganizationMode & OrganizationProfileMode & { /** @@ -2005,6 +2064,7 @@ export type OrganizationSwitcherProps = CreateOrganizationMode & organizationProfileProps?: Pick; }; +/** @document */ export type OrganizationListProps = { /** * Full URL or path to navigate to after creating a new Organization. @@ -2054,6 +2114,7 @@ export type OrganizationListProps = { afterSelectPersonalUrl?: ((user: UserResource) => string) | LooseExtractedParams>; }; +/** @document */ export type WaitlistProps = { /** * Full URL or path to navigate to after join waitlist. @@ -2071,6 +2132,7 @@ export type WaitlistProps = { signInUrl?: string; }; +/** @document */ export type WaitlistModalProps = WaitlistProps & { /** * Function that returns the container element where portals should be rendered. @@ -2080,6 +2142,7 @@ export type WaitlistModalProps = WaitlistProps & { getContainer?: () => HTMLElement | null; }; +/** @document */ type PricingTableDefaultProps = { /** * The position of the CTA button. @@ -2101,6 +2164,7 @@ type PricingTableDefaultProps = { newSubscriptionRedirectUrl?: string; }; +/** @document */ type PricingTableBaseProps = { /** * The subscriber type to display plans for. @@ -2124,8 +2188,10 @@ type PricingTableBaseProps = { type PortalRoot = HTMLElement | null | undefined; +/** @document */ export type PricingTableProps = PricingTableBaseProps & PricingTableDefaultProps; +/** @document */ export type APIKeysProps = { /** * The number of API keys to show per page. @@ -2147,6 +2213,7 @@ export type APIKeysProps = { showDescription?: boolean; }; +/** @document */ export type GetAPIKeysParams = ClerkPaginationParams<{ /** * The user or organization ID to query API keys by. If not provided, defaults to the [Active Organization](!active-organization), then the current User. @@ -2158,6 +2225,7 @@ export type GetAPIKeysParams = ClerkPaginationParams<{ query?: string; }>; +/** @document */ export type CreateAPIKeyParams = { /** * The name of the API key. @@ -2177,6 +2245,7 @@ export type CreateAPIKeyParams = { description?: string; }; +/** @document */ export type RevokeAPIKeyParams = { /** * The ID of the API key to revoke. @@ -2348,20 +2417,18 @@ export type __internal_OAuthConsentProps = { onDeny: () => void; }; +/** @document */ export interface HandleEmailLinkVerificationParams { /** - * Full URL or path to navigate to after successful magic link verification - * on completed sign up or sign in on the same device. + * The full URL to navigate to after successful email link verification on completed sign-up or sign-in on the same device. */ redirectUrlComplete?: string; /** - * Full URL or path to navigate to after successful magic link verification - * on the same device, but not completed sign in or sign up. + * The full URL to navigate to after successful email link verification on the same device, but without completing sign-in or sign-up. */ redirectUrl?: string; /** - * Callback function to be executed after successful magic link - * verification on another device. + * Callback function to be executed after successful email link verification on another device. */ onVerifiedOnOtherDevice?: () => void; } @@ -2404,37 +2471,63 @@ export type SignUpButtonProps = (SignUpButtonPropsModal | ButtonPropsRedirect) & | 'oauthFlow' >; +/** @document */ export type TaskChooseOrganizationProps = { /** * Full URL or path to navigate to after successfully resolving all tasks */ redirectUrlComplete: string; + /** + * Customization options to fully match the Clerk components to your own brand. + */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type TaskResetPasswordProps = { /** * Full URL or path to navigate to after successfully resolving all tasks */ redirectUrlComplete: string; + /** + * Customization options to fully match the Clerk components to your own brand. + */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type TaskSetupMFAProps = { /** * Full URL or path to navigate to after successfully resolving all tasks */ redirectUrlComplete: string; + /** + * Customization options to fully match the Clerk components to your own brand. + */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type CreateOrganizationInvitationParams = { + /** + * The email address of the user to invite. + */ emailAddress: string; + /** + * The role of the user to invite. + */ role: OrganizationCustomRoleKey; }; +/** @document */ export type CreateBulkOrganizationInvitationParams = { + /** + * The email addresses of the users to invite. + */ emailAddresses: string[]; + /** + * The role of the users to invite. + */ role: OrganizationCustomRoleKey; }; @@ -2452,60 +2545,175 @@ export interface CreateOrganizationParams { slug?: string; } +/** @document */ export interface ClerkAuthenticateWithWeb3Params { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * The strategy to use for the sign-in flow. + */ strategy: Web3Strategy; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; + /** + * The URL to navigate to if [second factor](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) is required. + */ secondFactorUrl?: string; + /** + * The name of the wallet to use for authentication. + */ walletName?: string; } +/** @document */ export interface AuthenticateWithMetamaskParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithCoinbaseWalletParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithOKXWalletParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithGoogleOneTapParams { + /** + * The Google credential token from the Google Identity Services response. + */ token: string; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithBaseParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithSolanaParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; + /** + * The name of the Solana wallet to use for authentication. + */ walletName: string; } @@ -2518,6 +2726,11 @@ export interface BrowserClerkConstructor { } export interface HeadlessBrowserClerk extends Clerk { + /** + * Initializes the `Clerk` object and loads all necessary environment configuration and instance settings from the [Frontend API](/docs/reference/frontend-api){{ target: '_blank' }}. + * + * For the JavaScript SDK, you must call this method before using the `Clerk` object in your code. Refer to the [quickstart guide](/docs/js-frontend/getting-started/quickstart) for more information. + */ load: (opts?: Without) => Promise; updateClient: (client: ClientResource) => void; } diff --git a/packages/shared/src/types/client.ts b/packages/shared/src/types/client.ts index 1c89ed554aa..27bf97ef0ff 100644 --- a/packages/shared/src/types/client.ts +++ b/packages/shared/src/types/client.ts @@ -5,27 +5,106 @@ import type { SignInResource } from './signIn'; import type { SignUpResource } from './signUp'; import type { ClientJSONSnapshot } from './snapshots'; +/** + * The `Client` object keeps track of the authenticated sessions in the current device. The device can be a browser, a native application, or any other medium that is usually the requesting part in a request/response architecture. + * + * The `Client` object also holds information about any sign-in or sign-up attempts that might be in progress, tracking the sign-in or sign-up progress. + */ export interface ClientResource extends ClerkResource { + /** + * A list of sessions that have been created on this client. + */ sessions: SessionResource[]; + /** + * A list of sessions on this client where the user has completed the full sign-in flow. Sessions can be in one of the following states: + * + * - **active**: The user has completed the full sign-in flow and all pending tasks. + * - **pending**: The user has completed the sign-in flow but still needs to complete one or more required steps (**pending tasks**). + */ signedInSessions: SignedInSessionResource[]; + /** + * The current sign up attempt, or `null` if there is none. + */ signUp: SignUpResource; + /** + * The current sign in attempt, or `null` if there is none. + */ signIn: SignInResource; + /** + * Returns `true` if this client hasn't been saved (created) yet in the Frontend API. Returns `false` otherwise. + */ isNew: () => boolean; + /** + * Creates a new client for the current instance along with its cookie. + */ create: () => Promise; + /** + * + * Deletes the client. All sessions will be reset. + */ destroy: () => Promise; + /** + * + * Removes all sessions created on the client. + */ removeSessions: () => Promise; + /** + * + * Clears any locally cached session data for the current client. + */ clearCache: () => void; + /** + * + * Resets the current sign-in attempt. Clears the in-progress sign-in state on the client. + */ resetSignIn: () => void; + /** + * Resets the current sign-up attempt. Clears the in-progress sign-up state on the client. + */ resetSignUp: () => void; + /** + * + * Returns `true` if the client cookie is due to expire in 8 days or less. Returns `false` otherwise. + */ isEligibleForTouch: () => boolean; + /** + * + * Builds a URL that refreshes the current client's authentication state and then redirects the user to the specified URL. + * + */ buildTouchUrl: (params: { redirectUrl: URL }) => string; + /** + * The ID of the last active [`Session`](https://clerk.com/docs/reference/objects/session) on this client. + */ lastActiveSessionId: string | null; - /** Last authentication strategy used by this client; `null` when unknown or feature disabled. */ + /** + * Last authentication strategy used by this client; `null` when unknown or feature disabled. + */ lastAuthenticationStrategy: LastAuthenticationStrategy | null; + /** + * Indicates whether CAPTCHA checks are skipped for this client. + */ captchaBypass: boolean; + /** + * The date and time when the client's authentication cookie will expire. + */ cookieExpiresAt: Date | null; + /** + * The date and time when the client was created. + */ createdAt: Date | null; + /** + * The date and time when the client was last updated. + */ updatedAt: Date | null; + /** + * Sends a CAPTCHA token to the client. + * @internal + */ __internal_sendCaptchaToken: (params: unknown) => Promise; + /** + * Converts the client to a snapshot. + * @internal + */ __internal_toSnapshot: () => ClientJSONSnapshot; } diff --git a/packages/shared/src/types/multiDomain.ts b/packages/shared/src/types/multiDomain.ts index 3661e7132bf..de7bf6fe432 100644 --- a/packages/shared/src/types/multiDomain.ts +++ b/packages/shared/src/types/multiDomain.ts @@ -60,13 +60,16 @@ export type MultiDomainAndOrProxyPrimitives = domain?: never; }; +/** + * Only one of the two properties are allowed to be set at a time. + */ export type DomainOrProxyUrl = { /** - * **Required for applications that run behind a reverse proxy**. The URL that Clerk will proxy requests to. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`). + * **Required for applications that run behind a reverse proxy**. The URL that Clerk will proxy requests to. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`), or a function that will be called with a `URL` made from `window.location.href`. */ proxyUrl?: string | ((url: URL) => string); /** - * **Required if your application is a satellite application**. Sets the domain of the satellite application. + * **Required if your application is a satellite application**. Sets the domain of the satellite application. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`), or a function that will be called with a `URL` made from `window.location.href`. */ domain?: string | ((url: URL) => string); }; diff --git a/packages/shared/src/types/redirects.ts b/packages/shared/src/types/redirects.ts index bafcdf1db63..5bcd3ea7f3c 100644 --- a/packages/shared/src/types/redirects.ts +++ b/packages/shared/src/types/redirects.ts @@ -17,7 +17,6 @@ export type AfterMultiSessionSingleSignOutUrl = { /** * Redirect URLs for different actions. - * Mainly used to be used to type internal Clerk functions. */ export type RedirectOptions = SignInForceRedirectUrl & SignInFallbackRedirectUrl & @@ -25,6 +24,7 @@ export type RedirectOptions = SignInForceRedirectUrl & SignUpFallbackRedirectUrl & RedirectUrlProp; +/** @document */ export type AuthenticateWithRedirectParams = { /** * The full URL or path to the route that will complete the OAuth or SAML flow. @@ -91,7 +91,7 @@ export type RedirectUrlProp = { export type SignUpForceRedirectUrl = { /** - * This URL will always be redirected to after the user signs up. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. + * If provided, this URL will always be redirected to after the user signs up. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. */ signUpForceRedirectUrl?: string | null; }; @@ -116,7 +116,7 @@ export type SignInFallbackRedirectUrl = { export type SignInForceRedirectUrl = { /** - * This URL will always be redirected to after the user signs in. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. + * If provided, this URL will always be redirected to after the user signs in. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. */ signInForceRedirectUrl?: string | null; }; diff --git a/packages/shared/src/types/waitlist.ts b/packages/shared/src/types/waitlist.ts index 52f16a427b6..0a88d19eaaf 100644 --- a/packages/shared/src/types/waitlist.ts +++ b/packages/shared/src/types/waitlist.ts @@ -24,5 +24,8 @@ export interface WaitlistResource extends ClerkResource { } export type JoinWaitlistParams = { + /** + * The email address of the user to add to the waitlist. + */ emailAddress: string; }; diff --git a/turbo.json b/turbo.json index b595ebd4da0..3bc444f71a6 100644 --- a/turbo.json +++ b/turbo.json @@ -342,7 +342,13 @@ }, "//#typedoc:generate": { "dependsOn": ["@clerk/nextjs#build", "@clerk/react#build", "@clerk/shared#build"], - "inputs": ["tsconfig.typedoc.json", "typedoc.config.mjs"], + "inputs": [ + "tsconfig.typedoc.json", + "typedoc.config.mjs", + ".typedoc/reference-objects.mjs", + ".typedoc/extract-methods.mjs", + ".typedoc/extract-returns-and-params.mjs" + ], "outputs": [".typedoc/**"], "outputLogs": "new-only" }, From ac8f1331ad6fae920e7017e6955ec47935337c99 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:58:49 -0700 Subject: [PATCH 06/51] use custom-plugin in extract-methods for link replacements --- .typedoc/custom-plugin.mjs | 27 ++++++++++++++++++++------- .typedoc/extract-methods.mjs | 12 ++++++++---- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 90437859175..83e1ab6d8eb 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -260,6 +260,24 @@ function getCatchAllReplacements() { ]; } +/** + * Same replacement pass as the catch-all loop in `MarkdownPageEvent.END` (after relative links). + * Used by `extract-methods.mjs`, which writes MDX outside TypeDoc and never hits that hook. + * + * @param {string} contents + */ +export function applyCatchAllMdReplacements(contents) { + if (!contents) { + return contents; + } + let out = contents; + for (const { pattern, replace } of getCatchAllReplacements()) { + // @ts-ignore — string | function + out = out.replace(pattern, replace); + } + return out; +} + /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ @@ -274,13 +292,8 @@ export function load(app) { } } - const catchAllReplacements = getCatchAllReplacements(); - - for (const { pattern, replace } of catchAllReplacements) { - if (output.contents) { - // @ts-ignore - Mixture of string and function replacements - output.contents = output.contents.replace(pattern, replace); - } + if (output.contents) { + output.contents = applyCatchAllMdReplacements(output.contents); } if (fileName) { diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index deff922d20c..55ecec63388 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url'; import { Application, Comment, ReflectionKind } from 'typedoc'; import typedocConfig from '../typedoc.config.mjs'; +import { applyCatchAllMdReplacements } from './custom-plugin.mjs'; import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_PAGE_SYMBOLS } from './reference-objects.mjs'; const __filename = fileURLToPath(import.meta.url); @@ -501,11 +502,14 @@ function buildMethodMdx(decl) { const ts = ['```typescript', formatTypeScriptSignature(sig, name), '```'].join('\n'); const paramsMd = parametersMarkdownTable(sig, decl); - const parts = [title, '', description, '', ts, '']; - if (paramsMd) { - parts.push(paramsMd); + // Same catch-all pass as `custom-plugin.mjs` — not run automatically because this MDX bypasses TypeDoc's renderer. Skip the ```typescript``` fence so signatures stay plain code. + const head = applyCatchAllMdReplacements([title, '', description].join('\n')); + const paramsProcessed = paramsMd ? applyCatchAllMdReplacements(paramsMd) : ''; + const chunks = [head, ts]; + if (paramsProcessed) { + chunks.push(paramsProcessed); } - return parts.join('\n').trim() + '\n'; + return chunks.join('\n\n').trim() + '\n'; } /** From 9139e2725ab481ed1f51ed5b1269a645d04cf92c Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 19:08:55 -0700 Subject: [PATCH 07/51] catchall link replacements should never replace text in headings --- .typedoc/custom-plugin.mjs | 85 +++++++++++++++++--------- packages/shared/src/types/clerk.ts | 53 ++++++++-------- packages/shared/src/types/waitlist.ts | 1 + packages/ui/src/internal/appearance.ts | 2 +- 4 files changed, 87 insertions(+), 54 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 83e1ab6d8eb..763245ab221 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -129,102 +129,115 @@ function getRelativeLinkReplacements() { function getCatchAllReplacements() { return [ { - pattern: /(?/g, + pattern: /(?/g, replace: '[`Appearance`](/docs/guides/customizing-clerk/appearance-prop/overview)', }, { - pattern: /\(CreateOrganizationParams\)/g, + pattern: /(? `[\`${type}\`](/docs/reference/types/errors)`, }, { - pattern: /(? { + if (ATX_HEADING_LINE.test(line.replace(/\r$/, ''))) { + return line; + } + let out = line; + for (const { pattern, replace } of getCatchAllReplacements()) { + // @ts-ignore — string | function + out = out.replace(pattern, replace); + } + return out; + }) + .join('\n'); } /** diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index ba992ec780b..1799b8abc12 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -891,6 +891,9 @@ export interface Clerk { /** * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/waitlist) is mounted or a custom waitlist page is rendered. + * + * @param opts - Options to control the waitlist URL. + * @param opts.initialValues - Initial values to prefill the waitlist form. */ buildWaitlistUrl(opts?: { initialValues?: Record }): string; @@ -930,7 +933,7 @@ export interface Clerk { redirectToUserProfile: () => Promise; /** - * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/organization/organization-profile) is mounted. This method uses the [`navigate()`](#navigate) method under the hood. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/organization/organization-profile) is mounted. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. * * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ @@ -1157,12 +1160,11 @@ export type HandleSamlCallbackParams = HandleOAuthCallbackParams; /** * A function used to navigate to a given URL after certain steps in the Clerk processes. - * @inline - * @param to - The URL or relative path to navigate to. - * @param options - Optional configuration. `NavigateOptions` is an object that accepts the following properties: * - * - `replace?` (boolean): If `true`, replace the current history entry instead of pushing a new one. - * - `metadata?` (RouterMetadata): Optional router metadata. + * @param to - The URL or relative path to navigate to. + * @param options - Optional configuration. + * @param options.replace? - If `true`, replace the current history entry instead of pushing a new one. + * @param options.metadata? - Optional router metadata. */ export type CustomNavigation = (to: string, options?: NavigateOptions) => Promise | void; @@ -1361,8 +1363,15 @@ export type ClerkOptions = ClerkOptionsNavigation & taskUrls?: Partial>; }; +/** @inline */ export interface NavigateOptions { + /** + * If `true`, replace the current history entry instead of pushing a new one. + */ replace?: boolean; + /** + * Optional router metadata. + */ metadata?: RouterMetadata; } @@ -1484,13 +1493,9 @@ export type SetActiveParams = { redirectUrl?: string; /** - * A custom navigation function to be called just before the session and/or Organization is set. + * A custom navigation function to be called just before the session and/or Organization is set. When provided, it takes precedence over the `redirectUrl` parameter for navigation. * - * When provided, it takes precedence over the `redirectUrl` parameter for navigation. - * - * The callback receives a `decorateUrl` function that should be used to wrap destination URLs. - * This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL - * (starting with `https://`) that requires `window.location.href` instead of client-side navigation. + * The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. * * @example * ```typescript @@ -1553,7 +1558,7 @@ export type SignInProps = RoutingOptions & { */ signUpUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -1636,7 +1641,7 @@ export type __internal_UserVerificationProps = RoutingOptions & { level?: SessionVerificationLevel; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -1725,7 +1730,7 @@ export type SignUpProps = RoutingOptions & { */ signInUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -1772,7 +1777,7 @@ export type SignUpModalProps = WithoutRouting & { /** @document */ export type UserProfileProps = RoutingOptions & { /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -1828,7 +1833,7 @@ export type OrganizationProfileProps = RoutingOptions & { */ afterLeaveOrganizationUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -1888,7 +1893,7 @@ export type CreateOrganizationProps = RoutingOptions & { */ skipInvitationScreen?: boolean; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -1950,7 +1955,7 @@ export type UserButtonProps = UserButtonProfileMode & { */ afterSwitchSessionUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -2052,7 +2057,7 @@ export type OrganizationSwitcherProps = CreateOrganizationMode & */ skipInvitationScreen?: boolean; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider(if one is provided) */ @@ -2084,7 +2089,7 @@ export type OrganizationListProps = { | ((organization: OrganizationResource) => string) | LooseExtractedParams>; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -2121,7 +2126,7 @@ export type WaitlistProps = { */ afterJoinWaitlistUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvided (if one is provided) */ @@ -2174,7 +2179,7 @@ type PricingTableBaseProps = { */ for?: ForPayerType; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ @@ -2200,7 +2205,7 @@ export type APIKeysProps = { */ perPage?: number; /** - * Customisation options to fully match the Clerk components to your own brand. + * Customization options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` * prop of ClerkProvider (if one is provided) */ diff --git a/packages/shared/src/types/waitlist.ts b/packages/shared/src/types/waitlist.ts index 0a88d19eaaf..74540af1692 100644 --- a/packages/shared/src/types/waitlist.ts +++ b/packages/shared/src/types/waitlist.ts @@ -23,6 +23,7 @@ export interface WaitlistResource extends ClerkResource { join: (params: JoinWaitlistParams) => Promise<{ error: ClerkError | null }>; } +/** @document */ export type JoinWaitlistParams = { /** * The email address of the user to add to the waitlist. diff --git a/packages/ui/src/internal/appearance.ts b/packages/ui/src/internal/appearance.ts index cf4b8ac09d6..0a4d0197b2c 100644 --- a/packages/ui/src/internal/appearance.ts +++ b/packages/ui/src/internal/appearance.ts @@ -837,7 +837,7 @@ export type BaseTheme = (BaseThemeTaggedType | 'clerk' | 'simple') & { cssLayerN export type Theme = { /** * A theme used as the base theme for the components. - * For further customisation, you can use the {@link Theme.options}, {@link Theme.variables} and {@link Theme.elements} props. + * For further customization, you can use the {@link Theme.options}, {@link Theme.variables} and {@link Theme.elements} props. * * Supports both object-based themes and string-based themes: * From 2978152f8fbe614dba0c04907b3d48e706149a11 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 19:22:31 -0700 Subject: [PATCH 08/51] protectPipeDelimitedInlineCodeSpans; handle if members live on alias with no typeDecl.type --- .typedoc/custom-plugin.mjs | 41 ++++++++++++++++++++++++++++-- .typedoc/extract-methods.mjs | 8 +++++- packages/shared/src/types/clerk.ts | 17 +++++++------ 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 763245ab221..744c96814fa 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -276,6 +276,39 @@ function getCatchAllReplacements() { /** CommonMark ATX heading: optional indent, 1–6 `#`, then space or end — entire line is left unchanged. */ const ATX_HEADING_LINE = /^\s{0,3}#{1,6}(?:\s|$)/; +/** Private-use placeholders — must not appear in real MDX and must not match catch-all patterns. */ +const PIPE_CODE_PH = /\uE000(\d+)\uE001/g; + +/** + * Inline code that contains a pipe (e.g. `` `a \\| b` `` or `` `a | b` ``) cannot receive per-token + * link replacements without breaking MDX. Replace those whole spans with placeholders, run catch-alls, + * then restore. + * + * @param {string} line + * @returns {{ text: string, placeholders: string[] }} + */ +function protectPipeDelimitedInlineCodeSpans(line) { + /** @type {string[]} */ + const placeholders = []; + const text = line.replace(/`([^`\n]*)`/g, (full, inner) => { + if (!inner.includes('|')) { + return full; + } + const id = placeholders.length; + placeholders.push(full); + return `\uE000${id}\uE001`; + }); + return { text, placeholders }; +} + +/** + * @param {string} text + * @param {string[]} placeholders + */ +function restoreProtectedInlineCodeSpans(text, placeholders) { + return text.replace(PIPE_CODE_PH, (_, /** @type {string} */ i) => placeholders[Number(i)] ?? ''); +} + /** * Same replacement pass as the catch-all loop in `MarkdownPageEvent.END` (after relative links). * Used by `extract-methods.mjs`, which writes MDX outside TypeDoc and never hits that hook. @@ -283,6 +316,9 @@ const ATX_HEADING_LINE = /^\s{0,3}#{1,6}(?:\s|$)/; * Skips ATX heading lines (`#` … `######`) so titles like `#### SetActiveParams` are never linkified. * (A lone `(? void | Promise; +/** + * A callback that runs after sign out completes. + * @inline */ export type SignOutCallback = () => void | Promise; +/** + * Configuration options. + */ export type SignOutOptions = { /** - * Specify a specific session to sign out. Useful for - * multi-session applications. + * Specify a specific session to sign out. Useful for multi-session applications. */ sessionId?: string; /** - * Specify a redirect URL to navigate to after sign out is complete. + * Specify a redirect URL to navigate to after sign-out is complete. */ redirectUrl?: string; }; @@ -326,11 +331,7 @@ export interface Clerk { __internal_country?: string | null; /** - * Signs out the current user on single-session instances, or all users on multi-session instances - * - * @param signOutCallback - Optional A callback that runs after sign out completes. - * @param options - Optional Configuration options, see {@link SignOutOptions} - * @returns A promise that resolves when the sign out process completes. + * Signs out the current user on single-session instances, or all users on multi-session instances. */ signOut: SignOut; From adb43df69a972d9d481671ae2f3425e14a312f00 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 9 Apr 2026 19:33:11 -0700 Subject: [PATCH 09/51] add client resource to link replacements --- .typedoc/custom-plugin.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 744c96814fa..a21550e4840 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -100,6 +100,7 @@ const LINK_REPLACEMENTS = [ ['checkout-flow-resource', '/docs/reference/hooks/use-checkout#checkout-flow-resource'], ['organization-creation-defaults-resource', '#organization-creation-defaults-resource'], ['billing-namespace', '/docs/reference/objects/billing'], + ['client-resource', '/docs/reference/objects/client'], ]; /** From a0a9731e546d70f7299cb1ce855d7ae2505671a9 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 10 Apr 2026 15:42:25 -0700 Subject: [PATCH 10/51] refactor extract-methods to use existing plugins to create tables,etc --- .typedoc/custom-plugin.mjs | 38 ++- .typedoc/extract-methods.mjs | 350 +++++++++++++++++++------ .typedoc/prepare-markdown-renderer.mjs | 118 +++++++++ packages/shared/src/types/clerk.ts | 13 +- packages/shared/src/types/redirects.ts | 5 + 5 files changed, 434 insertions(+), 90 deletions(-) create mode 100644 .typedoc/prepare-markdown-renderer.mjs diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index a21550e4840..b0884c5ad8a 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -101,6 +101,8 @@ const LINK_REPLACEMENTS = [ ['organization-creation-defaults-resource', '#organization-creation-defaults-resource'], ['billing-namespace', '/docs/reference/objects/billing'], ['client-resource', '/docs/reference/objects/client'], + ['redirect-options', '/docs/reference/types/redirect-options'], + ['handle-o-auth-call-back-params', '/docs/reference/types/handle-o-auth-call-back-params'], ]; /** @@ -127,6 +129,24 @@ function getRelativeLinkReplacements() { }); } +/** + * First pass of `MarkdownPageEvent.END`: rewrite `(foo.mdx)` / relative paths to `/docs/...` (see {@link LINK_REPLACEMENTS}). + * Used by `extract-methods.mjs`, which does not go through the renderer hook. + * + * @param {string} contents + */ +export function applyRelativeLinkReplacements(contents) { + if (!contents) { + return contents; + } + let out = contents; + for (const { pattern, replace } of getRelativeLinkReplacements()) { + // @ts-ignore — string | function + out = out.replace(pattern, replace); + } + return out; +} + function getCatchAllReplacements() { return [ { @@ -180,7 +200,10 @@ function getCatchAllReplacements() { pattern: /(? { const fileName = output.url.split('/').pop(); - const linkReplacements = getRelativeLinkReplacements(); - for (const { pattern, replace } of linkReplacements) { - if (output.contents) { - output.contents = output.contents.replace(pattern, replace); - } + if (output.contents) { + output.contents = applyRelativeLinkReplacements(output.contents); } if (output.contents) { diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index d57dea75491..8da026b81d2 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -4,19 +4,185 @@ * and writes one .mdx per method (kebab file name) next to the main reference page output. * * Run after `typedoc` (same cwd as repo root). Uses a second TypeDoc convert pass to read reflections. + * + * Like `extract-returns-and-params.mjs`, parameter tables are not hand-built: they use the same + * `MarkdownThemeContext.partials` as TypeDoc markdown output (`parametersTable` / `propertiesTable`, which call + * `someType` and therefore pick up `custom-theme.mjs` union/`<code>` behavior). Router + theme are prepared + * via `prepare-markdown-renderer.mjs` (same idea as `typedoc-plugin-markdown` `render()`). */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { Application, Comment, ReflectionKind } from 'typedoc'; +import { Application, Comment, PageKind, ReflectionKind } from 'typedoc'; +import { MarkdownPageEvent, MarkdownTheme } from 'typedoc-plugin-markdown'; import typedocConfig from '../typedoc.config.mjs'; -import { applyCatchAllMdReplacements } from './custom-plugin.mjs'; +import { applyCatchAllMdReplacements, applyRelativeLinkReplacements } from './custom-plugin.mjs'; +import { prepareMarkdownRenderer } from './prepare-markdown-renderer.mjs'; import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_PAGE_SYMBOLS } from './reference-objects.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +/** + * @param {number} level + * @param {string} text + */ +function markdownHeading(level, text) { + const l = Math.min(Math.max(level, 1), 6); + return `${'#'.repeat(l)} ${text}`; +} + +/** + * Same as typedoc-plugin-markdown `removeLineBreaks` for table cells. + * + * @param {string | undefined} str + */ +function removeLineBreaksForTableCell(str) { + return str?.replace(/\r?\n/g, ' ').replace(/ {2,}/g, ' '); +} + +/** + * Append data rows to a markdown table string (header + separator + rows). + * + * @param {string} tableMd + * @param {string[]} rowLines Lines like `| a | b | c |` + */ +function appendMarkdownTableRows(tableMd, rowLines) { + if (!rowLines.length) { + return tableMd; + } + return `${tableMd.trimEnd()}\n${rowLines.join('\n')}\n`; +} + +/** + * Post-process the theme’s parameters markdown table. TypeDoc flattens object params as `parent.child` and may + * interleave those rows with other parameters. Here we (1) move each `parent.*` block directly under `parent`, + * and (2) rewrite dotted paths in the name column to optional-chaining (`parent?.child`, `a?.b?.c`). Top-level + * names are unchanged (`foo?`, `exa`). + * + * @param {string} tableMd + */ +function formatMethodParametersTable(tableMd) { + const leadingNewlines = (tableMd.match(/^\n+/) ?? [''])[0]; + const nonEmpty = tableMd.split('\n').filter(l => l.trim().length); + if (nonEmpty.length < 3) { + return tableMd; + } + const header = nonEmpty[0]; + const sep = nonEmpty[1]; + const dataLines = nonEmpty.slice(2).filter(l => l.trim().startsWith('|')); + if (dataLines.length <= 1) { + return tableMd; + } + + /** @param {string} line */ + const firstName = line => { + const m = line.match(/^\|\s*(?:<\/a>\s*)?`([^`]+)`/); + return m ? m[1] : ''; + }; + /** `parent.child` / `parent?.child` → grouping key `parent` (matches top-level `parent` or `parent?` via fallback below). */ + /** @param {string} raw */ + const parentOfNested = raw => { + const j = raw.indexOf('?.'); + if (j !== -1) { + return raw.slice(0, j); + } + const i = raw.indexOf('.'); + return i === -1 ? '' : raw.slice(0, i); + }; + /** `a.b.c` → `a?.b?.c`; leave `foo?` and names without `.` alone. */ + /** @param {string} raw */ + const nameForDisplay = raw => (!raw.includes('.') || raw.includes('?.') ? raw : raw.split('.').join('?.')); + /** @param {string} line @param {string} name */ + const replaceFirstName = (line, name) => + line.replace(/^(\|\s*(?:<\/a>\s*)?)`[^`]+`/, `$1\`${name}\``); + + const topLevelOrder = []; + const seenTop = new Set(); + /** @type {Map} */ + const childrenOf = new Map(); + + for (const line of dataLines) { + const raw = firstName(line); + if (!raw) { + continue; + } + if (!raw.includes('.')) { + if (!seenTop.has(raw)) { + seenTop.add(raw); + topLevelOrder.push(raw); + } + continue; + } + const p = parentOfNested(raw); + if (!p) { + continue; + } + let bucket = childrenOf.get(p); + if (!bucket) { + bucket = []; + childrenOf.set(p, bucket); + } + bucket.push(line); + } + + for (const lines of childrenOf.values()) { + lines.sort((a, b) => firstName(a).localeCompare(firstName(b))); + } + + /** @param {string} top */ + const rowsForParent = top => + childrenOf.get(top) ?? (top.endsWith('?') ? childrenOf.get(top.slice(0, -1)) : undefined); + + const body = []; + const emitted = new Set(); + + for (const top of topLevelOrder) { + const topLine = dataLines.find(l => firstName(l) === top); + if (topLine) { + const r = firstName(topLine); + body.push(replaceFirstName(topLine, nameForDisplay(r))); + emitted.add(topLine); + } + const kids = rowsForParent(top); + if (kids) { + for (const line of kids) { + body.push(replaceFirstName(line, nameForDisplay(firstName(line)))); + emitted.add(line); + } + } + } + + for (const line of dataLines) { + if (!emitted.has(line)) { + const r = firstName(line); + body.push(r ? replaceFirstName(line, nameForDisplay(r)) : line); + } + } + + return `${leadingNewlines}${[header, sep, ...body].join('\n')}\n`; +} + +/** + * @param {import('typedoc').Application} app + * @param {import('typedoc').ProjectReflection} project + * @param {string} pageUrl e.g. `shared/clerk.mdx` + * @param {import('typedoc').DeclarationReflection} interfaceDecl + */ +function createThemeContextForReferencePage(app, project, pageUrl, interfaceDecl) { + const page = new MarkdownPageEvent(interfaceDecl); + page.url = pageUrl; + page.filename = path.join(app.options.getValue('out') ?? '', pageUrl); + page.pageKind = PageKind.Reflection; + page.project = project; + const theme = /** @type {InstanceType | undefined} */ (app.renderer.theme); + if (!theme || typeof theme.getRenderContext !== 'function') { + throw new Error('[extract-methods] Renderer theme is not ready; call prepareMarkdownRenderer(app) after convert'); + } + return /** @type {import('typedoc-plugin-markdown').MarkdownThemeContext} */ (theme.getRenderContext(page)); +} + /** * TypeDoc `code` display parts often already include backticks (same as {@link Comment.combineDisplayParts}). * Wrapping again would produce `` `Client` `` in MDX. @@ -177,6 +343,55 @@ function formatReturnsLineFromTag(tag) { return `Returns ${body}`; } +/** + * @param {import('typedoc').Comment | undefined} comment + */ +/** + * `typedoc-plugin-markdown` table partials include `@example` in Description cells. For extract-methods, we want to exclude examples from the generated output. + * + * Uses the same `getFlattenedDeclarations` list as `propertiesTable` so nested property rows omit examples too. + * + * @template T + * @param {import('typedoc').Reflection[]} roots + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {() => T} render + * @returns {T} + */ +function renderMemberTableOmittingExampleBlocks(roots, ctx, render) { + const flatten = + typeof ctx.helpers?.getFlattenedDeclarations === 'function' + ? ctx.helpers.getFlattenedDeclarations( + /** @type {import('typedoc').DeclarationReflection[]} */ (/** @type {unknown} */ (roots)), + ) + : roots; + /** @type {Set} */ + const processedComments = new Set(); + /** @type {{ ref: import('typedoc').Reflection; orig: import('typedoc').Comment }[]} */ + const restore = []; + for (const r of flatten) { + const c = 'comment' in r ? r.comment : undefined; + if (!c?.getTag('@example') || processedComments.has(c)) { + continue; + } + processedComments.add(c); + const next = c.clone(); + next.removeTags('@example'); + for (const ref of flatten) { + if (ref.comment === c) { + ref.comment = next; + restore.push({ ref, orig: c }); + } + } + } + try { + return render(); + } finally { + for (const { ref, orig } of restore) { + ref.comment = orig; + } + } +} + /** * @param {import('typedoc').Comment | undefined} comment */ @@ -214,23 +429,6 @@ function appendSignatureOnlyReturns(declComment, sigComment) { return formatReturnsLineFromTag(tag); } -/** - * @param {import('typedoc').SignatureReflection} sig - * @param {import('typedoc').ParameterReflection} param - * @param {import('typedoc').DeclarationReflection} decl - */ -function getParamDescription(sig, param, decl) { - if (param.comment?.summary?.length) { - return displayPartsToString(param.comment.summary).trim(); - } - const tag = - sig.comment?.getIdentifiedTag(param.name, '@param') ?? decl.comment?.getIdentifiedTag(param.name, '@param'); - if (tag?.content?.length) { - return Comment.combineDisplayParts(tag.content).trim(); - } - return ''; -} - /** * Object / type-literal declaration for a parameter type (reference, inlined reflection, intersection). * TypeDoc applies `@param parent.prop` descriptions onto property reflections under this declaration. @@ -278,12 +476,10 @@ function resolveDeclarationWithObjectMembers(t) { /** * @param {string} baseName * @param {string[]} pathSegments - * @param {boolean} parentOptional */ -function formatNestedParamNameColumn(baseName, pathSegments, parentOptional) { - const chain = pathSegments.join('.'); - const inner = parentOptional ? `${baseName}?.${chain}` : `${baseName}.${chain}`; - return `\`${inner}\``; +function formatNestedParamNameColumn(baseName, pathSegments) { + const pathChain = pathSegments.join('?.'); + return `\`${baseName}?.${pathChain}\``; } /** @@ -293,7 +489,20 @@ function formatNestedParamNameColumn(baseName, pathSegments, parentOptional) { * @param {import('typedoc').ParameterReflection} param * @returns {string[]} */ -function nestedParameterRowsFromDocumentedProperties(param) { +/** + * @param {import('typedoc').ParameterReflection} param + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + */ +function nestedParameterRowsFromDocumentedProperties(param, ctx) { + // `parametersTable` already flattens inline `{ ... }` params (see typedoc-plugin-markdown `parseParams`). + // Adding rows here would duplicate those (e.g. `options.skipInitialEmit` twice on `addListener`). + if (param.type?.type === 'reflection') { + const d = /** @type {import('typedoc').DeclarationReflection} */ (param.type.declaration); + if (d?.kind === ReflectionKind.TypeLiteral && d.children?.length) { + return []; + } + } + const holder = resolveDeclarationWithObjectMembers(param.type); if (!holder?.children?.length) { return []; @@ -307,11 +516,10 @@ function nestedParameterRowsFromDocumentedProperties(param) { if (!summary?.length) { continue; } - const nestedTypeRaw = child.type?.toString(); - const nestedTypeStr = nestedTypeRaw ? `\`${nestedTypeRaw.replace(/\|/g, '\\|')}\`` : '`unknown`'; - const nestedNameCol = formatNestedParamNameColumn(param.name, [child.name], param.flags.isOptional); + const typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; + const nestedNameCol = formatNestedParamNameColumn(param.name, [child.name]); const nestedDesc = displayPartsToString(summary).trim() || '—'; - rows.push(`| ${nestedNameCol} | ${nestedTypeStr} | ${nestedDesc} |`); + rows.push(`| ${nestedNameCol} | ${typeCell} | ${nestedDesc} |`); } return rows; } @@ -406,34 +614,15 @@ function isNominalParamTypeDocumented(typeDecl, props) { return props.some(p => p.comment?.summary?.length); } -/** - * @param {import('typedoc').DeclarationReflection} holder - * @returns {string[]} - */ -function propertyTableRowsForDeclaration(holder) { - const props = (holder.children ?? []).filter(c => c.kindOf(ReflectionKind.Property)); - props.sort((a, b) => a.name.localeCompare(b.name)); - /** @type {string[]} */ - const rows = []; - for (const child of props) { - const opt = child.flags.isOptional ? '?' : ''; - const nameCol = `\`${child.name}${opt}\``; - const nestedTypeRaw = child.type?.toString(); - const nestedTypeStr = nestedTypeRaw ? `\`${nestedTypeRaw.replace(/\|/g, '\\|')}\`` : '`unknown`'; - const nestedDesc = child.comment?.summary?.length ? displayPartsToString(child.comment.summary).trim() : '—'; - rows.push(`| ${nameCol} | ${nestedTypeStr} | ${nestedDesc} |`); - } - return rows; -} - /** * Single parameter that is a named object type (interface / type alias): one section titled after the type, - * table lists every property (not the outer `params` row). + * table lists every property (not the outer `params` row). Uses the same `propertiesTable` partial as TypeDoc. * * @param {import('typedoc').SignatureReflection} sig + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx * @returns {string | undefined} */ -function trySingleNominalParameterTypeSection(sig) { +function trySingleNominalParameterTypeSection(sig, ctx) { const params = sig.parameters ?? []; if (params.length !== 1) { return undefined; @@ -450,48 +639,53 @@ function trySingleNominalParameterTypeSection(sig) { if (!isNominalParamTypeDocumented(nominal.typeDecl, props)) { return undefined; } - const rows = propertyTableRowsForDeclaration(nominal.holder); - if (rows.length === 0) { + const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => + ctx.partials.propertiesTable(props, { + kind: nominal.typeDecl.kind, + isEventProps: false, + }), + ); + if (!tableMd?.trim()) { return undefined; } - return [`#### ${nominal.sectionTitle}`, '', '| Name | Type | Description |', '| --- | --- | --- |', ...rows, ''].join( - '\n', - ); + return [markdownHeading(4, nominal.sectionTitle), '', tableMd, ''].join('\n'); } /** * @param {import('typedoc').SignatureReflection} sig - * @param {import('typedoc').DeclarationReflection} decl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx */ -function parametersMarkdownTable(sig, decl) { +function parametersMarkdownTable(sig, ctx) { const params = sig.parameters ?? []; if (params.length === 0) { return ''; } - const singleNominal = trySingleNominalParameterTypeSection(sig); + const singleNominal = trySingleNominalParameterTypeSection(sig, ctx); if (singleNominal) { return singleNominal; } + let tableMd = renderMemberTableOmittingExampleBlocks(params, ctx, () => ctx.partials.parametersTable(params)); /** @type {string[]} */ - const rows = []; + const nested = []; for (const p of params) { - const typeStr = p.type ? `\`${p.type.toString().replace(/\|/g, '\\|')}\`` : '`unknown`'; - const opt = p.flags.isOptional ? '?' : ''; - const nameCol = `\`${p.name}${opt}\``; - const desc = getParamDescription(sig, p, decl); - rows.push(`| ${nameCol} | ${typeStr} | ${desc || '—'} |`); - rows.push(...nestedParameterRowsFromDocumentedProperties(p)); + nested.push(...nestedParameterRowsFromDocumentedProperties(p, ctx)); + } + if (nested.length) { + tableMd = appendMarkdownTableRows(tableMd, nested); } - return ['#### Parameters', '', '| Name | Type | Description |', '| --- | --- | --- |', ...rows, ''].join('\n'); + tableMd = formatMethodParametersTable(tableMd); + + return [markdownHeading(4, ReflectionKind.pluralString(ReflectionKind.Parameter)), '', tableMd, ''].join('\n'); } /** * @param {import('typedoc').DeclarationReflection} decl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx */ -function buildMethodMdx(decl) { +function buildMethodMdx(decl, ctx) { const name = decl.name; const sig = getPrimaryCallSignature(decl); if (!sig) { @@ -506,11 +700,12 @@ function buildMethodMdx(decl) { description = [description, sigReturns].filter(Boolean).join('\n\n'); } const ts = ['```typescript', formatTypeScriptSignature(sig, name), '```'].join('\n'); - const paramsMd = parametersMarkdownTable(sig, decl); + const paramsMd = parametersMarkdownTable(sig, ctx); - // Same catch-all pass as `custom-plugin.mjs` — not run automatically because this MDX bypasses TypeDoc's renderer. Skip the ```typescript``` fence so signatures stay plain code. - const head = applyCatchAllMdReplacements([title, '', description].join('\n')); - const paramsProcessed = paramsMd ? applyCatchAllMdReplacements(paramsMd) : ''; + // Same post-process as `custom-plugin.mjs` `MarkdownPageEvent.END`: relative `.mdx` links, then catch-alls. + // Skip the ```typescript``` fence so signatures stay plain code. + const head = applyCatchAllMdReplacements(applyRelativeLinkReplacements([title, '', description].join('\n'))); + const paramsProcessed = paramsMd ? applyCatchAllMdReplacements(applyRelativeLinkReplacements(paramsMd)) : ''; const chunks = [head, ts]; if (paramsProcessed) { chunks.push(paramsProcessed); @@ -521,8 +716,9 @@ function buildMethodMdx(decl) { /** * @param {string} pageUrl * @param {import('typedoc').ProjectReflection} project + * @param {import('typedoc').Application} app */ -function extractMethodsForPage(pageUrl, project) { +function extractMethodsForPage(pageUrl, project, app) { const symbol = /** @type {Record} */ (/** @type {unknown} */ (REFERENCE_OBJECT_PAGE_SYMBOLS))[ pageUrl ]; @@ -538,6 +734,8 @@ function extractMethodsForPage(pageUrl, project) { return 0; } + const ctx = createThemeContextForReferencePage(app, project, pageUrl, decl); + const outDir = path.join(__dirname, 'temp-docs', path.dirname(pageUrl), `${path.basename(pageUrl, '.mdx')}-methods`); fs.mkdirSync(outDir, { recursive: true }); @@ -549,7 +747,7 @@ function extractMethodsForPage(pageUrl, project) { if (!isCallableMember(/** @type {import('typedoc').DeclarationReflection} */ (child))) { continue; } - const mdx = buildMethodMdx(/** @type {import('typedoc').DeclarationReflection} */ (child)); + const mdx = buildMethodMdx(/** @type {import('typedoc').DeclarationReflection} */ (child), ctx); if (!mdx) { continue; } @@ -575,9 +773,11 @@ async function main() { process.exit(1); } + prepareMarkdownRenderer(app, project); + let total = 0; for (const pageUrl of REFERENCE_OBJECTS_LIST) { - total += extractMethodsForPage(pageUrl, project); + total += extractMethodsForPage(pageUrl, project, app); } console.log(`[extract-methods] Wrote ${total} method files total`); } diff --git a/.typedoc/prepare-markdown-renderer.mjs b/.typedoc/prepare-markdown-renderer.mjs new file mode 100644 index 00000000000..bbf373c63f7 --- /dev/null +++ b/.typedoc/prepare-markdown-renderer.mjs @@ -0,0 +1,118 @@ +// @ts-check +/** + * Mirrors `prepareRouter` + `prepareTheme` from `typedoc-plugin-markdown` `render()` so code outside the + * markdown render pass can build a `MarkdownThemeContext` (same `partials` as generated pages). + * + * Only `member`, `module`, and plugin-registered routers (e.g. `clerk-router`) are supported — matching this repo's + * TypeDoc config. + * + * @see https://github.com/typedoc2md/typedoc-plugin-markdown/blob/main/packages/typedoc-plugin-markdown/src/renderer/render.ts + */ +import { MarkdownTheme, MemberRouter, ModuleRouter } from 'typedoc-plugin-markdown'; + +/** + * @param {import('typedoc').Renderer} renderer + * @returns {string} + */ +function getRouterName(renderer) { + const routerOption = renderer.application.options.getValue('router'); + if (!renderer.application.options.isSet('router')) { + if (renderer.application.options.isSet('outputFileStrategy')) { + const outputFileStrategy = renderer.application.options.getValue('outputFileStrategy'); + return outputFileStrategy === 'modules' ? 'module' : 'member'; + } + return 'member'; + } + return routerOption; +} + +/** + * TypeDoc types `Renderer['routers']` as private; at runtime plugins register routers on this map (e.g. `clerk-router`). + * + * @param {import('typedoc').Renderer} renderer + * @param {string} routerName + * @returns {typeof MemberRouter | typeof ModuleRouter | (new (application: import('typedoc').Application) => import('typedoc').Router) | undefined} + */ +function getRouterConstructor(renderer, routerName) { + if (routerName === 'member') { + return MemberRouter; + } + if (routerName === 'module') { + return ModuleRouter; + } + const routers = + /** @type {{ routers: Map import('typedoc').Router> }} */ ( + /** @type {unknown} */ (renderer) + ).routers; + return routers.get(routerName); +} + +/** + * Same situation as {@link getRouterConstructor}: `themes` is public at runtime but typed private. + * + * @param {import('typedoc').Renderer} renderer + * @returns {Map import('typedoc').Theme>} + */ +function getThemeRegistry(renderer) { + return /** @type {{ themes: Map import('typedoc').Theme> }} */ ( + /** @type {unknown} */ (renderer) + ).themes; +} + +/** + * @param {import('typedoc').Renderer} renderer + */ +function prepareRouter(renderer) { + const routerName = getRouterName(renderer); + const RouterCtor = getRouterConstructor(renderer, routerName); + if (!RouterCtor) { + throw new Error( + `[prepare-markdown-renderer] Router "${routerName}" is not registered (expected member, module, or a custom router from a plugin)`, + ); + } + renderer.router = new RouterCtor(renderer.application); +} + +/** + * @param {import('typedoc').Renderer} renderer + */ +function getThemeName(renderer) { + const themeOption = renderer.application.options.getValue('theme'); + return themeOption === 'default' ? 'markdown' : themeOption; +} + +/** + * @param {import('typedoc').Renderer} renderer + */ +function prepareTheme(renderer) { + const themes = getThemeRegistry(renderer); + const themeName = getThemeName(renderer); + const ThemeCtor = themes.get(themeName); + if (!ThemeCtor) { + throw new Error(`[prepare-markdown-renderer] Theme "${themeName}" is not registered`); + } + const theme = new ThemeCtor(renderer); + if (!(theme instanceof MarkdownTheme)) { + renderer.application.logger.warn( + `[prepare-markdown-renderer] Theme "${themeName}" is not MarkdownTheme; falling back to built-in markdown theme`, + ); + renderer.theme = new /** @type {typeof MarkdownTheme} */ (themes.get('markdown'))(renderer); + return; + } + renderer.theme = theme; +} + +/** + * @param {import('typedoc').Application} app + * @param {import('typedoc').ProjectReflection} project + */ +export function prepareMarkdownRenderer(app, project) { + prepareRouter(app.renderer); + prepareTheme(app.renderer); + // Required so `referenceType` / links can resolve (`getFullUrl`); same as `render()` before each page. + const router = app.renderer.router; + if (!router) { + throw new Error('[prepare-markdown-renderer] Router was not set after prepareRouter'); + } + router.buildPages(project); +} diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 13e770af47c..35a1c0b51e2 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -865,13 +865,15 @@ export interface Clerk { /** * Returns the configured `afterSignInUrl` of the instance. - * @param params - Optional query parameters to append to the URL. + * @param params - Optional configuration. + * @param params.params - Optional query parameters to append to the URL. */ buildAfterSignInUrl({ params }?: { params?: URLSearchParams }): string; /** * Returns the configured `afterSignInUrl` of the instance. - * @param params - Optional query parameters to append to the URL. + * @param params - Optional configuration. + * @param params.params - Optional query parameters to append to the URL. */ buildAfterSignUpUrl({ params }?: { params?: URLSearchParams }): string; @@ -999,7 +1001,7 @@ export interface Clerk { ) => Promise; /** - * Completes an email link verification flow started by {@link Clerk.client.signIn.createEmailLinkFlow} or {@link Clerk.client.signUp.createEmailLinkFlow}, by processing the verification results from the redirect URL query parameters. This method should be called after the user is redirected back from visiting the verification link in their email. + * Completes an email link verification flow started by `Clerk.client.signIn.createEmailLinkFlow` or `Clerk.client.signUp.createEmailLinkFlow`, by processing the verification results from the redirect URL query parameters. This method should be called after the user is redirected back from visiting the verification link in their email. * @param params - Allows you to define the URLs where the user should be redirected to on successful verification or pending/completed sign-up or sign-in attempts. If the email link is successfully verified on another device, there's a callback function parameter that allows custom code execution. * @param customNavigate - A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. */ @@ -1496,7 +1498,7 @@ export type SetActiveParams = { /** * A custom navigation function to be called just before the session and/or Organization is set. When provided, it takes precedence over the `redirectUrl` parameter for navigation. * - * The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. + * The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. See the [section on using the `navigate()` parameter](https://clerk.com/docs/reference/objects/clerk#using-the-navigate-parameter) for more details. * * @example * ```typescript @@ -1598,6 +1600,9 @@ export type SignInProps = RoutingOptions & { SignUpFallbackRedirectUrl & AfterSignOutUrl; +/** + * @interface + */ export interface TransferableOption { /** * Indicates whether or not sign-in attempts are transferable to the sign-up flow. Defaults to `true`. When set to `false`, prevents [opaque sign-ups](!opaque-sign-up) when a user attempts to sign in via OAuth with an email that doesn't exist. diff --git a/packages/shared/src/types/redirects.ts b/packages/shared/src/types/redirects.ts index 5bcd3ea7f3c..41e53ebc2cb 100644 --- a/packages/shared/src/types/redirects.ts +++ b/packages/shared/src/types/redirects.ts @@ -82,6 +82,7 @@ export type AuthenticateWithRedirectParams = { export type AuthenticateWithPopupParams = AuthenticateWithRedirectParams & { popup: Window | null }; +/** @document */ export type RedirectUrlProp = { /** * Full URL or path to navigate to after a successful action. @@ -89,6 +90,7 @@ export type RedirectUrlProp = { redirectUrl?: string | null; }; +/** @document */ export type SignUpForceRedirectUrl = { /** * If provided, this URL will always be redirected to after the user signs up. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. @@ -96,6 +98,7 @@ export type SignUpForceRedirectUrl = { signUpForceRedirectUrl?: string | null; }; +/** @document */ export type SignUpFallbackRedirectUrl = { /** * The fallback URL to redirect to after the user signs up, if there's no `redirect_url` in the path already. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. @@ -105,6 +108,7 @@ export type SignUpFallbackRedirectUrl = { signUpFallbackRedirectUrl?: string | null; }; +/** @document */ export type SignInFallbackRedirectUrl = { /** * The fallback URL to redirect to after the user signs in, if there's no `redirect_url` in the path already. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. @@ -114,6 +118,7 @@ export type SignInFallbackRedirectUrl = { signInFallbackRedirectUrl?: string | null; }; +/** @document */ export type SignInForceRedirectUrl = { /** * If provided, this URL will always be redirected to after the user signs in. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. From c9129a46177551608a1cd4a4d8024d88486634d0 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:00:27 -0700 Subject: [PATCH 11/51] type intersections should merge all type information into the generated file --- .typedoc/custom-theme.mjs | 249 +++++++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 3 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 7d58e428401..0cd40c37df5 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -6,6 +6,237 @@ import { REFERENCE_OBJECTS_LIST } from './reference-objects.mjs'; export { REFERENCE_OBJECTS_LIST }; +/** + * Unwrap optional TypeDoc types so referenced object shapes are still found. + * + * @param {import('typedoc').Type} t + * @returns {import('typedoc').Type} + */ +/** + * Prefer structural checks over `instanceof` so we still match when multiple TypeDoc copies are loaded + * (otherwise `instanceof IntersectionType` is false at render time). + * + * @param {import('typedoc').Type | undefined} t + * @returns {t is import('typedoc').IntersectionType} + */ +function isIntersectionTypeDoc(t) { + const o = /** @type {{ type?: string; types?: import('typedoc').Type[] } | null} */ (t); + return Boolean(o && typeof o === 'object' && o.type === 'intersection' && Array.isArray(o.types)); +} + +/** + * @param {import('typedoc').Type | undefined} t + * @returns {t is import('typedoc').ReferenceType} + */ +function isReferenceTypeDoc(/** @type {import('typedoc').Type | undefined} */ t) { + return Boolean(t && typeof t === 'object' && /** @type {{ type?: string }} */ (t).type === 'reference'); +} + +/** + * @param {import('typedoc').Type | undefined} t + * @returns {t is import('typedoc').ReflectionType} + */ +function isReflectionTypeDoc(/** @type {import('typedoc').Type | undefined} */ t) { + return Boolean(t && typeof t === 'object' && /** @type {{ type?: string }} */ (t).type === 'reflection'); +} + +/** + * @param {import('typedoc').Type | undefined} t + */ +function unwrapOptionalType(t) { + if ( + t && + typeof t === 'object' && + 'type' in t && + /** @type {{ type: string }} */ (t).type === 'optional' && + 'elementType' in t + ) { + return /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + } + return t; +} + +/** + * When `ReferenceType.reflection` is unset (common for imported aliases), resolve by name in the converted project. + * + * @param {import('typedoc').ProjectReflection | undefined} project + * @param {string} name + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function findNamedTypeDeclaration(project, name) { + if (!project?.reflections) { + return undefined; + } + for (const r of Object.values(project.reflections)) { + if (r.name !== name) { + continue; + } + if (r.kind === ReflectionKind.TypeAlias || r.kind === ReflectionKind.Interface) { + return /** @type {import('typedoc').DeclarationReflection} */ (r); + } + } + return undefined; +} + +/** + * Collect documented property reflections from one intersection arm (object literal, type alias, interface, nested `&`). + * + * @param {import('typedoc').Type} t + * @param {Set} visitedReflectionIds + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection[]} + */ +function collectPropertyReflectionsFromIntersectionArm(t, visitedReflectionIds, project) { + const unwrapped = unwrapOptionalType(t); + if (!unwrapped) { + return []; + } + + if (isReflectionTypeDoc(unwrapped)) { + const decl = unwrapped.declaration; + if (!decl) { + return []; + } + if (decl.signatures?.length && !decl.children?.length) { + return []; + } + return (decl.children ?? []).filter(c => c.kind === ReflectionKind.Property); + } + + if (isReferenceTypeDoc(unwrapped)) { + let ref = unwrapped.reflection; + if (!ref && unwrapped.name && project) { + ref = findNamedTypeDeclaration(project, unwrapped.name); + } + if (!ref) { + return []; + } + const declRef = /** @type {import('typedoc').DeclarationReflection | undefined} */ ( + 'kind' in ref ? ref : undefined + ); + if (!declRef) { + return []; + } + const id = declRef.id; + if (id != null) { + if (visitedReflectionIds.has(id)) { + return []; + } + visitedReflectionIds.add(id); + } + try { + if (declRef.kind === ReflectionKind.TypeAlias) { + if (declRef.children?.length) { + return declRef.children.filter( + /** @param {import('typedoc').DeclarationReflection} c */ + c => c.kind === ReflectionKind.Property, + ); + } + if (declRef.type) { + return collectPropertyReflectionsFromIntersectionArm(declRef.type, visitedReflectionIds, project); + } + return []; + } + if ( + (declRef.kind === ReflectionKind.Interface || declRef.kind === ReflectionKind.Class) && + declRef.children?.length + ) { + return declRef.children.filter( + /** @param {import('typedoc').DeclarationReflection} c */ + c => c.kind === ReflectionKind.Property, + ); + } + } finally { + if (id != null) { + visitedReflectionIds.delete(id); + } + } + return []; + } + + if (isIntersectionTypeDoc(unwrapped)) { + /** @type {import('typedoc').DeclarationReflection[]} */ + const out = []; + for (const arm of unwrapped.types) { + out.push(...collectPropertyReflectionsFromIntersectionArm(arm, visitedReflectionIds, project)); + } + return out; + } + + return []; +} + +/** + * Merge intersection arms into one property list (later duplicate names override earlier ones, then sort by name). + * + * @param {import('typedoc').IntersectionType} intersection + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection[]} + */ +function mergeIntersectionPropertyReflections(intersection, project) { + /** @type {Map} */ + const byName = new Map(); + const visited = new Set(); + for (const arm of intersection.types) { + for (const p of collectPropertyReflectionsFromIntersectionArm(arm, visited, project)) { + byName.set(p.name, p); + } + } + return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); +} + +/** + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {import('typedoc').DeclarationReflection} model + * @param {{ nested?: boolean; headingLevel?: number }} opts + * @param {import('typedoc').DeclarationReflection[]} mergedChildren + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['partials']} superPartials + */ +function renderMergedIntersectionDeclaration(ctx, model, opts, mergedChildren, superPartials) { + /** @type {string[]} */ + const md = []; + const headingLevel = opts.headingLevel ?? 2; + const nested = opts.nested ?? false; + + if (!nested && model.sources && !ctx.options.getValue('disableSources')) { + md.push(superPartials.sources(model)); + } + if (model?.documents) { + md.push(superPartials.documents(model, { headingLevel })); + } + if (model.comment) { + md.push( + superPartials.comment(model.comment, { + headingLevel, + showSummary: true, + showTags: false, + }), + ); + } + + const synthetic = /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: mergedChildren, + parent: model, + kind: ReflectionKind.TypeLiteral, + }) + ); + md.push(superPartials.typeDeclaration(synthetic, { headingLevel })); + + if (model.comment) { + md.push( + superPartials.comment(model.comment, { + headingLevel, + showSummary: false, + showTags: true, + showReturns: true, + }), + ); + } + md.push(superPartials.inheritance(model, { headingLevel: opts.headingLevel ?? headingLevel })); + return md.filter(Boolean).join('\n\n'); +} + /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ @@ -355,6 +586,21 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {{ headingLevel: number, nested?: boolean }} options */ declaration: (model, options = { headingLevel: 2, nested: false }) => { + const opts = { nested: false, ...options }; + const customizedModel = model; + customizedModel.typeParameters = undefined; + + if (!opts.nested && model.type && isIntersectionTypeDoc(model.type)) { + const merged = mergeIntersectionPropertyReflections( + /** @type {import('typedoc').IntersectionType} */ (model.type), + model.project, + ); + if (merged.length > 0) { + const output = renderMergedIntersectionDeclaration(this, customizedModel, opts, merged, superPartials); + return output.replace(/^## Type declaration$/gm, ''); + } + } + // Create a local override const localPartials = { ...this.partials, @@ -363,9 +609,6 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { // Store original so that we can restore it later const originalPartials = this.partials; - const customizedModel = model; - customizedModel.typeParameters = undefined; - this.partials = localPartials; const output = superPartials.declaration(customizedModel, options); this.partials = originalPartials; From 2d1ceecc851416a91e66f3fa83aad748e8fa8dfd Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:55:56 -0700 Subject: [PATCH 12/51] objects get their own folders with -properties file and -methods folder --- .typedoc/__tests__/file-structure.test.ts | 4 ++ .typedoc/custom-plugin.mjs | 40 +++++++++---- .typedoc/custom-router.mjs | 22 +++++++ .typedoc/extract-methods.mjs | 72 +++++++++++++++++++++-- .typedoc/reference-objects.mjs | 10 +++- 5 files changed, 127 insertions(+), 21 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 983a0972d5b..650ff374e25 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -52,6 +52,10 @@ describe('Typedoc output', () => { expect(nestedFolders).toMatchInlineSnapshot(` [ "react/legacy", + "shared/clerk", + "shared/clerk/clerk-methods", + "shared/client-resource", + "shared/client-resource/client-resource-methods", ] `); }); diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index b0884c5ad8a..018e3d02a42 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -329,6 +329,20 @@ function restoreProtectedInlineCodeSpans(text, placeholders) { return text.replace(PIPE_CODE_PH, (_, /** @type {string} */ i) => placeholders[Number(i)] ?? ''); } +/** + * Remove the Properties section (heading + table) from reference object pages (e.g. `shared/clerk/clerk.mdx`); + * the table is copied into `shared//-properties.mdx` by `extract-methods.mjs`. + * + * @param {string} contents + */ +export function stripReferenceObjectPropertiesSection(contents) { + if (!contents) { + return contents; + } + const stripped = contents.replace(/\r\n/g, '\n').replace(/\n## Properties\n+[\s\S]*$/, ''); + return stripped.trimEnd() + '\n'; +} + /** * Second pass of `MarkdownPageEvent.END` (after {@link applyRelativeLinkReplacements}). * Used by `extract-methods.mjs`, which writes MDX outside TypeDoc and never hits that hook. @@ -347,18 +361,20 @@ export function applyCatchAllMdReplacements(contents) { } return contents .split('\n') - .map(line => { - if (ATX_HEADING_LINE.test(line.replace(/\r$/, ''))) { - return line; - } - const { text: withPh, placeholders } = protectPipeDelimitedInlineCodeSpans(line); - let out = withPh; - for (const { pattern, replace } of getCatchAllReplacements()) { - // @ts-ignore — string | function - out = out.replace(pattern, replace); - } - return restoreProtectedInlineCodeSpans(out, placeholders); - }) + .map( + /** @param {string} line */ line => { + if (ATX_HEADING_LINE.test(line.replace(/\r$/, ''))) { + return line; + } + const { text: withPh, placeholders } = protectPipeDelimitedInlineCodeSpans(line); + let out = withPh; + for (const { pattern, replace } of getCatchAllReplacements()) { + // @ts-ignore — string | function + out = out.replace(pattern, replace); + } + return restoreProtectedInlineCodeSpans(out, placeholders); + }, + ) .join('\n'); } diff --git a/.typedoc/custom-router.mjs b/.typedoc/custom-router.mjs index 97cf8acef8d..b273745b2cb 100644 --- a/.typedoc/custom-router.mjs +++ b/.typedoc/custom-router.mjs @@ -1,6 +1,12 @@ // @ts-check +import { ReflectionKind } from 'typedoc'; import { MemberRouter } from 'typedoc-plugin-markdown'; +import { REFERENCE_OBJECT_PAGE_SYMBOLS } from './reference-objects.mjs'; + +/** @type {Set} */ +const REFERENCE_OBJECT_SYMBOL_NAMES = new Set(Object.values(REFERENCE_OBJECT_PAGE_SYMBOLS)); + /** * From a filepath divided by `/` only keep the first and last part * @param {string} filePath @@ -72,6 +78,22 @@ class ClerkRouter extends MemberRouter { */ filePath = flattenDirName(filePath); + /** + * Put each reference object in its own folder alongside `-properties.mdx` and `-methods/` from `extract-methods.mjs`. + * E.g. `shared/clerk.mdx` -> `shared/clerk/clerk.mdx` and `shared/clerk/clerk-properties.mdx` and `shared/clerk/clerk-methods/`. + */ + if ( + (reflection.kind === ReflectionKind.Interface || reflection.kind === ReflectionKind.Class) && + REFERENCE_OBJECT_SYMBOL_NAMES.has(reflection.name) + ) { + const kebab = toKebabCase(reflection.name); + const m = filePath.match(/^([^/]+)\/([^/]+)$/); + if (m) { + const [, pkg] = m; + return `${pkg}/${kebab}/${kebab}`; + } + } + return filePath; } } diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 8da026b81d2..a75189fe027 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -1,7 +1,6 @@ // @ts-check /** - * For each entry in REFERENCE_OBJECTS_LIST, finds callable members on the mapped interface/class via TypeDoc - * and writes one .mdx per method (kebab file name) next to the main reference page output. + * For each entry in REFERENCE_OBJECTS_LIST, reads the TypeDoc output (e.g. `shared/clerk/clerk.mdx`), strips **Properties** from the main generated file and copies it into `-properties.mdx`, and writes one .mdx per method under `-methods/`. * * Run after `typedoc` (same cwd as repo root). Uses a second TypeDoc convert pass to read reflections. * @@ -17,7 +16,11 @@ import { Application, Comment, PageKind, ReflectionKind } from 'typedoc'; import { MarkdownPageEvent, MarkdownTheme } from 'typedoc-plugin-markdown'; import typedocConfig from '../typedoc.config.mjs'; -import { applyCatchAllMdReplacements, applyRelativeLinkReplacements } from './custom-plugin.mjs'; +import { + applyCatchAllMdReplacements, + applyRelativeLinkReplacements, + stripReferenceObjectPropertiesSection, +} from './custom-plugin.mjs'; import { prepareMarkdownRenderer } from './prepare-markdown-renderer.mjs'; import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_PAGE_SYMBOLS } from './reference-objects.mjs'; @@ -167,7 +170,7 @@ function formatMethodParametersTable(tableMd) { /** * @param {import('typedoc').Application} app * @param {import('typedoc').ProjectReflection} project - * @param {string} pageUrl e.g. `shared/clerk.mdx` + * @param {string} pageUrl e.g. `shared/clerk/index.mdx` * @param {import('typedoc').DeclarationReflection} interfaceDecl */ function createThemeContextForReferencePage(app, project, pageUrl, interfaceDecl) { @@ -277,12 +280,64 @@ function isCallableMember(decl) { if (decl.kind === ReflectionKind.Method) { return true; } - if (decl.kind === ReflectionKind.Property) { + if (decl.kind === ReflectionKind.Property || decl.kind === ReflectionKind.Accessor) { return !!getPrimaryCallSignature(decl); } return false; } +/** + * @param {string} markdown + * @returns {string | undefined} Body under `## Properties` (no heading), or undefined + */ +function extractPropertiesSectionBody(markdown) { + const normalized = markdown.replace(/\r\n/g, '\n'); + const m = normalized.match(/(^|\n)## Properties\n+/); + if (!m || m.index === undefined) { + return undefined; + } + const start = m.index + m[0].length; + const rest = normalized.slice(start); + const nextH2 = rest.search(/\n## /); + const section = nextH2 === -1 ? rest : rest.slice(0, nextH2); + const trimmed = section.trim(); + return trimmed.length ? trimmed : undefined; +} + +/** + * @param {string} pageUrl e.g. `shared/clerk/clerk.mdx` + */ +function extractPropertiesAndTrimSourcePage(pageUrl) { + const sourcePath = path.join(__dirname, 'temp-docs', pageUrl); + if (!fs.existsSync(sourcePath)) { + console.warn(`[extract-methods] Expected TypeDoc output missing: ${sourcePath}`); + return; + } + const raw = fs.readFileSync(sourcePath, 'utf-8'); + const body = extractPropertiesSectionBody(raw); + const pageDir = path.dirname(pageUrl); + const slug = path.basename(pageUrl, '.mdx'); + const objectDir = path.join(__dirname, 'temp-docs', pageDir); + fs.mkdirSync(objectDir, { recursive: true }); + + if (body) { + const propertiesDoc = [`## Properties`, '', body.trimEnd(), ''].join('\n'); + const propertiesPath = path.join(objectDir, `${slug}-properties.mdx`); + fs.writeFileSync( + propertiesPath, + applyCatchAllMdReplacements(applyRelativeLinkReplacements(propertiesDoc)), + 'utf-8', + ); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), propertiesPath)}`); + } + + const stripped = stripReferenceObjectPropertiesSection(raw); + if (stripped !== raw) { + fs.writeFileSync(sourcePath, stripped, 'utf-8'); + console.log(`[extract-methods] Stripped Properties from ${path.relative(path.join(__dirname, '..'), sourcePath)}`); + } +} + /** * @param {string} name */ @@ -734,9 +789,14 @@ function extractMethodsForPage(pageUrl, project, app) { return 0; } + extractPropertiesAndTrimSourcePage(pageUrl); + const ctx = createThemeContextForReferencePage(app, project, pageUrl, decl); - const outDir = path.join(__dirname, 'temp-docs', path.dirname(pageUrl), `${path.basename(pageUrl, '.mdx')}-methods`); + const pageDir = path.dirname(pageUrl); + const slug = path.basename(pageUrl, '.mdx'); + const objectDir = path.join(__dirname, 'temp-docs', pageDir); + const outDir = path.join(objectDir, `${slug}-methods`); fs.mkdirSync(outDir, { recursive: true }); let count = 0; diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index db12e70bbb7..aa4e76bbdf4 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -4,12 +4,16 @@ * `page.url` values are relative to TypeDoc `out` (e.g. `.typedoc/temp-docs`). */ -export const REFERENCE_OBJECTS_LIST = ['shared/clerk.mdx', 'shared/client-resource.mdx']; +/** + * TypeDoc output paths for the main reference pages (`shared//.mdx`, see `ClerkRouter`). + * `extract-methods.mjs` reads each file, writes `-properties.mdx` with the same Properties table as TypeDoc, strips Properties from `.mdx`, and writes methods under `-methods/`. + */ +export const REFERENCE_OBJECTS_LIST = ['shared/clerk/clerk.mdx', 'shared/client-resource/client-resource.mdx']; /** * Primary interface/class documented on each reference object page (used to resolve TypeDoc reflections). */ export const REFERENCE_OBJECT_PAGE_SYMBOLS = { - 'shared/clerk.mdx': 'Clerk', - 'shared/client-resource.mdx': 'ClientResource', + 'shared/clerk/clerk.mdx': 'Clerk', + 'shared/client-resource/client-resource.mdx': 'ClientResource', }; From 6eedb5b4b3d707621afdc862f3e826f41dc1bb02 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:18:37 -0700 Subject: [PATCH 13/51] include typealias functions in the methods generation --- .typedoc/custom-plugin.mjs | 2 +- .typedoc/custom-theme.mjs | 2 ++ .typedoc/extract-methods.mjs | 41 ++++++++++++++++++++++++++++++++---- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 018e3d02a42..c6a8dde0f3a 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -102,7 +102,7 @@ const LINK_REPLACEMENTS = [ ['billing-namespace', '/docs/reference/objects/billing'], ['client-resource', '/docs/reference/objects/client'], ['redirect-options', '/docs/reference/types/redirect-options'], - ['handle-o-auth-call-back-params', '/docs/reference/types/handle-o-auth-call-back-params'], + ['handle-o-auth-callback-params', '/docs/reference/types/handle-o-auth-callback-params'], ]; /** diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 0cd40c37df5..a1b29dc10c1 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1018,3 +1018,5 @@ function isCallablePropertyValueType(t, helpers, seenReflectionIds) { } return false; } + +export { isCallableInterfaceProperty }; diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index a75189fe027..c17ebd5ed1c 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -16,6 +16,7 @@ import { Application, Comment, PageKind, ReflectionKind } from 'typedoc'; import { MarkdownPageEvent, MarkdownTheme } from 'typedoc-plugin-markdown'; import typedocConfig from '../typedoc.config.mjs'; +import { isCallableInterfaceProperty } from './custom-theme.mjs'; import { applyCatchAllMdReplacements, applyRelativeLinkReplacements, @@ -270,18 +271,50 @@ function getPrimaryCallSignature(decl) { if (t && 'declaration' in t && t.declaration?.signatures?.length) { return t.declaration.signatures[0]; } + /** + * e.g. `navigate: CustomNavigation` — for `type Fn = () => void`, signatures often live on the inner `declaration` + * of `alias.type` (ReflectionType), not on `alias.signatures` (see `custom-theme.mjs` `isCallablePropertyValueType`). + */ + if (t && typeof t === 'object' && 'type' in t && /** @type {{ type?: string }} */ (t).type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + const target = ref.reflection; + const sigs = + target && 'signatures' in target + ? /** @type {{ signatures?: import('typedoc').SignatureReflection[] }} */ (target).signatures + : undefined; + if (sigs?.length) { + return sigs[0]; + } + const aliasTarget = /** @type {import('typedoc').DeclarationReflection | undefined} */ ( + target && 'kind' in target ? target : undefined + ); + if (aliasTarget?.kind === ReflectionKind.TypeAlias && aliasTarget.type && 'declaration' in aliasTarget.type) { + const inner = /** @type {import('typedoc').ReflectionType} */ (aliasTarget.type).declaration; + if (inner?.signatures?.length) { + return inner.signatures[0]; + } + } + } return undefined; } /** + * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (callable members are + * extracted here, not listed as properties). + * * @param {import('typedoc').DeclarationReflection} decl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx */ -function isCallableMember(decl) { +function shouldExtractCallableMember(decl, ctx) { if (decl.kind === ReflectionKind.Method) { return true; } - if (decl.kind === ReflectionKind.Property || decl.kind === ReflectionKind.Accessor) { - return !!getPrimaryCallSignature(decl); + if ( + decl.kind === ReflectionKind.Property || + decl.kind === ReflectionKind.Accessor || + decl.kind === ReflectionKind.Variable + ) { + return isCallableInterfaceProperty(decl, ctx.helpers); } return false; } @@ -804,7 +837,7 @@ function extractMethodsForPage(pageUrl, project, app) { if (child.name.startsWith('__')) { continue; } - if (!isCallableMember(/** @type {import('typedoc').DeclarationReflection} */ (child))) { + if (!shouldExtractCallableMember(/** @type {import('typedoc').DeclarationReflection} */ (child), ctx)) { continue; } const mdx = buildMethodMdx(/** @type {import('typedoc').DeclarationReflection} */ (child), ctx); From ac0f71d652d9189df56f8fa787392b840e0961c0 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:11:09 -0700 Subject: [PATCH 14/51] add helpers for documenting union types in property tables --- .typedoc/__tests__/file-structure.test.ts | 2 +- .typedoc/custom-theme.mjs | 202 +++++++++++++++++++++- .typedoc/reference-objects.mjs | 2 + packages/shared/src/types/clerk.ts | 50 +++++- packages/shared/src/types/redirects.ts | 7 +- 5 files changed, 251 insertions(+), 12 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 650ff374e25..3dabedcc589 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -47,7 +47,7 @@ describe('Typedoc output', () => { it('should only have these nested folders', async () => { const folders = await scanDirectory('directory'); - const nestedFolders = folders.filter(folder => !isTopLevelPath(folder)); + const nestedFolders = folders.filter(folder => !isTopLevelPath(folder)).sort((a, b) => a.localeCompare(b)); expect(nestedFolders).toMatchInlineSnapshot(` [ diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index a1b29dc10c1..5efcb2a36fe 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1,6 +1,8 @@ // @ts-check -import { IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; +import { i18n, IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; +import { backTicks, htmlTable, table } from '../node_modules/typedoc-plugin-markdown/dist/libs/markdown/index.js'; +import { TypeDeclarationVisibility } from '../node_modules/typedoc-plugin-markdown/dist/options/maps.js'; import { REFERENCE_OBJECTS_LIST } from './reference-objects.mjs'; @@ -185,6 +187,161 @@ function mergeIntersectionPropertyReflections(intersection, project) { return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); } +/** + * For properties typed something like `false \| { a?: … }`, `getFlattenedDeclarations` does not walk the union, so nested keys + * never become table rows. Collect object members from each union arm (primitives/literals yield nothing). + * + * @param {import('typedoc').Type | undefined} t + * @param {Set} visitedReflectionIds + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection[]} + */ +function collectPropertyReflectionsFromUnionObjectArms(t, visitedReflectionIds, project) { + const unwrapped = unwrapOptionalType(t); + if (!unwrapped || /** @type {{ type?: string }} */ (unwrapped).type !== 'union') { + return []; + } + const union = /** @type {import('typedoc').UnionType} */ (unwrapped); + /** @type {Map} */ + const byName = new Map(); + for (const arm of union.types) { + for (const p of collectPropertyReflectionsFromIntersectionArm(arm, visitedReflectionIds, project)) { + byName.set(p.name, p); + } + } + return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); +} + +/** + * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs + * `getFlattenedDeclarations` (including our union-object expansion). The default plugin skips + * flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').DeclarationReflection[]} model + * @param {{ kind?: import('typedoc').ReflectionKind }} options + */ +function clerkTypeDeclarationTable(model, options) { + const tableColumnsOptions = /** @type {{ leftAlignHeaders?: boolean; hideSources?: boolean }} */ ( + this.options.getValue('tableColumnSettings') ?? {} + ); + const leftAlignHeadings = tableColumnsOptions.leftAlignHeaders; + const isCompact = this.options.getValue('typeDeclarationVisibility') === TypeDeclarationVisibility.Compact; + const hasSources = !tableColumnsOptions.hideSources && !this.options.getValue('disableSources'); + const headers = []; + const declarations = this.helpers.getFlattenedDeclarations(model, { + includeSignatures: true, + }); + const hasDefaultValues = declarations.some( + declaration => Boolean(declaration.defaultValue) && declaration.defaultValue !== '...', + ); + const hasComments = declarations.some(declaration => Boolean(declaration.comment)); + const theme = /** @type {Record string>} */ (/** @type {unknown} */ (i18n)); + headers.push(theme.theme_name()); + headers.push(theme.theme_type()); + if (hasDefaultValues) { + headers.push(theme.theme_default_value()); + } + if (hasComments) { + headers.push(theme.theme_description()); + } + if (hasSources) { + headers.push(theme.theme_defined_in()); + } + /** @type {string[][]} */ + const rows = []; + declarations.forEach(declaration => { + const declType = /** @type {{ declaration?: import('typedoc').DeclarationReflection } | undefined} */ ( + /** @type {unknown} */ (declaration.type) + ); + const optional = declaration.flags.isOptional ? '?' : ''; + const isSignature = declType?.declaration?.signatures?.length || declaration.signatures?.length; + const row = []; + const nameColumn = []; + if (this.router.hasUrl(declaration) && this.router.getAnchor(declaration)) { + nameColumn.push(``); + } + const name = backTicks(`${declaration.name}${isSignature ? '()' : ''}${optional}`); + nameColumn.push(name); + row.push(nameColumn.join(' ')); + if (isCompact && declaration.type instanceof ReflectionType) { + row.push( + this.partials.reflectionType(declaration.type, { + forceCollapse: isCompact, + }), + ); + } else { + const type = []; + const signatures = declaration.signatures; + if (signatures?.length) { + signatures.forEach(sig => { + type.push(`${this.partials.signatureParameters(sig.parameters || [])} => `); + }); + type.push(this.partials.someType(declaration.type)); + } else { + type.push(this.partials.someType(declaration.type)); + } + row.push(type.join('')); + } + if (hasDefaultValues) { + row.push( + !declaration.defaultValue || declaration.defaultValue === '...' ? '-' : backTicks(declaration.defaultValue), + ); + } + if (hasComments) { + const commentsOut = []; + if (declaration.comment) { + commentsOut.push( + this.partials.comment(declaration.comment, { + isTableColumn: true, + }), + ); + } + if (declType?.declaration?.signatures?.length) { + for (const sig of declType.declaration.signatures) { + if (sig.comment) { + commentsOut.push( + this.partials.comment(sig.comment, { + isTableColumn: true, + }), + ); + } + } + } + if (commentsOut.length) { + row.push(commentsOut.join('\n\n')); + } else { + row.push('-'); + } + } + if (hasSources) { + row.push(this.partials.sources(declaration, { hideLabel: true })); + } + rows.push(row); + }); + return clerkShouldDisplayHtmlTable(this, options?.kind) + ? htmlTable(headers, rows, leftAlignHeadings) + : table(headers, rows, leftAlignHeadings); +} + +/** + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} context + * @param {import('typedoc').ReflectionKind | undefined} kind + */ +function clerkShouldDisplayHtmlTable(context, kind) { + if ( + kind && + [ReflectionKind.CallSignature, ReflectionKind.Variable, ReflectionKind.TypeAlias].includes(kind) && + context.options.getValue('typeDeclarationFormat') == 'htmlTable' + ) { + return true; + } + if (kind === ReflectionKind.Property && context.options.getValue('propertyMembersFormat') == 'htmlTable') { + return true; + } + return false; +} + /** * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx * @param {import('typedoc').DeclarationReflection} model @@ -294,6 +451,38 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { constructor(theme, page, options) { super(theme, page, options); + const origGetFlattenedDeclarations = this.helpers.getFlattenedDeclarations.bind(this.helpers); + this.helpers.getFlattenedDeclarations = (model, options) => { + const base = origGetFlattenedDeclarations(model, options); + const project = this.page?.project ?? this.page?.model?.project; + if (!project) { + return base; + } + /** @type {typeof base} */ + const out = []; + const h = this.helpers; + for (const prop of base) { + out.push(prop); + if (prop.name.includes('.')) { + continue; + } + const nested = collectPropertyReflectionsFromUnionObjectArms(h.getDeclarationType(prop), new Set(), project); + for (const child of nested) { + out.push( + /** @type {typeof prop} */ ( + /** @type {unknown} */ ({ + ...child, + name: `${prop.name}.${child.name}`, + getFullName: () => prop.getFullName(), + getFriendlyFullName: () => prop.getFriendlyFullName(), + }) + ), + ); + } + } + return out; + }; + const superPartials = this.partials; this._insideFunctionSignature = false; @@ -315,6 +504,17 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { const filtered = allowlisted ? model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)) : model; return superPartials.propertiesTable(filtered, options); }, + /** + * In `compact` mode the default plugin skips `getFlattenedDeclarations`, so union object members never get + * rows. Delegate to {@link clerkTypeDeclarationTable} which always flattens (and picks up union expansion from + * our `getFlattenedDeclarations` wrapper). + * + * @param {import('typedoc').DeclarationReflection[]} model + * @param {{ kind?: import('typedoc').ReflectionKind }} options + */ + typeDeclarationTable: (model, options) => { + return clerkTypeDeclarationTable.call(this, model, options); + }, /** * This hides the "Type parameters" section and the signature title from the output (by default). Shows the signature title if the `@displayFunctionSignature` tag is present. * @param {import('typedoc').SignatureReflection} model diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index aa4e76bbdf4..e55f0f24bdb 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -12,6 +12,8 @@ export const REFERENCE_OBJECTS_LIST = ['shared/clerk/clerk.mdx', 'shared/client- /** * Primary interface/class documented on each reference object page (used to resolve TypeDoc reflections). + * keys = stable MDX paths under .typedoc output + * values = which TypeDoc declaration to treat as that page’s “main” type for routing and per-method extraction. */ export const REFERENCE_OBJECT_PAGE_SYMBOLS = { 'shared/clerk/clerk.mdx': 'Clerk', diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 35a1c0b51e2..1cd78cc27c4 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -215,8 +215,28 @@ type EventHandler = (payload: ClerkEventPayload[E]) => voi export type ClerkEventPayload = { status: ClerkStatus; }; -type OnEventListener = (event: E, handler: EventHandler, opt?: { notify: boolean }) => void; -type OffEventListener = (event: E, handler: EventHandler) => void; + +/** + * Registers an event listener for a specific Clerk event. + * + * @param event - The event name to subscribe to. + * @param handler - The callback function to execute when the event is dispatched. + * @param opt - Optional configuration. + * @param opt.notify - If true and the event was previously dispatched, handler will be called immediately with the latest payload. + */ +export type OnEventListener = ( + event: E, + handler: EventHandler, + opt?: { notify: boolean }, +) => void; + +/** + * Unregisters an event listener for a specific Clerk event. + * + * @param event - The event name to unsubscribe from. + * @param handler - The callback function to remove. + */ +export type OffEventListener = (event: E, handler: EventHandler) => void; /** * @inline @@ -1195,6 +1215,7 @@ type ClerkOptionsNavigation = routerDebug?: boolean; }; +/** @document */ type ClerkUnsafeOptions = { /** * Disables the console warning that is logged when Clerk is initialized with development keys. @@ -1207,6 +1228,9 @@ type ClerkUnsafeOptions = { unsafe_disableDevelopmentModeConsoleWarning?: boolean; }; +/** + * @document + */ export type ClerkOptions = ClerkOptionsNavigation & SignInForceRedirectUrl & SignInFallbackRedirectUrl & @@ -1230,13 +1254,17 @@ export type ClerkOptions = ClerkOptionsNavigation & * Optional object to localize your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ localization?: LocalizationResource; + /** + * Indicates whether Clerk should poll against Clerk's backend every 5 minutes. + * @default true + */ polling?: boolean; /** * By default, the last signed-in session is used during client initialization. This option allows you to override that behavior, e.g. by selecting a specific session. */ selectInitialSession?: (client: ClientResource) => SignedInSessionResource | null; /** - * By default, ClerkJS is loaded with the assumption that cookies can be set (browser setup). On native platforms this value must be set to `false`. + * Indicates whether ClerkJS is loaded with the assumption that cookies can be set (browser setup). On native platforms this value must be set to `false`. */ standardBrowser?: boolean; /** @@ -1252,7 +1280,7 @@ export type ClerkOptions = ClerkOptionsNavigation & */ signInUrl?: string; /** - * This URL will be used for any redirects that might happen and needs to point to your primary application on the client-side. This option is optional for production instances but **must be set for a satellite application in a development instance**. It's recommended to use [the environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. + * This URL will be used for any redirects that might happen and needs to point to your primary application on the client-side. This option is optional for production instances. **It is required to be set for a satellite application in a development instance**. It's recommended to use [the environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. */ signUpUrl?: string; /** @@ -1264,7 +1292,7 @@ export type ClerkOptions = ClerkOptionsNavigation & */ allowedRedirectProtocols?: Array; /** - * This option defines that the application is a satellite application. + * Indicates whether the application is a satellite application. */ isSatellite?: boolean | ((url: URL) => boolean); /** @@ -1287,9 +1315,12 @@ export type ClerkOptions = ClerkOptionsNavigation & telemetry?: | false | { + /** + * If `true`, telemetry will not be collected. + */ disabled?: boolean; /** - * Telemetry events are only logged to the console and not sent to Clerk + * If `true`, telemetry events are only logged to the console and not sent to Clerk */ debug?: boolean; /** @@ -2736,11 +2767,14 @@ export interface BrowserClerkConstructor { new (publishableKey: string, options?: DomainOrProxyUrl): BrowserClerk; } +/** + * Browser `Clerk` instance after `@clerk/clerk-js` loads. Extends [`Clerk`](https://clerk.com/docs/reference/objects/clerk) with `load()` and related browser-only APIs. + */ export interface HeadlessBrowserClerk extends Clerk { /** - * Initializes the `Clerk` object and loads all necessary environment configuration and instance settings from the [Frontend API](/docs/reference/frontend-api){{ target: '_blank' }}. + * Initializes the `Clerk` object and loads all necessary environment configuration and instance settings from the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }}. * - * For the JavaScript SDK, you must call this method before using the `Clerk` object in your code. Refer to the [quickstart guide](/docs/js-frontend/getting-started/quickstart) for more information. + * When using the JavaScript SDK, you must call the `load()` method before using the `Clerk` object in your code. Refer to the [quickstart guide](https://clerk.com/docs/js-frontend/getting-started/quickstart) for more information. */ load: (opts?: Without) => Promise; updateClient: (client: ClientResource) => void; diff --git a/packages/shared/src/types/redirects.ts b/packages/shared/src/types/redirects.ts index 41e53ebc2cb..8788da83bb0 100644 --- a/packages/shared/src/types/redirects.ts +++ b/packages/shared/src/types/redirects.ts @@ -1,12 +1,14 @@ import type { EnterpriseSSOStrategy, OAuthStrategy } from './strategies'; +/** @document */ export type AfterSignOutUrl = { /** - * Full URL or path to navigate to after successful sign out. + * The full URL or path to navigate to after successful sign out. */ afterSignOutUrl?: string | null; }; +/** @document */ export type AfterMultiSessionSingleSignOutUrl = { /** * The full URL or path to navigate to after signing out the current user is complete. @@ -126,9 +128,10 @@ export type SignInForceRedirectUrl = { signInForceRedirectUrl?: string | null; }; +/** @document */ export type NewSubscriptionRedirectUrl = { /** - * The URL to navigate to after the user completes the checkout and clicks the "Continue" button. + * The full URL or path to navigate to after the user completes the checkout and clicks the "Continue" button. */ newSubscriptionRedirectUrl?: string | null; }; From acfba2411975c65af67f0dd29642d83dc6757878 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:30:17 -0700 Subject: [PATCH 15/51] cleaning up comments --- .typedoc/custom-theme.mjs | 74 ++++++++++++++---------------- .typedoc/extract-methods.mjs | 25 +++------- packages/shared/src/types/clerk.ts | 4 +- 3 files changed, 43 insertions(+), 60 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 5efcb2a36fe..75c8cbb3d69 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -82,6 +82,7 @@ function findNamedTypeDeclaration(project, name) { /** * Collect documented property reflections from one intersection arm (object literal, type alias, interface, nested `&`). + * E.g. `{ a: string } & { b: number }` => `[{ name: 'a', type: 'string' }, { name: 'b', type: 'number' }]` * * @param {import('typedoc').Type} t * @param {Set} visitedReflectionIds @@ -170,6 +171,8 @@ function collectPropertyReflectionsFromIntersectionArm(t, visitedReflectionIds, /** * Merge intersection arms into one property list (later duplicate names override earlier ones, then sort by name). + * type A = type B & type C; type A's properties will be the union of B's and C's properties. + * E.g. `ClerkOptions` in clerk.ts * * @param {import('typedoc').IntersectionType} intersection * @param {import('typedoc').ProjectReflection | undefined} project @@ -190,6 +193,7 @@ function mergeIntersectionPropertyReflections(intersection, project) { /** * For properties typed something like `false \| { a?: … }`, `getFlattenedDeclarations` does not walk the union, so nested keys * never become table rows. Collect object members from each union arm (primitives/literals yield nothing). + * E.g. `telemetry` prop in clerk.ts * * @param {import('typedoc').Type | undefined} t * @param {Set} visitedReflectionIds @@ -213,9 +217,27 @@ function collectPropertyReflectionsFromUnionObjectArms(t, visitedReflectionIds, } /** - * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs - * `getFlattenedDeclarations` (including our union-object expansion). The default plugin skips - * flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. + * Used in `clerkTypeDeclarationTable()` to determine if the table should be displayed as an HTML table. + * + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} context + * @param {import('typedoc').ReflectionKind | undefined} kind + */ +function clerkShouldDisplayHtmlTable(context, kind) { + if ( + kind && + [ReflectionKind.CallSignature, ReflectionKind.Variable, ReflectionKind.TypeAlias].includes(kind) && + context.options.getValue('typeDeclarationFormat') == 'htmlTable' + ) { + return true; + } + if (kind === ReflectionKind.Property && context.options.getValue('propertyMembersFormat') == 'htmlTable') { + return true; + } + return false; +} + +/** + * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` (including our union-object expansion). The default plugin skips flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. * * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} * @param {import('typedoc').DeclarationReflection[]} model @@ -324,24 +346,6 @@ function clerkTypeDeclarationTable(model, options) { : table(headers, rows, leftAlignHeadings); } -/** - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} context - * @param {import('typedoc').ReflectionKind | undefined} kind - */ -function clerkShouldDisplayHtmlTable(context, kind) { - if ( - kind && - [ReflectionKind.CallSignature, ReflectionKind.Variable, ReflectionKind.TypeAlias].includes(kind) && - context.options.getValue('typeDeclarationFormat') == 'htmlTable' - ) { - return true; - } - if (kind === ReflectionKind.Property && context.options.getValue('propertyMembersFormat') == 'htmlTable') { - return true; - } - return false; -} - /** * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx * @param {import('typedoc').DeclarationReflection} model @@ -423,11 +427,12 @@ const unionCommentMap = new Map(); /** * Only for the specified pages do we remove function-valued members from property tables in the "Properties" section. + * (Created for the extract-methods script as these methods will be extracted to separate files.) * * @param {string | undefined} pageUrl - The URL of the page to check. * @param {readonly string[]} allowlist - The list of pages to check. */ -function pageMatchesPropertyTableFunctionFilterAllowlist(pageUrl, allowlist) { +function pageMatchesAllowlist(pageUrl, allowlist) { if (!pageUrl) { return false; } @@ -490,9 +495,6 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, /** - * On allowlisted output pages only (see `PROPERTY_TABLE_EXCLUDE_FUNCTIONS_ALLOWLIST`): drop function-valued - * interface/class properties from property tables (property syntax with function types). Other pages unchanged. - * * @param {import('typedoc').DeclarationReflection[]} model * @param {Parameters[1]} [options] */ @@ -500,14 +502,14 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { if (!Array.isArray(model)) { return superPartials.propertiesTable(/** @type {any} */ (model), options); } - const allowlisted = pageMatchesPropertyTableFunctionFilterAllowlist(this.page?.url, REFERENCE_OBJECTS_LIST); + + // On allowlisted output pages only, drop function-valued interface/class properties from property tables (property syntax with function types). Other pages unchanged. + const allowlisted = pageMatchesAllowlist(this.page?.url, REFERENCE_OBJECTS_LIST); const filtered = allowlisted ? model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)) : model; return superPartials.propertiesTable(filtered, options); }, /** - * In `compact` mode the default plugin skips `getFlattenedDeclarations`, so union object members never get - * rows. Delegate to {@link clerkTypeDeclarationTable} which always flattens (and picks up union expansion from - * our `getFlattenedDeclarations` wrapper). + * In `compact` mode the default plugin skips `getFlattenedDeclarations`, so union object members never get rows. Delegate to {@link clerkTypeDeclarationTable} which always flattens (and picks up union expansion from our `getFlattenedDeclarations` wrapper). * * @param {import('typedoc').DeclarationReflection[]} model * @param {{ kind?: import('typedoc').ReflectionKind }} options @@ -1122,10 +1124,7 @@ function swap(arr, i, j) { * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers */ function isCallableInterfaceProperty(prop, helpers) { - /** - * Use the declared value type for properties. `getDeclarationType` mirrors accessor/parameter behavior and can - * return the wrong node when TypeDoc attaches signatures to the property (same class of bug as TypeAlias + `decl.type`). - */ + // Use the declared value type for properties. `getDeclarationType` mirrors accessor/parameter behavior and can return the wrong node when TypeDoc attaches signatures to the property (same class of bug as TypeAlias + `decl.type`). const t = (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type ? prop.type @@ -1134,8 +1133,8 @@ function isCallableInterfaceProperty(prop, helpers) { } /** - * True when the property's value type is callable (function type, union/intersection of callables, or reference to a - * type alias of a function type). Object types with properties (e.g. namespaces) stay false. + * True when the property's value type is callable (function type, union/intersection of callables, or reference to a type alias of a function type). Object types with properties (e.g. namespaces) stay false. + * E.g. `navigate: CustomNavigation` in clerk.ts * * @param {import('typedoc').Type | undefined} t * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers @@ -1174,10 +1173,7 @@ function isCallablePropertyValueType(t, helpers, seenReflectionIds) { } if (t instanceof ReferenceType) { /** - * Unresolved reference (`reflection` missing): TypeDoc did not link the symbol (not in entry graph, external, - * filtered, etc.). We cannot tell a function alias from an interface, so we only treat a few **name** patterns as - * callable (`*Function`, `*Listener`). For anything else, ensure the type is part of the documented program so - * `reflection` resolves and the structural checks above apply — do not add one-off type names here. + * Unresolved reference (`reflection` missing): TypeDoc did not link the symbol (not in entry graph, external, filtered, etc.). We cannot tell a function alias from an interface, so we only treat a few **name** patterns as callable (`*Function`, `*Listener`). For anything else, ensure the type is part of the documented program so `reflection` resolves and the structural checks above apply — do not add one-off type names here. * E.g. `CustomNavigation`, `RouterFn`, etc. */ if (!t.reflection && typeof t.name === 'string' && /(?:Function|Listener)$/.test(t.name)) { diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index c17ebd5ed1c..eb80497999f 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -60,10 +60,7 @@ function appendMarkdownTableRows(tableMd, rowLines) { } /** - * Post-process the theme’s parameters markdown table. TypeDoc flattens object params as `parent.child` and may - * interleave those rows with other parameters. Here we (1) move each `parent.*` block directly under `parent`, - * and (2) rewrite dotted paths in the name column to optional-chaining (`parent?.child`, `a?.b?.c`). Top-level - * names are unchanged (`foo?`, `exa`). + * Post-process the theme’s parameters markdown table. TypeDoc flattens object params as `parent.child` and may interleave those rows with other parameters. Here we (1) move each `parent.*` block directly under `parent`, and (2) rewrite dotted paths in the name column to optional-chaining (`parent?.child`, `a?.b?.c`). Top-level names are unchanged (`foo?`, `exa`). * * @param {string} tableMd */ @@ -271,10 +268,7 @@ function getPrimaryCallSignature(decl) { if (t && 'declaration' in t && t.declaration?.signatures?.length) { return t.declaration.signatures[0]; } - /** - * e.g. `navigate: CustomNavigation` — for `type Fn = () => void`, signatures often live on the inner `declaration` - * of `alias.type` (ReflectionType), not on `alias.signatures` (see `custom-theme.mjs` `isCallablePropertyValueType`). - */ + // E.g. `navigate: CustomNavigation` — for `type Fn = () => void`, signatures often live on the inner `declaration` of `alias.type` (ReflectionType), not on `alias.signatures` (see `custom-theme.mjs` `isCallablePropertyValueType`). if (t && typeof t === 'object' && 'type' in t && /** @type {{ type?: string }} */ (t).type === 'reference') { const ref = /** @type {import('typedoc').ReferenceType} */ (t); const target = ref.reflection; @@ -299,8 +293,7 @@ function getPrimaryCallSignature(decl) { } /** - * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (callable members are - * extracted here, not listed as properties). + * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (callable members are extracted here, not listed as properties). * * @param {import('typedoc').DeclarationReflection} decl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx @@ -400,8 +393,7 @@ function formatTypeScriptSignature(sig, memberName) { } /** - * `@returns - foo` is often stored with a leading dash, which renders as a bullet. Normalize to prose for - * "Returns …" lines. + * `@returns - foo` is often stored with a leading dash, which renders as a bullet. Normalize to prose for "Returns …" lines. * @param {string} body */ function normalizeReturnsBody(body) { @@ -571,8 +563,7 @@ function formatNestedParamNameColumn(baseName, pathSegments) { } /** - * Rows for object properties that have documentation (including from `@param parent.prop` on the method), - * which TypeDoc stores on property reflections rather than leaving `@param` block tags on the signature. + * Rows for object properties that have documentation (including from `@param parent.prop` on the method), which TypeDoc stores on property reflections rather than leaving `@param` block tags on the signature. * * @param {import('typedoc').ParameterReflection} param * @returns {string[]} @@ -642,8 +633,7 @@ function lookupInterfaceOrTypeAliasByName(project, name) { } /** - * Unwrap optional wrappers. When the parameter is a single named interface or type alias for an object - * shape, returns that name and the declaration holding object properties. + * Unwrap optional wrappers. When the parameter is a single named interface or type alias for an object shape, returns that name and the declaration holding object properties. * * @param {import('typedoc').SomeType | undefined} t * @param {import('typedoc').ProjectReflection} project @@ -703,8 +693,7 @@ function isNominalParamTypeDocumented(typeDecl, props) { } /** - * Single parameter that is a named object type (interface / type alias): one section titled after the type, - * table lists every property (not the outer `params` row). Uses the same `propertiesTable` partial as TypeDoc. + * Single parameter that is a named object type (interface / type alias): one section titled after the type, table lists every property (not the outer `params` row). Uses the same `propertiesTable` partial as TypeDoc. * * @param {import('typedoc').SignatureReflection} sig * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 1cd78cc27c4..6b2422b15e5 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -1228,9 +1228,7 @@ type ClerkUnsafeOptions = { unsafe_disableDevelopmentModeConsoleWarning?: boolean; }; -/** - * @document - */ +/** @document */ export type ClerkOptions = ClerkOptionsNavigation & SignInForceRedirectUrl & SignInFallbackRedirectUrl & From 67bfa0bdf87af0c02a8c1d826aabf206f2d53ced Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:38:58 -0700 Subject: [PATCH 16/51] fix links --- packages/shared/src/types/clerk.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 6b2422b15e5..b51e094242c 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -864,7 +864,7 @@ export interface Clerk { buildSignUpUrl(opts?: RedirectOptions): string; /** - * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/user-profile) is mounted or a custom user-profile page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/user/user-profile) is mounted or a custom user-profile page is rendered. */ buildUserProfileUrl(): string; @@ -990,7 +990,7 @@ export interface Clerk { redirectToWaitlist: () => void; /** - * Redirects to the configured URL where [session tasks](https://clerk.com/docs/reference/objects/session#currenttask) are mounted. + * Redirects to the configured URL where [session tasks](https://clerk.com/docs/reference/objects/session) are mounted. * * @param opts - Options to control the redirect (e.g. redirect URL after tasks are complete). */ From 064abe1d8885e81b2cda8e2837fba7fc88ad4bcf Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:54:09 -0700 Subject: [PATCH 17/51] fix: handle union objects only for type declaration tables --- .typedoc/custom-theme.mjs | 82 +++++++++++++++----------- packages/shared/src/types/clerk.ts | 26 ++++---- packages/shared/src/types/telemetry.ts | 12 ++++ 3 files changed, 75 insertions(+), 45 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 75c8cbb3d69..38007884841 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -216,6 +216,43 @@ function collectPropertyReflectionsFromUnionObjectArms(t, visitedReflectionIds, return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); } +/** + * Appends `parent.child` rows for union object arms (e.g. `false \| { disabled?: … }`). **Only** used when building + * {@link clerkTypeDeclarationTable}; we intentionally do **not** hook `helpers.getFlattenedDeclarations` globally — + * otherwise top-level `propertiesTable` output (e.g. `Clerk`) would gain synthetic rows like `client.*` for every + * property whose type is a union such as `ClientResource \| undefined`. + * + * @template {import('typedoc').DeclarationReflection} T + * @param {T[]} base + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + * @param {import('typedoc').ProjectReflection} project + * @returns {T[]} + */ +function appendUnionObjectChildPropertyRows(base, helpers, project) { + /** @type {T[]} */ + const out = []; + for (const prop of base) { + out.push(prop); + if (prop.name.includes('.')) { + continue; + } + const nested = collectPropertyReflectionsFromUnionObjectArms(helpers.getDeclarationType(prop), new Set(), project); + for (const child of nested) { + out.push( + /** @type {T} */ ( + /** @type {unknown} */ ({ + ...child, + name: `${prop.name}.${child.name}`, + getFullName: () => prop.getFullName(), + getFriendlyFullName: () => prop.getFriendlyFullName(), + }) + ), + ); + } + } + return out; +} + /** * Used in `clerkTypeDeclarationTable()` to determine if the table should be displayed as an HTML table. * @@ -237,7 +274,9 @@ function clerkShouldDisplayHtmlTable(context, kind) { } /** - * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` (including our union-object expansion). The default plugin skips flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. + * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` + * and then {@link appendUnionObjectChildPropertyRows} (union-object arm rows like `telemetry.*`). The default plugin + * skips flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. * * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} * @param {import('typedoc').DeclarationReflection[]} model @@ -251,9 +290,13 @@ function clerkTypeDeclarationTable(model, options) { const isCompact = this.options.getValue('typeDeclarationVisibility') === TypeDeclarationVisibility.Compact; const hasSources = !tableColumnsOptions.hideSources && !this.options.getValue('disableSources'); const headers = []; - const declarations = this.helpers.getFlattenedDeclarations(model, { + const baseDeclarations = this.helpers.getFlattenedDeclarations(model, { includeSignatures: true, }); + const project = this.page?.project ?? this.page?.model?.project; + const declarations = project + ? appendUnionObjectChildPropertyRows(baseDeclarations, this.helpers, project) + : baseDeclarations; const hasDefaultValues = declarations.some( declaration => Boolean(declaration.defaultValue) && declaration.defaultValue !== '...', ); @@ -456,38 +499,6 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { constructor(theme, page, options) { super(theme, page, options); - const origGetFlattenedDeclarations = this.helpers.getFlattenedDeclarations.bind(this.helpers); - this.helpers.getFlattenedDeclarations = (model, options) => { - const base = origGetFlattenedDeclarations(model, options); - const project = this.page?.project ?? this.page?.model?.project; - if (!project) { - return base; - } - /** @type {typeof base} */ - const out = []; - const h = this.helpers; - for (const prop of base) { - out.push(prop); - if (prop.name.includes('.')) { - continue; - } - const nested = collectPropertyReflectionsFromUnionObjectArms(h.getDeclarationType(prop), new Set(), project); - for (const child of nested) { - out.push( - /** @type {typeof prop} */ ( - /** @type {unknown} */ ({ - ...child, - name: `${prop.name}.${child.name}`, - getFullName: () => prop.getFullName(), - getFriendlyFullName: () => prop.getFriendlyFullName(), - }) - ), - ); - } - } - return out; - }; - const superPartials = this.partials; this._insideFunctionSignature = false; @@ -509,7 +520,8 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { return superPartials.propertiesTable(filtered, options); }, /** - * In `compact` mode the default plugin skips `getFlattenedDeclarations`, so union object members never get rows. Delegate to {@link clerkTypeDeclarationTable} which always flattens (and picks up union expansion from our `getFlattenedDeclarations` wrapper). + * In `compact` mode the default plugin skips `getFlattenedDeclarations`, so union object members never get rows. + * Delegate to {@link clerkTypeDeclarationTable} which always flattens and applies {@link appendUnionObjectChildPropertyRows}. * * @param {import('typedoc').DeclarationReflection[]} model * @param {{ kind?: import('typedoc').ReflectionKind }} options diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index b51e094242c..172e4f9d8fd 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -240,6 +240,10 @@ export type OffEventListener = (event: E, handler: EventHa /** * @inline + * @property {ClerkStatus} degraded - Set when Clerk is partially operational. + * @property {ClerkStatus} error - Set when hotloading `clerk-js` or `Clerk.load()` failed. + * @property {ClerkStatus} loading - Set during initialization. + * @property {ClerkStatus} ready - Set when Clerk is fully operational. */ export type ClerkStatus = 'degraded' | 'error' | 'loading' | 'ready'; @@ -265,10 +269,12 @@ export interface Clerk { /** * The status of the `Clerk` instance. Possible values are: - * - `"error"`: Set when hotloading `clerk-js` or `Clerk.load()` failed. - * - `"loading"`: Set during initialization. - * - `"ready"`: Set when Clerk is fully operational. - * - `"degraded"`: Set when Clerk is partially operational. + *
    + *
  • `"error"`: Set when hotloading `clerk-js` or `Clerk.load()` failed.
  • + *
  • `"loading"`: Set during initialization.
  • + *
  • `"ready"`: Set when Clerk is fully operational.
  • + *
  • `"degraded"`: Set when Clerk is partially operational.
  • + *
*/ status: ClerkStatus; @@ -282,7 +288,7 @@ export interface Clerk { /** Your Clerk [Publishable Key](!publishable-key). */ publishableKey: string; - /** Your Clerk app's proxy URL. Required for applications that run behind a reverse proxy. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`). */ + /** **Required for applications that run behind a reverse proxy**. Your Clerk app's proxy URL. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`). */ proxyUrl: string | undefined; /** The current Clerk app's domain. Prefixed with `clerk.` on production if not already prefixed. Returns `""` when ran on the server. */ @@ -1031,27 +1037,27 @@ export interface Clerk { ) => Promise; /** - * Starts a sign-in flow that uses the MetaMask browser extension to authenticate the user using their Metamask wallet address. + * Starts a sign-in flow that uses MetaMask to authenticate the user using their Metamask wallet address. */ authenticateWithMetamask: (params?: AuthenticateWithMetamaskParams) => Promise; /** - * Starts a sign-in flow that uses the Coinbase Smart Wallet and browser extension to authenticate the user using their Coinbase wallet address. + * Starts a sign-in flow that uses Coinbase Smart Wallet to authenticate the user using their Coinbase wallet address. */ authenticateWithCoinbaseWallet: (params?: AuthenticateWithCoinbaseWalletParams) => Promise; /** - * Starts a sign-in flow that uses the OKX Wallet browser extension to authenticate the user using their OKX wallet address. + * Starts a sign-in flow that uses OKX Wallet to authenticate the user using their OKX wallet address. */ authenticateWithOKXWallet: (params?: AuthenticateWithOKXWalletParams) => Promise; /** - * Starts a sign-in flow that uses the Base Account SDK to authenticate the user using their Base wallet address. + * Starts a sign-in flow that uses Base to authenticate the user using their Web3 wallet address. */ authenticateWithBase: (params?: AuthenticateWithBaseParams) => Promise; /** - * Starts a sign-in flow that uses a Solana supported Web3 wallet browser extension to authenticate the user using their Solana wallet address. + * Starts a sign-in flow that uses Solana to authenticate the user using their Solana wallet address. */ authenticateWithSolana: (params: AuthenticateWithSolanaParams) => Promise; diff --git a/packages/shared/src/types/telemetry.ts b/packages/shared/src/types/telemetry.ts index 14643c3ea65..b7009bffb6b 100644 --- a/packages/shared/src/types/telemetry.ts +++ b/packages/shared/src/types/telemetry.ts @@ -61,8 +61,20 @@ export interface TelemetryLogEntry { * @inline */ export interface TelemetryCollector { + /** + * If `true`, telemetry events are only logged to the console and not sent to Clerk. + */ isEnabled: boolean; + /** + * If `true`, telemetry events are only logged to the console and not sent to Clerk. + */ isDebug: boolean; + /** + * Records a telemetry event. + */ record(event: TelemetryEventRaw): void; + /** + * Records a telemetry log entry. + */ recordLog(entry: TelemetryLogEntry): void; } From a6f285751bd34cf7cad2d41c0e18290a9da0adcb Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Tue, 14 Apr 2026 17:42:22 -0700 Subject: [PATCH 18/51] remove slugs from properties file/methods folder; do not flatten inline parameters with single properties --- .typedoc/__tests__/file-structure.test.ts | 4 +- .typedoc/custom-plugin.mjs | 2 +- .typedoc/custom-router.mjs | 4 +- .typedoc/custom-theme.mjs | 124 ++++++++++++++++++++++ .typedoc/extract-methods.mjs | 8 +- .typedoc/reference-objects.mjs | 2 +- packages/shared/src/types/client.ts | 14 +-- packages/shared/src/types/resource.ts | 4 + 8 files changed, 142 insertions(+), 20 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 3dabedcc589..d562a054ec1 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -53,9 +53,9 @@ describe('Typedoc output', () => { [ "react/legacy", "shared/clerk", - "shared/clerk/clerk-methods", + "shared/clerk/methods", "shared/client-resource", - "shared/client-resource/client-resource-methods", + "shared/client-resource/methods", ] `); }); diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index c6a8dde0f3a..93b6f88ea3a 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -331,7 +331,7 @@ function restoreProtectedInlineCodeSpans(text, placeholders) { /** * Remove the Properties section (heading + table) from reference object pages (e.g. `shared/clerk/clerk.mdx`); - * the table is copied into `shared//-properties.mdx` by `extract-methods.mjs`. + * the table is copied into `shared//properties.mdx` by `extract-methods.mjs`. * * @param {string} contents */ diff --git a/.typedoc/custom-router.mjs b/.typedoc/custom-router.mjs index b273745b2cb..32e6baabb42 100644 --- a/.typedoc/custom-router.mjs +++ b/.typedoc/custom-router.mjs @@ -79,8 +79,8 @@ class ClerkRouter extends MemberRouter { filePath = flattenDirName(filePath); /** - * Put each reference object in its own folder alongside `-properties.mdx` and `-methods/` from `extract-methods.mjs`. - * E.g. `shared/clerk.mdx` -> `shared/clerk/clerk.mdx` and `shared/clerk/clerk-properties.mdx` and `shared/clerk/clerk-methods/`. + * Put each reference object in its own folder alongside `properties.mdx` and `methods/` from `extract-methods.mjs`. + * E.g. `shared/clerk.mdx` -> `shared/clerk/clerk.mdx`, `shared/clerk/properties.mdx`, and `shared/clerk/methods/`. */ if ( (reflection.kind === ReflectionKind.Interface || reflection.kind === ReflectionKind.Class) && diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 38007884841..ad4aed1b854 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -2,6 +2,7 @@ import { i18n, IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; import { backTicks, htmlTable, table } from '../node_modules/typedoc-plugin-markdown/dist/libs/markdown/index.js'; +import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; import { TypeDeclarationVisibility } from '../node_modules/typedoc-plugin-markdown/dist/options/maps.js'; import { REFERENCE_OBJECTS_LIST } from './reference-objects.mjs'; @@ -253,6 +254,121 @@ function appendUnionObjectChildPropertyRows(base, helpers, project) { return out; } +/** + * @param {import('typedoc').ParameterReflection[]} parameters + */ +function hasDefaultValuesForParameters(parameters) { + const defaultValues = parameters.map( + param => param.defaultValue !== '{}' && param.defaultValue !== '...' && !!param.defaultValue, + ); + return !defaultValues.every(value => !value); +} + +/** + * Same as typedoc-plugin-markdown `member.parametersTable`, but does **not** flatten inline object parameters when the object has only one property (avoids duplicate rows). + * E.g. `params: { redirectUrl: string }` would result in duplicate rows like `params` and `params.redirectUrl` with the same `@param` description. This is because the default plugin flattens inline object parameters when the object has only one property, but we don't want to do that. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').ParameterReflection[]} model + */ +function clerkParametersTable(model) { + const tableColumnsOptions = /** @type {{ leftAlignHeaders?: boolean; hideDefaults?: boolean }} */ ( + this.options.getValue('tableColumnSettings') ?? {} + ); + const leftAlignHeadings = tableColumnsOptions.leftAlignHeaders; + /** + * @param {import('typedoc').ParameterReflection} current + * @param {import('typedoc').ParameterReflection[]} acc + * @returns {import('typedoc').ParameterReflection[]} + */ + const parseParams = (current, acc) => { + const decl = /** @type {{ declaration?: import('typedoc').DeclarationReflection } | undefined} */ ( + /** @type {unknown} */ (current.type) + )?.declaration; + const children = decl?.children; + const shouldFlatten = decl?.kind === ReflectionKind.TypeLiteral && children && children.length > 1; + return shouldFlatten ? [...acc, current, ...flattenParams(current)] : [...acc, current]; + }; + /** + * @param {import('typedoc').ParameterReflection} current + * @returns {import('typedoc').ParameterReflection[]} + */ + const flattenParams = current => { + const decl = /** @type {{ declaration?: import('typedoc').DeclarationReflection } | undefined} */ ( + /** @type {unknown} */ (current.type) + )?.declaration; + return ( + decl?.children?.reduce( + /** + * @param {import('typedoc').ParameterReflection[]} acc + * @param {import('typedoc').DeclarationReflection} child + * @returns {import('typedoc').ParameterReflection[]} + */ + (acc, child) => { + const childObj = { + ...child, + name: `${current.name}.${child.name}`, + }; + return parseParams( + /** @type {import('typedoc').ParameterReflection} */ (/** @type {unknown} */ (childObj)), + acc, + ); + }, + /** @type {import('typedoc').ParameterReflection[]} */ ([]), + ) ?? [] + ); + }; + const showDefaults = !tableColumnsOptions.hideDefaults && hasDefaultValuesForParameters(model); + const parsedParams = /** @type {import('typedoc').ParameterReflection[]} */ ( + model.reduce( + (acc, current) => parseParams(current, acc), + /** @type {import('typedoc').ParameterReflection[]} */ ([]), + ) + ); + const hasComments = parsedParams.some(param => Boolean(param.comment)); + const theme = /** @type {Record string>} */ (/** @type {unknown} */ (i18n)); + const headers = [ReflectionKind.singularString(ReflectionKind.Parameter), theme.theme_type()]; + if (showDefaults) { + headers.push(theme.theme_default_value()); + } + if (hasComments) { + headers.push(theme.theme_description()); + } + const firstOptionalParamIndex = model.findIndex(parameter => parameter.flags.isOptional); + /** @type {string[][]} */ + const rows = []; + parsedParams.forEach((parameter, i) => { + const row = []; + const isOptional = parameter.flags.isOptional || (firstOptionalParamIndex !== -1 && i > firstOptionalParamIndex); + const rest = parameter.flags?.isRest ? '...' : ''; + const optional = isOptional ? '?' : ''; + row.push(`${rest}${backTicks(`${parameter.name}${optional}`)}`); + if (parameter.type) { + const displayType = + parameter.type instanceof ReflectionType + ? this.partials.reflectionType(parameter.type, { + forceCollapse: true, + }) + : this.partials.someType(parameter.type); + row.push(removeLineBreaks(displayType)); + } + if (showDefaults) { + row.push(backTicks(this.helpers.getParameterDefaultValue(parameter))); + } + if (hasComments) { + if (parameter.comment) { + row.push(this.partials.comment(parameter.comment, { isTableColumn: true })); + } else { + row.push('-'); + } + } + rows.push(row); + }); + return this.options.getValue('parametersFormat') == 'table' + ? table(headers, rows, leftAlignHeadings) + : htmlTable(headers, rows, leftAlignHeadings); +} + /** * Used in `clerkTypeDeclarationTable()` to determine if the table should be displayed as an HTML table. * @@ -519,6 +635,14 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { const filtered = allowlisted ? model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)) : model; return superPartials.propertiesTable(filtered, options); }, + /** + * Parameter tables: same as the stock partial except single-property inline object params are not expanded to nested rows. + * + * @param {import('typedoc').ParameterReflection[]} model + */ + parametersTable: model => { + return clerkParametersTable.call(this, model); + }, /** * In `compact` mode the default plugin skips `getFlattenedDeclarations`, so union object members never get rows. * Delegate to {@link clerkTypeDeclarationTable} which always flattens and applies {@link appendUnionObjectChildPropertyRows}. diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index eb80497999f..ac612371f03 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -1,6 +1,6 @@ // @ts-check /** - * For each entry in REFERENCE_OBJECTS_LIST, reads the TypeDoc output (e.g. `shared/clerk/clerk.mdx`), strips **Properties** from the main generated file and copies it into `-properties.mdx`, and writes one .mdx per method under `-methods/`. + * For each entry in REFERENCE_OBJECTS_LIST, reads the TypeDoc output (e.g. `shared/clerk/clerk.mdx`), strips **Properties** from the main generated file and copies it into `properties.mdx`, and writes one .mdx per method under `methods/` (alongside the main page in that resource folder). * * Run after `typedoc` (same cwd as repo root). Uses a second TypeDoc convert pass to read reflections. * @@ -342,13 +342,12 @@ function extractPropertiesAndTrimSourcePage(pageUrl) { const raw = fs.readFileSync(sourcePath, 'utf-8'); const body = extractPropertiesSectionBody(raw); const pageDir = path.dirname(pageUrl); - const slug = path.basename(pageUrl, '.mdx'); const objectDir = path.join(__dirname, 'temp-docs', pageDir); fs.mkdirSync(objectDir, { recursive: true }); if (body) { const propertiesDoc = [`## Properties`, '', body.trimEnd(), ''].join('\n'); - const propertiesPath = path.join(objectDir, `${slug}-properties.mdx`); + const propertiesPath = path.join(objectDir, 'properties.mdx'); fs.writeFileSync( propertiesPath, applyCatchAllMdReplacements(applyRelativeLinkReplacements(propertiesDoc)), @@ -816,9 +815,8 @@ function extractMethodsForPage(pageUrl, project, app) { const ctx = createThemeContextForReferencePage(app, project, pageUrl, decl); const pageDir = path.dirname(pageUrl); - const slug = path.basename(pageUrl, '.mdx'); const objectDir = path.join(__dirname, 'temp-docs', pageDir); - const outDir = path.join(objectDir, `${slug}-methods`); + const outDir = path.join(objectDir, 'methods'); fs.mkdirSync(outDir, { recursive: true }); let count = 0; diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index e55f0f24bdb..29937f9902d 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -6,7 +6,7 @@ /** * TypeDoc output paths for the main reference pages (`shared//.mdx`, see `ClerkRouter`). - * `extract-methods.mjs` reads each file, writes `-properties.mdx` with the same Properties table as TypeDoc, strips Properties from `.mdx`, and writes methods under `-methods/`. + * `extract-methods.mjs` reads each file, writes `properties.mdx` with the same Properties table as TypeDoc, strips Properties from `.mdx`, and writes methods under `methods/`. */ export const REFERENCE_OBJECTS_LIST = ['shared/clerk/clerk.mdx', 'shared/client-resource/client-resource.mdx']; diff --git a/packages/shared/src/types/client.ts b/packages/shared/src/types/client.ts index 27bf97ef0ff..3ef3f16d0cb 100644 --- a/packages/shared/src/types/client.ts +++ b/packages/shared/src/types/client.ts @@ -17,9 +17,10 @@ export interface ClientResource extends ClerkResource { sessions: SessionResource[]; /** * A list of sessions on this client where the user has completed the full sign-in flow. Sessions can be in one of the following states: - * - * - **active**: The user has completed the full sign-in flow and all pending tasks. - * - **pending**: The user has completed the sign-in flow but still needs to complete one or more required steps (**pending tasks**). + *
    + *
  • `"active"`: The user has completed the full sign-in flow and all pending tasks.
  • + *
  • `"pending"`: The user has completed the sign-in flow but still needs to complete one or more required steps (**pending tasks**).
  • + *
*/ signedInSessions: SignedInSessionResource[]; /** @@ -39,22 +40,18 @@ export interface ClientResource extends ClerkResource { */ create: () => Promise; /** - * * Deletes the client. All sessions will be reset. */ destroy: () => Promise; /** - * * Removes all sessions created on the client. */ removeSessions: () => Promise; /** - * * Clears any locally cached session data for the current client. */ clearCache: () => void; /** - * * Resets the current sign-in attempt. Clears the in-progress sign-in state on the client. */ resetSignIn: () => void; @@ -63,14 +60,13 @@ export interface ClientResource extends ClerkResource { */ resetSignUp: () => void; /** - * * Returns `true` if the client cookie is due to expire in 8 days or less. Returns `false` otherwise. */ isEligibleForTouch: () => boolean; /** - * * Builds a URL that refreshes the current client's authentication state and then redirects the user to the specified URL. * + * @param params - The URL to redirect the user to. */ buildTouchUrl: (params: { redirectUrl: URL }) => string; /** diff --git a/packages/shared/src/types/resource.ts b/packages/shared/src/types/resource.ts index 140af2e7fe2..787b9fc3bda 100644 --- a/packages/shared/src/types/resource.ts +++ b/packages/shared/src/types/resource.ts @@ -1,4 +1,8 @@ +/** @document */ export type ClerkResourceReloadParams = { + /** + * The nonce for the rotating token. + */ rotatingTokenNonce?: string; }; From 6fef09a71a46663f417ba6c65c627a89356fe583 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 15 Apr 2026 11:57:08 -0600 Subject: [PATCH 19/51] docs review --- .typedoc/custom-plugin.mjs | 9 +++++++-- packages/shared/src/types/clerk.ts | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 93b6f88ea3a..a484e9cad31 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -103,6 +103,7 @@ const LINK_REPLACEMENTS = [ ['client-resource', '/docs/reference/objects/client'], ['redirect-options', '/docs/reference/types/redirect-options'], ['handle-o-auth-callback-params', '/docs/reference/types/handle-o-auth-callback-params'], + ['session-task', '/docs/reference/types/session-task'], ]; /** @@ -121,8 +122,8 @@ const LINK_REPLACEMENTS = [ function getRelativeLinkReplacements() { return LINK_REPLACEMENTS.map(([fileName, newPath]) => { return { - // Match both path and optional anchor - pattern: new RegExp(`\\((?:(?:\\.{1,2}\\/)+[^()]*?|)${fileName}\\.mdx(#[^)]+)?\\)`, 'g'), + // Match both flat links and nested object-doc links + pattern: new RegExp(`\\((?:(?:\\.{1,2}\\/)+[^()]*?|)(?:${fileName}\\/)?${fileName}\\.mdx(#[^)]+)?\\)`, 'g'), // Preserve the anchor in replacement if it exists replace: (/** @type {string} */ _match, anchor = '') => `(${newPath}${anchor})`, }; @@ -260,6 +261,10 @@ function getCatchAllReplacements() { pattern: /(?(event: E, handler: EventHa export type ClerkStatus = 'degraded' | 'error' | 'loading' | 'ready'; /** - * Main Clerk SDK object. + * The `Clerk` class serves as the central interface for working with Clerk's authentication and user management functionality in your application. As a top-level class in the Clerk SDK, it provides access to key methods and properties for managing users, sessions, API keys, billing, organizations, and more. */ export interface Clerk { /** @@ -1003,7 +1003,7 @@ export interface Clerk { redirectToTasks(opts?: TasksRedirectOptions): Promise; /** - * Completes a Google One Tap redirection flow started by [`authenticateWithGoogleOneTap()`](https://clerk.com/reference/objects/clerk#authenticate-with-google-one-tap). This method should be called after the user is redirected back from visiting the Google One Tap prompt. + * Completes a Google One Tap redirection flow started by [`authenticateWithGoogleOneTap()`](#authenticate-with-google-one-tap). This method should be called after the user is redirected back from visiting the Google One Tap prompt. * * @param signInOrUp - The resource returned from the initial `authenticateWithGoogleOneTap()` call (before redirect). * @param params - Additional props that define where the user will be redirected to at the end of a successful Google One Tap flow. From c99e565641ef7cbe194452db697ffcaea38f91d4 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:18:23 -0700 Subject: [PATCH 20/51] fix clerkui descriptions; fix handlegoogleonetapcallback link; add appearance link to description --- packages/shared/src/types/clerk.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index b8cf5eeeaa5..dfd3d04249c 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -1003,7 +1003,7 @@ export interface Clerk { redirectToTasks(opts?: TasksRedirectOptions): Promise; /** - * Completes a Google One Tap redirection flow started by [`authenticateWithGoogleOneTap()`](#authenticate-with-google-one-tap). This method should be called after the user is redirected back from visiting the Google One Tap prompt. + * Completes a Google One Tap redirection flow started by [`authenticateWithGoogleOneTap()`](https://clerk.com/docs/reference/objects/clerk#authenticate-with-google-one-tap). This method should be called after the user is redirected back from visiting the Google One Tap prompt. * * @param signInOrUp - The resource returned from the initial `authenticateWithGoogleOneTap()` call (before redirect). * @param params - Additional props that define where the user will be redirected to at the end of a successful Google One Tap flow. @@ -1245,12 +1245,16 @@ export type ClerkOptions = ClerkOptionsNavigation & AfterMultiSessionSingleSignOutUrl & ClerkUnsafeOptions & { /** - * Clerk UI module. Pass the `ui` export from `@clerk/ui` to bundle the UI - * with your application instead of loading it from the CDN. + * **Only required if you're bundling Clerk's UI (`@clerk/ui`) instead of loading it from the Clerk CDN**. Provide the UI module to embed Clerk's prebuilt authentication components directly in your application. */ - ui?: { ClerkUI?: ClerkUIConstructor | Promise }; + ui?: { + /** + * Pass the `ui` export from `@clerk/ui`. + */ + ClerkUI?: ClerkUIConstructor | Promise; + }; /** - * Optional object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * Optional [object](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ // TODO @nikos appearance?: any; From 1250e86e36975e8e923df4c7fac314e1ddd5fec6 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:25:41 -0700 Subject: [PATCH 21/51] inline handleemaillinkverificationparams --- packages/shared/src/types/clerk.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index dfd3d04249c..1e7e18b6121 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -2467,7 +2467,7 @@ export type __internal_OAuthConsentProps = { onDeny: () => void; }; -/** @document */ +/** @inline */ export interface HandleEmailLinkVerificationParams { /** * The full URL to navigate to after successful email link verification on completed sign-up or sign-in on the same device. From a70521cb5c6928504583d7d106b1f828cbc04bb4 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:52:43 -0700 Subject: [PATCH 22/51] refactor to combine all reference_object info into one object --- .typedoc/extract-methods.mjs | 12 +++++------- .typedoc/reference-objects.mjs | 33 +++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index ac612371f03..e5f72e867df 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -23,7 +23,7 @@ import { stripReferenceObjectPropertiesSection, } from './custom-plugin.mjs'; import { prepareMarkdownRenderer } from './prepare-markdown-renderer.mjs'; -import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_PAGE_SYMBOLS } from './reference-objects.mjs'; +import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_CONFIG } from './reference-objects.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -795,16 +795,14 @@ function buildMethodMdx(decl, ctx) { * @param {import('typedoc').Application} app */ function extractMethodsForPage(pageUrl, project, app) { - const symbol = /** @type {Record} */ (/** @type {unknown} */ (REFERENCE_OBJECT_PAGE_SYMBOLS))[ - pageUrl - ]; - if (!symbol) { + const entry = REFERENCE_OBJECT_CONFIG[/** @type {keyof typeof REFERENCE_OBJECT_CONFIG} */ (pageUrl)]; + if (!entry) { console.warn(`[extract-methods] No symbol mapping for ${pageUrl}, skipping`); return 0; } - const hint = symbol === 'Clerk' ? 'types/clerk' : symbol === 'ClientResource' ? 'types/client' : undefined; - const decl = findInterfaceOrClass(project, symbol, hint); + const { symbol, declarationHint } = entry; + const decl = findInterfaceOrClass(project, symbol, declarationHint); if (!decl?.children) { console.warn(`[extract-methods] Could not find interface/class "${symbol}"`); return 0; diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 29937f9902d..cf8b0aa2a1b 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -5,17 +5,34 @@ */ /** - * TypeDoc output paths for the main reference pages (`shared//.mdx`, see `ClerkRouter`). + * Reference object page: MDX path → TypeDoc symbol + optional source hint. + * + * `declarationHint` is a substring of `packages/shared/src/**` file paths (used by `findInterfaceOrClass()` in `extract-methods.mjs` when multiple reflections share the same interface/class name). + * * `extract-methods.mjs` reads each file, writes `properties.mdx` with the same Properties table as TypeDoc, strips Properties from `.mdx`, and writes methods under `methods/`. */ -export const REFERENCE_OBJECTS_LIST = ['shared/clerk/clerk.mdx', 'shared/client-resource/client-resource.mdx']; +export const REFERENCE_OBJECT_CONFIG = { + 'shared/clerk/clerk.mdx': { + symbol: 'Clerk', + declarationHint: 'types/clerk', + }, + 'shared/client-resource/client-resource.mdx': { + symbol: 'ClientResource', + declarationHint: 'types/client', + }, + 'shared/session/session.mdx': { + symbol: 'SessionResource', + declarationHint: 'types/session', + }, +}; + +/** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ +export const REFERENCE_OBJECTS_LIST = Object.keys(REFERENCE_OBJECT_CONFIG); /** * Primary interface/class documented on each reference object page (used to resolve TypeDoc reflections). - * keys = stable MDX paths under .typedoc output - * values = which TypeDoc declaration to treat as that page’s “main” type for routing and per-method extraction. + * Derived from {@link REFERENCE_OBJECT_CONFIG}; kept for callers that only need `pageUrl → symbol`. */ -export const REFERENCE_OBJECT_PAGE_SYMBOLS = { - 'shared/clerk/clerk.mdx': 'Clerk', - 'shared/client-resource/client-resource.mdx': 'ClientResource', -}; +export const REFERENCE_OBJECT_PAGE_SYMBOLS = Object.fromEntries( + Object.entries(REFERENCE_OBJECT_CONFIG).map(([url, { symbol }]) => [url, symbol]), +); From 9680e81fad996b1245a4d5d71abccb37176949f9 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:32:24 -0700 Subject: [PATCH 23/51] fix session object addition; add generation logic for union literals --- .typedoc/__tests__/file-structure.test.ts | 2 + .typedoc/custom-plugin.mjs | 1 + .typedoc/custom-theme.mjs | 213 +++++++++++++++++++++- .typedoc/reference-objects.mjs | 5 +- packages/shared/src/types/session.ts | 92 +++++++++- 5 files changed, 308 insertions(+), 5 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index d562a054ec1..48519c7f1d0 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -56,6 +56,8 @@ describe('Typedoc output', () => { "shared/clerk/methods", "shared/client-resource", "shared/client-resource/methods", + "shared/session-resource", + "shared/session-resource/methods", ] `); }); diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index a484e9cad31..acb2a779d94 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -104,6 +104,7 @@ const LINK_REPLACEMENTS = [ ['redirect-options', '/docs/reference/types/redirect-options'], ['handle-o-auth-callback-params', '/docs/reference/types/handle-o-auth-callback-params'], ['session-task', '/docs/reference/types/session-task'], + ['public-user-data', '/docs/reference/types/public-user-data'], ]; /** diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index ad4aed1b854..67d8d02e029 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1,7 +1,12 @@ // @ts-check -import { i18n, IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; +import { ArrayType, i18n, IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; -import { backTicks, htmlTable, table } from '../node_modules/typedoc-plugin-markdown/dist/libs/markdown/index.js'; +import { + backTicks, + heading, + htmlTable, + table, +} from '../node_modules/typedoc-plugin-markdown/dist/libs/markdown/index.js'; import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; import { TypeDeclarationVisibility } from '../node_modules/typedoc-plugin-markdown/dist/options/maps.js'; @@ -389,6 +394,50 @@ function clerkShouldDisplayHtmlTable(context, kind) { return false; } +/** + * Same rules as typedoc-plugin-markdown `shouldDisplayHTMLTable` in `member.propertiesTable.js` (that helper is not exported). Drives **property-style** tables: `propertiesFormat`, `interfacePropertiesFormat`, etc. + * + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} context + * @param {import('typedoc').ReflectionKind | undefined} kind + */ +function propertiesTableUsesHtmlTable(context, kind) { + if (context.options.getValue('propertiesFormat') === 'htmlTable') { + return true; + } + if (kind === ReflectionKind.Interface && context.options.getValue('interfacePropertiesFormat') === 'htmlTable') { + return true; + } + if (kind === ReflectionKind.Class && context.options.getValue('classPropertiesFormat') === 'htmlTable') { + return true; + } + if (kind === ReflectionKind.TypeAlias && context.options.getValue('typeAliasPropertiesFormat') === 'htmlTable') { + return true; + } + return false; +} + +/** + * Renders a pipe or HTML table using {@link propertiesTableUsesHtmlTable} and `tableColumnSettings.leftAlignHeaders`, matching `member.propertiesTable` output conventions. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {{ + * headers: string[]; + * rows: string[][]; + * kind?: import('typedoc').ReflectionKind; + * }} args + */ +function renderPropertiesFormatTable(args) { + const tableColumnsOptions = /** @type {{ leftAlignHeaders?: boolean }} */ ( + this.options.getValue('tableColumnSettings') ?? {} + ); + const leftAlignHeadings = tableColumnsOptions.leftAlignHeaders; + const kind = args.kind ?? ReflectionKind.TypeAlias; + const useHtml = propertiesTableUsesHtmlTable(this, kind); + return useHtml + ? htmlTable(args.headers, args.rows, leftAlignHeadings) + : table(args.headers, args.rows, leftAlignHeadings); +} + /** * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` * and then {@link appendUnionObjectChildPropertyRows} (union-object arm rows like `telemetry.*`). The default plugin @@ -557,6 +606,150 @@ function renderMergedIntersectionDeclaration(ctx, model, opts, mergedChildren, s return md.filter(Boolean).join('\n\n'); } +/** + * Union of literals with JSDoc on each `|` arm: **## Properties** + a table via {@link renderPropertiesFormatTable} (same formatting rules as `member.propertiesTable`). + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').DeclarationReflection} model + * @param {{ headingLevel?: number }} options + */ +function clerkLiteralUnionAsPropertiesTable(model, options) { + const unionType = /** @type {import('typedoc').UnionType} */ (model.type); + const headingLevel = options.headingLevel ?? 2; + const ti = /** @type {{ theme_type: () => string; theme_description: () => string }} */ ( + /** @type {unknown} */ (i18n) + ); + + const headers = [ReflectionKind.singularString(ReflectionKind.Property), ti.theme_type(), ti.theme_description()]; + + /** @type {string[][]} */ + const rows = []; + unionType.types.forEach((type, i) => { + const typeStr = removeLineBreaks(this.partials.someType(type)); + const anchorRaw = typeStr.replace(/^`|`$/g, '').replace(/"/g, '').trim(); + const anchorId = anchorRaw.toLowerCase().replace(/[^a-z0-9]/g, '') || `member-${i}`; + + const propertyCell = [``, backTicks(anchorRaw || `member-${i}`)].join(' '); + + const summary = unionType.elementSummaries?.[i]; + const description = summary ? this.helpers.getCommentParts(summary) : '-'; + + rows.push([propertyCell, typeStr, description]); + }); + + const tableBody = renderPropertiesFormatTable.call(this, { + headers, + rows, + kind: ReflectionKind.TypeAlias, + }); + + return [heading(headingLevel, ReflectionKind.pluralString(ReflectionKind.Property)), tableBody].join('\n\n'); +} + +/** + * Same as typedoc-plugin-markdown `member.declaration`, but type aliases whose value is a **union of only literals** (no `ReflectionType` members) still get a "Type declaration" section when TypeDoc populated {@link UnionType.elementSummaries} from JSDoc before each `|` arm. + * + * Stock `hasTypeDeclaration` required `types.some(t => t instanceof ReflectionType)`, so `SessionStatus` never reached {@link MarkdownThemeContext.partials.typeDeclarationUnionContainer} and only the summary line was emitted. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').DeclarationReflection} model + * @param {{ headingLevel?: number; nested?: boolean }} [options] + */ +function clerkDeclaration(model, options = { headingLevel: 2, nested: false }) { + const md = []; + const opts = { + nested: false, + ...options, + }; + const headingLevel = opts.headingLevel ?? 2; + const optionsWithHeading = { ...options, headingLevel }; + + md.push(this.partials.declarationTitle(model)); + if (!opts.nested && model.sources && !this.options.getValue('disableSources')) { + md.push(this.partials.sources(model)); + } + if (model?.documents) { + md.push(this.partials.documents(model, { headingLevel })); + } + /** @type {import('typedoc').DeclarationReflection | undefined} */ + let typeDeclaration = + model.type && 'declaration' in model.type + ? /** @type {{ declaration?: import('typedoc').DeclarationReflection }} */ (model.type).declaration + : undefined; + if (model.type instanceof ArrayType && model.type?.elementType instanceof ReflectionType) { + typeDeclaration = model.type?.elementType?.declaration; + } + const hasTypeDeclaration = + Boolean(typeDeclaration) || + (model.type instanceof UnionType && + (model.type.types.some(type => type instanceof ReflectionType) || Boolean(model.type.elementSummaries?.length))); + if (model.comment) { + md.push( + this.partials.comment(model.comment, { + headingLevel, + showSummary: true, + showTags: false, + }), + ); + } + if (model.type instanceof IntersectionType) { + model.type?.types?.forEach(intersectionType => { + if ( + intersectionType instanceof ReflectionType && + !intersectionType.declaration.signatures && + intersectionType.declaration.children + ) { + md.push(heading(headingLevel, i18n.theme_type_declaration())); + md.push( + this.partials.typeDeclaration(intersectionType.declaration, { + headingLevel, + }), + ); + } + }); + } + if (model.typeParameters) { + md.push(heading(headingLevel, ReflectionKind.pluralString(ReflectionKind.TypeParameter))); + if (this.helpers.useTableFormat('parameters')) { + md.push(this.partials.typeParametersTable(model.typeParameters)); + } else { + md.push( + this.partials.typeParametersList(model.typeParameters, { + headingLevel, + }), + ); + } + } + if (hasTypeDeclaration) { + if (model.type instanceof UnionType) { + if (this.helpers.hasUsefulTypeDetails(model.type)) { + md.push(heading(headingLevel, i18n.theme_type_declaration())); + md.push(this.partials.typeDeclarationUnionContainer(model, optionsWithHeading)); + } + } else if (typeDeclaration) { + const useHeading = + typeDeclaration.children?.length && + (model.kind !== ReflectionKind.Property || this.helpers.useTableFormat('properties')); + if (useHeading) { + md.push(heading(headingLevel, i18n.theme_type_declaration())); + } + md.push(this.partials.typeDeclarationContainer(model, typeDeclaration, optionsWithHeading)); + } + } + if (model.comment) { + md.push( + this.partials.comment(model.comment, { + headingLevel, + showSummary: false, + showTags: true, + showReturns: true, + }), + ); + } + md.push(this.partials.inheritance(model, { headingLevel })); + return md.join('\n\n'); +} + /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ @@ -948,7 +1141,7 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { const originalPartials = this.partials; this.partials = localPartials; - const output = superPartials.declaration(customizedModel, options); + const output = clerkDeclaration.call(this, customizedModel, options); this.partials = originalPartials; // Remove the "Type declaration" heading from the output @@ -1047,6 +1240,20 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {{ headingLevel: number }} options */ typeDeclarationUnionContainer: (model, options) => { + const optionsWithHeading = { ...options, headingLevel: options?.headingLevel ?? 2 }; + + if (model.type instanceof UnionType) { + const ut = model.type; + const unionReturnHeadings = model.comment?.getTag('@unionReturnHeadings'); + if ( + !ut.types.some(arm => arm instanceof ReflectionType) && + ut.elementSummaries?.length && + !unionReturnHeadings + ) { + return clerkLiteralUnionAsPropertiesTable.call(this, model, optionsWithHeading); + } + } + /** * @type {string[]} */ diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index cf8b0aa2a1b..9d0a0b06a7a 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -7,6 +7,9 @@ /** * Reference object page: MDX path → TypeDoc symbol + optional source hint. * + * Keys **must** match `custom-router.mjs` (`ClerkRouter`): for each symbol, `shared//.mdx` + * (e.g. `SessionResource` → `shared/session-resource/session-resource.mdx`). If the path drifts, TypeDoc writes one folder while `extract-methods.mjs` strips/writes under another. + * * `declarationHint` is a substring of `packages/shared/src/**` file paths (used by `findInterfaceOrClass()` in `extract-methods.mjs` when multiple reflections share the same interface/class name). * * `extract-methods.mjs` reads each file, writes `properties.mdx` with the same Properties table as TypeDoc, strips Properties from `.mdx`, and writes methods under `methods/`. @@ -20,7 +23,7 @@ export const REFERENCE_OBJECT_CONFIG = { symbol: 'ClientResource', declarationHint: 'types/client', }, - 'shared/session/session.mdx': { + 'shared/session-resource/session-resource.mdx': { symbol: 'SessionResource', declarationHint: 'types/session', }, diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index a512fc2baf9..7ecdb40b998 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -217,23 +217,53 @@ export interface SessionResource extends ClerkResource { * The current state of the session. */ status: SessionStatus; + /** + * The date and time when the session will expire. + */ expireAt: Date; + /** + * The date and time when the session was abandoned by the user. + */ abandonAt: Date; /** * An array where each item represents the number of minutes since the last verification of a first or second factor: `[firstFactorAge, secondFactorAge]`. */ factorVerificationAge: [firstFactorAge: number, secondFactorAge: number] | null; + /** + * The token that was last used to authenticate the session. + */ lastActiveToken: TokenResource | null; + /** + * The ID of the last [Active Organization](!active-organization). + */ lastActiveOrganizationId: string | null; + /** + * The date and time when the session was last active on the [`Client`](https://clerk.com/docs/reference/objects/client). + */ lastActiveAt: Date; + /** + * The JWT actor for the session. Holds identifier for the user that is impersonating the current user. Read more about [impersonation](https://clerk.com/docs/guides/users/impersonation). + */ actor: ActClaim | null; + /** + * When the session's actor claim has `type: 'agent'`, this property exposes information about the agent and [Agent Task](https://clerk.com/docs/reference/objects/agent-task) that was used to create the session. + */ agent: AgentActClaim | null; + /** + * The user's pending [session tasks](https://clerk.com/docs/guides/configure/session-tasks). + */ tasks: Array | null; + /** + * The user's current pending [session task](https://clerk.com/docs/guides/configure/session-tasks). + */ currentTask?: SessionTask; /** - * The user associated with the session. + * The [`User`](https://clerk.com/docs/reference/objects/user) associated with the session. */ user: UserResource | null; + /** + * Publicly available information about the current [`User`](https://clerk.com/docs/reference/objects/user). + */ publicUserData: PublicUserData; /** * Marks the session as ended. The session will no longer be active for this `Client` and its status will become **ended**. @@ -244,7 +274,13 @@ export interface SessionResource extends ClerkResource { getToken: GetToken; checkAuthorization: CheckAuthorization; clearCache: () => void; + /** + * The date and time when the session was first created. + */ createdAt: Date; + /** + * The date and time when the session was last updated. + */ updatedAt: Date; startVerification: (params: SessionVerifyCreateParams) => Promise; @@ -312,14 +348,41 @@ export interface SessionActivity { isMobile?: boolean; } +/** + * The current state of the session. + */ export type SessionStatus = + /** + * The session was abandoned client-side. + */ | 'abandoned' + /** + * The session is valid and all activity is allowed. + */ | 'active' + /** + * The user signed out of the session, but the [`Session`](https://clerk.com/docs/reference/objects/session) remains in the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'ended' + /** + * The period of allowed activity for this session has passed. + */ | 'expired' + /** + * The user signed out of the session and the [`Session`](https://clerk.com/docs/reference/objects/session) was removed from the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'removed' + /** + * The session has been replaced by another one, but the previous [`Session`](https://clerk.com/docs/reference/objects/session) remains in the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'replaced' + /** + * The application ended the session and the [`Session`](https://clerk.com/docs/reference/objects/session) was removed from the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'revoked' + /** + * The user has signed in but hasn't completed [session tasks](https://clerk.com/docs/guides/configure/session-tasks). + */ | 'pending'; export type SessionTouchIntent = 'focus' | 'select_session' | 'select_org'; @@ -328,14 +391,41 @@ export type SessionTouchParams = { intent?: SessionTouchIntent; }; +/** + * Information about the user that's publicly available. + */ export interface PublicUserData { + /** + * The user's first name. + */ firstName: string | null; + /** + * The user's last name. + */ lastName: string | null; + /** + * Holds the default avatar or user's uploaded profile image. Compatible with Clerk's [Image Optimization](https://clerk.com/docs/guides/development/image-optimization). + */ imageUrl: string; + /** + * Indicates whether the user has a profile picture. + */ hasImage: boolean; + /** + * The user's identifier, such as their email address or phone number. Can be used for user identification. + */ identifier: string; + /** + * The user's unique identifier. + */ userId?: string; + /** + * The user's username. + */ username?: string; + /** + * Indicates whether the user is banned. + */ banned?: boolean; } From 3a7b36b9f17736ae771d81de4eb7d582077a0c1d Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:10:05 -0700 Subject: [PATCH 24/51] add back children in property tables if they're documented; handle CheckAuthorizationParams<> --- .typedoc/custom-plugin.mjs | 1 + .typedoc/custom-router.mjs | 19 +- .typedoc/custom-theme.mjs | 90 +++++-- .typedoc/extract-methods.mjs | 242 +++++++++++++++++- .../src/app-router/server/currentUser.ts | 2 + packages/shared/src/types/clerk.ts | 2 +- packages/shared/src/types/factors.ts | 223 ++++++++++++++++ packages/shared/src/types/identifiers.ts | 5 + packages/shared/src/types/oauth.ts | 31 +++ packages/shared/src/types/phoneCodeChannel.ts | 5 + packages/shared/src/types/session.ts | 47 +++- packages/shared/src/types/strategies.ts | 17 ++ packages/shared/src/types/web3.ts | 7 + typedoc.config.mjs | 4 + 14 files changed, 659 insertions(+), 36 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index acb2a779d94..451fdd3783d 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -105,6 +105,7 @@ const LINK_REPLACEMENTS = [ ['handle-o-auth-callback-params', '/docs/reference/types/handle-o-auth-callback-params'], ['session-task', '/docs/reference/types/session-task'], ['public-user-data', '/docs/reference/types/public-user-data'], + ['session-status', '/docs/reference/types/session-status'], ]; /** diff --git a/.typedoc/custom-router.mjs b/.typedoc/custom-router.mjs index 32e6baabb42..c7aabc36813 100644 --- a/.typedoc/custom-router.mjs +++ b/.typedoc/custom-router.mjs @@ -53,7 +53,24 @@ class ClerkRouter extends MemberRouter { const isExactMatch = page.url.toLocaleLowerCase().endsWith('readme.mdx'); const isMatchWithNumber = page.url.toLocaleLowerCase().match(/readme-\d+\.mdx$/); - return !(isExactMatch || isMatchWithNumber); + if (isExactMatch || isMatchWithNumber) { + return false; + } + + /** + * `@inline` marks types that should be expanded at use sites, not documented as their own page. + * TypeDoc still assigns `fullUrls` for exported aliases, so we also strip links in the theme's `referenceType` partial (`custom-theme.mjs`). + */ + const model = page.model; + if ( + model && + 'comment' in model && + /** @type {{ comment?: import('typedoc').Comment | undefined }} */ (model).comment?.hasModifier('@inline') + ) { + return false; + } + + return true; }); return modifiedPages; diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 67d8d02e029..a2358b72b7d 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -270,8 +270,66 @@ function hasDefaultValuesForParameters(parameters) { } /** - * Same as typedoc-plugin-markdown `member.parametersTable`, but does **not** flatten inline object parameters when the object has only one property (avoids duplicate rows). - * E.g. `params: { redirectUrl: string }` would result in duplicate rows like `params` and `params.redirectUrl` with the same `@param` description. This is because the default plugin flattens inline object parameters when the object has only one property, but we don't want to do that. + * Object shape for a parameter: inline `{ … }`, optional-wrapped, or reference to a type alias / interface. + * + * @param {import('typedoc').Type | undefined} t + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function getParameterObjectShapeDeclaration(t) { + if (!t || typeof t !== 'object') { + return undefined; + } + const o = + /** @type {{ type?: string; declaration?: import('typedoc').DeclarationReflection; elementType?: import('typedoc').Type }} */ ( + /** @type {unknown} */ (t) + ); + if (o.type === 'optional' && o.elementType) { + return getParameterObjectShapeDeclaration(o.elementType); + } + if (o.type === 'reflection' && o.declaration) { + const d = o.declaration; + if (d.kind === ReflectionKind.TypeLiteral || d.kind === ReflectionKind.Interface) { + return d; + } + } + if (o.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + const sym = ref.reflection; + if (!sym) { + return undefined; + } + const target = /** @type {import('typedoc').DeclarationReflection} */ (sym); + if (sym.kind === ReflectionKind.TypeAlias && target.type) { + return getParameterObjectShapeDeclaration(target.type); + } + if (sym.kind === ReflectionKind.Interface) { + return target; + } + } + return undefined; +} + +/** + * Same as typedoc-plugin-markdown `member.parametersTable`, but avoids useless duplicate rows when a one-property object has **no** property-level JSDoc (the description lives only on `@param`). When the sole property **does** have its own documentation, we flatten so both rows appear. + * + * @param {import('typedoc').DeclarationReflection | undefined} decl + */ +function shouldFlattenInlineObjectParameter(decl) { + if (!decl?.children?.length) { + return false; + } + if (decl.kind !== ReflectionKind.TypeLiteral && decl.kind !== ReflectionKind.Interface) { + return false; + } + if (decl.children.length > 1) { + return true; + } + const only = decl.children[0]; + return Boolean(only?.comment?.hasVisibleComponent()); +} + +/** + * Same as typedoc-plugin-markdown `member.parametersTable`, with `shouldFlattenInlineObjectParameter` and `getParameterObjectShapeDeclaration`. * * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} * @param {import('typedoc').ParameterReflection[]} model @@ -287,11 +345,8 @@ function clerkParametersTable(model) { * @returns {import('typedoc').ParameterReflection[]} */ const parseParams = (current, acc) => { - const decl = /** @type {{ declaration?: import('typedoc').DeclarationReflection } | undefined} */ ( - /** @type {unknown} */ (current.type) - )?.declaration; - const children = decl?.children; - const shouldFlatten = decl?.kind === ReflectionKind.TypeLiteral && children && children.length > 1; + const decl = getParameterObjectShapeDeclaration(current.type); + const shouldFlatten = shouldFlattenInlineObjectParameter(decl); return shouldFlatten ? [...acc, current, ...flattenParams(current)] : [...acc, current]; }; /** @@ -299,9 +354,7 @@ function clerkParametersTable(model) { * @returns {import('typedoc').ParameterReflection[]} */ const flattenParams = current => { - const decl = /** @type {{ declaration?: import('typedoc').DeclarationReflection } | undefined} */ ( - /** @type {unknown} */ (current.type) - )?.declaration; + const decl = getParameterObjectShapeDeclaration(current.type); return ( decl?.children?.reduce( /** @@ -664,7 +717,6 @@ function clerkDeclaration(model, options = { headingLevel: 2, nested: false }) { const headingLevel = opts.headingLevel ?? 2; const optionsWithHeading = { ...options, headingLevel }; - md.push(this.partials.declarationTitle(model)); if (!opts.nested && model.sources && !this.options.getValue('disableSources')) { md.push(this.partials.sources(model)); } @@ -814,6 +866,12 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, + /** + * Remove the blockquote signature line. + * + * @returns {string} + */ + declarationTitle: () => '', /** * @param {import('typedoc').DeclarationReflection[]} model * @param {Parameters[1]} [options] @@ -1132,17 +1190,7 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { } } - // Create a local override - const localPartials = { - ...this.partials, - declarationTitle: () => '', - }; - // Store original so that we can restore it later - const originalPartials = this.partials; - - this.partials = localPartials; const output = clerkDeclaration.call(this, customizedModel, options); - this.partials = originalPartials; // Remove the "Type declaration" heading from the output // This heading is generated by the original declaration partial diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index e5f72e867df..50d71c3ee4b 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -12,8 +12,19 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { Application, Comment, PageKind, ReflectionKind } from 'typedoc'; +import { + Application, + Comment, + IntersectionType, + OptionalType, + PageKind, + ReferenceType, + ReflectionKind, + ReflectionType, + UnionType, +} from 'typedoc'; import { MarkdownPageEvent, MarkdownTheme } from 'typedoc-plugin-markdown'; +import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; import typedocConfig from '../typedoc.config.mjs'; import { isCallableInterfaceProperty } from './custom-theme.mjs'; @@ -256,6 +267,81 @@ function findInterfaceOrClass(project, name, sourcePathHint) { return candidates[0]; } +/** + * Walk instantiated generic / alias chains (e.g. `CheckAuthorization` → `CheckAuthorizationFn` → `(…) => boolean`) until we find a {@link ReflectionType} call signature. Uses reflection IDs to avoid infinite loops. + * + * @param {import('typedoc').Type | undefined} t + * @param {Set} visitedReflectionIds + * @returns {import('typedoc').SignatureReflection | undefined} + */ +function getCallSignatureFromType(t, visitedReflectionIds) { + if (!t || typeof t !== 'object') { + return undefined; + } + const tag = /** @type {{ type?: string }} */ (t).type; + if (tag === 'optional' && 'elementType' in t) { + return getCallSignatureFromType( + /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType, + visitedReflectionIds, + ); + } + if (t instanceof ReflectionType) { + if (t.declaration?.signatures?.length) { + return t.declaration.signatures[0]; + } + return undefined; + } + if (t instanceof ReferenceType) { + const target = t.reflection; + if ( + target && + 'signatures' in target && + /** @type {{ signatures?: import('typedoc').SignatureReflection[] }} */ (target).signatures?.length + ) { + return /** @type {import('typedoc').DeclarationReflection} */ (target).signatures[0]; + } + if (!target || !('kind' in target)) { + return undefined; + } + const decl = /** @type {import('typedoc').DeclarationReflection} */ (target); + const id = decl.id; + if (id != null) { + if (visitedReflectionIds.has(id)) { + return undefined; + } + visitedReflectionIds.add(id); + } + try { + if (decl.kind === ReflectionKind.TypeAlias && decl.type) { + return getCallSignatureFromType(decl.type, visitedReflectionIds); + } + } finally { + if (id != null) { + visitedReflectionIds.delete(id); + } + } + return undefined; + } + if (t instanceof UnionType) { + for (const arm of t.types) { + const sig = getCallSignatureFromType(arm, visitedReflectionIds); + if (sig) { + return sig; + } + } + return undefined; + } + if (t instanceof IntersectionType) { + for (const arm of t.types) { + const sig = getCallSignatureFromType(arm, visitedReflectionIds); + if (sig) { + return sig; + } + } + } + return undefined; +} + /** * @param {import('typedoc').DeclarationReflection} decl * @returns {import('typedoc').SignatureReflection | undefined} @@ -288,10 +374,122 @@ function getPrimaryCallSignature(decl) { return inner.signatures[0]; } } + // `type X = SomeFn` — RHS is often ReferenceType (generic alias), not ReflectionType; recurse (e.g. `checkAuthorization: CheckAuthorization`). + if (aliasTarget?.kind === ReflectionKind.TypeAlias && aliasTarget.type) { + const fromRhs = getCallSignatureFromType(aliasTarget.type, new Set()); + if (fromRhs) { + return fromRhs; + } + } + const fromRef = getCallSignatureFromType(ref, new Set()); + if (fromRef) { + return fromRef; + } } return undefined; } +/** + * @param {import('typedoc').Type | undefined} t + */ +function unwrapOptionalType(t) { + if (!t || typeof t !== 'object') { + return t; + } + if (/** @type {{ type?: string }} */ (t).type === 'optional' && 'elementType' in t) { + return /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + } + return t; +} + +/** + * For `prop: OuterAlias` where `type OuterAlias = SomeFn`, maps generic parameter names on `SomeFn` to the instantiated type arguments (e.g. `Params` → `CheckAuthorizationParams`). + * + * @param {import('typedoc').DeclarationReflection} propertyDecl + * @returns {Map | undefined} + */ +function getGenericInstantiationMapFromCallableProperty(propertyDecl) { + const t = unwrapOptionalType(propertyDecl.type); + if (!(t instanceof ReferenceType) || !t.reflection) { + return undefined; + } + const alias = /** @type {import('typedoc').DeclarationReflection} */ (t.reflection); + if (!alias.kindOf(ReflectionKind.TypeAlias) || !alias.type) { + return undefined; + } + const inner = unwrapOptionalType(alias.type); + if (!(inner instanceof ReferenceType) || !inner.typeArguments?.length || !inner.reflection) { + return undefined; + } + const generic = /** @type {import('typedoc').DeclarationReflection} */ (inner.reflection); + const tpls = generic.typeParameters; + if (!tpls?.length) { + return undefined; + } + /** @type {Map} */ + const map = new Map(); + for (let i = 0; i < inner.typeArguments.length; i++) { + const tp = tpls[i]; + const arg = inner.typeArguments[i]; + if (tp?.name && arg) { + map.set(tp.name, arg); + } + } + return map.size ? map : undefined; +} + +/** + * Replace references to generic type parameters with instantiated types from {@link getGenericInstantiationMapFromCallableProperty}. + * + * @param {import('typedoc').Type | undefined} t + * @param {Map | undefined} map + * @returns {import('typedoc').Type | undefined} + */ +function substituteGenericParamRefsInType(t, map) { + if (!t || !map?.size) { + return t; + } + if (/** @type {{ type?: string }} */ (t).type === 'optional' && 'elementType' in t) { + const el = /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + const next = substituteGenericParamRefsInType(el, map); + if (next && next !== el) { + return new OptionalType(/** @type {import('typedoc').SomeType} */ (/** @type {unknown} */ (next))); + } + return t; + } + if (t instanceof ReferenceType && map.has(t.name)) { + return map.get(t.name) ?? t; + } + return t; +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {Map | undefined} instantiationMap + */ +function signatureWithInstantiation(sig, instantiationMap) { + if (!instantiationMap?.size) { + return sig; + } + const parameters = (sig.parameters ?? []).map(p => { + const newType = substituteGenericParamRefsInType(p.type, instantiationMap); + if (newType === p.type) { + return p; + } + return Object.assign(Object.create(Object.getPrototypeOf(p)), p, { type: newType }); + }); + const newReturn = substituteGenericParamRefsInType(sig.type, instantiationMap) ?? sig.type; + const out = Object.assign(Object.create(Object.getPrototypeOf(sig)), sig, { + parameters, + type: newReturn, + typeParameters: undefined, + }); + if (sig.project) { + out.project = sig.project; + } + return out; +} + /** * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (callable members are extracted here, not listed as properties). * @@ -373,21 +571,37 @@ function toKebabCase(name) { .toLowerCase(); } +/** + * Plain TypeScript-like type text for ```typescript``` fences (no markdown / backticks from {@link MarkdownThemeContext.partials.someType}). + * + * @param {import('typedoc').Type | undefined} t + */ +function typeStringForTypeScriptFence(t) { + if (!t) { + return 'unknown'; + } + return removeLineBreaks(t.toString()); +} + /** * @param {import('typedoc').SignatureReflection} sig * @param {string} memberName + * @param {Map | undefined} instantiationMap */ -function formatTypeScriptSignature(sig, memberName) { - const typeParams = sig.typeParameters?.map(tp => tp.name).join(', ') ?? ''; - const typeParamStr = typeParams ? `<${typeParams}>` : ''; +function formatTypeScriptSignature(sig, memberName, instantiationMap) { + const hideOuterTypeParams = Boolean(instantiationMap?.size) && (sig.typeParameters?.length ?? 0) > 0; + const typeParamStr = + !hideOuterTypeParams && sig.typeParameters?.length ? `<${sig.typeParameters.map(tp => tp.name).join(', ')}>` : ''; const params = sig.parameters?.map(p => { const opt = p.flags.isOptional ? '?' : ''; const rest = p.flags.isRest ? '...' : ''; - const typeStr = p.type ? p.type.toString() : 'unknown'; + const t = substituteGenericParamRefsInType(p.type, instantiationMap) ?? p.type; + const typeStr = typeStringForTypeScriptFence(t); return `${rest}${p.name}${opt}: ${typeStr}`; }) ?? []; - const ret = sig.type ? sig.type.toString() : 'void'; + const retT = substituteGenericParamRefsInType(sig.type, instantiationMap) ?? sig.type; + const ret = retT ? typeStringForTypeScriptFence(retT) : 'void'; return `function ${memberName}${typeParamStr}(${params.join(', ')}): ${ret}`; } @@ -704,7 +918,8 @@ function trySingleNominalParameterTypeSection(sig, ctx) { return undefined; } const p = params[0]; - const nominal = resolveNominalObjectTypeForSingleParam(p.type, sig.project); + const project = sig.project ?? ctx.page?.project; + const nominal = resolveNominalObjectTypeForSingleParam(p.type, project); if (!nominal) { return undefined; } @@ -730,14 +945,16 @@ function trySingleNominalParameterTypeSection(sig, ctx) { /** * @param {import('typedoc').SignatureReflection} sig * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {Map | undefined} instantiationMap */ -function parametersMarkdownTable(sig, ctx) { - const params = sig.parameters ?? []; +function parametersMarkdownTable(sig, ctx, instantiationMap) { + const sigForDisplay = signatureWithInstantiation(sig, instantiationMap); + const params = sigForDisplay.parameters ?? []; if (params.length === 0) { return ''; } - const singleNominal = trySingleNominalParameterTypeSection(sig, ctx); + const singleNominal = trySingleNominalParameterTypeSection(sigForDisplay, ctx); if (singleNominal) { return singleNominal; } @@ -775,8 +992,9 @@ function buildMethodMdx(decl, ctx) { if (sigReturns) { description = [description, sigReturns].filter(Boolean).join('\n\n'); } - const ts = ['```typescript', formatTypeScriptSignature(sig, name), '```'].join('\n'); - const paramsMd = parametersMarkdownTable(sig, ctx); + const instantiationMap = getGenericInstantiationMapFromCallableProperty(decl); + const ts = ['```typescript', formatTypeScriptSignature(sig, name, instantiationMap), '```'].join('\n'); + const paramsMd = parametersMarkdownTable(sig, ctx, instantiationMap); // Same post-process as `custom-plugin.mjs` `MarkdownPageEvent.END`: relative `.mdx` links, then catch-alls. // Skip the ```typescript``` fence so signatures stay plain code. diff --git a/packages/nextjs/src/app-router/server/currentUser.ts b/packages/nextjs/src/app-router/server/currentUser.ts index 02ac8fabfb6..b833bc14f3c 100644 --- a/packages/nextjs/src/app-router/server/currentUser.ts +++ b/packages/nextjs/src/app-router/server/currentUser.ts @@ -15,6 +15,8 @@ type CurrentUserOptions = PendingSessionOptions; * - uses the [`GET /v1/users/{user_id}`](https://clerk.com/docs/reference/backend-api/tag/Users#operation/GetUser) endpoint. * - counts towards the [Backend API request rate limit](https://clerk.com/docs/guides/how-clerk-works/system-limits). * + * @param opts - Optional configuration object. + * * @example * ```tsx {{ filename: 'app/page.tsx' }} * import { currentUser } from '@clerk/nextjs/server' diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 1e7e18b6121..af68bf3cc78 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -1254,7 +1254,7 @@ export type ClerkOptions = ClerkOptionsNavigation & ClerkUI?: ClerkUIConstructor | Promise; }; /** - * Optional [object](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * Optional object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. See the [Appearance](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ // TODO @nikos appearance?: any; diff --git a/packages/shared/src/types/factors.ts b/packages/shared/src/types/factors.ts index 04aa639e87b..7896b708380 100644 --- a/packages/shared/src/types/factors.ts +++ b/packages/shared/src/types/factors.ts @@ -15,169 +15,392 @@ import type { Web3Strategy, } from './strategies'; +/** @document */ export type EmailCodeFactor = { + /** + * The strategy type. + */ strategy: EmailCodeStrategy; + /** + * The ID of the email address used for the email code factor. + */ emailAddressId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type EmailLinkFactor = { + /** + * The strategy type. + */ strategy: EmailLinkStrategy; + /** + * The ID of the email address used for the email link factor. + */ emailAddressId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type PhoneCodeFactor = { + /** + * The strategy type. + */ strategy: PhoneCodeStrategy; + /** + * The ID of the phone number used for the phone code factor. + */ phoneNumberId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the phone number is set as the primary phone number, as multiple can be added to a user's profile. + */ primary?: boolean; + /** + * Indicates whether the phone number is set as the default identifier. + */ default?: boolean; + /** + * The channel used for the phone code factor. + */ channel?: PhoneCodeChannel; }; +/** @document */ export type Web3SignatureFactor = { + /** + * The strategy type. + */ strategy: Web3Strategy; + /** + * The ID of the Web3 Wallet. + */ web3WalletId: string; + /** + * Indicates whether the Web3 Wallet is set as the primary Web3 Wallet, as multiple can be added to a user's profile. + */ primary?: boolean; + /** + * The name of the Web3 Wallet. + */ walletName?: string; }; +/** @inline */ export type PasswordFactor = { strategy: PasswordStrategy; }; +/** @inline */ export type PasskeyFactor = { strategy: PasskeyStrategy; }; +/** @inline */ export type OauthFactor = { strategy: OAuthStrategy; }; +/** @document */ export type EnterpriseSSOFactor = { + /** + * The strategy type. + */ strategy: EnterpriseSSOStrategy; /** + * The ID of the enterprise connection. * @experimental */ enterpriseConnectionId?: string; /** + * The name of the enterprise connection. * @experimental */ enterpriseConnectionName?: string; }; +/** @inline */ export type TOTPFactor = { strategy: TOTPStrategy; }; +/** @inline */ export type BackupCodeFactor = { strategy: BackupCodeStrategy; }; +/** @document */ export type ResetPasswordPhoneCodeFactor = { + /** + * The strategy type. + */ strategy: ResetPasswordPhoneCodeStrategy; + /** + * The ID of the phone number used for the reset password phone code factor. + */ phoneNumberId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the phone number is set as the primary phone number, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type ResetPasswordEmailCodeFactor = { + /** + * The strategy type. + */ strategy: ResetPasswordEmailCodeStrategy; + /** + * The ID of the email address used for the reset password email code factor. + */ emailAddressId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type ResetPasswordCodeFactor = ResetPasswordEmailCodeFactor | ResetPasswordPhoneCodeFactor; +/** @document */ export type ResetPasswordPhoneCodeFactorConfig = Omit; +/** @document */ export type ResetPasswordEmailCodeFactorConfig = Omit; +/** @document */ export type EmailCodeConfig = Omit; +/** @document */ export type EmailLinkConfig = Omit & { + /** + * The URL to redirect to after the email link is clicked. + */ redirectUrl: string; }; +/** @document */ export type PhoneCodeConfig = Omit; +/** @document */ export type Web3SignatureConfig = Web3SignatureFactor; +/** @inline */ export type PassKeyConfig = PasskeyFactor; + +/** @document */ export type OAuthConfig = OauthFactor & { + /** + * The URL to redirect to after the OAuth flow is completed. + */ redirectUrl: string; + /** + * + */ actionCompleteRedirectUrl: string; + /** + * The OIDC prompt parameter to use for the OAuth flow. + */ oidcPrompt?: string; + /** + * The OIDC login hint parameter to use for the OAuth flow. + */ oidcLoginHint?: string; }; +/** @document */ export type EnterpriseSSOConfig = EnterpriseSSOFactor & { + /** + * The URL to redirect to after the OAuth flow is completed. + */ redirectUrl: string; + /** + * + */ actionCompleteRedirectUrl: string; + /** + * The OIDC prompt parameter to use for the OAuth flow. + */ oidcPrompt?: string; /** + * The ID of the email address used for the enterprise SSO factor. * @experimental */ emailAddressId?: string; /** + * The ID of the enterprise connection used for the enterprise SSO factor. * @experimental */ enterpriseConnectionId?: string; }; +/** @document */ export type PhoneCodeSecondFactorConfig = { + /** + * The strategy type. + */ strategy: PhoneCodeStrategy; + /** + * The ID of the phone number used for the phone code second factor. + */ phoneNumberId?: string; }; +/** @document */ export type EmailCodeSecondFactorConfig = { + /** + * The strategy type. + */ strategy: EmailCodeStrategy; + /** + * The ID of the email address used for the email code second factor. + */ emailAddressId?: string; }; +/** + * @document + */ export type EmailCodeAttempt = { + /** + * The strategy type. + */ strategy: EmailCodeStrategy; + /** + * The one-time code sent to the user's email. + */ code: string; }; +/** + * @document + */ export type PhoneCodeAttempt = { + /** + * The strategy type. + */ strategy: PhoneCodeStrategy; + /** + * The one-time code sent via SMS. + */ code: string; }; +/** + * @document + */ export type PasswordAttempt = { + /** + * The strategy type. + */ strategy: PasswordStrategy; + /** + * The user's password. + */ password: string; }; +/** + * @document + */ export type PasskeyAttempt = { + /** + * The strategy type. + */ strategy: PasskeyStrategy; + /** + * The Web Authentication assertion returned by the browser. + */ publicKeyCredential: PublicKeyCredentialWithAuthenticatorAssertionResponse; }; +/** @document */ export type Web3Attempt = { + /** + * The strategy type. + */ strategy: Web3Strategy; + /** + * The signature of the Web3 transaction. + */ signature: string; }; +/** @document */ export type TOTPAttempt = { + /** + * The strategy type. + */ strategy: TOTPStrategy; + /** + * The code generated by the authenticator app. + */ code: string; }; +/** @document */ export type BackupCodeAttempt = { + /** + * The strategy type. + */ strategy: BackupCodeStrategy; + /** + * The backup code. + */ code: string; }; +/** @document */ export type ResetPasswordPhoneCodeAttempt = { + /** + * The strategy type. + */ strategy: ResetPasswordPhoneCodeStrategy; + /** + * The one-time code sent via SMS. + */ code: string; + /** + * The password provided by the user. + */ password?: string; }; +/** @document */ export type ResetPasswordEmailCodeAttempt = { + /** + * The strategy type. + */ strategy: ResetPasswordEmailCodeStrategy; + /** + * The one-time code sent to the user's email. + */ code: string; + /** + * The password provided by the user. + */ password?: string; }; diff --git a/packages/shared/src/types/identifiers.ts b/packages/shared/src/types/identifiers.ts index 0b1802f7130..9e967d7d43d 100644 --- a/packages/shared/src/types/identifiers.ts +++ b/packages/shared/src/types/identifiers.ts @@ -1,5 +1,10 @@ +/** @inline */ export type UsernameIdentifier = 'username'; +/** @inline */ export type EmailAddressIdentifier = 'email_address'; +/** @inline */ export type PhoneNumberIdentifier = 'phone_number'; +/** @inline */ export type Web3WalletIdentifier = 'web3_wallet'; +/** @inline */ export type EmailAddressOrPhoneNumberIdentifier = 'email_address_or_phone_number'; diff --git a/packages/shared/src/types/oauth.ts b/packages/shared/src/types/oauth.ts index cf20b674495..83cd8c30e41 100644 --- a/packages/shared/src/types/oauth.ts +++ b/packages/shared/src/types/oauth.ts @@ -9,37 +9,68 @@ export interface OAuthProviderData { docsUrl: string; } +/** @inline */ export type FacebookOauthProvider = 'facebook'; +/** @inline */ export type GoogleOauthProvider = 'google'; +/** @inline */ export type HubspotOauthProvider = 'hubspot'; +/** @inline */ export type GithubOauthProvider = 'github'; +/** @inline */ export type TiktokOauthProvider = 'tiktok'; +/** @inline */ export type GitlabOauthProvider = 'gitlab'; +/** @inline */ export type DiscordOauthProvider = 'discord'; +/** @inline */ export type TwitterOauthProvider = 'twitter'; +/** @inline */ export type TwitchOauthProvider = 'twitch'; +/** @inline */ export type LinkedinOauthProvider = 'linkedin'; +/** @inline */ export type LinkedinOIDCOauthProvider = 'linkedin_oidc'; +/** @inline */ export type DropboxOauthProvider = 'dropbox'; +/** @inline */ export type AtlassianOauthProvider = 'atlassian'; +/** @inline */ export type BitbucketOauthProvider = 'bitbucket'; +/** @inline */ export type MicrosoftOauthProvider = 'microsoft'; +/** @inline */ export type NotionOauthProvider = 'notion'; +/** @inline */ export type AppleOauthProvider = 'apple'; +/** @inline */ export type LineOauthProvider = 'line'; +/** @inline */ export type InstagramOauthProvider = 'instagram'; +/** @inline */ export type CoinbaseOauthProvider = 'coinbase'; +/** @inline */ export type SpotifyOauthProvider = 'spotify'; +/** @inline */ export type XeroOauthProvider = 'xero'; +/** @inline */ export type BoxOauthProvider = 'box'; +/** @inline */ export type SlackOauthProvider = 'slack'; +/** @inline */ export type LinearOauthProvider = 'linear'; +/** @inline */ export type XOauthProvider = 'x'; +/** @inline */ export type EnstallOauthProvider = 'enstall'; +/** @inline */ export type HuggingfaceOAuthProvider = 'huggingface'; +/** @inline */ export type VercelOauthProvider = 'vercel'; +/** @inline */ export type CustomOauthProvider = `custom_${string}`; +/** Represents the available OAuth providers. */ export type OAuthProvider = | FacebookOauthProvider | GoogleOauthProvider diff --git a/packages/shared/src/types/phoneCodeChannel.ts b/packages/shared/src/types/phoneCodeChannel.ts index 516e9e57324..e75163d79c6 100644 --- a/packages/shared/src/types/phoneCodeChannel.ts +++ b/packages/shared/src/types/phoneCodeChannel.ts @@ -3,8 +3,13 @@ export interface PhoneCodeChannelData { name: string; } +/** @inline */ export type PhoneCodeSMSChannel = 'sms'; +/** @inline */ export type PhoneCodeWhatsAppChannel = 'whatsapp'; +/** @inline */ export type PhoneCodeChannel = PhoneCodeSMSChannel | PhoneCodeWhatsAppChannel; + +/** @inline */ export type PhoneCodeProvider = PhoneCodeChannel; diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index 7ecdb40b998..d89bb7f4463 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -100,6 +100,7 @@ export type CheckAuthorizationParamsWithCustomPermissions = WithReverification< | { role?: never; permission?: never; feature?: never; plan?: never } >; +/** @document */ export type CheckAuthorization = CheckAuthorizationFn; type CheckAuthorizationParams = WithReverification< @@ -265,14 +266,34 @@ export interface SessionResource extends ClerkResource { * Publicly available information about the current [`User`](https://clerk.com/docs/reference/objects/user). */ publicUserData: PublicUserData; + /** * Marks the session as ended. The session will no longer be active for this `Client` and its status will become **ended**. */ end: () => Promise; + /** + * Invalidates the current session by marking it as removed. Once removed, the session will be deactivated for the current Client instance and its `status` will be set to `removed`. This operation cannot be undone. + */ remove: () => Promise; + /** + * Updates the session's last active timestamp to the current time. This method should be called periodically to indicate ongoing user activity and prevent the session from becoming stale. The updated timestamp is used for session management and analytics purposes. + */ touch: (params?: SessionTouchParams) => Promise; + /** + * Retrieves the current user's [session token](https://clerk.com/docs/guides/sessions/session-tokens) or a [custom JWT template](https://clerk.com/docs/guides/sessions/jwt-templates). + * + * This method uses a cache so a network request will only be made if the token in memory has expired. The TTL for a Clerk token is one minute. It retries on transient failures (e.g. network errors); when the browser is offline and retries are exhausted, it throws `ClerkOfflineError`. + * + * Tokens can only be generated if the user is signed in. + */ getToken: GetToken; + /** + * Checks if the user is [authorized for the specified Role, Permission, Feature, or Plan](https://clerk.com/docs/guides/secure/authorization-checks) or requires the user to [reverify their credentials](https://clerk.com/docs/guides/secure/reverification) if their last verification is older than allowed. + */ checkAuthorization: CheckAuthorization; + /** + * Clears the cache for the current session. This is useful if the session has been updated and the cache is no longer valid. + */ clearCache: () => void; /** * The date and time when the session was first created. @@ -282,20 +303,43 @@ export interface SessionResource extends ClerkResource { * The date and time when the session was last updated. */ updatedAt: Date; - + /** + * Initiates the reverification flow. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ startVerification: (params: SessionVerifyCreateParams) => Promise; + /** + * Initiates the [first factor verification](!first-factor-verification) process. This is a required step to complete a reverification flow when using a preparable factor. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ prepareFirstFactorVerification: ( factor: SessionVerifyPrepareFirstFactorParams, ) => Promise; + /** + * Attempts to complete the [first factor verification](!first-factor-verification) process. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ attemptFirstFactorVerification: ( attemptFactor: SessionVerifyAttemptFirstFactorParams, ) => Promise; + /** + * Initiates the [second factor verification](!second-factor-verification) process. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ prepareSecondFactorVerification: ( params: SessionVerifyPrepareSecondFactorParams, ) => Promise; + /** + * Attempts to complete the [second factor verification](!second-factor-verification) process. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ attemptSecondFactorVerification: ( params: SessionVerifyAttemptSecondFactorParams, ) => Promise; + /** + * Initiates a verification flow using passkeys. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ verifyWithPasskey: () => Promise; __internal_toSnapshot: () => SessionJSONSnapshot; __internal_touch: (params?: SessionTouchParams) => Promise; @@ -461,6 +505,7 @@ export type SessionVerifyPrepareFirstFactorParams = * @experimental */ | Omit; + export type SessionVerifyAttemptFirstFactorParams = | EmailCodeAttempt | PhoneCodeAttempt diff --git a/packages/shared/src/types/strategies.ts b/packages/shared/src/types/strategies.ts index eb6dde71ed5..2c03adbf685 100644 --- a/packages/shared/src/types/strategies.ts +++ b/packages/shared/src/types/strategies.ts @@ -1,20 +1,37 @@ import type { OAuthProvider } from './oauth'; import type { Web3Provider } from './web3'; +/** @inline */ export type GoogleOneTapStrategy = 'google_one_tap'; +/** @inline */ export type AppleIdTokenStrategy = 'oauth_token_apple'; +/** @inline */ export type PasskeyStrategy = 'passkey'; +/** @inline */ export type PasswordStrategy = 'password'; +/** @inline */ export type PhoneCodeStrategy = 'phone_code'; +/** @inline */ export type EmailCodeStrategy = 'email_code'; +/** @inline */ export type EmailLinkStrategy = 'email_link'; +/** @inline */ export type TicketStrategy = 'ticket'; +/** @inline */ export type TOTPStrategy = 'totp'; +/** @inline */ export type BackupCodeStrategy = 'backup_code'; +/** @inline */ export type ResetPasswordPhoneCodeStrategy = 'reset_password_phone_code'; +/** @inline */ export type ResetPasswordEmailCodeStrategy = 'reset_password_email_code'; +/** @inline */ export type CustomOAuthStrategy = `oauth_custom_${string}`; +/** @inline */ export type EnterpriseSSOStrategy = 'enterprise_sso'; +/** @inline */ export type OAuthStrategy = `oauth_${OAuthProvider}` | CustomOAuthStrategy; + +/** @inline */ export type Web3Strategy = `web3_${Web3Provider}_signature`; diff --git a/packages/shared/src/types/web3.ts b/packages/shared/src/types/web3.ts index 450c8533946..0b38b6f52cb 100644 --- a/packages/shared/src/types/web3.ts +++ b/packages/shared/src/types/web3.ts @@ -6,14 +6,21 @@ export interface Web3ProviderData { name: string; } +/** @inline */ export type MetamaskWeb3Provider = 'metamask'; +/** @inline */ export type CoinbaseWalletWeb3Provider = 'coinbase_wallet'; +/** @inline */ export type OKXWalletWeb3Provider = 'okx_wallet'; +/** @inline */ export type BaseWeb3Provider = 'base'; +/** @inline */ export type SolanaWeb3Provider = 'solana'; +/** @inline */ export type Web3Provider = EthereumWeb3Provider | SolanaWeb3Provider; +/** @inline */ export type EthereumWeb3Provider = | MetamaskWeb3Provider | BaseWeb3Provider diff --git a/typedoc.config.mjs b/typedoc.config.mjs index bbd02278b1a..e077c0808e2 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -97,6 +97,10 @@ const config = { gitRevision: 'main', blockTags: [...OptionDefaults.blockTags, ...CUSTOM_BLOCK_TAGS], modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental')], + /** + * Stops TypeDoc from generating standalone pages for inline types. + */ + excludeTags: OptionDefaults.excludeTags.filter(tag => tag !== '@inline' && tag !== '@inlineType'), exclude: ['src/**/*.test.ts', 'src/**/*.test.tsx'], readme: 'none', disableGit: true, From 4fdd354e5e4c5c1236abaece64313ff25daded80 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:45:52 -0700 Subject: [PATCH 25/51] do not flatten parent props if they link to a dedicated page --- .typedoc/custom-theme.mjs | 19 ++++++++ .typedoc/extract-methods.mjs | 73 +++++++++++++++++++++++----- packages/shared/src/types/clerk.ts | 6 +-- packages/shared/src/types/session.ts | 15 ++++++ 4 files changed, 98 insertions(+), 15 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index a2358b72b7d..54adf32219b 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -872,6 +872,25 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @returns {string} */ declarationTitle: () => '', + /** + * TypeDoc's default links every {@link ReferenceType} to a URL. Types marked `@inline` are expanded at use sites and (via the router) have no standalone page — linking produces broken relative `.mdx` paths in extracted method docs. Render the **aliased type** (RHS) so literals and unions show as `'phone_code'`, not `PhoneCodeStrategy`. + * + * @param {import('typedoc').ReferenceType} model + */ + referenceType: model => { + if (model.reflection?.comment?.hasModifier('@inline')) { + const decl = /** @type {import('typedoc').DeclarationReflection} */ (model.reflection); + // Generic instantiation, e.g. `Fn` — let `someType` apply type arguments. + if (model.typeArguments?.length) { + return removeLineBreaks(this.partials.someType(model)); + } + if (decl.kindOf(ReflectionKind.TypeAlias) && decl.type) { + return removeLineBreaks(this.partials.someType(decl.type)); + } + return backTicks(decl.name); + } + return superPartials.referenceType.call(this, model); + }, /** * @param {import('typedoc').DeclarationReflection[]} model * @param {Parameters[1]} [options] diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 50d71c3ee4b..cfcdda934bb 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -753,12 +753,30 @@ function resolveDeclarationWithObjectMembers(t) { } } if (t.type === 'intersection') { - for (const inner of /** @type {import('typedoc').IntersectionType} */ (t).types) { + const inter = /** @type {import('typedoc').IntersectionType} */ (t); + /** @type {Map} */ + const byName = new Map(); + for (const inner of inter.types) { const res = resolveDeclarationWithObjectMembers(inner); - if (res) { - return res; + if (res?.children?.length) { + for (const c of res.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } } } + if (byName.size === 0) { + return undefined; + } + // Synthetic holder so nominal param sections list every `&` arm (e.g. `RedirectOptions`). + return /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)), + kind: ReflectionKind.TypeLiteral, + name: '__intersectionMerged', + }) + ); } if (t.type === 'optional') { return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType); @@ -776,12 +794,44 @@ function formatNestedParamNameColumn(baseName, pathSegments) { } /** - * Rows for object properties that have documentation (including from `@param parent.prop` on the method), which TypeDoc stores on property reflections rather than leaving `@param` block tags on the signature. + * This function unwraps a TypeDoc parameter type if it is an optional type. If the provided type is of type "optional", it returns the underlying element type (the real type being wrapped). If it is not optional or is undefined, it returns the type as-is. * - * @param {import('typedoc').ParameterReflection} param - * @returns {string[]} + * @param {import('typedoc').SomeType | undefined} t + * @returns {import('typedoc').SomeType | undefined} + */ +function unwrapOptionalParamType(t) { + if (t?.type === 'optional') { + return /** @type {import('typedoc').OptionalType} */ (t).elementType; + } + return t; +} + +/** + * When TypeDoc renders a parameter type as a markdown link to another generated `.mdx` file, that type has a dedicated page — omit nested `param?.prop` rows so readers follow the type link instead. + * `@inline` aliases are expanded by the theme and do not link to a standalone page. + * + * @param {import('typedoc').SomeType | undefined} t + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx */ +function parameterTypeLinksToStandaloneMdxPage(t, ctx) { + const bare = unwrapOptionalParamType(t); + if (!bare) { + return false; + } + if (bare.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (bare); + if (ref.reflection?.comment?.hasModifier('@inline')) { + return false; + } + } + const md = removeLineBreaksForTableCell(ctx.partials.someType(bare) ?? '') ?? ''; + return /\.mdx(?:#[^)]*)?\)/.test(md); +} + /** + * Rows for object properties on a nominal param type (e.g. `HandleOAuthCallbackParams`), including from `@param parent.prop` on the method. + * Lists every property on the resolved shape; uses the property comment when present, otherwise `—` (intersection aliases often omit comments on some arms in the model). + * * @param {import('typedoc').ParameterReflection} param * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx */ @@ -795,22 +845,23 @@ function nestedParameterRowsFromDocumentedProperties(param, ctx) { } } + if (parameterTypeLinksToStandaloneMdxPage(param.type, ctx)) { + return []; + } + const holder = resolveDeclarationWithObjectMembers(param.type); if (!holder?.children?.length) { return []; } - const props = holder.children.filter(c => c.kindOf(ReflectionKind.Property) && c.comment?.summary?.length); + const props = holder.children.filter(c => c.kindOf(ReflectionKind.Property)); props.sort((a, b) => a.name.localeCompare(b.name)); /** @type {string[]} */ const rows = []; for (const child of props) { const summary = child.comment?.summary; - if (!summary?.length) { - continue; - } const typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; const nestedNameCol = formatNestedParamNameColumn(param.name, [child.name]); - const nestedDesc = displayPartsToString(summary).trim() || '—'; + const nestedDesc = summary?.length ? displayPartsToString(summary).trim() || '—' : '—'; rows.push(`| ${nestedNameCol} | ${typeCell} | ${nestedDesc} |`); } return rows; diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index af68bf3cc78..ce416725fd0 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -891,15 +891,13 @@ export interface Clerk { /** * Returns the configured `afterSignInUrl` of the instance. - * @param params - Optional configuration. - * @param params.params - Optional query parameters to append to the URL. + * @param params - Optional query parameters to append to the URL. */ buildAfterSignInUrl({ params }?: { params?: URLSearchParams }): string; /** * Returns the configured `afterSignInUrl` of the instance. - * @param params - Optional configuration. - * @param params.params - Optional query parameters to append to the URL. + * @param params - Optional query parameters to append to the URL. */ buildAfterSignUpUrl({ params }?: { params?: URLSearchParams }): string; diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index d89bb7f4463..b4a4f82bcee 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -429,9 +429,14 @@ export type SessionStatus = */ | 'pending'; +/** @inline */ export type SessionTouchIntent = 'focus' | 'select_session' | 'select_org'; +/** @document */ export type SessionTouchParams = { + /** + * The intent of the touch operation. + */ intent?: SessionTouchIntent; }; @@ -483,9 +488,19 @@ export interface SessionTask { key: 'choose-organization' | 'reset-password' | 'setup-mfa'; } +/** @document */ export type GetTokenOptions = { + /** + * The Organization associated with the generated session token. _Does not modify the session's currently [Active Organization](!active-organization)._ + */ organizationId?: string; + /** + * Whether to skip the cache lookup and force a call to the server instead, even within the TTL. Useful if the token claims are time-sensitive or depend on data that can be updated (e.g. user fields). Defaults to `false`. + */ skipCache?: boolean; + /** + * The name of the JWT template from the [Clerk Dashboard](https://dashboard.clerk.com/~/jwt-templates) to generate a new token from. E.g. 'firebase', 'grafbase', or your custom template's name. + */ template?: string; }; /** From 6e3e360dca055eea1be43f72f51e16073f902c3b Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:01:02 -0700 Subject: [PATCH 26/51] remove jsdoc comment from constructor --- .typedoc/extract-methods.mjs | 5 ++++- packages/clerk-js/src/core/clerk.ts | 8 -------- packages/shared/src/types/session.ts | 6 ++++++ typedoc.config.mjs | 6 +++++- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index cfcdda934bb..9361ec22dd0 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -1045,7 +1045,10 @@ function buildMethodMdx(decl, ctx) { } const instantiationMap = getGenericInstantiationMapFromCallableProperty(decl); const ts = ['```typescript', formatTypeScriptSignature(sig, name, instantiationMap), '```'].join('\n'); - const paramsMd = parametersMarkdownTable(sig, ctx, instantiationMap); + const skipParametersSection = + Boolean(decl.comment?.hasModifier('@skipParametersSection')) || + Boolean(sig.comment?.hasModifier('@skipParametersSection')); + const paramsMd = skipParametersSection ? '' : parametersMarkdownTable(sig, ctx, instantiationMap); // Same post-process as `custom-plugin.mjs` `MarkdownPageEvent.END`: relative `.mdx` links, then catch-alls. // Skip the ```typescript``` fence so signatures stay plain code. diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 6ab532be38c..e199021fa03 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -423,14 +423,6 @@ export class Clerk implements ClerkInterface { return !!this.session; } - /** - * Create an instance of the `Clerk` class with dedicated options. - * - * This method is only available when importing `Clerk` from `@clerk/clerk-js`, rather than using a Window script. - * - * @param key - Your Clerk [Publishable Key](!publishable-key). - * @param options - The satellite domain or reverse proxy URL used to connect to Clerk. - */ public constructor(key: string, options?: DomainOrProxyUrl) { key = (key || '').trim(); diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index b4a4f82bcee..918cd593233 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -289,6 +289,7 @@ export interface SessionResource extends ClerkResource { getToken: GetToken; /** * Checks if the user is [authorized for the specified Role, Permission, Feature, or Plan](https://clerk.com/docs/guides/secure/authorization-checks) or requires the user to [reverify their credentials](https://clerk.com/docs/guides/secure/reverification) if their last verification is older than allowed. + * @skipParametersSection */ checkAuthorization: CheckAuthorization; /** @@ -311,6 +312,7 @@ export interface SessionResource extends ClerkResource { /** * Initiates the [first factor verification](!first-factor-verification) process. This is a required step to complete a reverification flow when using a preparable factor. * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection */ prepareFirstFactorVerification: ( factor: SessionVerifyPrepareFirstFactorParams, @@ -318,6 +320,7 @@ export interface SessionResource extends ClerkResource { /** * Attempts to complete the [first factor verification](!first-factor-verification) process. * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection */ attemptFirstFactorVerification: ( attemptFactor: SessionVerifyAttemptFirstFactorParams, @@ -325,6 +328,7 @@ export interface SessionResource extends ClerkResource { /** * Initiates the [second factor verification](!second-factor-verification) process. * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection */ prepareSecondFactorVerification: ( params: SessionVerifyPrepareSecondFactorParams, @@ -332,6 +336,7 @@ export interface SessionResource extends ClerkResource { /** * Attempts to complete the [second factor verification](!second-factor-verification) process. * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection */ attemptSecondFactorVerification: ( params: SessionVerifyAttemptSecondFactorParams, @@ -508,6 +513,7 @@ export type GetTokenOptions = { */ export type GetToken = (options?: GetTokenOptions) => Promise; +/** @document */ export type SessionVerifyCreateParams = { level: SessionVerificationLevel; }; diff --git a/typedoc.config.mjs b/typedoc.config.mjs index e077c0808e2..edfe0188ce3 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -96,7 +96,11 @@ const config = { excludeNotDocumented: true, gitRevision: 'main', blockTags: [...OptionDefaults.blockTags, ...CUSTOM_BLOCK_TAGS], - modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental')], + modifierTags: [ + ...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), + /** Suppresses the Parameters table in `.typedoc/extract-methods.mjs` method MDX. */ + '@skipParametersSection', + ], /** * Stops TypeDoc from generating standalone pages for inline types. */ From 3657c389e31dc8b37988229c22bf89a5452306eb Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:02:11 -0700 Subject: [PATCH 27/51] remove update to current-user --- packages/nextjs/src/app-router/server/currentUser.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/nextjs/src/app-router/server/currentUser.ts b/packages/nextjs/src/app-router/server/currentUser.ts index b833bc14f3c..02ac8fabfb6 100644 --- a/packages/nextjs/src/app-router/server/currentUser.ts +++ b/packages/nextjs/src/app-router/server/currentUser.ts @@ -15,8 +15,6 @@ type CurrentUserOptions = PendingSessionOptions; * - uses the [`GET /v1/users/{user_id}`](https://clerk.com/docs/reference/backend-api/tag/Users#operation/GetUser) endpoint. * - counts towards the [Backend API request rate limit](https://clerk.com/docs/guides/how-clerk-works/system-limits). * - * @param opts - Optional configuration object. - * * @example * ```tsx {{ filename: 'app/page.tsx' }} * import { currentUser } from '@clerk/nextjs/server' From 63e195c5ad9f1a924b02a3ba68f3a4fc3b953db0 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 4 May 2026 12:21:56 -0700 Subject: [PATCH 28/51] remove use of 'optional' in jscomments --- .../backend/src/api/endpoints/ActorTokenApi.ts | 2 +- packages/backend/src/api/endpoints/SessionApi.ts | 4 ++-- packages/backend/src/api/resources/APIKey.ts | 2 +- packages/backend/src/tokens/authObjects.ts | 5 ++--- .../clerk-js/src/core/modules/debug/index.ts | 8 ++++---- packages/expo/src/google-one-tap/types.ts | 2 +- packages/express/src/getAuth.ts | 2 +- packages/nextjs/src/server/createGetAuth.ts | 2 +- packages/react/src/components/uiComponents.tsx | 2 +- packages/react/src/types.ts | 4 ++-- packages/shared/src/eventBus.ts | 4 ++-- .../shared/src/react/billing/payment-element.tsx | 6 +++--- packages/shared/src/types/apiKeys.ts | 2 +- packages/shared/src/types/clerk.ts | 16 ++++++++-------- .../ui/src/styledSystem/StyleCacheProvider.tsx | 4 ++-- packages/vue/src/plugin.ts | 2 +- 16 files changed, 33 insertions(+), 34 deletions(-) diff --git a/packages/backend/src/api/endpoints/ActorTokenApi.ts b/packages/backend/src/api/endpoints/ActorTokenApi.ts index c3dca6a9706..2c4e3e114ba 100644 --- a/packages/backend/src/api/endpoints/ActorTokenApi.ts +++ b/packages/backend/src/api/endpoints/ActorTokenApi.ts @@ -28,7 +28,7 @@ type ActorTokenCreateParams = { */ actor: ActorTokenActorCreateParams; /** - * Optional parameter to specify the life duration of the actor token in seconds. + * The lifetime of the actor token in seconds. * * @remarks * By default, the duration is 1 hour. diff --git a/packages/backend/src/api/endpoints/SessionApi.ts b/packages/backend/src/api/endpoints/SessionApi.ts index a4fc70ddf55..047aeb8fb4f 100644 --- a/packages/backend/src/api/endpoints/SessionApi.ts +++ b/packages/backend/src/api/endpoints/SessionApi.ts @@ -75,8 +75,8 @@ export class SessionAPI extends AbstractAPI { * Retrieves a session token or generates a JWT using a specified template. * * @param sessionId - The ID of the session for which to generate the token - * @param template - Optional name of the JWT template configured in the Clerk Dashboard. - * @param expiresInSeconds - Optional expiration time for the token in seconds. + * @param template - The name of the JWT template configured in the Clerk Dashboard. + * @param expiresInSeconds - The expiration time for the token in seconds. * If not provided, uses the default expiration. * * @returns A promise that resolves to the generated token diff --git a/packages/backend/src/api/resources/APIKey.ts b/packages/backend/src/api/resources/APIKey.ts index 6e3c3fbf044..5add117abc6 100644 --- a/packages/backend/src/api/resources/APIKey.ts +++ b/packages/backend/src/api/resources/APIKey.ts @@ -50,7 +50,7 @@ export class APIKey { */ readonly createdBy: string | null, /** - * An optional description for the API key. + * A description for the API key. */ readonly description: string | null, /** diff --git a/packages/backend/src/tokens/authObjects.ts b/packages/backend/src/tokens/authObjects.ts index 205d7d1fd3a..8353fa1286d 100644 --- a/packages/backend/src/tokens/authObjects.ts +++ b/packages/backend/src/tokens/authObjects.ts @@ -390,7 +390,7 @@ export const makeAuthObjectSerializable = >(ob * * @param sessionId - The ID of the session * @param template - The JWT template name to use for token generation - * @param expiresInSeconds - Optional expiration time in seconds for the token + * @param expiresInSeconds - The expiration time in seconds for the token * @returns A promise that resolves to the token string */ type TokenFetcher = (sessionId: string, template?: string, expiresInSeconds?: number) => Promise; @@ -406,8 +406,7 @@ type CreateGetToken = (params: { sessionId: string; sessionToken: string; fetche /** * Creates a token retrieval function for authenticated sessions. * - * This factory function returns a getToken function that can either return the raw session token - * or generate a JWT using a specified template with optional custom expiration. + * This factory function returns a getToken function that can either return the raw session token or generate a JWT using a specified template with custom expiration. * * @param params - Configuration object * @param params.sessionId - The session ID for token generation diff --git a/packages/clerk-js/src/core/modules/debug/index.ts b/packages/clerk-js/src/core/modules/debug/index.ts index cb7ec30695e..30e37e51a13 100644 --- a/packages/clerk-js/src/core/modules/debug/index.ts +++ b/packages/clerk-js/src/core/modules/debug/index.ts @@ -22,11 +22,11 @@ function validateLoggerOptions(options: T): vo * Options for configuring the debug logger. */ export interface LoggerOptions { - /** Optional URL to which telemetry logs will be sent. */ + /** The URL to which telemetry logs will be sent. */ endpoint?: string; /** Minimum log level to capture. */ logLevel?: DebugLogLevel; - /** Optional collector instance for custom telemetry handling. */ + /** A collector instance for custom telemetry handling. */ telemetryCollector?: TelemetryCollector; } @@ -42,11 +42,11 @@ export interface ConsoleLoggerOptions { * Configuration options for a telemetry-based debug logger. */ export interface TelemetryLoggerOptions { - /** Optional URL to which telemetry logs will be sent. */ + /** The URL to which telemetry logs will be sent. */ endpoint?: string; /** Minimum log level to capture. */ logLevel?: DebugLogLevel; - /** Optional collector instance for custom telemetry handling. */ + /** A collector instance for custom telemetry handling. */ telemetryCollector?: TelemetryCollector; } diff --git a/packages/expo/src/google-one-tap/types.ts b/packages/expo/src/google-one-tap/types.ts index fdc8e95df74..7133fc1be46 100644 --- a/packages/expo/src/google-one-tap/types.ts +++ b/packages/expo/src/google-one-tap/types.ts @@ -18,7 +18,7 @@ export type ConfigureParams = { iosClientId?: string; /** - * Optional hosted domain to restrict sign-in to a specific domain. + * The hosted domain to restrict sign-in to a specific domain. */ hostedDomain?: string; diff --git a/packages/express/src/getAuth.ts b/packages/express/src/getAuth.ts index 0119cbeec7f..31b12cdff04 100644 --- a/packages/express/src/getAuth.ts +++ b/packages/express/src/getAuth.ts @@ -8,7 +8,7 @@ import { requestHasAuthObject } from './utils'; /** * Retrieves the Clerk AuthObject using the current request object. * - * @param {GetAuthOptions} options - Optional configuration for retriving auth object. + * @param {GetAuthOptions} options - Optional configuration for retrieving auth object. * @returns {AuthObject} Object with information about the request state and claims. * @throws {Error} `clerkMiddleware` or `requireAuth` is required to be set in the middleware chain before this util is used. */ diff --git a/packages/nextjs/src/server/createGetAuth.ts b/packages/nextjs/src/server/createGetAuth.ts index 0b29d105b27..dfd60fa861b 100644 --- a/packages/nextjs/src/server/createGetAuth.ts +++ b/packages/nextjs/src/server/createGetAuth.ts @@ -94,7 +94,7 @@ export const createSyncGetAuth = ({ * > If you are using App Router, use the [`auth()` helper](https://clerk.com/docs/reference/nextjs/app-router/auth) instead. * * @param req - The Next.js request object. - * @param [options] - An optional object that can be used to configure the behavior of the `getAuth()` function. + * @param [options] - An object that can be used to configure the behavior of the `getAuth()` function. * @param [options.secretKey] - A string that represents the Secret Key used to sign the session token. If not provided, the Secret Key is retrieved from the environment variable `CLERK_SECRET_KEY`. * @returns The `Auth` object. See the [Auth reference](https://clerk.com/docs/reference/backend/types/auth-object) for more information. * diff --git a/packages/react/src/components/uiComponents.tsx b/packages/react/src/components/uiComponents.tsx index a87b83af675..cc6ed141e29 100644 --- a/packages/react/src/components/uiComponents.tsx +++ b/packages/react/src/components/uiComponents.tsx @@ -54,7 +54,7 @@ import { withClerk } from './withClerk'; type FallbackProp = { /** - * An optional element to render while the component is mounting. + * The element to render while the component is mounting. */ fallback?: ReactNode; }; diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 9c38a97c989..bf773d029c9 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -53,11 +53,11 @@ export type ClerkProviderProps = Omit< */ __internal_bypassMissingPublishableKey?: boolean; /** - * Optional object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * An object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ appearance?: ExtractAppearanceType; /** - * Optional object to use the bundled Clerk UI instead of loading from CDN. + * An object to use the bundled Clerk UI instead of loading from CDN. * Import `ui` from `@clerk/ui` and pass it here to bundle the UI with your application. * When omitted, UI is loaded from Clerk's CDN. * Note: When `ui` is used, appearance is automatically typed based on the specific UI version. diff --git a/packages/shared/src/eventBus.ts b/packages/shared/src/eventBus.ts index ad6766b4d7c..d89fd2e4bb3 100644 --- a/packages/shared/src/eventBus.ts +++ b/packages/shared/src/eventBus.ts @@ -47,7 +47,7 @@ type EventBus> = { * Unsubscribe from an event * * @param event - The event name to unsubscribe from - * @param handler - Optional specific handler to remove. If omitted, all handlers for the event are removed + * @param handler - A specific handler to remove. If omitted, all handlers for the event are removed * @returns void */ off: (event: Event, handler?: EventHandler) => void; @@ -56,7 +56,7 @@ type EventBus> = { * Unsubscribe from a pre-dispatch event * * @param event - The event name to unsubscribe from - * @param handler - Optional specific handler to remove. If omitted, all pre-dispatch handlers for the event are removed + * @param handler - A specific handler to remove. If omitted, all pre-dispatch handlers for the event are removed * @returns void */ prioritizedOff: (event: Event, handler?: EventHandler) => void; diff --git a/packages/shared/src/react/billing/payment-element.tsx b/packages/shared/src/react/billing/payment-element.tsx index f293399fac2..35c0ac3fd3c 100644 --- a/packages/shared/src/react/billing/payment-element.tsx +++ b/packages/shared/src/react/billing/payment-element.tsx @@ -98,11 +98,11 @@ type internalStripeAppearance = { */ export type PaymentElementProviderProps = { /** - * An optional checkout resource object. When provided, the payment element is scoped to the specific checkout session. + * A checkout resource object. When provided, the payment element is scoped to the specific checkout session. */ checkout?: CheckoutFlowResource | BillingCheckoutResource | ReturnType['checkout']; /** - * An optional object to customize the appearance of the Stripe Payment Element. This allows you to match the form's styling to your application's theme. + * An object to customize the appearance of the Stripe Payment Element. This allows you to match the form's styling to your application's theme. */ stripeAppearance?: internalStripeAppearance; /** @@ -112,7 +112,7 @@ export type PaymentElementProviderProps = { */ for?: ForPayerType; /** - * An optional description to display to the user within the payment element UI. + * A description to display to the user within the payment element UI. */ paymentDescription?: string; }; diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts index 0cd1f6e8bc0..280761ee2a9 100644 --- a/packages/shared/src/types/apiKeys.ts +++ b/packages/shared/src/types/apiKeys.ts @@ -48,7 +48,7 @@ export interface APIKeyResource extends ClerkResource { */ createdBy: string | null; /** - * An optional description for the API key. + * A description for the API key. */ description: string | null; /** diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index a2f572ea060..f5e5292f5df 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -829,7 +829,7 @@ export interface Clerk { * * @param event - The event name to subscribe to. * @param handler - The callback function to execute when the event is triggered. - * @param opt - An optional object to control the behavior of the event handler. If true, and the event was previously dispatched, handler will be called immediately with the latest payload. + * @param opt - An object to control the behavior of the event handler. If true, and the event was previously dispatched, handler will be called immediately with the latest payload. * @param opt.notify - If `true` and the event was previously dispatched, the handler will be called immediately with the latest payload. */ on: OnEventListener; @@ -1284,12 +1284,12 @@ export type ClerkOptions = ClerkOptionsNavigation & ClerkUI?: ClerkUIConstructor | Promise; }; /** - * Optional object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. See the [Appearance](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. + * An object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. See the [Appearance](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ // TODO @nikos appearance?: any; /** - * Optional object to localize your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * An object to localize your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ localization?: LocalizationResource; /** @@ -1306,7 +1306,7 @@ export type ClerkOptions = ClerkOptionsNavigation & */ standardBrowser?: boolean; /** - * Optional support email for display in authentication screens. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * The support email address for display in authentication screens. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ supportEmail?: string; /** @@ -1322,11 +1322,11 @@ export type ClerkOptions = ClerkOptionsNavigation & */ signUpUrl?: string; /** - * An optional array of domains to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. + * An array of domains to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. */ allowedRedirectOrigins?: Array; /** - * An optional array of protocols to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. + * An array of protocols to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. */ allowedRedirectProtocols?: Array; /** @@ -1442,7 +1442,7 @@ export interface NavigateOptions { */ replace?: boolean; /** - * Optional router metadata. + * Router metadata. */ metadata?: RouterMetadata; } @@ -1495,7 +1495,7 @@ type RouterFn = ( */ to: string, /** - * Optional metadata + * Metadata */ metadata?: { /** diff --git a/packages/ui/src/styledSystem/StyleCacheProvider.tsx b/packages/ui/src/styledSystem/StyleCacheProvider.tsx index 55ee736f5f4..0863f32172b 100644 --- a/packages/ui/src/styledSystem/StyleCacheProvider.tsx +++ b/packages/ui/src/styledSystem/StyleCacheProvider.tsx @@ -7,9 +7,9 @@ import React, { useMemo } from 'react'; const el = document.querySelector('style#cl-style-insertion-point'); type StyleCacheProviderProps = React.PropsWithChildren<{ - /** Optional nonce value for CSP (Content Security Policy) */ + /** The nonce value for CSP (Content Security Policy). */ nonce?: string; - /** Optional CSS layer name to wrap styles in */ + /** The CSS layer name to wrap styles in. */ cssLayerName?: string; }>; diff --git a/packages/vue/src/plugin.ts b/packages/vue/src/plugin.ts index 0aba60a8d0c..7c6ff11109e 100644 --- a/packages/vue/src/plugin.ts +++ b/packages/vue/src/plugin.ts @@ -32,7 +32,7 @@ export type PluginOptions = Without< initialState?: InitialState; appearance?: Appearance; /** - * Optional object to use the bundled Clerk UI instead of loading from CDN. + * An object to use the bundled Clerk UI instead of loading from CDN. * Import `ui` from `@clerk/ui` and pass it here to bundle the UI with your application. * When omitted, UI is loaded from Clerk's CDN. */ From a49d4b4e50e88b712ac5a3a66b03b886e9d0966c Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 4 May 2026 13:30:42 -0700 Subject: [PATCH 29/51] fix tsconfig error --- tsconfig.typedoc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.typedoc.json b/tsconfig.typedoc.json index f2461cbc421..71338f87dc4 100644 --- a/tsconfig.typedoc.json +++ b/tsconfig.typedoc.json @@ -6,5 +6,5 @@ "strict": false, "importHelpers": false }, - "exclude": ["node_modules", "dist", "**/dist/**", "packages/*/dist/**", "**/*.test.ts', '**/*.test.tsx"] + "exclude": ["node_modules", "dist", "**/dist/**", "packages/*/dist/**", "**/*.test.ts", "**/*.test.tsx"] } From ee8d16a8e8cae83972235574b7dea79ab27f5a4b Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 4 May 2026 13:36:21 -0700 Subject: [PATCH 30/51] remove inline tag from getting generated in output --- .typedoc/custom-theme.mjs | 18 ++++++++++++++++++ packages/clerk-js/src/core/resources/Client.ts | 1 - packages/shared/src/types/client.ts | 2 +- typedoc.config.mjs | 11 ++++++++++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 54adf32219b..894fe3e079d 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -866,6 +866,24 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, + /** + * Stock `comments.comment` prints every {@link Comment.modifierTags} as **`TitleCase`** before the summary. + * `@inline` / `@inlineType` are router/type hints only — they must not appear in property tables or prose. + * + * @param {import('typedoc').Comment} model + * @param {Parameters[1]} [options] + */ + comment: (model, options) => { + const hidden = new Set(['@inline', '@inlineType']); + const modTags = model?.modifierTags ? Array.from(model.modifierTags) : []; + if (modTags.some(/** @param {string} t */ t => hidden.has(t))) { + const clone = Object.assign(Object.create(Object.getPrototypeOf(model)), model, { + modifierTags: new Set(modTags.filter(/** @param {string} t */ t => !hidden.has(t))), + }); + return superPartials.comment.call(this, clone, options); + } + return superPartials.comment.call(this, model, options); + }, /** * Remove the blockquote signature line. * diff --git a/packages/clerk-js/src/core/resources/Client.ts b/packages/clerk-js/src/core/resources/Client.ts index 986252b20dd..6b690c7261c 100644 --- a/packages/clerk-js/src/core/resources/Client.ts +++ b/packages/clerk-js/src/core/resources/Client.ts @@ -33,7 +33,6 @@ export class Client extends BaseResource implements ClientResource { lastActiveSessionId: string | null = null; captchaBypass = false; cookieExpiresAt: Date | null = null; - /** Last authentication strategy used by this client; `null` when unknown/disabled. */ lastAuthenticationStrategy: LastAuthenticationStrategy | null = null; createdAt: Date | null = null; updatedAt: Date | null = null; diff --git a/packages/shared/src/types/client.ts b/packages/shared/src/types/client.ts index 3ef3f16d0cb..ff7d1456abc 100644 --- a/packages/shared/src/types/client.ts +++ b/packages/shared/src/types/client.ts @@ -74,7 +74,7 @@ export interface ClientResource extends ClerkResource { */ lastActiveSessionId: string | null; /** - * Last authentication strategy used by this client; `null` when unknown or feature disabled. + * The last authentication strategy used by this client; `null` when unknown or feature disabled. */ lastAuthenticationStrategy: LastAuthenticationStrategy | null; /** diff --git a/typedoc.config.mjs b/typedoc.config.mjs index edfe0188ce3..4b16c5f41bf 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -85,7 +85,13 @@ const config = { theme: 'clerkTheme', router: 'clerk-router', readme: 'none', - notRenderedTags: [...OptionDefaults.notRenderedTags, ...CUSTOM_BLOCK_TAGS], + notRenderedTags: [ + ...OptionDefaults.notRenderedTags, + ...CUSTOM_BLOCK_TAGS, + /** Parsed for router/theme; must not appear as a doc section (otherwise renders as **Inline**). */ + '@inline', + '@inlineType', + ], packageOptions: { includeVersion: false, excludePrivate: true, @@ -100,6 +106,9 @@ const config = { ...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), /** Suppresses the Parameters table in `.typedoc/extract-methods.mjs` method MDX. */ '@skipParametersSection', + /** Type-only / router hints; not user-facing prose (see `notRenderedTags`). */ + '@inline', + '@inlineType', ], /** * Stops TypeDoc from generating standalone pages for inline types. From 3327641acda2b67e6df8db92ecbd960532556008 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 4 May 2026 15:27:09 -0700 Subject: [PATCH 31/51] add user object and comments; support generic instantiation + intersection e.g. ClerkPaginationParams --- .typedoc/extract-methods.mjs | 60 +++++- .typedoc/reference-objects.mjs | 4 + packages/shared/src/types/billing.ts | 1 + packages/shared/src/types/user.ts | 263 ++++++++++++++++++++++++++- 4 files changed, 312 insertions(+), 16 deletions(-) diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 9361ec22dd0..171d4737ed6 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -744,6 +744,53 @@ function resolveDeclarationWithObjectMembers(t) { const r = ref.reflection; if (r && 'type' in r) { const decl = /** @type {import('typedoc').DeclarationReflection} */ (r); + const typeArgs = ref.typeArguments ?? []; + + /** + * Generic aliases like `ClerkPaginationParams<{ status?: … }>` are a reference with `typeArguments`. + * TypeDoc often puts pagination fields only on the target alias `children` and omits `decl.type`, so returning `decl` early drops the type argument object. Merge base + each type argument's properties. + */ + if (typeArgs.length > 0) { + /** @type {Map} */ + const byName = new Map(); + if (decl.type) { + const fromType = resolveDeclarationWithObjectMembers(decl.type); + if (fromType?.children?.length) { + for (const c of fromType.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } + } + } + if (byName.size === 0 && decl.children?.length) { + for (const c of decl.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } + } + for (const ta of typeArgs) { + const fromArg = resolveDeclarationWithObjectMembers(ta); + if (fromArg?.children?.length) { + for (const c of fromArg.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } + } + } + if (byName.size > 0) { + return /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)), + kind: ReflectionKind.TypeLiteral, + name: '__referenceMerged', + }) + ); + } + } + if (decl.children?.length) { return decl; } @@ -929,12 +976,13 @@ function resolveNominalObjectTypeForSingleParam(t, project) { return { sectionTitle: typeDecl.name, holder: typeDecl, typeDecl }; } if (typeDecl.kindOf(ReflectionKind.TypeAlias)) { - // Same as `resolveDeclarationWithObjectMembers` for a reference: members may live on the alias - // (`typeDecl.children`) with no `typeDecl.type` (e.g. `SignOutOptions`, `JoinWaitlistParams`). - const holder = typeDecl.children?.length - ? typeDecl - : typeDecl.type - ? resolveDeclarationWithObjectMembers(typeDecl.type) + // Prefer resolving `typeAlias.type` so intersections and generic instantiations (e.g. `ClerkPaginationParams<{ status?: … }>`) merge every `&` arm into one property list. + // Some aliases only attach members on `typeDecl.children` with no object shape on `.type`; keep that fallback (e.g. `SignOutOptions`, `JoinWaitlistParams`). + const fromResolvedType = typeDecl.type ? resolveDeclarationWithObjectMembers(typeDecl.type) : undefined; + const holder = fromResolvedType?.children?.length + ? fromResolvedType + : typeDecl.children?.length + ? typeDecl : undefined; if (!holder?.children?.length) { return undefined; diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 9d0a0b06a7a..835ebbcbda0 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -27,6 +27,10 @@ export const REFERENCE_OBJECT_CONFIG = { symbol: 'SessionResource', declarationHint: 'types/session', }, + 'shared/user-resource/user-resource.mdx': { + symbol: 'UserResource', + declarationHint: 'types/user', + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ diff --git a/packages/shared/src/types/billing.ts b/packages/shared/src/types/billing.ts index 786887fd2b6..e4c34f3722f 100644 --- a/packages/shared/src/types/billing.ts +++ b/packages/shared/src/types/billing.ts @@ -107,6 +107,7 @@ export type BillingSubscriptionPlanPeriod = 'month' | 'annual'; */ export interface BillingPayerMethods { /** + * Initializes a payment method for the user. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ initializePaymentMethod: (params: InitializePaymentMethodParams) => Promise; diff --git a/packages/shared/src/types/user.ts b/packages/shared/src/types/user.ts index 43e5aa0a492..b7dcab08538 100644 --- a/packages/shared/src/types/user.ts +++ b/packages/shared/src/types/user.ts @@ -63,70 +63,221 @@ declare global { } /** - * The `User` object holds all of the information for a single user of your application and provides a set of methods to manage their account. + * The `User` object holds all of the information for a single user of your application and provides a set of methods to manage their account. Each `User` has at least one authentication identifier, which might be their email address, phone number, or a username. * - * A user can be contacted at their primary email address or primary phone number. They can have more than one registered email address, but only one of them will be their primary email address. This goes for phone numbers as well; a user can have more than one, but only one phone number will be their primary. At the same time, a user can also have one or more external accounts by connecting to [social providers](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview) such as Google, Apple, Facebook, and many more. + * A user can be contacted at their primary email address or primary phone number. They can have more than one registered email address or phone number, but only one of them will be their primary email address (`User.primaryEmailAddress`) or primary phone number (`User.primaryPhoneNumber`). At the same time, a user can also have one or more external accounts by connecting to [social providers](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview) such as Google, Apple, Facebook, and many more (`User.externalAccounts`). * - * Finally, a `User` object holds profile data like the user's name, profile picture, and a set of [metadata](/docs/guides/users/extending) that can be used internally to store arbitrary information. The metadata are split into `publicMetadata` and `privateMetadata`. Both types are set from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but public metadata can also be accessed from the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }}. - * - * The ClerkJS SDK provides some helper [methods](#methods) on the `User` object to help retrieve and update user information and authentication status. + * Finally, a `User` object holds profile data like the user's name, profile picture, and a set of [metadata](https://clerk.com/docs/guides/users/extending) that can be used internally to store arbitrary information. The metadata are split into `publicMetadata` and `privateMetadata`. Both types are set from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but public metadata can also be accessed from the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }}. + */ export interface UserResource extends ClerkResource, BillingPayerMethods { + /** + * The unique identifier of the user. + */ id: string; + /** + * The user's ID as used in your external systems. Must be unique across your instance. + */ externalId: string | null; + /** + * The ID of the user's primary email address. + */ primaryEmailAddressId: string | null; + /** + * The user's primary email address. + */ primaryEmailAddress: EmailAddressResource | null; + /** + * The ID of the user's primary phone number. + */ primaryPhoneNumberId: string | null; + /** + * The user's primary phone number. + */ primaryPhoneNumber: PhoneNumberResource | null; + /** + * The ID of the user's primary Web3 wallet. + */ primaryWeb3WalletId: string | null; + /** + * The user's primary Web3 wallet. + */ primaryWeb3Wallet: Web3WalletResource | null; + /** + * The user's username. + */ username: string | null; + /** + * The user's full name. + */ fullName: string | null; + /** + * The user's first name. + */ firstName: string | null; + /** + * The user's last name. + */ lastName: string | null; + /** + * Holds the default avatar or user's uploaded profile image. Compatible with Clerk's [Image Optimization](https://clerk.com/docs/guides/development/image-optimization). + */ imageUrl: string; + /** + * Indicates whether the user has uploaded an image or one was copied from OAuth. Returns `false` if Clerk is displaying an avatar for the user. + */ hasImage: boolean; + /** + * An array of all the `EmailAddress` objects associated with the user. Includes the primary. + */ emailAddresses: EmailAddressResource[]; + /** + * An array of all the `PhoneNumber` objects associated with the user. Includes the primary. + */ phoneNumbers: PhoneNumberResource[]; + /** + * An array of all the `Web3Wallet` objects associated with the user. Includes the primary. + */ web3Wallets: Web3WalletResource[]; + /** + * An array of all the `ExternalAccount` objects associated with the user via OAuth. + */ externalAccounts: ExternalAccountResource[]; + /** + * An array of all the `EnterpriseAccount` objects associated with the user via enterprise SSO. + */ enterpriseAccounts: EnterpriseAccountResource[]; + /** + * An array of all the `Passkey` objects associated with the user. + */ passkeys: PasskeyResource[]; + /** + * An array of all the `OrganizationMembership` objects associated with the user. + */ organizationMemberships: OrganizationMembershipResource[]; + /** + * Indicates whether the user has a password on their account. + */ passwordEnabled: boolean; + /** + * Indicates whether the user has enabled TOTP. + */ totpEnabled: boolean; + /** + * Indicates whether the user has enabled backup codes. + */ backupCodeEnabled: boolean; + /** + * Indicates whether the user has enabled two-factor authentication. + */ twoFactorEnabled: boolean; + /** + * Metadata that can be read from the Frontend API and Backend API and can be set only from the Backend API. + */ publicMetadata: UserPublicMetadata; + /** + * Metadata that can be read and set from the Frontend API. It's considered unsafe because it can be modified from the frontend. + * + * There is also an `unsafeMetadata` attribute in the [`SignUp`](https://clerk.com/docs/reference/objects/sign-up-future) object. The value of that field will be automatically copied to the user's unsafe metadata once the sign up is complete. + */ unsafeMetadata: UserUnsafeMetadata; + /** + * The date and time when the user last signed in. + */ lastSignInAt: Date | null; + /** + * The date and time when the user accepted the legal compliance documents. + */ legalAcceptedAt: Date | null; + /** + * Indicates whether the user can create organizations. + */ createOrganizationEnabled: boolean; + /** + * The maximum number of organizations the user can create. + */ createOrganizationsLimit: number | null; + /** + * Indicates whether the user can delete their own account. + */ deleteSelfEnabled: boolean; + /** + * The date and time when the user was last updated. + */ updatedAt: Date | null; + /** + * The date and time when the user was created. + */ createdAt: Date | null; update: (params: UpdateUserParams) => Promise; + /** + * Deletes the current user. + */ delete: () => Promise; updatePassword: (params: UpdateUserPasswordParams) => Promise; removePassword: (params: RemoveUserPasswordParams) => Promise; + /** + * Adds an email address for the user. A new [`EmailAddress`](https://clerk.com/docs/reference/types/email-address) will be created and associated with the user. + * + * > [!WARNING] + * > [**Email** must be enabled](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) in your app's settings in the Clerk Dashboard. + */ createEmailAddress: (params: CreateEmailAddressParams) => Promise; + /** + * Creates a passkey for the signed-in user. For an example, see the [custom flow guide](https://clerk.com/docs/guides/development/custom-flows/authentication/passkeys#create-user-passkeys). + * @returns A [`PasskeyResource`](https://clerk.com/docs/reference/types/passkey-resource) object. + * + * > [!NOTE] + * > When creating a passkey for a user in a multi-domain Clerk app, `createPasskey()` must be called from the primary domain. + */ createPasskey: () => Promise; + /** + * Adds a phone number for the user. A new [`PhoneNumber`](https://clerk.com/docs/reference/types/phone-number) will be created and associated with the user. + * + * > [!WARNING] + * > [**Phone** must be enabled](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) in your app's settings in the Clerk Dashboard. + */ createPhoneNumber: (params: CreatePhoneNumberParams) => Promise; + /** + * Adds a Web3 wallet for the user. A new [`Web3WalletResource`](https://clerk.com/docs/reference/types/web3-wallet) will be created and associated with the user. + */ createWeb3Wallet: (params: CreateWeb3WalletParams) => Promise; isPrimaryIdentification: (ident: EmailAddressResource | PhoneNumberResource | Web3WalletResource) => boolean; getSessions: () => Promise; setProfileImage: (params: SetProfileImageParams) => Promise; + /** + * Adds an external account for the user. A new [`ExternalAccount`](https://clerk.com/docs/reference/types/external-account) will be created and associated with the user. This method is useful if you want to allow an already signed-in user to connect their account with an external provider, such as Facebook, GitHub, etc., so that they can sign in with that provider in the future. + * + * > [!WARNING] + * > The social provider that you want to connect to [must be enabled](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#sso-connections) in your app's settings in the Clerk Dashboard. + */ createExternalAccount: (params: CreateExternalAccountParams) => Promise; getOrganizationMemberships: GetOrganizationMemberships; + /** + * Retrieves a list of Organization invitations for the user. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`UserOrganizationInvitation`](https://clerk.com/docs/reference/types/user-organization-invitation) objects. + */ getOrganizationInvitations: ( params?: GetUserOrganizationInvitationsParams, ) => Promise>; + /** + * Retrieves a list of Organization suggestions for the user. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationSuggestionResource`](https://clerk.com/docs/reference/types/organization-suggestion) objects. + */ getOrganizationSuggestions: ( params?: GetUserOrganizationSuggestionsParams, ) => Promise>; + /** + * Retrieves organization creation defaults for the current user. + * @returns A [`OrganizationCreationDefaultsResource`](https://clerk.com/docs/reference/types/organization-creation-defaults) object. + */ getOrganizationCreationDefaults: () => Promise; + /** + * Leaves an organization that the user is a member of. + * @param organizationId - The ID of the organization to leave. + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource) object. + */ leaveOrganization: (organizationId: string) => Promise; getEnterpriseConnections: (params?: GetEnterpriseConnectionsParams) => Promise; createEnterpriseConnection: (params: CreateMeEnterpriseConnectionParams) => Promise; @@ -142,9 +293,27 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { enterpriseConnectionId: string, params?: GetEnterpriseConnectionTestRunsParams, ) => Promise>; + /** + * Generates a TOTP secret for a user that can be used to register the application on the user's authenticator app of choice. If this method is called again (while still unverified), it replaces the previously generated secret. + * @returns A [`TOTPResource`](https://clerk.com/docs/reference/types/totp-resource) object. + * + * > [!WARNING] + * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. + */ createTOTP: () => Promise; verifyTOTP: (params: VerifyTOTPParams) => Promise; + /** + * Disables TOTP by deleting the user's TOTP secret. + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource) object. + * + * > [!WARNING] + * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. + */ disableTOTP: () => Promise; + /** + * Generates a fresh new set of backup codes for the user. Every time the method is called, it will replace the previously generated backup codes. + * @returns A [`BackupCodeResource`](https://clerk.com/docs/reference/types/backup-code-resource) object. + */ createBackupCode: () => Promise; get verifiedExternalAccounts(): ExternalAccountResource[]; @@ -160,19 +329,69 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { __internal_toSnapshot: () => UserJSONSnapshot; } -export type CreateEmailAddressParams = { email: string }; -export type CreatePhoneNumberParams = { phoneNumber: string }; -export type CreateWeb3WalletParams = { web3Wallet: string }; -export type SetProfileImageParams = { file: Blob | File | string | null }; +/** @document */ +export type CreateEmailAddressParams = { + /** + * The email address to add to the user. + */ + email: string; +}; +/** @document */ +export type CreatePhoneNumberParams = { + /** + * The phone number to add to the user. + */ + phoneNumber: string; +}; +/** @document */ +export type CreateWeb3WalletParams = { + /** + * The Web3 wallet to add to the user. + */ + web3Wallet: string; +}; +/** @document */ +export type SetProfileImageParams = { + /** + * The file to set as the user's profile image, or `null` to remove the current image. + */ + file: Blob | File | string | null; +}; +/** @document */ export type CreateExternalAccountParams = { + /** + * The strategy corresponding to the OAuth provider. For example: `'oauth_google'`. + */ strategy?: OAuthStrategy; + /** + * The ID of the enterprise connection to connect to the user. + */ enterpriseConnectionId?: string; + /** + * The full URL or path that the OAuth provider should redirect to, on successful authorization on their part. Typically, this will be a simple `/sso-callback` route that calls [`Clerk.handleRedirectCallback`](https://clerk.com/docs/reference/objects/clerk#handle-redirect-callback) or mounts the [``](https://clerk.com/docs/reference/components/control/authenticate-with-redirect-callback) component. See the [custom flow](https://clerk.com/docs/guides/development/custom-flows/authentication/oauth-connections) for implementation details. + + */ redirectUrl?: string; + /** + * Additional scopes for your user to be prompted to approve. + */ additionalScopes?: OAuthScope[]; + /** + * The value to pass to the [OIDC `prompt` parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) in the generated OAuth redirect URL. + */ oidcPrompt?: string; + /** + * The value to pass to the [OIDC `login_hint` parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=login_hint,in%20\(if%20necessary\).) in the generated OAuth redirect URL. + */ oidcLoginHint?: string; }; -export type VerifyTOTPParams = { code: string }; +/** @document */ +export type VerifyTOTPParams = { + /** + * The code to verify. + */ + code: string; +}; type UpdateUserJSON = Pick< UserJSON, @@ -187,24 +406,48 @@ type UpdateUserJSON = Pick< export type UpdateUserParams = Partial>; +/** @document */ export type UpdateUserPasswordParams = { + /** + * The user's new password. + */ newPassword: string; + /** + * The user's current password. + */ currentPassword?: string; + /** + * Whether to sign out the user from all their active sessions once their password is updated. + */ signOutOfOtherSessions?: boolean; }; +/** @document */ export type RemoveUserPasswordParams = Pick; +/** @document */ export type GetUserOrganizationInvitationsParams = ClerkPaginationParams<{ + /** + * The status an invitation can have. + */ status?: OrganizationInvitationStatus; }>; +/** @document */ export type GetUserOrganizationSuggestionsParams = ClerkPaginationParams<{ + /** + * The status a suggestion can have. + */ status?: OrganizationSuggestionStatus | OrganizationSuggestionStatus[]; }>; +/** @document */ export type GetUserOrganizationMembershipParams = ClerkPaginationParams; +/** + * Retrieves a list of Organization memberships for the user. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) objects. + */ export type GetOrganizationMemberships = ( params?: GetUserOrganizationMembershipParams, ) => Promise>; From d6fa55ff8214db648785a09c5d9dc5e38a82252b Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 4 May 2026 16:23:09 -0700 Subject: [PATCH 32/51] remove backticks from link replacements; add more link replacements --- .typedoc/custom-plugin.mjs | 76 ++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 3d9eed7e155..650ba9f0abd 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -161,33 +161,53 @@ function getCatchAllReplacements() { return [ { pattern: /(?/g, - replace: '[`Appearance`](/docs/guides/customizing-clerk/appearance-prop/overview)', + replace: '[Appearance](/docs/guides/customizing-clerk/appearance-prop/overview)', }, { pattern: /(? Date: Mon, 4 May 2026 16:23:27 -0700 Subject: [PATCH 33/51] omit experimental tag + text from output; add more jsdoc comments for user object --- .typedoc/custom-theme.mjs | 5 +++-- .typedoc/extract-methods.mjs | 5 ++++- packages/shared/src/types/billing.ts | 2 ++ packages/shared/src/types/image.ts | 10 +++++++++ packages/shared/src/types/resource.ts | 4 ++-- packages/shared/src/types/user.ts | 32 ++++++++++++++++++++++++++- 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 894fe3e079d..2412de9f2d3 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -868,13 +868,14 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { ...superPartials, /** * Stock `comments.comment` prints every {@link Comment.modifierTags} as **`TitleCase`** before the summary. - * `@inline` / `@inlineType` are router/type hints only — they must not appear in property tables or prose. + * `@inline` / `@inlineType` are router/type hints only; `@experimental` is SDK-only guidance — none of these + * must appear in property tables or prose. * * @param {import('typedoc').Comment} model * @param {Parameters[1]} [options] */ comment: (model, options) => { - const hidden = new Set(['@inline', '@inlineType']); + const hidden = new Set(['@inline', '@inlineType', '@experimental']); const modTags = model?.modifierTags ? Array.from(model.modifierTags) : []; if (modTags.some(/** @param {string} t */ t => hidden.has(t))) { const clone = Object.assign(Object.create(Object.getPrototypeOf(model)), model, { diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 171d4737ed6..6b7dc3a7107 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -685,6 +685,9 @@ function renderMemberTableOmittingExampleBlocks(roots, ctx, render) { } } +/** Block tags omitted from extracted method prose (see `custom-theme.mjs` `comment` partial for theme output). */ +const BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE = new Set(['@param', '@typeParam', '@returns', '@experimental']); + /** * @param {import('typedoc').Comment | undefined} comment */ @@ -694,7 +697,7 @@ function commentSummaryAndBody(comment) { } const summary = displayPartsToString(comment.summary).trim(); const block = comment.blockTags - ?.filter(t => !['@param', '@typeParam', '@returns'].includes(t.tag)) + ?.filter(t => !BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE.has(t.tag)) .map(t => displayPartsToString(t.content).trim()) .filter(Boolean) .join('\n\n'); diff --git a/packages/shared/src/types/billing.ts b/packages/shared/src/types/billing.ts index e4c34f3722f..5d0e6dfc7de 100644 --- a/packages/shared/src/types/billing.ts +++ b/packages/shared/src/types/billing.ts @@ -112,10 +112,12 @@ export interface BillingPayerMethods { */ initializePaymentMethod: (params: InitializePaymentMethodParams) => Promise; /** + * Adds a payment method for the user. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ addPaymentMethod: (params: AddPaymentMethodParams) => Promise; /** + * Retrieves a list of payment methods that have been stored for the user. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPaymentMethods: ( diff --git a/packages/shared/src/types/image.ts b/packages/shared/src/types/image.ts index 51b3c77fb61..10bdf2ba478 100644 --- a/packages/shared/src/types/image.ts +++ b/packages/shared/src/types/image.ts @@ -1,7 +1,17 @@ import type { ClerkResource } from './resource'; +/** @document */ export interface ImageResource extends ClerkResource { + /** + * The unique identifier of the image. + */ id?: string; + /** + * The name of the image. + */ name: string | null; + /** + * The public URL of the image. + */ publicUrl: string | null; } diff --git a/packages/shared/src/types/resource.ts b/packages/shared/src/types/resource.ts index 787b9fc3bda..28ca9f0f240 100644 --- a/packages/shared/src/types/resource.ts +++ b/packages/shared/src/types/resource.ts @@ -1,7 +1,7 @@ /** @document */ export type ClerkResourceReloadParams = { /** - * The nonce for the rotating token. + * A nonce to use for rotating the user's token. Used in native application OAuth flows to allow the native client to update its JWT once despite changes in its rotating token. */ rotatingTokenNonce?: string; }; @@ -19,7 +19,7 @@ export interface ClerkResource { */ pathRoot: string; /** - * Reload the resource and return the resource itself. + * Reloads the resource, which is useful when you want to access the latest user data after performing a mutation. To make the updated data immediately available, this method forces a session token refresh instead of waiting for the automatic refresh cycle that could temporarily retain stale information. Learn more about [forcing a token refresh](https://clerk.com/docs/guides/sessions/force-token-refresh). */ reload(p?: ClerkResourceReloadParams): Promise; } diff --git a/packages/shared/src/types/user.ts b/packages/shared/src/types/user.ts index b7dcab08538..7468c70a479 100644 --- a/packages/shared/src/types/user.ts +++ b/packages/shared/src/types/user.ts @@ -210,12 +210,24 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { */ createdAt: Date | null; + /** + * Updates the user's attributes. Use this method to save information you collected about the user. + * + * The appropriate settings must be enabled in the Clerk Dashboard for the user to be able to update their attributes. For example, if you want to use the `update({ firstName })` method, you must enable the **First and last name** setting. It can be found on the [**User & authentication**](https://dashboard.clerk.com/~/user-authentication/user-and-authentication?user_auth_tab=user-profile) page in the Clerk Dashboard. + * @skipParametersSection + */ update: (params: UpdateUserParams) => Promise; /** * Deletes the current user. */ delete: () => Promise; + /** + * Updates the user's password. + */ updatePassword: (params: UpdateUserPasswordParams) => Promise; + /** + * Removes the user's password. + */ removePassword: (params: RemoveUserPasswordParams) => Promise; /** * Adds an email address for the user. A new [`EmailAddress`](https://clerk.com/docs/reference/types/email-address) will be created and associated with the user. @@ -243,8 +255,19 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { * Adds a Web3 wallet for the user. A new [`Web3WalletResource`](https://clerk.com/docs/reference/types/web3-wallet) will be created and associated with the user. */ createWeb3Wallet: (params: CreateWeb3WalletParams) => Promise; + /** + * A check whether or not the given resource is the primary identifier for the user. + * @param ident - The resource checked against the user to see if it is the primary identifier. + */ isPrimaryIdentification: (ident: EmailAddressResource | PhoneNumberResource | Web3WalletResource) => boolean; + /** + * Retrieves all **active** sessions for this user. This method uses a cache so a network request will only be triggered only once. + * @returns An array of [`SessionWithActivities`](https://clerk.com/docs/reference/types/session-with-activities) objects. + */ getSessions: () => Promise; + /** + * Adds the user's profile image or replaces it if one already exists. This method will upload an image and associate it with the user. + */ setProfileImage: (params: SetProfileImageParams) => Promise; /** * Adds an external account for the user. A new [`ExternalAccount`](https://clerk.com/docs/reference/types/external-account) will be created and associated with the user. This method is useful if you want to allow an already signed-in user to connect their account with an external provider, such as Facebook, GitHub, etc., so that they can sign in with that provider in the future. @@ -298,9 +321,16 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { * @returns A [`TOTPResource`](https://clerk.com/docs/reference/types/totp-resource) object. * * > [!WARNING] - * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. + * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. */ createTOTP: () => Promise; + /** + * Verifies a TOTP secret after a user has created it. The user must provide a code from their authenticator app that has been generated using the previously created secret. This way, correct set up and ownership of the authenticator app can be validated. + * @returns A [`TOTPResource`](https://clerk.com/docs/reference/types/totp-resource) object. + * + * > [!WARNING] + * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. + */ verifyTOTP: (params: VerifyTOTPParams) => Promise; /** * Disables TOTP by deleting the user's TOTP secret. From 0e3cbd6e40c085ae76eae8fa37ec872cd82634c5 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Mon, 4 May 2026 17:16:02 -0700 Subject: [PATCH 34/51] finish documenting user object --- .typedoc/custom-plugin.mjs | 4 ++++ packages/shared/src/types/image.ts | 2 +- packages/shared/src/types/resource.ts | 3 --- packages/shared/src/types/strategies.ts | 1 - packages/shared/src/types/user.ts | 10 +++++++--- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 650ba9f0abd..7620c028d26 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -254,6 +254,10 @@ function getCatchAllReplacements() { pattern: /(?; +export type RemoveUserPasswordParams = { + /** + * The user's current password. + */ + currentPassword?: string; +}; /** @document */ export type GetUserOrganizationInvitationsParams = ClerkPaginationParams<{ @@ -471,7 +476,6 @@ export type GetUserOrganizationSuggestionsParams = ClerkPaginationParams<{ status?: OrganizationSuggestionStatus | OrganizationSuggestionStatus[]; }>; -/** @document */ export type GetUserOrganizationMembershipParams = ClerkPaginationParams; /** From 196fba449d4f55cbcac5652b8c23584fbe60870b Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 6 May 2026 15:45:51 -0700 Subject: [PATCH 35/51] generate signinfutureresource; fix file-structure test --- .typedoc/__tests__/file-structure.test.ts | 4 ++ .typedoc/reference-objects.mjs | 4 ++ packages/shared/src/types/signInCommon.ts | 10 +++ packages/shared/src/types/signInFuture.ts | 82 +++++++++-------------- 4 files changed, 51 insertions(+), 49 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 48519c7f1d0..17fbba8af6b 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -58,6 +58,10 @@ describe('Typedoc output', () => { "shared/client-resource/methods", "shared/session-resource", "shared/session-resource/methods", + "shared/user-resource", + "shared/user-resource/methods", + "shared/sign-in-future-resource", + "shared/sign-in-future-resource/methods", ] `); }); diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 835ebbcbda0..f7d3abf2ad0 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -31,6 +31,10 @@ export const REFERENCE_OBJECT_CONFIG = { symbol: 'UserResource', declarationHint: 'types/user', }, + 'shared/sign-in-future-resource/sign-in-future-resource.mdx': { + symbol: 'SignInFutureResource', + declarationHint: 'types/signInFuture', + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ diff --git a/packages/shared/src/types/signInCommon.ts b/packages/shared/src/types/signInCommon.ts index 40e255b8cf1..2213eb662ec 100644 --- a/packages/shared/src/types/signInCommon.ts +++ b/packages/shared/src/types/signInCommon.ts @@ -57,6 +57,16 @@ import type { } from './strategies'; import type { StartEmailLinkFlowParams } from './verification'; +/** + * @property {string} 'needs_identifier' - The user's identifier (e.g., email address, phone number, username) hasn't been provided. + * @property {string} 'needs_first_factor' - One of the following [first factor verification](!first-factor-verification) strategies is missing: `'email_link'`, `'email_code'`, `passkey`, `password`, `'phone_code'`, `'web3_base_signature'`, `'web3_metamask_signature'`, `'web3_coinbase_wallet_signature'`, `'web3_okx_wallet_signature'`, `'web3_solana_signature'`, [`OAuthStrategy`](https://clerk.com/docs/reference/types/sso#o-auth-strategy), or `'enterprise_sso'`. + * @property {string} 'needs_second_factor' - One of the following [second factor verification](!second-factor-verification) strategies is missing: `'phone_code'`, `'totp'`, `'backup_code'`, `'email_code'`, or `'email_link'`. + * @property {string} 'needs_client_trust' - The user is signing in from a new device and must complete a [second factor verification](!second-factor-verification) to establish [Client Trust](https://clerk.com/docs/guides/secure/client-trust). See the [Client Trust custom flow guide](https://clerk.com/docs/guides/development/custom-flows/authentication/client-trust) for more information. + * @property {string} 'needs_new_password' - The user needs to set a new password. See the [dedicated custom flow](https://clerk.com/docs/guides/development/custom-flows/account-updates/forgot-password) guide for more information. + * + * @interface + * @inline + */ export type SignInStatus = | 'needs_identifier' | 'needs_first_factor' diff --git a/packages/shared/src/types/signInFuture.ts b/packages/shared/src/types/signInFuture.ts index b320d3afcf7..8ea19dbc535 100644 --- a/packages/shared/src/types/signInFuture.ts +++ b/packages/shared/src/types/signInFuture.ts @@ -8,18 +8,15 @@ import type { Web3Provider } from './web3'; export interface SignInFutureCreateParams { /** - * The authentication identifier for the sign-in. This can be the value of the user's email address, phone number, - * username, or Web3 wallet address. + * The authentication identifier for the sign-in. This can be the value of the user's email address, phone number, username, or Web3 wallet address. */ identifier?: string; /** - * The user's password. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. + * The user's password. Only supported if [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. */ password?: string; /** - * The first factor verification strategy to use in the sign-in flow. Depends on the `identifier` value. Each - * authentication identifier supports different verification strategies. + * The first factor verification strategy to use in the sign-in flow. Depends on the `identifier` value. Each authentication identifier supports different verification strategies. */ strategy?: OAuthStrategy | 'enterprise_sso' | PasskeyStrategy | TicketStrategy; /** @@ -27,24 +24,19 @@ export interface SignInFutureCreateParams { */ redirectUrl?: string; /** - * The URL that the user will be redirected to, after successful authorization from the OAuth provider and - * Clerk sign-in. + * The URL that the user will be redirected to, after successful authorization from the OAuth provider and Clerk sign-in. */ actionCompleteRedirectUrl?: string; /** - * When set to `true`, the `SignIn` will attempt to retrieve information from the active `SignUp` instance and use it - * to complete the sign-in process. This is useful when you want to seamlessly transition a user from a sign-up - * attempt to a sign-in attempt. + * When set to `true`, the `SignIn` will attempt to retrieve information from the active `SignUp` instance and use it to complete the sign-in process. This is useful when you want to seamlessly transition a user from a sign-up attempt to a sign-in attempt. */ transfer?: boolean; /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. **Required** if `strategy` is set to `'ticket'`. + * **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket?: string; /** - * When set to `true`, if a user does not exist, the sign-up will prepare a transfer to sign up a new - * account. If bot sign-up protection is enabled, captcha will also be required on sign in. + * When set to `true`, if a user does not exist, the sign-up will prepare a transfer to sign up a new account. If bot sign-up protection is enabled, captcha will also be required on sign in. */ signUpIfMissing?: boolean; } @@ -90,6 +82,7 @@ export type SignInFuturePasswordParams = { } ); +/** Parameters for sending a sign-in email verification code. */ export type SignInFutureEmailCodeSendParams = | { /** @@ -107,6 +100,7 @@ export type SignInFutureEmailCodeSendParams = emailAddress?: never; }; +/** Parameters for sending a sign-in email link. */ export type SignInFutureEmailLinkSendParams = { /** * The full URL that the user will be redirected to when they visit the email link. @@ -115,15 +109,14 @@ export type SignInFutureEmailLinkSendParams = { } & ( | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists and you are sending to the identified user. */ emailAddress?: string; emailAddressId?: never; } | { /** - * The ID for the user's email address that will receive an email with the email link. + * The ID for the user's email address that will receive an email with the email link. Provide either `emailAddress` or `emailAddressId`, not both. */ emailAddressId?: string; emailAddress?: never; @@ -302,9 +295,7 @@ export interface SignInFutureFinalizeParams { } /** - * The `SignInFuture` class holds the state of the current sign-in and provides helper methods to navigate and complete - * the sign-in process. It is used to manage the sign-in lifecycle, including the first and second factor verification, - * and the creation of a new session. + * The `SignInFuture` class holds the state of the current sign-in and provides helper methods to navigate and complete the sign-in process. It is used to manage the sign-in lifecycle, including the first and second factor verification, and the creation of a new session. */ export interface SignInFutureResource { /** @@ -313,14 +304,12 @@ export interface SignInFutureResource { readonly id?: string; /** - * Array of the first factors that are supported in the current sign-in. Each factor contains information about the - * verification strategy that can be used. + * Array of the first factors that are supported in the current sign-in. Each factor contains information about the verification strategy that can be used. */ readonly supportedFirstFactors: SignInFirstFactor[]; /** - * Array of the second factors that are supported in the current sign-in. Each factor contains information about the - * verification strategy that can be used. This property is populated only when the first factor is verified. + * Array of the second factors that are supported in the current sign-in. Each factor contains information about the verification strategy that can be used. This property is populated only when the first factor is verified. */ readonly supportedSecondFactors: SignInSecondFactor[]; @@ -330,41 +319,38 @@ export interface SignInFutureResource { readonly status: SignInStatus; /** - * Indicates that there is not a matching user for the first-factor verification used, and that the sign-in can be - * transferred to a sign-up. + * Indicates that there is not a matching user for the first-factor verification used, and that the sign-in can be transferred to a sign-up. */ readonly isTransferable: boolean; + /** + * Reflects that the sign-in was not able to create a new session because the identifier already exists in an existing session. + * @property {string} sessionId - The ID of the existing session. + */ readonly existingSession?: { sessionId: string }; /** - * The state of the verification process for the selected first factor. Initially, this property contains an empty - * verification object, since there is no first factor selected. + * The state of the verification process for the selected first factor. Initially, this property contains an empty verification object, since there is no first factor selected. */ readonly firstFactorVerification: VerificationResource; /** - * The state of the verification process for the selected second factor. Initially, this property contains an empty - * verification object, since there is no second factor selected. + * The state of the verification process for the selected second factor. Initially, this property contains an empty verification object, since there is no second factor selected. */ readonly secondFactorVerification: VerificationResource; /** - * The authentication identifier value for the current sign-in. `null` if the `strategy` is `'oauth_'` - * or `'enterprise_sso'`. + * The authentication identifier value for the current sign-in. `null` if the `strategy` is `'oauth_'` or `'enterprise_sso'`. */ readonly identifier: string | null; /** - * The identifier of the session that was created upon completion of the current sign-in. The value of this property - * is `null` if the sign-in status is not `'complete'`. + * The ID of the session that was created upon completion of the current sign-in. The value of this property is `null` if the sign-in status is not `'complete'`. */ readonly createdSessionId: string | null; /** - * An object containing information about the user of the current sign-in. This property is populated only once an - * identifier is given to the `SignIn` object through `signIn.create()` or another method that populates the - * `identifier` property. + * An object containing information about the user of the current sign-in. This property is populated only once an identifier is given to the `SignIn` object through `signIn.create()` or another method that populates the `identifier` property. */ readonly userData: UserData; @@ -376,18 +362,14 @@ export interface SignInFutureResource { readonly canBeDiscarded: boolean; /** - * Creates a new `SignIn` instance initialized with the provided parameters. The instance maintains the sign-in - * lifecycle state through its `status` property, which updates as the authentication flow progresses. + * Creates a new `SignIn` instance initialized with the provided parameters. The instance maintains the sign-in lifecycle state through its `status` property, which updates as the authentication flow progresses. Once the sign-in process is complete, call the `signIn.finalize()` method to set the newly created session as the active session. * - * What you must pass to `params` depends on which [sign-in options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) - * you have enabled in your app's settings in the Clerk Dashboard. + * What you must pass to `params` depends on which [sign-in options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) you have enabled in your app's settings in the Clerk Dashboard. * - * You can complete the sign-in process in one step if you supply the required fields to `create()`. Otherwise, - * Clerk's sign-in process provides great flexibility and allows users to easily create multi-step sign-in flows. + * You can complete the sign-in process in one step if you supply the required fields to `create()`. Otherwise, Clerk's sign-in process provides great flexibility and allows users to easily create multi-step sign-in flows. * - * > [!WARNING] - * > Once the sign-in process is complete, call the `signIn.finalize()` method to set the newly created session as - * > the active session. + * > [!IMPORTANT] + * > The `signIn.create()` method is intended for advanced use cases. For most use cases, prefer the use of the factor-specific methods such as `signIn.password()`, `signIn.emailCode.sendCode()`, etc. */ create: (params: SignInFutureCreateParams) => Promise<{ error: ClerkError | null }>; @@ -426,7 +408,9 @@ export interface SignInFutureResource { waitForVerification: () => Promise<{ error: ClerkError | null }>; /** - * The verification status + * The verification status of the email link. This property is populated by reading query parameters from the URL after the user visits the email link. Returns `null` if no verification status is available. + * + * @propertyTableDoc */ verification: { /** @@ -435,7 +419,7 @@ export interface SignInFutureResource { status: 'verified' | 'expired' | 'failed' | 'client_mismatch'; /** - * The created session ID + * The ID of the session that was created upon completion of the current sign-in. */ createdSessionId: string; From a432b357d4e4dcfe493c5b64c077fe5c69aea6f5 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 6 May 2026 15:46:26 -0700 Subject: [PATCH 36/51] support new @propertyTableDoc tag; handle object properties that include only callable members --- .typedoc/custom-theme.mjs | 55 ++++++- .typedoc/extract-methods.mjs | 292 ++++++++++++++++++++++++++++++++--- typedoc.config.mjs | 2 + 3 files changed, 328 insertions(+), 21 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 2412de9f2d3..7908afed309 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -921,7 +921,13 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { // On allowlisted output pages only, drop function-valued interface/class properties from property tables (property syntax with function types). Other pages unchanged. const allowlisted = pageMatchesAllowlist(this.page?.url, REFERENCE_OBJECTS_LIST); - const filtered = allowlisted ? model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers)) : model; + const filtered = allowlisted + ? model.filter( + prop => + !isCallableInterfaceProperty(prop, this.helpers) && + !isCallableOnlyNamespaceProperty(prop, this.helpers), + ) + : model; return superPartials.propertiesTable(filtered, options); }, /** @@ -1561,6 +1567,51 @@ function isCallableInterfaceProperty(prop, helpers) { return isCallablePropertyValueType(t, helpers, new Set()); } +/** + * Inline object type whose **direct** members are exclusively callables (e.g. `emailCode: { sendCode, verifyCode }` in `SignInFutureResource`). + * Omitted from reference-object property tables; nested callables are documented via extract-methods. + * + * If any direct member is not callable, the parent stays in the property table. Use the @propertyTableDoc tag on non-callable members to avoid this behavior; it will create a page for that non-callable member in /methods that contains a heading and property table. (See `emailLink` in `SignInFutureResource` for an example; `emailLink.verification` is not callable and uses the @propertyTableDoc tag.) + * + * @param {import('typedoc').DeclarationReflection} prop + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + */ +function isCallableOnlyNamespaceProperty(prop, helpers) { + const t = + (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type + ? prop.type + : helpers.getDeclarationType(prop); + let cur = /** @type {import('typedoc').SomeType | undefined} */ (t); + while ( + cur && + typeof cur === 'object' && + /** @type {{ type?: string }} */ (cur).type === 'optional' && + 'elementType' in cur + ) { + cur = /** @type {import('typedoc').SomeType} */ (/** @type {import('typedoc').OptionalType} */ (cur).elementType); + } + if (!(cur instanceof ReflectionType)) { + return false; + } + const children = cur.declaration?.children ?? []; + if (children.length === 0) { + return false; + } + for (const c of children) { + if ( + c.kind !== ReflectionKind.Property && + c.kind !== ReflectionKind.Variable && + c.kind !== ReflectionKind.Accessor + ) { + return false; + } + if (!isCallableInterfaceProperty(c, helpers)) { + return false; + } + } + return true; +} + /** * True when the property's value type is callable (function type, union/intersection of callables, or reference to a type alias of a function type). Object types with properties (e.g. namespaces) stay false. * E.g. `navigate: CustomNavigation` in clerk.ts @@ -1644,4 +1695,4 @@ function isCallablePropertyValueType(t, helpers, seenReflectionIds) { return false; } -export { isCallableInterfaceProperty }; +export { isCallableInterfaceProperty, isCallableOnlyNamespaceProperty }; diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 6b7dc3a7107..dc848f49581 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -4,10 +4,10 @@ * * Run after `typedoc` (same cwd as repo root). Uses a second TypeDoc convert pass to read reflections. * - * Like `extract-returns-and-params.mjs`, parameter tables are not hand-built: they use the same - * `MarkdownThemeContext.partials` as TypeDoc markdown output (`parametersTable` / `propertiesTable`, which call - * `someType` and therefore pick up `custom-theme.mjs` union/`<code>` behavior). Router + theme are prepared - * via `prepare-markdown-renderer.mjs` (same idea as `typedoc-plugin-markdown` `render()`). + * Like `extract-returns-and-params.mjs`, parameter tables are not hand-built: they use the same `MarkdownThemeContext.partials` as TypeDoc markdown output (`parametersTable` / `propertiesTable`, which call `someType` and therefore pick up `custom-theme.mjs` union/`<code>` behavior). Router + theme are prepared via `prepare-markdown-renderer.mjs` (same idea as `typedoc-plugin-markdown` `render()`). + * + * Inline object properties whose members are **only** callables (e.g. `emailCode: { sendCode, verifyCode }`) are omitted from reference-object property tables and each nested callable is written under `methods/` (see `isCallableOnlyNamespaceProperty`). + * Mixed namespaces (e.g. `emailLink`) emit nested callables as methods and use the `@propertyTableDoc` tag to create a page in /methods that contains a heading and property table (see `buildPropertyTableDocMdx`). */ import fs from 'node:fs'; import path from 'node:path'; @@ -27,7 +27,7 @@ import { MarkdownPageEvent, MarkdownTheme } from 'typedoc-plugin-markdown'; import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; import typedocConfig from '../typedoc.config.mjs'; -import { isCallableInterfaceProperty } from './custom-theme.mjs'; +import { isCallableInterfaceProperty, isCallableOnlyNamespaceProperty } from './custom-theme.mjs'; import { applyCatchAllMdReplacements, applyRelativeLinkReplacements, @@ -491,7 +491,7 @@ function signatureWithInstantiation(sig, instantiationMap) { } /** - * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (callable members are extracted here, not listed as properties). + * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (callable members and callable-only namespaces are extracted here, not listed as properties). * * @param {import('typedoc').DeclarationReflection} decl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx @@ -510,6 +510,150 @@ function shouldExtractCallableMember(decl, ctx) { return false; } +/** + * Nested callable properties from namespaces that are only callable properties (object of functions only). E.g. `emailCode: { sendCode, verifyCode }` in `SignInFutureResource`. + * + * @param {import('typedoc').DeclarationReflection} parentProp + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {import('typedoc').DeclarationReflection[]} + */ +function nestedCallablesFromCallableOnlyNamespace(parentProp, ctx) { + if (!isCallableOnlyNamespaceProperty(parentProp, ctx.helpers)) { + return []; + } + const cur = unwrapOptionalLayersSomeType(parentProp.type); + if (!(cur instanceof ReflectionType)) { + return []; + } + /** @type {import('typedoc').DeclarationReflection[]} */ + const out = []; + for (const nested of cur.declaration.children ?? []) { + if (nested.name.startsWith('__')) { + continue; + } + out.push(/** @type {import('typedoc').DeclarationReflection} */ (nested)); + } + return out; +} + +/** + * @param {import('typedoc').SomeType | undefined} t + * @returns {import('typedoc').SomeType | undefined} + */ +function unwrapOptionalLayersSomeType(t) { + let cur = /** @type {import('typedoc').SomeType | undefined} */ (t); + while ( + cur && + typeof cur === 'object' && + /** @type {{ type?: string }} */ (cur).type === 'optional' && + 'elementType' in cur + ) { + cur = /** @type {import('typedoc').SomeType} */ (/** @type {import('typedoc').OptionalType} */ (cur).elementType); + } + return cur; +} + +/** + * Nested callables under inline object namespaces that also include non-callables (e.g. `emailLink.sendLink` in `SignInFutureResource`). + * Callable-only namespaces are handled by `nestedCallablesFromCallableOnlyNamespace` instead. + * + * @param {import('typedoc').DeclarationReflection} parentProp + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {import('typedoc').DeclarationReflection[]} + */ +function nestedCallablesFromMixedInlineNamespace(parentProp, ctx) { + if (isCallableOnlyNamespaceProperty(parentProp, ctx.helpers)) { + return []; + } + const cur = unwrapOptionalLayersSomeType(parentProp.type); + if (!(cur instanceof ReflectionType)) { + return []; + } + /** @type {import('typedoc').DeclarationReflection[]} */ + const out = []; + for (const nested of cur.declaration.children ?? []) { + if (nested.name.startsWith('__')) { + continue; + } + const nd = /** @type {import('typedoc').DeclarationReflection} */ (nested); + if (isCallableInterfaceProperty(nd, ctx.helpers)) { + out.push(nd); + } + } + return out; +} + +/** + * @param {import('typedoc').DeclarationReflection} parentProp + * @returns {import('typedoc').DeclarationReflection[]} + */ +function nestedPropertyTableDocMembers(parentProp) { + const cur = unwrapOptionalLayersSomeType(parentProp.type); + if (!(cur instanceof ReflectionType)) { + return []; + } + /** @type {import('typedoc').DeclarationReflection[]} */ + const out = []; + for (const nested of cur.declaration.children ?? []) { + if (nested.name.startsWith('__')) { + continue; + } + const nd = /** @type {import('typedoc').DeclarationReflection} */ (nested); + if (nd.comment?.getTag('@propertyTableDoc')) { + out.push(nd); + } + } + return out; +} + +/** + * Object-literal (or single object arm of `T | null`) property rows for a properties table. + * + * @param {import('typedoc').SomeType | undefined} valueType + * @returns {import('typedoc').DeclarationReflection[] | undefined} + */ +function resolveObjectShapeMembersForPropertyTable(valueType) { + let t = unwrapOptionalLayersSomeType(valueType); + if (t instanceof UnionType) { + const objectArms = t.types.filter(u => u instanceof ReflectionType && (u.declaration?.children?.length ?? 0) > 0); + if (objectArms.length !== 1) { + return undefined; + } + t = /** @type {import('typedoc').ReflectionType} */ (objectArms[0]); + } + if (!(t instanceof ReflectionType)) { + return undefined; + } + const kids = t.declaration?.children ?? []; + return kids.filter( + c => c.kind === ReflectionKind.Property || c.kind === ReflectionKind.Variable || c.kind === ReflectionKind.Accessor, + ); +} + +/** + * @param {string} parentName + * @param {import('typedoc').DeclarationReflection} nestedDecl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + */ +function buildPropertyTableDocMdx(parentName, nestedDecl, ctx) { + const qualifiedName = `${parentName}.${nestedDecl.name}`; + const title = `### \`${qualifiedName}\``; + const description = commentSummaryAndBody(nestedDecl.comment); + const props = resolveObjectShapeMembersForPropertyTable(nestedDecl.type); + if (!props?.length) { + return ''; + } + const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => + ctx.partials.propertiesTable(props, { + kind: ReflectionKind.Interface, + isEventProps: false, + }), + ); + const chunks = [title, '', description, '', tableMd].filter(Boolean); + const raw = chunks.join('\n\n'); + return `${applyCatchAllMdReplacements(applyRelativeLinkReplacements(raw)).trim()}\n`; +} + /** * @param {string} markdown * @returns {string | undefined} Body under `## Properties` (no heading), or undefined @@ -686,7 +830,13 @@ function renderMemberTableOmittingExampleBlocks(roots, ctx, render) { } /** Block tags omitted from extracted method prose (see `custom-theme.mjs` `comment` partial for theme output). */ -const BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE = new Set(['@param', '@typeParam', '@returns', '@experimental']); +const BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE = new Set([ + '@param', + '@typeParam', + '@returns', + '@experimental', + '@propertyTableDoc', +]); /** * @param {import('typedoc').Comment | undefined} comment @@ -725,6 +875,37 @@ function appendSignatureOnlyReturns(declComment, sigComment) { return formatReturnsLineFromTag(tag); } +/** + * @param {import('typedoc').DeclarationReflection} prop + */ +function propertyReflectionTypeIsNever(prop) { + let ty = prop.type; + while (ty?.type === 'optional') { + ty = /** @type {import('typedoc').OptionalType} */ (ty).elementType; + } + return ty?.type === 'intrinsic' && ty.name === 'never'; +} + +/** + * Union discriminators often use `otherProp?: never`. Prefer the branch with a documentable type. + * + * @param {import('typedoc').DeclarationReflection} existing + * @param {import('typedoc').DeclarationReflection} candidate + */ +function pickBetterUnionPropertyCandidate(existing, candidate) { + const existingNever = propertyReflectionTypeIsNever(existing); + const candidateNever = propertyReflectionTypeIsNever(candidate); + if (existingNever && !candidateNever) { + return candidate; + } + if (!existingNever && candidateNever) { + return existing; + } + const existingDoc = existing.comment?.summary?.length ?? 0; + const candidateDoc = candidate.comment?.summary?.length ?? 0; + return candidateDoc > existingDoc ? candidate : existing; +} + /** * Object / type-literal declaration for a parameter type (reference, inlined reflection, intersection). * TypeDoc applies `@param parent.prop` descriptions onto property reflections under this declaration. @@ -828,6 +1009,41 @@ function resolveDeclarationWithObjectMembers(t) { }) ); } + if (t.type === 'union') { + const u = /** @type {import('typedoc').UnionType} */ (t); + /** @type {Map} */ + const byName = new Map(); + for (const inner of u.types) { + const res = resolveDeclarationWithObjectMembers(inner); + if (!res?.children?.length) { + continue; + } + for (const c of res.children) { + if (!c.kindOf(ReflectionKind.Property)) { + continue; + } + if (propertyReflectionTypeIsNever(c)) { + continue; + } + const existing = byName.get(c.name); + if (!existing) { + byName.set(c.name, c); + } else { + byName.set(c.name, pickBetterUnionPropertyCandidate(existing, c)); + } + } + } + if (byName.size === 0) { + return undefined; + } + return /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)), + kind: ReflectionKind.TypeLiteral, + name: '__unionMerged', + }) + ); + } if (t.type === 'optional') { return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType); } @@ -1079,9 +1295,10 @@ function parametersMarkdownTable(sig, ctx, instantiationMap) { /** * @param {import('typedoc').DeclarationReflection} decl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {{ qualifiedName?: string }} [options] Nested namespace methods use `parent.child` for headings / signatures. */ -function buildMethodMdx(decl, ctx) { - const name = decl.name; +function buildMethodMdx(decl, ctx, options = {}) { + const name = options.qualifiedName ?? decl.name; const sig = getPrimaryCallSignature(decl); if (!sig) { return ''; @@ -1145,18 +1362,55 @@ function extractMethodsForPage(pageUrl, project, app) { if (child.name.startsWith('__')) { continue; } - if (!shouldExtractCallableMember(/** @type {import('typedoc').DeclarationReflection} */ (child), ctx)) { - continue; + const childDecl = /** @type {import('typedoc').DeclarationReflection} */ (child); + + if (shouldExtractCallableMember(childDecl, ctx)) { + const mdx = buildMethodMdx(childDecl, ctx); + if (mdx) { + const fileName = `${toKebabCase(child.name)}.mdx`; + const filePath = path.join(outDir, fileName); + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } } - const mdx = buildMethodMdx(/** @type {import('typedoc').DeclarationReflection} */ (child), ctx); - if (!mdx) { - continue; + + for (const nested of nestedCallablesFromCallableOnlyNamespace(childDecl, ctx)) { + const qualifiedName = `${child.name}.${nested.name}`; + const fileSlug = `${toKebabCase(child.name)}-${toKebabCase(nested.name)}`; + const mdx = buildMethodMdx(nested, ctx, { qualifiedName }); + if (!mdx) { + continue; + } + const filePath = path.join(outDir, `${fileSlug}.mdx`); + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } + + for (const nested of nestedCallablesFromMixedInlineNamespace(childDecl, ctx)) { + const fileSlug = `${toKebabCase(child.name)}-${toKebabCase(nested.name)}`; + const mdx = buildMethodMdx(nested, ctx, { qualifiedName: `${child.name}.${nested.name}` }); + if (!mdx) { + continue; + } + const filePath = path.join(outDir, `${fileSlug}.mdx`); + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } + + for (const nested of nestedPropertyTableDocMembers(childDecl)) { + const fileSlug = `${toKebabCase(child.name)}-${toKebabCase(nested.name)}`; + const mdx = buildPropertyTableDocMdx(child.name, nested, ctx); + if (!mdx) { + continue; + } + const filePath = path.join(outDir, `${fileSlug}.mdx`); + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; } - const fileName = `${toKebabCase(child.name)}.mdx`; - const filePath = path.join(outDir, fileName); - fs.writeFileSync(filePath, mdx, 'utf-8'); - console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); - count++; } return count; } diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 4b16c5f41bf..d88c12900c7 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -6,6 +6,8 @@ const CUSTOM_BLOCK_TAGS = [ '@paramExtension', '@experimental', '@hideReturns', + /** Nested property documented under `methods/` as heading + properties table (see extract-methods `buildPropertyTableDocMdx`). */ + '@propertyTableDoc', ]; /** @type {import("typedoc-plugin-markdown").PluginOptions} */ From 5f6a6b712c043bbba7840c191a0fc68918282b1b Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 6 May 2026 15:50:07 -0700 Subject: [PATCH 37/51] fix mixed namespaces (all callable members plus @propertyTableDoc tag) --- .typedoc/custom-theme.mjs | 51 +++++++++++++++++++++++++++++++++--- .typedoc/extract-methods.mjs | 2 +- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 7908afed309..ace02ea21fd 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -925,7 +925,7 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { ? model.filter( prop => !isCallableInterfaceProperty(prop, this.helpers) && - !isCallableOnlyNamespaceProperty(prop, this.helpers), + !isInlineNamespaceFullyExtractedToMethods(prop, this.helpers), ) : model; return superPartials.propertiesTable(filtered, options); @@ -1567,11 +1567,56 @@ function isCallableInterfaceProperty(prop, helpers) { return isCallablePropertyValueType(t, helpers, new Set()); } +/** + * Inline object namespace whose **direct** members are each documented under `methods/` (either a callable → method MDX, or `@propertyTableDoc` → nested property table). Omits the parent row from reference-object property tables (e.g. `emailCode`, `emailLink` on `SignInFutureResource`). + * + * @param {import('typedoc').DeclarationReflection} prop + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + */ +function isInlineNamespaceFullyExtractedToMethods(prop, helpers) { + const t = + (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type + ? prop.type + : helpers.getDeclarationType(prop); + let cur = /** @type {import('typedoc').Type | undefined} */ (t); + while ( + cur && + typeof cur === 'object' && + /** @type {{ type?: string }} */ (cur).type === 'optional' && + 'elementType' in cur + ) { + cur = /** @type {import('typedoc').Type} */ (/** @type {import('typedoc').OptionalType} */ (cur).elementType); + } + if (!(cur instanceof ReflectionType)) { + return false; + } + const children = cur.declaration?.children ?? []; + if (children.length === 0) { + return false; + } + for (const c of children) { + if ( + c.kind !== ReflectionKind.Property && + c.kind !== ReflectionKind.Variable && + c.kind !== ReflectionKind.Accessor + ) { + return false; + } + const documentedViaPropertyTable = Boolean( + /** @type {import('typedoc').DeclarationReflection} */ (c).comment?.getTag?.('@propertyTableDoc'), + ); + if (!isCallableInterfaceProperty(c, helpers) && !documentedViaPropertyTable) { + return false; + } + } + return true; +} + /** * Inline object type whose **direct** members are exclusively callables (e.g. `emailCode: { sendCode, verifyCode }` in `SignInFutureResource`). - * Omitted from reference-object property tables; nested callables are documented via extract-methods. + * Used by extract-methods to decide which namespaces need the mixed-namespace nested callable path. * - * If any direct member is not callable, the parent stays in the property table. Use the @propertyTableDoc tag on non-callable members to avoid this behavior; it will create a page for that non-callable member in /methods that contains a heading and property table. (See `emailLink` in `SignInFutureResource` for an example; `emailLink.verification` is not callable and uses the @propertyTableDoc tag.) + * For property-table omission, see {@link isInlineNamespaceFullyExtractedToMethods}. * * @param {import('typedoc').DeclarationReflection} prop * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index dc848f49581..281ca2e102a 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -491,7 +491,7 @@ function signatureWithInstantiation(sig, instantiationMap) { } /** - * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (callable members and callable-only namespaces are extracted here, not listed as properties). + * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (`isCallableInterfaceProperty` / `isInlineNamespaceFullyExtractedToMethods`: extracted here, not listed as properties). * * @param {import('typedoc').DeclarationReflection} decl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx From 809241d8c40db5ed1de1331cc938c1b91ef56ce1 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 6 May 2026 17:00:46 -0700 Subject: [PATCH 38/51] refactor how we handle objects with only callable members; introduce @extractMethods tag --- .typedoc/custom-theme.mjs | 105 +---------- .typedoc/extract-methods.mjs | 215 ++++++++-------------- packages/shared/src/types/signInFuture.ts | 62 ++++--- typedoc.config.mjs | 7 +- 4 files changed, 126 insertions(+), 263 deletions(-) diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index ace02ea21fd..c45475b42a7 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -911,21 +911,22 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { return superPartials.referenceType.call(this, model); }, /** + * On allowlisted reference-object pages, drop function-valued members and `@extractMethods` namespace parents from property tables (they are documented under `methods/`). + * `extract-methods.mjs` reuses this partial for nominal param tables and nested `@extractMethods` docs on the same URL; pass `applyAllowlistedPropertyTableRowFilters: false` so rows are not stripped. + * * @param {import('typedoc').DeclarationReflection[]} model - * @param {Parameters[1]} [options] + * @param {Parameters[1] & { applyAllowlistedPropertyTableRowFilters?: boolean }} [options] */ propertiesTable: (model, options) => { if (!Array.isArray(model)) { return superPartials.propertiesTable(/** @type {any} */ (model), options); } - // On allowlisted output pages only, drop function-valued interface/class properties from property tables (property syntax with function types). Other pages unchanged. const allowlisted = pageMatchesAllowlist(this.page?.url, REFERENCE_OBJECTS_LIST); - const filtered = allowlisted + const applyAllowlistFilters = allowlisted && options?.applyAllowlistedPropertyTableRowFilters !== false; + const filtered = applyAllowlistFilters ? model.filter( - prop => - !isCallableInterfaceProperty(prop, this.helpers) && - !isInlineNamespaceFullyExtractedToMethods(prop, this.helpers), + prop => !isCallableInterfaceProperty(prop, this.helpers) && !prop.comment?.hasModifier('@extractMethods'), ) : model; return superPartials.propertiesTable(filtered, options); @@ -1567,96 +1568,6 @@ function isCallableInterfaceProperty(prop, helpers) { return isCallablePropertyValueType(t, helpers, new Set()); } -/** - * Inline object namespace whose **direct** members are each documented under `methods/` (either a callable → method MDX, or `@propertyTableDoc` → nested property table). Omits the parent row from reference-object property tables (e.g. `emailCode`, `emailLink` on `SignInFutureResource`). - * - * @param {import('typedoc').DeclarationReflection} prop - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers - */ -function isInlineNamespaceFullyExtractedToMethods(prop, helpers) { - const t = - (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type - ? prop.type - : helpers.getDeclarationType(prop); - let cur = /** @type {import('typedoc').Type | undefined} */ (t); - while ( - cur && - typeof cur === 'object' && - /** @type {{ type?: string }} */ (cur).type === 'optional' && - 'elementType' in cur - ) { - cur = /** @type {import('typedoc').Type} */ (/** @type {import('typedoc').OptionalType} */ (cur).elementType); - } - if (!(cur instanceof ReflectionType)) { - return false; - } - const children = cur.declaration?.children ?? []; - if (children.length === 0) { - return false; - } - for (const c of children) { - if ( - c.kind !== ReflectionKind.Property && - c.kind !== ReflectionKind.Variable && - c.kind !== ReflectionKind.Accessor - ) { - return false; - } - const documentedViaPropertyTable = Boolean( - /** @type {import('typedoc').DeclarationReflection} */ (c).comment?.getTag?.('@propertyTableDoc'), - ); - if (!isCallableInterfaceProperty(c, helpers) && !documentedViaPropertyTable) { - return false; - } - } - return true; -} - -/** - * Inline object type whose **direct** members are exclusively callables (e.g. `emailCode: { sendCode, verifyCode }` in `SignInFutureResource`). - * Used by extract-methods to decide which namespaces need the mixed-namespace nested callable path. - * - * For property-table omission, see {@link isInlineNamespaceFullyExtractedToMethods}. - * - * @param {import('typedoc').DeclarationReflection} prop - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers - */ -function isCallableOnlyNamespaceProperty(prop, helpers) { - const t = - (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type - ? prop.type - : helpers.getDeclarationType(prop); - let cur = /** @type {import('typedoc').SomeType | undefined} */ (t); - while ( - cur && - typeof cur === 'object' && - /** @type {{ type?: string }} */ (cur).type === 'optional' && - 'elementType' in cur - ) { - cur = /** @type {import('typedoc').SomeType} */ (/** @type {import('typedoc').OptionalType} */ (cur).elementType); - } - if (!(cur instanceof ReflectionType)) { - return false; - } - const children = cur.declaration?.children ?? []; - if (children.length === 0) { - return false; - } - for (const c of children) { - if ( - c.kind !== ReflectionKind.Property && - c.kind !== ReflectionKind.Variable && - c.kind !== ReflectionKind.Accessor - ) { - return false; - } - if (!isCallableInterfaceProperty(c, helpers)) { - return false; - } - } - return true; -} - /** * True when the property's value type is callable (function type, union/intersection of callables, or reference to a type alias of a function type). Object types with properties (e.g. namespaces) stay false. * E.g. `navigate: CustomNavigation` in clerk.ts @@ -1740,4 +1651,4 @@ function isCallablePropertyValueType(t, helpers, seenReflectionIds) { return false; } -export { isCallableInterfaceProperty, isCallableOnlyNamespaceProperty }; +export { isCallableInterfaceProperty }; diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 281ca2e102a..d609312b20d 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -6,8 +6,7 @@ * * Like `extract-returns-and-params.mjs`, parameter tables are not hand-built: they use the same `MarkdownThemeContext.partials` as TypeDoc markdown output (`parametersTable` / `propertiesTable`, which call `someType` and therefore pick up `custom-theme.mjs` union/`<code>` behavior). Router + theme are prepared via `prepare-markdown-renderer.mjs` (same idea as `typedoc-plugin-markdown` `render()`). * - * Inline object properties whose members are **only** callables (e.g. `emailCode: { sendCode, verifyCode }`) are omitted from reference-object property tables and each nested callable is written under `methods/` (see `isCallableOnlyNamespaceProperty`). - * Mixed namespaces (e.g. `emailLink`) emit nested callables as methods and use the `@propertyTableDoc` tag to create a page in /methods that contains a heading and property table (see `buildPropertyTableDocMdx`). + * Inline object namespaces tagged **`@extractMethods`** on the parent property are omitted from the main Properties table (see `custom-theme.mjs`). For each direct member: callables become `methods/-.mdx` via `buildMethodMdx`; non-callables become a heading + property table via `buildPropertyTableDocMdx`. */ import fs from 'node:fs'; import path from 'node:path'; @@ -27,7 +26,7 @@ import { MarkdownPageEvent, MarkdownTheme } from 'typedoc-plugin-markdown'; import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; import typedocConfig from '../typedoc.config.mjs'; -import { isCallableInterfaceProperty, isCallableOnlyNamespaceProperty } from './custom-theme.mjs'; +import { isCallableInterfaceProperty } from './custom-theme.mjs'; import { applyCatchAllMdReplacements, applyRelativeLinkReplacements, @@ -491,7 +490,7 @@ function signatureWithInstantiation(sig, instantiationMap) { } /** - * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (`isCallableInterfaceProperty` / `isInlineNamespaceFullyExtractedToMethods`: extracted here, not listed as properties). + * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (`isCallableInterfaceProperty` and `@extractMethods`: extracted here, not listed as properties). Nested tables pass `applyAllowlistedPropertyTableRowFilters: false`. * * @param {import('typedoc').DeclarationReflection} decl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx @@ -510,32 +509,6 @@ function shouldExtractCallableMember(decl, ctx) { return false; } -/** - * Nested callable properties from namespaces that are only callable properties (object of functions only). E.g. `emailCode: { sendCode, verifyCode }` in `SignInFutureResource`. - * - * @param {import('typedoc').DeclarationReflection} parentProp - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @returns {import('typedoc').DeclarationReflection[]} - */ -function nestedCallablesFromCallableOnlyNamespace(parentProp, ctx) { - if (!isCallableOnlyNamespaceProperty(parentProp, ctx.helpers)) { - return []; - } - const cur = unwrapOptionalLayersSomeType(parentProp.type); - if (!(cur instanceof ReflectionType)) { - return []; - } - /** @type {import('typedoc').DeclarationReflection[]} */ - const out = []; - for (const nested of cur.declaration.children ?? []) { - if (nested.name.startsWith('__')) { - continue; - } - out.push(/** @type {import('typedoc').DeclarationReflection} */ (nested)); - } - return out; -} - /** * @param {import('typedoc').SomeType | undefined} t * @returns {import('typedoc').SomeType | undefined} @@ -553,59 +526,6 @@ function unwrapOptionalLayersSomeType(t) { return cur; } -/** - * Nested callables under inline object namespaces that also include non-callables (e.g. `emailLink.sendLink` in `SignInFutureResource`). - * Callable-only namespaces are handled by `nestedCallablesFromCallableOnlyNamespace` instead. - * - * @param {import('typedoc').DeclarationReflection} parentProp - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @returns {import('typedoc').DeclarationReflection[]} - */ -function nestedCallablesFromMixedInlineNamespace(parentProp, ctx) { - if (isCallableOnlyNamespaceProperty(parentProp, ctx.helpers)) { - return []; - } - const cur = unwrapOptionalLayersSomeType(parentProp.type); - if (!(cur instanceof ReflectionType)) { - return []; - } - /** @type {import('typedoc').DeclarationReflection[]} */ - const out = []; - for (const nested of cur.declaration.children ?? []) { - if (nested.name.startsWith('__')) { - continue; - } - const nd = /** @type {import('typedoc').DeclarationReflection} */ (nested); - if (isCallableInterfaceProperty(nd, ctx.helpers)) { - out.push(nd); - } - } - return out; -} - -/** - * @param {import('typedoc').DeclarationReflection} parentProp - * @returns {import('typedoc').DeclarationReflection[]} - */ -function nestedPropertyTableDocMembers(parentProp) { - const cur = unwrapOptionalLayersSomeType(parentProp.type); - if (!(cur instanceof ReflectionType)) { - return []; - } - /** @type {import('typedoc').DeclarationReflection[]} */ - const out = []; - for (const nested of cur.declaration.children ?? []) { - if (nested.name.startsWith('__')) { - continue; - } - const nd = /** @type {import('typedoc').DeclarationReflection} */ (nested); - if (nd.comment?.getTag('@propertyTableDoc')) { - out.push(nd); - } - } - return out; -} - /** * Object-literal (or single object arm of `T | null`) property rows for a properties table. * @@ -644,10 +564,15 @@ function buildPropertyTableDocMdx(parentName, nestedDecl, ctx) { return ''; } const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => - ctx.partials.propertiesTable(props, { - kind: ReflectionKind.Interface, - isEventProps: false, - }), + ctx.partials.propertiesTable( + props, + /** @type {Parameters[1]} */ + ({ + kind: ReflectionKind.Interface, + isEventProps: false, + applyAllowlistedPropertyTableRowFilters: false, + }), + ), ); const chunks = [title, '', description, '', tableMd].filter(Boolean); const raw = chunks.join('\n\n'); @@ -830,13 +755,7 @@ function renderMemberTableOmittingExampleBlocks(roots, ctx, render) { } /** Block tags omitted from extracted method prose (see `custom-theme.mjs` `comment` partial for theme output). */ -const BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE = new Set([ - '@param', - '@typeParam', - '@returns', - '@experimental', - '@propertyTableDoc', -]); +const BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE = new Set(['@param', '@typeParam', '@returns', '@experimental']); /** * @param {import('typedoc').Comment | undefined} comment @@ -1249,10 +1168,15 @@ function trySingleNominalParameterTypeSection(sig, ctx) { return undefined; } const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => - ctx.partials.propertiesTable(props, { - kind: nominal.typeDecl.kind, - isEventProps: false, - }), + ctx.partials.propertiesTable( + props, + /** @type {Parameters[1]} */ + ({ + kind: nominal.typeDecl.kind, + isEventProps: false, + applyAllowlistedPropertyTableRowFilters: false, + }), + ), ); if (!tableMd?.trim()) { return undefined; @@ -1329,6 +1253,59 @@ function buildMethodMdx(decl, ctx, options = {}) { return chunks.join('\n\n').trim() + '\n'; } +/** + * @param {import('typedoc').DeclarationReflection} decl + */ +function hasExtractMethodsModifier(decl) { + return Boolean(decl.comment?.hasModifier('@extractMethods')); +} + +/** + * Writes `methods/-.mdx` for each direct member of an `@extractMethods` inline object: callables via {@link buildMethodMdx}, non-callables with a resolvable object shape via {@link buildPropertyTableDocMdx}. + * + * @param {import('typedoc').DeclarationReflection} parentDecl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {string} outDir + * @returns {number} Number of files written + */ +function processExtractMethodsNamespace(parentDecl, ctx, outDir) { + const cur = unwrapOptionalLayersSomeType(parentDecl.type); + if (!(cur instanceof ReflectionType)) { + console.warn( + `[extract-methods] @extractMethods on "${parentDecl.name}" requires an inline object (reflection) type; skipping nested extraction`, + ); + return 0; + } + const parentName = parentDecl.name; + let count = 0; + for (const nested of cur.declaration?.children ?? []) { + if (nested.name.startsWith('__')) { + continue; + } + const nd = /** @type {import('typedoc').DeclarationReflection} */ (nested); + const fileSlug = `${toKebabCase(parentName)}-${toKebabCase(nd.name)}`; + const filePath = path.join(outDir, `${fileSlug}.mdx`); + if (shouldExtractCallableMember(nd, ctx)) { + const mdx = buildMethodMdx(nd, ctx, { qualifiedName: `${parentName}.${nd.name}` }); + if (!mdx) { + continue; + } + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + continue; + } + const propTableMdx = buildPropertyTableDocMdx(parentName, nd, ctx); + if (!propTableMdx) { + continue; + } + fs.writeFileSync(filePath, propTableMdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } + return count; +} + /** * @param {string} pageUrl * @param {import('typedoc').ProjectReflection} project @@ -1364,6 +1341,11 @@ function extractMethodsForPage(pageUrl, project, app) { } const childDecl = /** @type {import('typedoc').DeclarationReflection} */ (child); + if (hasExtractMethodsModifier(childDecl)) { + count += processExtractMethodsNamespace(childDecl, ctx, outDir); + continue; + } + if (shouldExtractCallableMember(childDecl, ctx)) { const mdx = buildMethodMdx(childDecl, ctx); if (mdx) { @@ -1374,43 +1356,6 @@ function extractMethodsForPage(pageUrl, project, app) { count++; } } - - for (const nested of nestedCallablesFromCallableOnlyNamespace(childDecl, ctx)) { - const qualifiedName = `${child.name}.${nested.name}`; - const fileSlug = `${toKebabCase(child.name)}-${toKebabCase(nested.name)}`; - const mdx = buildMethodMdx(nested, ctx, { qualifiedName }); - if (!mdx) { - continue; - } - const filePath = path.join(outDir, `${fileSlug}.mdx`); - fs.writeFileSync(filePath, mdx, 'utf-8'); - console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); - count++; - } - - for (const nested of nestedCallablesFromMixedInlineNamespace(childDecl, ctx)) { - const fileSlug = `${toKebabCase(child.name)}-${toKebabCase(nested.name)}`; - const mdx = buildMethodMdx(nested, ctx, { qualifiedName: `${child.name}.${nested.name}` }); - if (!mdx) { - continue; - } - const filePath = path.join(outDir, `${fileSlug}.mdx`); - fs.writeFileSync(filePath, mdx, 'utf-8'); - console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); - count++; - } - - for (const nested of nestedPropertyTableDocMembers(childDecl)) { - const fileSlug = `${toKebabCase(child.name)}-${toKebabCase(nested.name)}`; - const mdx = buildPropertyTableDocMdx(child.name, nested, ctx); - if (!mdx) { - continue; - } - const filePath = path.join(outDir, `${fileSlug}.mdx`); - fs.writeFileSync(filePath, mdx, 'utf-8'); - console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); - count++; - } } return count; } diff --git a/packages/shared/src/types/signInFuture.ts b/packages/shared/src/types/signInFuture.ts index 8ea19dbc535..f9f00b21c98 100644 --- a/packages/shared/src/types/signInFuture.ts +++ b/packages/shared/src/types/signInFuture.ts @@ -6,6 +6,7 @@ import type { OAuthStrategy, PasskeyStrategy, TicketStrategy, Web3Strategy } fro import type { VerificationResource } from './verification'; import type { Web3Provider } from './web3'; +/** @document */ export interface SignInFutureCreateParams { /** * The authentication identifier for the sign-in. This can be the value of the user's email address, phone number, username, or Web3 wallet address. @@ -109,20 +110,21 @@ export type SignInFutureEmailLinkSendParams = { } & ( | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists and you are sending to the identified user. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists. */ emailAddress?: string; emailAddressId?: never; } | { /** - * The ID for the user's email address that will receive an email with the email link. Provide either `emailAddress` or `emailAddressId`, not both. + * The ID for the user's email address that will receive an email with the email link. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists. */ emailAddressId?: string; emailAddress?: never; } ); +/** @document */ export interface SignInFutureEmailCodeVerifyParams { /** * The one-time code that was sent to the user. @@ -130,6 +132,7 @@ export interface SignInFutureEmailCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureResetPasswordSubmitParams { /** * The new password for the user. @@ -141,6 +144,7 @@ export interface SignInFutureResetPasswordSubmitParams { signOutOfOtherSessions?: boolean; } +/** @document */ export interface SignInFutureResetPasswordPhoneCodeSendParams { /** * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if @@ -149,6 +153,7 @@ export interface SignInFutureResetPasswordPhoneCodeSendParams { phoneNumber?: string; } +/** @document */ export type SignInFuturePhoneCodeSendParams = { /** * The mechanism to use to send the code to the provided phone number. Defaults to `'sms'`. @@ -172,6 +177,7 @@ export type SignInFuturePhoneCodeSendParams = { } ); +/** @document */ export interface SignInFuturePhoneCodeVerifyParams { /** * The one-time code that was sent to the user. @@ -179,6 +185,7 @@ export interface SignInFuturePhoneCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureResetPasswordPhoneCodeVerifyParams { /** * The one-time code that was sent to the user. @@ -186,6 +193,7 @@ export interface SignInFutureResetPasswordPhoneCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureSSOParams { /** * The strategy to use for authentication. @@ -229,6 +237,7 @@ export interface SignInFutureSSOParams { identifier?: string; } +/** @document */ export interface SignInFutureMFAPhoneCodeVerifyParams { /** * The one-time code that was sent to the user as part of the `signIn.mfa.sendPhoneCode()` method. @@ -236,6 +245,7 @@ export interface SignInFutureMFAPhoneCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureMFAEmailCodeVerifyParams { /** * The one-time code that was sent to the user as part of the `signIn.mfa.sendEmailCode()` method. @@ -243,6 +253,7 @@ export interface SignInFutureMFAEmailCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureTOTPVerifyParams { /** * The TOTP generated by the user's authenticator app. @@ -250,6 +261,7 @@ export interface SignInFutureTOTPVerifyParams { code: string; } +/** @document */ export interface SignInFutureBackupCodeVerifyParams { /** * The backup code that was provided to the user when they set up two-step authentication. @@ -257,6 +269,7 @@ export interface SignInFutureBackupCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureTicketParams { /** * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) @@ -265,6 +278,7 @@ export interface SignInFutureTicketParams { ticket: string; } +/** @document */ export interface SignInFutureWeb3Params { /** * The verification strategy to validate the user's sign-in request. @@ -280,6 +294,7 @@ export interface SignInFutureWeb3Params { walletName?: string; } +/** @document */ export interface SignInFuturePasskeyParams { /** * The flow to use for the passkey sign-in. @@ -290,7 +305,11 @@ export interface SignInFuturePasskeyParams { flow?: 'autofill' | 'discoverable'; } +/** @document */ export interface SignInFutureFinalizeParams { + /** + * A custom navigation function to be called just before the session and/or Organization is set. When provided, it takes precedence over the `redirectUrl` parameter for navigation. The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. See the [section on using the `navigate()` parameter](https://clerk.com/docs/reference/objects/clerk#using-the-navigate-parameter) for more details. + */ navigate?: SetActiveNavigate; } @@ -374,43 +393,37 @@ export interface SignInFutureResource { create: (params: SignInFutureCreateParams) => Promise<{ error: ClerkError | null }>; /** - * Used to submit a password to sign-in. + * Submits a password to sign-in. */ password: (params: SignInFuturePasswordParams) => Promise<{ error: ClerkError | null }>; - /** - * - */ + /** @extractMethods */ emailCode: { /** - * Used to send an email code to sign-in + * Sends an email code to sign-in. */ sendCode: (params?: SignInFutureEmailCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via email to sign-in + * Verifies a code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#email-code-send-code) method. */ verifyCode: (params: SignInFutureEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; - /** - * - */ + /** @extractMethods */ emailLink: { /** - * Used to send an email link to sign-in + * Sends an email link to sign in with. */ sendLink: (params: SignInFutureEmailLinkSendParams) => Promise<{ error: ClerkError | null }>; /** - * Will wait for verification to complete or expire + * Waits for email link verification to complete or expire. */ waitForVerification: () => Promise<{ error: ClerkError | null }>; /** * The verification status of the email link. This property is populated by reading query parameters from the URL after the user visits the email link. Returns `null` if no verification status is available. - * - * @propertyTableDoc */ verification: { /** @@ -430,9 +443,7 @@ export interface SignInFutureResource { } | null; }; - /** - * - */ + /** @extractMethods */ phoneCode: { /** * Used to send a phone code to sign-in @@ -445,9 +456,7 @@ export interface SignInFutureResource { verifyCode: (params: SignInFuturePhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; - /** - * - */ + /** @extractMethods */ resetPasswordEmailCode: { /** * Used to send a password reset code to the first email address on the account @@ -465,9 +474,7 @@ export interface SignInFutureResource { submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: ClerkError | null }>; }; - /** - * - */ + /** @extractMethods */ resetPasswordPhoneCode: { /** * Used to send a password reset code to the first phone number on the account @@ -490,9 +497,7 @@ export interface SignInFutureResource { */ sso: (params: SignInFutureSSOParams) => Promise<{ error: ClerkError | null }>; - /** - * - */ + /** @extractMethods */ mfa: { /** * Used to send a phone code as a second factor to sign-in @@ -544,8 +549,7 @@ export interface SignInFutureResource { passkey: (params?: SignInFuturePasskeyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to convert a sign-in with `status === 'complete'` into an active session. Will cause anything observing the - * session state (such as the `useUser()` hook) to update automatically. + * Converts a sign-in with `status === 'complete'` into an active session. Will cause anything observing the session state (such as the [`useUser()`](https://clerk.com/docs/reference/hooks/use-user) hook) to update automatically. */ finalize: (params?: SignInFutureFinalizeParams) => Promise<{ error: ClerkError | null }>; diff --git a/typedoc.config.mjs b/typedoc.config.mjs index d88c12900c7..56db774f8a2 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -6,8 +6,6 @@ const CUSTOM_BLOCK_TAGS = [ '@paramExtension', '@experimental', '@hideReturns', - /** Nested property documented under `methods/` as heading + properties table (see extract-methods `buildPropertyTableDocMdx`). */ - '@propertyTableDoc', ]; /** @type {import("typedoc-plugin-markdown").PluginOptions} */ @@ -108,6 +106,11 @@ const config = { ...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), /** Suppresses the Parameters table in `.typedoc/extract-methods.mjs` method MDX. */ '@skipParametersSection', + /** + * On a reference-object property whose value is an inline object type: omit the parent from the main Properties table; + * extract each callable member as `methods/-.mdx` and each non-callable object member as a nested heading + property table (see `.typedoc/extract-methods.mjs`). + */ + '@extractMethods', /** Type-only / router hints; not user-facing prose (see `notRenderedTags`). */ '@inline', '@inlineType', From 4347350ab61a7228f4ebc64d235072c58fe15040 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 6 May 2026 17:36:38 -0700 Subject: [PATCH 39/51] remove TODO from generated content --- .typedoc/comment-utils.mjs | 23 ++++++ .typedoc/custom-theme.mjs | 4 + .typedoc/extract-methods.mjs | 3 +- packages/shared/src/types/signInFuture.ts | 91 ++++++++++------------- turbo.json | 1 + 5 files changed, 71 insertions(+), 51 deletions(-) create mode 100644 .typedoc/comment-utils.mjs diff --git a/.typedoc/comment-utils.mjs b/.typedoc/comment-utils.mjs new file mode 100644 index 00000000000..2a4dab89574 --- /dev/null +++ b/.typedoc/comment-utils.mjs @@ -0,0 +1,23 @@ +// @ts-check +import { Comment } from 'typedoc'; + +const TODO_IN_COMMENT = /\bTODO\b/i; + +/** + * @param {import('typedoc').Comment | undefined} comment + */ +export function commentContainsTodo(comment) { + if (!comment) { + return false; + } + const chunks = []; + if (comment.summary?.length) { + chunks.push(Comment.combineDisplayParts(comment.summary)); + } + for (const tag of comment.blockTags ?? []) { + if (tag.content?.length) { + chunks.push(Comment.combineDisplayParts(tag.content)); + } + } + return chunks.some(text => TODO_IN_COMMENT.test(text)); +} diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index c45475b42a7..471ebd88ed8 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -10,6 +10,7 @@ import { import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; import { TypeDeclarationVisibility } from '../node_modules/typedoc-plugin-markdown/dist/options/maps.js'; +import { commentContainsTodo } from './comment-utils.mjs'; import { REFERENCE_OBJECTS_LIST } from './reference-objects.mjs'; export { REFERENCE_OBJECTS_LIST }; @@ -875,6 +876,9 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {Parameters[1]} [options] */ comment: (model, options) => { + if (commentContainsTodo(model)) { + return ''; + } const hidden = new Set(['@inline', '@inlineType', '@experimental']); const modTags = model?.modifierTags ? Array.from(model.modifierTags) : []; if (modTags.some(/** @param {string} t */ t => hidden.has(t))) { diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index d609312b20d..6ebc0193837 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -33,6 +33,7 @@ import { stripReferenceObjectPropertiesSection, } from './custom-plugin.mjs'; import { prepareMarkdownRenderer } from './prepare-markdown-renderer.mjs'; +import { commentContainsTodo } from './comment-utils.mjs'; import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_CONFIG } from './reference-objects.mjs'; const __filename = fileURLToPath(import.meta.url); @@ -761,7 +762,7 @@ const BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE = new Set(['@param', '@type * @param {import('typedoc').Comment | undefined} comment */ function commentSummaryAndBody(comment) { - if (!comment) { + if (!comment || commentContainsTodo(comment)) { return ''; } const summary = displayPartsToString(comment.summary).trim(); diff --git a/packages/shared/src/types/signInFuture.ts b/packages/shared/src/types/signInFuture.ts index f9f00b21c98..476947b9db1 100644 --- a/packages/shared/src/types/signInFuture.ts +++ b/packages/shared/src/types/signInFuture.ts @@ -42,6 +42,7 @@ export interface SignInFutureCreateParams { signUpIfMissing?: boolean; } +/** Parameters for submitting a password to sign-in. */ export type SignInFuturePasswordParams = { /** * The user's password. Only supported if @@ -51,8 +52,7 @@ export type SignInFuturePasswordParams = { } & ( | { /** - * The authentication identifier for the sign-in. This can be the value of the user's email address, phone number, - * username, or Web3 wallet address. + * The authentication identifier for the sign-in (email address, phone number, username, or Web3 wallet address). Provide exactly one of `identifier`, `emailAddress`, or `phoneNumber`. Omit all when a sign-in already exists to use the current identifier. */ identifier: string; emailAddress?: never; @@ -60,8 +60,7 @@ export type SignInFuturePasswordParams = { } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Provide exactly one of `identifier`, `emailAddress`, or `phoneNumber`. */ emailAddress: string; identifier?: never; @@ -69,8 +68,7 @@ export type SignInFuturePasswordParams = { } | { /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Provide exactly one of `identifier`, `emailAddress`, or `phoneNumber`. */ phoneNumber: string; identifier?: never; @@ -162,15 +160,14 @@ export type SignInFuturePhoneCodeSendParams = { } & ( | { /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Provide either `phoneNumber` or `phoneNumberId`, not both. Omit both when a sign-in already exists. */ phoneNumber?: string; phoneNumberId?: never; } | { /** - * The ID for the user's phone number that will receive a message with the one-time authentication code. + * The ID for the user's phone number that will receive a message with the one-time authentication code. Provide either `phoneNumber` or `phoneNumberId`, not both. Omit both when a sign-in already exists. */ phoneNumberId: string; phoneNumber?: never; @@ -204,12 +201,12 @@ export interface SignInFutureSSOParams { */ redirectUrl: string; /** + * The URL to redirect to if a session was not created, and needs additional information. * TODO @revamp-hooks: This should be handled by FAPI instead. */ redirectCallbackUrl: string; /** - * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an - * OAuth provider. + * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an OAuth provider. * * @example * ```ts @@ -222,13 +219,12 @@ export interface SignInFutureSSOParams { */ popup?: Window; /** - * Optional for `oauth_` or `enterprise_sso` strategies. The value to pass to the - * [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) - * in the generated OAuth redirect URL. + * The value to pass to the [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) in the generated OAuth redirect URL. */ oidcPrompt?: string; /** * @experimental + * The identifier of the enterprise connection to target when using the `enterprise_sso` strategy. */ enterpriseConnectionId?: string; /** @@ -240,7 +236,7 @@ export interface SignInFutureSSOParams { /** @document */ export interface SignInFutureMFAPhoneCodeVerifyParams { /** - * The one-time code that was sent to the user as part of the `signIn.mfa.sendPhoneCode()` method. + * The one-time code that was sent to the user. */ code: string; } @@ -248,7 +244,7 @@ export interface SignInFutureMFAPhoneCodeVerifyParams { /** @document */ export interface SignInFutureMFAEmailCodeVerifyParams { /** - * The one-time code that was sent to the user as part of the `signIn.mfa.sendEmailCode()` method. + * The one-time code that was sent to the user. */ code: string; } @@ -264,7 +260,7 @@ export interface SignInFutureTOTPVerifyParams { /** @document */ export interface SignInFutureBackupCodeVerifyParams { /** - * The backup code that was provided to the user when they set up two-step authentication. + * The backup code that was provided to the user when they set up backup codes. */ code: string; } @@ -272,8 +268,7 @@ export interface SignInFutureBackupCodeVerifyParams { /** @document */ export interface SignInFutureTicketParams { /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. + * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket: string; } @@ -289,7 +284,7 @@ export interface SignInFutureWeb3Params { */ provider: Web3Provider; /** - * The name of the wallet to use for Solana sign-ins. Required when `provider` is set to `'solana'`. + * **Required** when `provider` is set to `'solana'`. The name of the wallet to use for Solana sign-ins. */ walletName?: string; } @@ -299,8 +294,10 @@ export interface SignInFuturePasskeyParams { /** * The flow to use for the passkey sign-in. * - * - `'autofill'`: The client prompts your users to select a passkey before they interact with your app. - * - `'discoverable'`: The client requires the user to interact with the client. + *
    + *
  • `'autofill'`: The client prompts your users to select a passkey before they interact with your app.
  • + *
  • `'discoverable'`: The client requires the user to interact with the client.
  • + *
*/ flow?: 'autofill' | 'discoverable'; } @@ -427,7 +424,7 @@ export interface SignInFutureResource { */ verification: { /** - * The verification status + * The verification status. */ status: 'verified' | 'expired' | 'failed' | 'client_mismatch'; @@ -437,7 +434,7 @@ export interface SignInFutureResource { createdSessionId: string; /** - * Whether the verification was from the same client + * Whether the verification was from the same client. */ verifiedFromTheSameClient: boolean; } | null; @@ -446,12 +443,12 @@ export interface SignInFutureResource { /** @extractMethods */ phoneCode: { /** - * Used to send a phone code to sign-in + * Sends a phone code to sign in with. */ sendCode: (params?: SignInFuturePhoneCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via phone to sign-in + * Verifies a code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#phone-code-send-code) method. */ verifyCode: (params: SignInFuturePhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; @@ -459,17 +456,17 @@ export interface SignInFutureResource { /** @extractMethods */ resetPasswordEmailCode: { /** - * Used to send a password reset code to the first email address on the account + * Sends a password reset code to the first email address on the account. */ sendCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify a password reset code sent via email. Will cause `signIn.status` to become `'needs_new_password'`. + * Verifies a password reset code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-email-code-send-code) method. Will cause `signIn.status` to become `'needs_new_password'`. This is when you will call the [`resetPasswordEmailCode.submitPassword()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-email-code-submit-password) method to complete the password reset flow. */ verifyCode: (params: SignInFutureEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to submit a new password, and move the `signIn.status` to `'complete'`. + * Submits a new password and moves the sign-in status to `'complete'`. */ submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: ClerkError | null }>; }; @@ -477,74 +474,71 @@ export interface SignInFutureResource { /** @extractMethods */ resetPasswordPhoneCode: { /** - * Used to send a password reset code to the first phone number on the account + * Sends a password reset code to the first phone number on the account. */ sendCode: (params?: SignInFutureResetPasswordPhoneCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a password reset code sent via phone. Will cause `signIn.status` to become `'needs_new_password'`. + * Verifies a password reset code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-phone-code-send-code) method. Will cause `signIn.status` to become `'needs_new_password'`. This is when you will call the [`resetPasswordPhoneCode.submitPassword()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-phone-code-submit-password) method to complete the password reset flow. */ verifyCode: (params: SignInFutureResetPasswordPhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to submit a new password, and move the `signIn.status` to `'complete'`. + * Submits a new password and moves the sign-in status to `'complete'`. */ submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: ClerkError | null }>; }; /** - * Used to perform OAuth authentication. + * Performs an SSO-based sign-in (Social/OAuth or Enterprise). */ sso: (params: SignInFutureSSOParams) => Promise<{ error: ClerkError | null }>; /** @extractMethods */ mfa: { /** - * Used to send a phone code as a second factor to sign-in + * Sends a phone code to sign in with as a second factor. */ sendPhoneCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify a phone code sent as a second factor to sign-in + * Verifies a phone code sent with the [`sendPhoneCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-phone-code) method. */ verifyPhoneCode: (params: SignInFutureMFAPhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to send an email code as a second factor to sign-in + * Sends an email code to sign in with as a second factor. */ sendEmailCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify an email code sent as a second factor to sign-in + * Verifies an email code sent with the [`sendEmailCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-email-code) method. */ verifyEmailCode: (params: SignInFutureMFAEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a TOTP code as a second factor to sign-in + * Verifies an authenticator app (TOTP) code to sign in with as a second factor. */ verifyTOTP: (params: SignInFutureTOTPVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a backup code as a second factor to sign-in + * Verifies a backup code to sign in with as a second factor. */ verifyBackupCode: (params: SignInFutureBackupCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; /** - * Used to perform a ticket-based sign-in. + * Performs a ticket-based sign-in. */ ticket: (params?: SignInFutureTicketParams) => Promise<{ error: ClerkError | null }>; /** - * Used to perform a Web3-based sign-in. + * Performs a Web3-based sign-in. */ web3: (params: SignInFutureWeb3Params) => Promise<{ error: ClerkError | null }>; /** - * Initiates a passkey-based authentication flow, enabling users to authenticate using a previously - * registered passkey. When called without parameters, this method requires a prior call to - * `SignIn.create({ strategy: 'passkey' })` to initialize the sign-in context. This pattern is particularly useful in - * scenarios where the authentication strategy needs to be determined dynamically at runtime. + * Initiates a passkey-based authentication flow, enabling users to authenticate using a previously registered passkey. When called without parameters, this method requires a prior call to `SignIn.create({ strategy: 'passkey' })` to initialize the sign-in context. This pattern is particularly useful in scenarios where the authentication strategy needs to be determined dynamically at runtime. */ passkey: (params?: SignInFuturePasskeyParams) => Promise<{ error: ClerkError | null }>; @@ -554,12 +548,9 @@ export interface SignInFutureResource { finalize: (params?: SignInFutureFinalizeParams) => Promise<{ error: ClerkError | null }>; /** - * Resets the current sign-in attempt by clearing all local state back to null. - * This is useful when you want to allow users to go back to the beginning of - * the sign-in flow (e.g., to change their identifier during verification). + * Resets the current sign-in attempt by clearing all local state back to null. This is useful when you want to allow users to go back to the beginning of the sign-in flow (e.g., to change their identifier during verification). * - * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change - * to `'fetching'` and does not make any API calls - it only clears local state. + * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change to `'fetching'` and does not make any API calls - it only clears local state. */ reset: () => Promise<{ error: ClerkError | null }>; } diff --git a/turbo.json b/turbo.json index 776fb7cf983..d19e070127a 100644 --- a/turbo.json +++ b/turbo.json @@ -343,6 +343,7 @@ "tsconfig.typedoc.json", "typedoc.config.mjs", ".typedoc/reference-objects.mjs", + ".typedoc/comment-utils.mjs", ".typedoc/extract-methods.mjs", ".typedoc/extract-returns-and-params.mjs" ], From 86b3e8ff001894c014d05700ac0fa3d4914a4266 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Wed, 6 May 2026 18:14:11 -0700 Subject: [PATCH 40/51] fix TODO handling; add some links to custom-plugin --- .typedoc/comment-utils.mjs | 74 ++++++++++++++++++++++- .typedoc/custom-plugin.mjs | 12 ++++ .typedoc/custom-theme.mjs | 13 ++-- .typedoc/extract-methods.mjs | 17 +++--- packages/shared/src/types/signInCommon.ts | 10 --- packages/shared/src/types/signInFuture.ts | 24 +++++++- 6 files changed, 122 insertions(+), 28 deletions(-) diff --git a/.typedoc/comment-utils.mjs b/.typedoc/comment-utils.mjs index 2a4dab89574..842047b19d7 100644 --- a/.typedoc/comment-utils.mjs +++ b/.typedoc/comment-utils.mjs @@ -1,7 +1,7 @@ // @ts-check import { Comment } from 'typedoc'; -const TODO_IN_COMMENT = /\bTODO\b/i; +const TODO_WORD = /\bTODO\b/i; /** * @param {import('typedoc').Comment | undefined} comment @@ -19,5 +19,75 @@ export function commentContainsTodo(comment) { chunks.push(Comment.combineDisplayParts(tag.content)); } } - return chunks.some(text => TODO_IN_COMMENT.test(text)); + return chunks.some(text => TODO_WORD.test(text)); +} + +/** + * Truncate at the first word "TODO" (case-insensitive). Used when flattening display parts to a string. + * + * @param {string} text + */ +export function stripTextAfterTodo(text) { + if (!text) { + return ''; + } + const m = TODO_WORD.exec(text); + if (!m) { + return text; + } + return text.slice(0, m.index).trimEnd(); +} + +/** + * Drop display parts from the first `TODO` onward; truncate the containing text part if `TODO` appears mid-string. + * + * @param {import('typedoc').CommentDisplayPart[] | undefined} parts + * @returns {import('typedoc').CommentDisplayPart[]} + */ +function stripTodoFromDisplayParts(parts) { + if (!parts?.length) { + return parts ?? []; + } + /** @type {import('typedoc').CommentDisplayPart[]} */ + const out = []; + for (const p of parts) { + if (p.kind === 'text' && 'text' in p && typeof p.text === 'string') { + const match = TODO_WORD.exec(p.text); + if (match) { + const before = p.text.slice(0, match.index).trimEnd(); + if (before.length) { + out.push(/** @type {import('typedoc').CommentDisplayPart} */ ({ kind: 'text', text: before })); + } + return out; + } + } + out.push(p); + } + return out; +} + +/** + * Returns a clone with `TODO` and everything after it removed from the summary and from any block tag that contains `TODO`. + * Comments without `TODO` are returned unchanged (same reference). Undefined in, undefined out. + * + * @param {import('typedoc').Comment | undefined} comment + * @returns {import('typedoc').Comment | undefined} + */ +export function applyTodoStrippingToComment(comment) { + if (!comment) { + return undefined; + } + if (!commentContainsTodo(comment)) { + return comment; + } + const c = comment.clone(); + if (c.summary?.length && TODO_WORD.test(Comment.combineDisplayParts(c.summary))) { + c.summary = stripTodoFromDisplayParts(c.summary); + } + for (const tag of c.blockTags ?? []) { + if (tag.content?.length && TODO_WORD.test(Comment.combineDisplayParts(tag.content))) { + tag.content = stripTodoFromDisplayParts(tag.content); + } + } + return c; } diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 7620c028d26..6266454701a 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -222,10 +222,18 @@ function getCatchAllReplacements() { replace: (/** @type {string} */ _match, /** @type {string} */ type) => `[\`${type}\`](/docs/reference/types/errors)`, }, + { + pattern: /(?[1]} [options] */ comment: (model, options) => { - if (commentContainsTodo(model)) { - return ''; + if (!model) { + return superPartials.comment.call(this, model, options); } + const modelToRender = applyTodoStrippingToComment(model) ?? model; const hidden = new Set(['@inline', '@inlineType', '@experimental']); - const modTags = model?.modifierTags ? Array.from(model.modifierTags) : []; + const modTags = Array.from(modelToRender.modifierTags ?? []); if (modTags.some(/** @param {string} t */ t => hidden.has(t))) { - const clone = Object.assign(Object.create(Object.getPrototypeOf(model)), model, { + const clone = Object.assign(Object.create(Object.getPrototypeOf(modelToRender)), modelToRender, { modifierTags: new Set(modTags.filter(/** @param {string} t */ t => !hidden.has(t))), }); return superPartials.comment.call(this, clone, options); } - return superPartials.comment.call(this, model, options); + return superPartials.comment.call(this, modelToRender, options); }, /** * Remove the blockquote signature line. diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 6ebc0193837..975dda146e6 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -33,7 +33,7 @@ import { stripReferenceObjectPropertiesSection, } from './custom-plugin.mjs'; import { prepareMarkdownRenderer } from './prepare-markdown-renderer.mjs'; -import { commentContainsTodo } from './comment-utils.mjs'; +import { applyTodoStrippingToComment } from './comment-utils.mjs'; import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_CONFIG } from './reference-objects.mjs'; const __filename = fileURLToPath(import.meta.url); @@ -560,10 +560,12 @@ function buildPropertyTableDocMdx(parentName, nestedDecl, ctx) { const qualifiedName = `${parentName}.${nestedDecl.name}`; const title = `### \`${qualifiedName}\``; const description = commentSummaryAndBody(nestedDecl.comment); - const props = resolveObjectShapeMembersForPropertyTable(nestedDecl.type); - if (!props?.length) { + const propsUnsorted = resolveObjectShapeMembersForPropertyTable(nestedDecl.type); + if (!propsUnsorted?.length) { return ''; } + /** Match nominal param tables and merged intersection holders: stable A–Z by property name (TypeDoc inline literal `children` order is declaration order). */ + const props = [...propsUnsorted].sort((a, b) => a.name.localeCompare(b.name)); const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => ctx.partials.propertiesTable( props, @@ -762,17 +764,18 @@ const BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE = new Set(['@param', '@type * @param {import('typedoc').Comment | undefined} comment */ function commentSummaryAndBody(comment) { - if (!comment || commentContainsTodo(comment)) { + if (!comment) { return ''; } - const summary = displayPartsToString(comment.summary).trim(); - const block = comment.blockTags + const c = applyTodoStrippingToComment(comment) ?? comment; + const summary = displayPartsToString(c.summary).trim(); + const block = c.blockTags ?.filter(t => !BLOCK_TAGS_OMITTED_FROM_EXTRACTED_METHOD_PROSE.has(t.tag)) .map(t => displayPartsToString(t.content).trim()) .filter(Boolean) .join('\n\n'); const returnsLines = - comment.blockTags + c.blockTags ?.filter(t => t.tag === '@returns') .map(t => formatReturnsLineFromTag(t)) .filter(Boolean) ?? []; diff --git a/packages/shared/src/types/signInCommon.ts b/packages/shared/src/types/signInCommon.ts index 2213eb662ec..40e255b8cf1 100644 --- a/packages/shared/src/types/signInCommon.ts +++ b/packages/shared/src/types/signInCommon.ts @@ -57,16 +57,6 @@ import type { } from './strategies'; import type { StartEmailLinkFlowParams } from './verification'; -/** - * @property {string} 'needs_identifier' - The user's identifier (e.g., email address, phone number, username) hasn't been provided. - * @property {string} 'needs_first_factor' - One of the following [first factor verification](!first-factor-verification) strategies is missing: `'email_link'`, `'email_code'`, `passkey`, `password`, `'phone_code'`, `'web3_base_signature'`, `'web3_metamask_signature'`, `'web3_coinbase_wallet_signature'`, `'web3_okx_wallet_signature'`, `'web3_solana_signature'`, [`OAuthStrategy`](https://clerk.com/docs/reference/types/sso#o-auth-strategy), or `'enterprise_sso'`. - * @property {string} 'needs_second_factor' - One of the following [second factor verification](!second-factor-verification) strategies is missing: `'phone_code'`, `'totp'`, `'backup_code'`, `'email_code'`, or `'email_link'`. - * @property {string} 'needs_client_trust' - The user is signing in from a new device and must complete a [second factor verification](!second-factor-verification) to establish [Client Trust](https://clerk.com/docs/guides/secure/client-trust). See the [Client Trust custom flow guide](https://clerk.com/docs/guides/development/custom-flows/authentication/client-trust) for more information. - * @property {string} 'needs_new_password' - The user needs to set a new password. See the [dedicated custom flow](https://clerk.com/docs/guides/development/custom-flows/account-updates/forgot-password) guide for more information. - * - * @interface - * @inline - */ export type SignInStatus = | 'needs_identifier' | 'needs_first_factor' diff --git a/packages/shared/src/types/signInFuture.ts b/packages/shared/src/types/signInFuture.ts index 476947b9db1..0a955dce168 100644 --- a/packages/shared/src/types/signInFuture.ts +++ b/packages/shared/src/types/signInFuture.ts @@ -223,8 +223,8 @@ export interface SignInFutureSSOParams { */ oidcPrompt?: string; /** - * @experimental * The identifier of the enterprise connection to target when using the `enterprise_sso` strategy. + * @experimental */ enterpriseConnectionId?: string; /** @@ -331,6 +331,14 @@ export interface SignInFutureResource { /** * The current status of the sign-in. + *
    + *
  • `'complete'` - The sign-in process has been completed successfully.
  • + *
  • `'needs_client_trust'` - The user is signing in from a new device and must complete a [second factor verification](!second-factor-verification) to establish [Client Trust](https://clerk.com/docs/guides/secure/client-trust). See the [Client Trust custom flow guide](https://clerk.com/docs/guides/development/custom-flows/authentication/client-trust) for more information.
  • + *
  • `'needs_identifier'` - The user's identifier (e.g., email address, phone number, username) hasn't been provided.
  • + *
  • `'needs_first_factor'` - One of the following [first factor verification](!first-factor-verification) strategies is missing: `'email_link'`, `'email_code'`, `passkey`, `password`, `'phone_code'`, `'web3_base_signature'`, `'web3_metamask_signature'`, `'web3_coinbase_wallet_signature'`, `'web3_okx_wallet_signature'`, `'web3_solana_signature'`, [`OAuthStrategy`](https://clerk.com/docs/reference/types/sso#o-auth-strategy), or `'enterprise_sso'`.
  • + *
  • `'needs_second_factor'` - One of the following [second factor verification](!second-factor-verification) strategies is missing: `'phone_code'`, `'totp'`, `'backup_code'`, `'email_code'`, or `'email_link'`.
  • + *
  • `'needs_new_password'` - The user needs to set a new password. See the [dedicated custom flow](https://clerk.com/docs/guides/development/custom-flows/account-updates/forgot-password) guide for more information.
  • + *
*/ readonly status: SignInStatus; @@ -341,9 +349,13 @@ export interface SignInFutureResource { /** * Reflects that the sign-in was not able to create a new session because the identifier already exists in an existing session. - * @property {string} sessionId - The ID of the existing session. */ - readonly existingSession?: { sessionId: string }; + readonly existingSession?: { + /** + * The ID of the existing session. + */ + sessionId: string; + }; /** * The state of the verification process for the selected first factor. Initially, this property contains an empty verification object, since there is no first factor selected. @@ -367,6 +379,12 @@ export interface SignInFutureResource { /** * An object containing information about the user of the current sign-in. This property is populated only once an identifier is given to the `SignIn` object through `signIn.create()` or another method that populates the `identifier` property. + *
    + *
  • `'firstName'`: The user's first name.
  • + *
  • `'lastName'`: The user's last name.
  • + *
  • `'imageUrl'`: The user's profile image URL.
  • + *
  • `'hasImage'`: Whether the user has a profile image.
  • + *
*/ readonly userData: UserData; From 833ccf93b6b328df87b1128f7afd57564044f4cc Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 7 May 2026 17:18:28 -0700 Subject: [PATCH 41/51] Apply suggestion from @alexisintech --- .typedoc/custom-plugin.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 7353892683a..3b821559b3e 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -143,6 +143,7 @@ function getRelativeLinkReplacements() { return LINK_REPLACEMENTS.map(([fileName, newPath]) => { return { // Match both flat links and nested object-doc links + // Also matches optional anchors (#) pattern: new RegExp(`\\((?:(?:\\.{1,2}\\/)+[^()]*?|)(?:${fileName}\\/)?${fileName}\\.mdx(#[^)]+)?\\)`, 'g'), // Preserve the anchor in replacement if it exists replace: (/** @type {string} */ _match, anchor = '') => `(${newPath}${anchor})`, From b2350a64d74680d247cd3c22466cd840b4ca77bf Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 7 May 2026 17:30:21 -0700 Subject: [PATCH 42/51] comment clean up --- .typedoc/custom-plugin.mjs | 7 ++----- .typedoc/custom-theme.mjs | 12 +++--------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 3b821559b3e..973e88a66b1 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -382,9 +382,7 @@ const ATX_HEADING_LINE = /^\s{0,3}#{1,6}(?:\s|$)/; const PIPE_CODE_PH = /\uE000(\d+)\uE001/g; /** - * Inline code that contains a pipe (e.g. `` `a \\| b` `` or `` `a | b` ``) cannot receive per-token - * link replacements without breaking MDX. Replace those whole spans with placeholders, run catch-alls, - * then restore. + * Inline code that contains a pipe (e.g. `` `a \\| b` `` or `` `a | b` ``) cannot receive link replacements without breaking MDX. Replace those whole spans with placeholders, run catch-alls, then restore. * * @param {string} line * @returns {{ text: string, placeholders: string[] }} @@ -412,8 +410,7 @@ function restoreProtectedInlineCodeSpans(text, placeholders) { } /** - * Remove the Properties section (heading + table) from reference object pages (e.g. `shared/clerk/clerk.mdx`); - * the table is copied into `shared//properties.mdx` by `extract-methods.mjs`. + * Remove the Properties section (heading + table) from reference object pages (e.g. `shared/clerk/clerk.mdx`); the table is copied into `shared//properties.mdx` by `extract-methods.mjs`. * * @param {string} contents */ diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index d136332ecf8..f02c6d6b7c0 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -198,8 +198,7 @@ function mergeIntersectionPropertyReflections(intersection, project) { } /** - * For properties typed something like `false \| { a?: … }`, `getFlattenedDeclarations` does not walk the union, so nested keys - * never become table rows. Collect object members from each union arm (primitives/literals yield nothing). + * For properties typed something like `false \| { a?: … }`, `getFlattenedDeclarations` does not walk the union, so nested keys never become table rows. Collect object members from each union arm (primitives/literals yield nothing). * E.g. `telemetry` prop in clerk.ts * * @param {import('typedoc').Type | undefined} t @@ -224,10 +223,7 @@ function collectPropertyReflectionsFromUnionObjectArms(t, visitedReflectionIds, } /** - * Appends `parent.child` rows for union object arms (e.g. `false \| { disabled?: … }`). **Only** used when building - * {@link clerkTypeDeclarationTable}; we intentionally do **not** hook `helpers.getFlattenedDeclarations` globally — - * otherwise top-level `propertiesTable` output (e.g. `Clerk`) would gain synthetic rows like `client.*` for every - * property whose type is a union such as `ClientResource \| undefined`. + * Appends `parent.child` rows for union object arms (e.g. `false \| { disabled?: … }`). **Only** used when building {@link clerkTypeDeclarationTable}; we intentionally do **not** hook `helpers.getFlattenedDeclarations` globally — otherwise top-level `propertiesTable` output (e.g. `Clerk`) would gain synthetic rows like `client.*` for every property whose type is a union such as `ClientResource \| undefined`. * * @template {import('typedoc').DeclarationReflection} T * @param {T[]} base @@ -493,9 +489,7 @@ function renderPropertiesFormatTable(args) { } /** - * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` - * and then {@link appendUnionObjectChildPropertyRows} (union-object arm rows like `telemetry.*`). The default plugin - * skips flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. + * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` and then {@link appendUnionObjectChildPropertyRows} (union-object arm rows like `telemetry.*`). The default plugin skips flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. * * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} * @param {import('typedoc').DeclarationReflection[]} model From 5cded2a448febc08a0c94318f03f679126d0fa9c Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 7 May 2026 21:00:12 -0700 Subject: [PATCH 43/51] generate signupfutureresource --- .typedoc/__tests__/file-structure.test.ts | 2 + .typedoc/extract-methods.mjs | 61 ++++- .typedoc/reference-objects.mjs | 4 + packages/shared/src/types/attributes.ts | 4 + packages/shared/src/types/signInFuture.ts | 2 +- packages/shared/src/types/signUpCommon.ts | 5 + packages/shared/src/types/signUpFuture.ts | 267 +++++++++------------- 7 files changed, 179 insertions(+), 166 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 17fbba8af6b..55f38270bce 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -62,6 +62,8 @@ describe('Typedoc output', () => { "shared/user-resource/methods", "shared/sign-in-future-resource", "shared/sign-in-future-resource/methods", + "shared/sign-up-future-resource", + "shared/sign-up-future-resource/methods", ] `); }); diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 975dda146e6..c3f9b58348e 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -582,6 +582,39 @@ function buildPropertyTableDocMdx(parentName, nestedDecl, ctx) { return `${applyCatchAllMdReplacements(applyRelativeLinkReplacements(raw)).trim()}\n`; } +/** + * Parent-level property table for an `@extractMethods` namespace. + * Lists non-callable direct members on the namespace (e.g. `verifications.emailAddress`, `verifications.phoneNumber`). + * + * @param {import('typedoc').DeclarationReflection} parentDecl + * @param {import('typedoc').DeclarationReflection[]} nonCallableMembers + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + */ +function buildExtractMethodsNamespacePropertyTableMdx(parentDecl, nonCallableMembers, ctx) { + if (!nonCallableMembers.length) { + return ''; + } + const title = `### \`${parentDecl.name}\``; + const description = commentSummaryAndBody(parentDecl.comment); + const props = [...nonCallableMembers].sort((a, b) => a.name.localeCompare(b.name)); + const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => + ctx.partials.propertiesTable( + props, + /** @type {Parameters[1]} */ + ({ + kind: ReflectionKind.Interface, + isEventProps: false, + applyAllowlistedPropertyTableRowFilters: false, + }), + ), + ); + if (!tableMd?.trim()) { + return ''; + } + const raw = [title, '', description, '', tableMd].filter(Boolean).join('\n\n'); + return `${applyCatchAllMdReplacements(applyRelativeLinkReplacements(raw)).trim()}\n`; +} + /** * @param {string} markdown * @returns {string | undefined} Body under `## Properties` (no heading), or undefined @@ -1265,7 +1298,12 @@ function hasExtractMethodsModifier(decl) { } /** - * Writes `methods/-.mdx` for each direct member of an `@extractMethods` inline object: callables via {@link buildMethodMdx}, non-callables with a resolvable object shape via {@link buildPropertyTableDocMdx}. + * Writes `methods/-.mdx` for each direct member of an `@extractMethods` object-like type: + * callables via {@link buildMethodMdx}, non-callables with a resolvable object shape via + * {@link buildPropertyTableDocMdx}. + * + * Supports inline object literals and named references (`interface` / object-like `type` aliases) by resolving + * the holder with {@link resolveDeclarationWithObjectMembers}. * * @param {import('typedoc').DeclarationReflection} parentDecl * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx @@ -1273,16 +1311,19 @@ function hasExtractMethodsModifier(decl) { * @returns {number} Number of files written */ function processExtractMethodsNamespace(parentDecl, ctx, outDir) { - const cur = unwrapOptionalLayersSomeType(parentDecl.type); - if (!(cur instanceof ReflectionType)) { + const holder = resolveDeclarationWithObjectMembers(parentDecl.type); + const members = holder?.children ?? []; + if (members.length === 0) { console.warn( - `[extract-methods] @extractMethods on "${parentDecl.name}" requires an inline object (reflection) type; skipping nested extraction`, + `[extract-methods] @extractMethods on "${parentDecl.name}" requires an object-like type with members; skipping nested extraction`, ); return 0; } const parentName = parentDecl.name; let count = 0; - for (const nested of cur.declaration?.children ?? []) { + /** @type {import('typedoc').DeclarationReflection[]} */ + const nonCallableMembers = []; + for (const nested of members) { if (nested.name.startsWith('__')) { continue; } @@ -1299,6 +1340,7 @@ function processExtractMethodsNamespace(parentDecl, ctx, outDir) { count++; continue; } + nonCallableMembers.push(nd); const propTableMdx = buildPropertyTableDocMdx(parentName, nd, ctx); if (!propTableMdx) { continue; @@ -1307,6 +1349,15 @@ function processExtractMethodsNamespace(parentDecl, ctx, outDir) { console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); count++; } + if (nonCallableMembers.length) { + const namespaceMdx = buildExtractMethodsNamespacePropertyTableMdx(parentDecl, nonCallableMembers, ctx); + if (namespaceMdx) { + const namespacePath = path.join(outDir, `${toKebabCase(parentName)}.mdx`); + fs.writeFileSync(namespacePath, namespaceMdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), namespacePath)}`); + count++; + } + } return count; } diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index f7d3abf2ad0..4ca0356b7c5 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -35,6 +35,10 @@ export const REFERENCE_OBJECT_CONFIG = { symbol: 'SignInFutureResource', declarationHint: 'types/signInFuture', }, + 'shared/sign-up-future-resource/sign-up-future-resource.mdx': { + symbol: 'SignUpFutureResource', + declarationHint: 'types/signUpFuture', + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ diff --git a/packages/shared/src/types/attributes.ts b/packages/shared/src/types/attributes.ts index 7cc23cbcc46..0b2ca92c6fc 100644 --- a/packages/shared/src/types/attributes.ts +++ b/packages/shared/src/types/attributes.ts @@ -1,4 +1,8 @@ +/** @inline */ export type FirstNameAttribute = 'first_name'; +/** @inline */ export type LastNameAttribute = 'last_name'; +/** @inline */ export type PasswordAttribute = 'password'; +/** @inline */ export type LegalAcceptedAttribute = 'legal_accepted'; diff --git a/packages/shared/src/types/signInFuture.ts b/packages/shared/src/types/signInFuture.ts index 0a955dce168..06d9fb380c2 100644 --- a/packages/shared/src/types/signInFuture.ts +++ b/packages/shared/src/types/signInFuture.ts @@ -348,7 +348,7 @@ export interface SignInFutureResource { readonly isTransferable: boolean; /** - * Reflects that the sign-in was not able to create a new session because the identifier already exists in an existing session. + * Indicates that the sign-in was not able to create a new session because the identifier already exists in an existing session. */ readonly existingSession?: { /** diff --git a/packages/shared/src/types/signUpCommon.ts b/packages/shared/src/types/signUpCommon.ts index 41c15035b46..db5ca815f03 100644 --- a/packages/shared/src/types/signUpCommon.ts +++ b/packages/shared/src/types/signUpCommon.ts @@ -22,8 +22,10 @@ import type { import type { SnakeToCamel } from './utils'; import type { VerificationResource } from './verification'; +/** @inline */ export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned'; +/** @inline */ export type SignUpField = SignUpAttributeField | SignUpIdentificationField; export type PrepareVerificationParams = @@ -64,9 +66,11 @@ export type AttemptVerificationParams = signature: string; }; +/** @inline */ export type SignUpAttributeField = FirstNameAttribute | LastNameAttribute | PasswordAttribute | LegalAcceptedAttribute; // TODO: SignUpVerifiableField or SignUpIdentifier? +/** @inline */ export type SignUpVerifiableField = | UsernameIdentifier | EmailAddressIdentifier @@ -75,6 +79,7 @@ export type SignUpVerifiableField = | Web3WalletIdentifier; // TODO: Does it make sense that the identification *field* holds a *strategy*? +/** @inline */ export type SignUpIdentificationField = SignUpVerifiableField | OAuthStrategy | EnterpriseSSOStrategy; // TODO: Replace with discriminated union type diff --git a/packages/shared/src/types/signUpFuture.ts b/packages/shared/src/types/signUpFuture.ts index 1daf1239ece..d42be58fbfb 100644 --- a/packages/shared/src/types/signUpFuture.ts +++ b/packages/shared/src/types/signUpFuture.ts @@ -13,42 +13,42 @@ import type { } from './strategies'; import type { VerificationResource } from './verification'; +/** @document */ export interface SignUpFutureAdditionalParams { /** - * The user's first name. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The user's first name. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ firstName?: string; /** - * The user's last name. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The user's last name. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ lastName?: string; /** - * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be - * automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use - * it to implement custom fields that can be collected during sign-up and will automatically be attached to the - * created User object. + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created User object. */ unsafeMetadata?: SignUpUnsafeMetadata; /** - * A boolean indicating whether the user has agreed to the - * [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. */ legalAccepted?: boolean; /** - * The locale to assign to the user in [BCP 47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) - * format (e.g., "en-US", "fr-FR"). If omitted, defaults to the browser's locale. + * The locale to assign to the user in [BCP 47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) format (e.g., "en-US", "fr-FR"). If omitted, defaults to the browser's locale. */ locale?: string; } +/** @document */ export interface SignUpFutureCreateParams extends SignUpFutureAdditionalParams { /** - * The first factor verification strategy to use in the sign-in flow. Depends on the `identifier` value. Each - * authentication identifier supports different verification strategies. + * The strategy to use for the sign-up. The following strategies are supported: + *
    + *
  • `'oauth_'`: The user will be authenticated with their [social connection account](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview). See a list of [supported values for ``](https://clerk.com/docs/reference/types/sso).
  • + *
  • `'enterprise_sso'`: The user will be authenticated either through SAML or OIDC depending on the configuration of their [enterprise SSO account](https://clerk.com/docs/guides/configure/auth-strategies/enterprise-connections/overview).
  • + *
  • `'ticket'`: The user will be authenticated via the ticket _or token_ generated from the Backend API.
  • + *
  • `'google_one_tap'`: The user will be authenticated with the Google One Tap UI. It's recommended to use [`authenticateWithGoogleOneTap()`](https://clerk.com/docs/reference/components/authentication/google-one-tap#authenticate-with-google-one-tap) instead, as it will also set the user's current session as active for you.
  • + *
  • `'oauth_token_apple'`: The user will be authenticated using a native [Sign in with Apple](https://clerk.com/docs/guides/configure/auth-strategies/sign-in-with-apple) identity token.
  • + *
  • `'phone_code'`: The user will receive a one-time code via SMS to verify their phone number.
  • + *
*/ strategy?: | OAuthStrategy @@ -58,66 +58,52 @@ export interface SignUpFutureCreateParams extends SignUpFutureAdditionalParams { | AppleIdTokenStrategy | PhoneCodeStrategy; /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; /** - * The user's password. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. + * The user's password. Only supported if [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. */ password?: string; /** - * When set to `true`, the `SignUp` will attempt to retrieve information from the active `SignIn` instance and use it - * to complete the sign-up process. This is useful when you want to seamlessly transition a user from a sign-in - * attempt to a sign-up attempt. + * When set to `true`, the `SignUp` will attempt to retrieve information from the active `SignIn` instance and use it to complete the sign-up process. This is useful when you want to seamlessly transition a user from a sign-in attempt to a sign-up attempt. */ transfer?: boolean; /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. **Required** if `strategy` is set to `'ticket'`. + * **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket?: string; /** - * The Web3 wallet address, made up of 0x + 40 hexadecimal characters. **Required** if - * [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) - * is enabled. + * The Web3 wallet address, made up of 0x + 40 hexadecimal characters. Only supported if [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) is enabled. */ web3Wallet?: string; } +/** @document */ export interface SignUpFutureUpdateParams extends SignUpFutureAdditionalParams { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } +/** @document */ export interface SignUpFutureEmailCodeVerifyParams { /** * The code that was sent to the user. @@ -125,6 +111,7 @@ export interface SignUpFutureEmailCodeVerifyParams { code: string; } +/** @document */ export interface SignUpFutureEmailLinkSendParams { /** * The full URL that the user will be redirected to when they visit the email link. @@ -132,91 +119,72 @@ export interface SignUpFutureEmailLinkSendParams { verificationUrl: string; } +/** @document */ export type SignUpFuturePasswordParams = SignUpFutureAdditionalParams & { /** - * The user's password. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. + * The user's password. Only supported if [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. */ password: string; } & ( | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Keep in mind that the email address requires an extra verification process. */ emailAddress: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Keep in mind that the phone number requires an extra verification process. */ phoneNumber: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username: string; } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } ); +/** @document */ export interface SignUpFuturePhoneCodeSendParams { /** * The mechanism to use to send the code to the provided phone number. Defaults to `'sms'`. @@ -224,6 +192,7 @@ export interface SignUpFuturePhoneCodeSendParams { channel?: PhoneCodeChannel; } +/** @document */ export interface SignUpFuturePhoneCodeVerifyParams { /** * The code that was sent to the user. @@ -231,23 +200,23 @@ export interface SignUpFuturePhoneCodeVerifyParams { code: string; } +/** @document */ export interface SignUpFutureSSOParams extends SignUpFutureAdditionalParams { /** * The strategy to use for authentication. */ strategy: string; /** - * The URL or path to navigate to after the OAuth or SAML flow completes. Can be provided as a relative URL (such as - * `/dashboard`), in which case it will be prefixed with the base URL of the current page. + * The URL or path to navigate to after the OAuth or SAML flow completes. Can be provided as a relative URL (such as `/dashboard`), in which case it will be prefixed with the base URL of the current page. */ redirectUrl: string; /** + * The URL or path to navigate to if a session was not created, and needs additional information. * TODO @revamp-hooks: This should be handled by FAPI instead. */ redirectCallbackUrl: string; /** - * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an - * OAuth provider. + * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an OAuth provider. * * @example * ```ts @@ -260,29 +229,29 @@ export interface SignUpFutureSSOParams extends SignUpFutureAdditionalParams { */ popup?: Window; /** - * Optional for `oauth_` or `enterprise_sso` strategies. The value to pass to the - * [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) - * in the generated OAuth redirect URL. + * The value to pass to the [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) in the generated OAuth redirect URL. */ oidcPrompt?: string; /** + * The identifier of the enterprise connection to target when using the `enterprise_sso` strategy. * @experimental */ enterpriseConnectionId?: string; /** - * Email address to use for targeting an enterprise connection at sign-up. + * The email address to use for targeting an enterprise connection at sign-up. */ emailAddress?: string; } +/** @document */ export interface SignUpFutureTicketParams extends SignUpFutureAdditionalParams { /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. **Required** if `strategy` is set to `'ticket'`. + * **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket: string; } +/** @document */ export interface SignUpFutureWeb3Params extends SignUpFutureAdditionalParams { /** * The verification strategy to validate the user's sign-up request. @@ -290,31 +259,35 @@ export interface SignUpFutureWeb3Params extends SignUpFutureAdditionalParams { strategy: Web3Strategy; } +/** @document */ export interface SignUpFutureFinalizeParams { + /** + * A custom navigation function to be called just before the session and/or Organization is set. When provided, it takes precedence over the `redirectUrl` parameter for navigation. The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. See the [section on using the `navigate()` parameter](https://clerk.com/docs/reference/objects/clerk#using-the-navigate-parameter) for more details. + */ navigate?: SetActiveNavigate; } /** - * An object that contains information about all available verification strategies. + * Contains information about the available verification strategies for a sign-up attempt. */ export interface SignUpFutureVerifications { /** - * An object holding information about the email address verification. + * Holds information about the email address verification. */ readonly emailAddress: SignUpVerificationResource; /** - * An object holding information about the phone number verification. + * Holds information about the phone number verification. */ readonly phoneNumber: SignUpVerificationResource; /** - * An object holding information about the Web3 wallet verification. + * Holds information about the Web3 wallet verification. */ readonly web3Wallet: VerificationResource; /** - * An object holding information about the external account verification. + * Holds information about the external account verification. */ readonly externalAccount: VerificationResource; @@ -339,39 +312,38 @@ export interface SignUpFutureVerifications { } | null; /** - * Used to send an email code to verify an email address. + * Sends an email code to verify an email address. */ sendEmailCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via email. + * Verifies a code sent via [`verifications.sendEmailCode()`](https://clerk.com/docs/reference/objects/sign-up-future#verifications-send-email-code). */ verifyEmailCode: (params: SignUpFutureEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to send an email link to verify an email address. + * Sends an email link to verify an email address. */ sendEmailLink: (params: SignUpFutureEmailLinkSendParams) => Promise<{ error: ClerkError | null }>; /** - * Will wait for email link verification to complete or expire. + * Will wait for email link verification to complete or expire after calling [`verifications.sendEmailLink()`](https://clerk.com/docs/reference/objects/sign-up-future#verifications-send-email-link). */ waitForEmailLinkVerification: () => Promise<{ error: ClerkError | null }>; /** - * Used to send a phone code to verify a phone number. + * Sends a phone code to verify a phone number. */ sendPhoneCode: (params?: SignUpFuturePhoneCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via phone. + * Verifies a code sent via [`verifications.sendPhoneCode()`](https://clerk.com/docs/reference/objects/sign-up-future#verifications-send-phone-code). */ verifyPhoneCode: (params: SignUpFuturePhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; } /** - * The `SignUpFuture` class holds the state of the current sign-up attempt and provides methods to drive custom sign-up - * flows, including email/phone verification, password, SSO, ticket-based, and Web3-based account creation. + * The `SignUpFuture` class holds the state of the current sign-up attempt and provides methods to drive custom sign-up flows, including email/phone verification, password, SSO, ticket-based, and Web3-based account creation. */ export interface SignUpFutureResource { /** @@ -385,92 +357,77 @@ export interface SignUpFutureResource { readonly status: SignUpStatus; /** - * An array of all the required fields that need to be supplied and verified in order for this sign-up to be marked - * as complete and converted into a user. + * An array of all the required fields that need to be supplied and verified in order for this sign-up to be marked as complete and converted into a user. */ readonly requiredFields: SignUpField[]; /** - * An array of all the fields that can be supplied to the sign-up, but their absence does not prevent the sign-up - * from being marked as complete. + * An array of all the fields that can be supplied to the sign-up, but their absence does not prevent the sign-up from being marked as complete. */ readonly optionalFields: SignUpField[]; /** - * An array of all the fields whose values are not supplied yet but they are mandatory in order for a sign-up to be - * marked as complete. + * An array of all the fields whose values are not supplied yet but they are mandatory in order for a sign-up to be marked as complete. */ readonly missingFields: SignUpField[]; /** - * An array of all the fields whose values have been supplied, but they need additional verification in order for - * them to be accepted. Examples of such fields are `email_address` and `phone_number`. + * An array of all the fields whose values have been supplied, but they need additional verification in order for them to be accepted. Examples of such fields are `email_address` and `phone_number`. */ readonly unverifiedFields: SignUpIdentificationField[]; /** - * Indicates that there is a matching user for provided identifier, and that the sign-up can be transferred to - * a sign-in. + * Indicates that there is a matching user for provided identifier, and that the sign-up can be transferred to a sign-in. */ readonly isTransferable: boolean; - readonly existingSession?: { sessionId: string }; + /** + * Indicates that the sign-up was not able to create a new session because the identifier already exists in an existing session. + */ + readonly existingSession?: { + /** + * The ID of the existing session. + */ + sessionId: string; + }; /** - * The `username` supplied to the current sign-up. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The `username` supplied to the current sign-up. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ readonly username: string | null; /** - * The `firstName` supplied to the current sign-up. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The `firstName` supplied to the current sign-up. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ readonly firstName: string | null; /** - * The `lastName` supplied to the current sign-up. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The `lastName` supplied to the current sign-up. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ readonly lastName: string | null; /** - * The `emailAddress` supplied to the current sign-up. Only supported if - * [email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled - * in the instance settings. + * The `emailAddress` supplied to the current sign-up. Only supported if [email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. */ readonly emailAddress: string | null; /** - * The `phoneNumber` supplied to the current sign-up in E.164 format. Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled - * in the instance settings. + * The `phoneNumber` supplied to the current sign-up in E.164 format. Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. */ readonly phoneNumber: string | null; /** - * The Web3 wallet address supplied to the current sign-up, made up of 0x + 40 hexadecimal characters. Only supported - * if - * [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) - * is enabled in the instance settings. + * The Web3 wallet address supplied to the current sign-up, made up of 0x + 40 hexadecimal characters. Only supported if [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) is enabled in the instance settings. */ readonly web3Wallet: string | null; /** - * The value of this attribute is true if a password was supplied to the current sign-up. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled in - * the instance settings. + * The value of this attribute is true if a password was supplied to the current sign-up. Only supported if [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled in the instance settings. */ readonly hasPassword: boolean; /** - * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be - * automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use - * it to implement custom fields that can be collected during sign-up and will automatically be attached to the - * created User object. + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created User object. */ readonly unsafeMetadata: SignUpUnsafeMetadata; @@ -490,8 +447,7 @@ export interface SignUpFutureResource { readonly abandonAt: number | null; /** - * The epoch numerical time when the user agreed to the - * [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + * The epoch numerical time when the user agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. */ readonly legalAcceptedAt: number | null; @@ -508,20 +464,14 @@ export interface SignUpFutureResource { readonly canBeDiscarded: boolean; /** - * Creates a new `SignUp` instance initialized with the provided parameters. The instance maintains the sign-up - * lifecycle state through its `status` property, which updates as the authentication flow progresses. Will also - * deactivate any existing sign-up process the client may already have in progress. + * Creates a new `SignUp` instance initialized with the provided parameters. The instance maintains the sign-up lifecycle state through its `status` property, which updates as the authentication flow progresses. Will also deactivate any existing sign-up process the client may already have in progress. Once the sign-up process is complete, call the [`signUp.finalize()`](https://clerk.com/docs/reference/objects/sign-up-future#finalize) method to set the newly created session as the active session. * - * What you must pass to `params` depends on which - * [sign-up options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) you have - * enabled in your app's settings in the Clerk Dashboard. + * What you must pass to `params` depends on which [sign-up options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) you have enabled in your app's settings in the Clerk Dashboard. * - * You can complete the sign-up process in one step if you supply the required fields to `create()`. Otherwise, - * Clerk's sign-up process provides great flexibility and allows users to easily create multi-step sign-up flows. + * You can complete the sign-up process in one step if you supply the required fields to `create()`. Otherwise, Clerk's sign-up process provides great flexibility and allows users to easily create multi-step sign-up flows. * - * > [!WARNING] - * > Once the sign-up process is complete, call the `signUp.finalize()` method to set the newly created session as - * > the active session. + * > [!IMPORTANT] + * > The `signUp.create()` method is intended for advanced use cases. For most use cases, prefer the use of the factor-specific methods such as `signUp.password()`, `signUp.sso()`, etc. */ create: (params: SignUpFutureCreateParams) => Promise<{ error: ClerkError | null }>; @@ -532,42 +482,39 @@ export interface SignUpFutureResource { /** * An object that contains information about all available verification strategies. + * @extractMethods */ verifications: SignUpFutureVerifications; /** - * Used to sign up using an email address and password. + * Performs a password-based sign-up. */ password: (params: SignUpFuturePasswordParams) => Promise<{ error: ClerkError | null }>; /** - * Used to create an account using an OAuth connection. + * Performs an OAuth-based sign-up. */ sso: (params: SignUpFutureSSOParams) => Promise<{ error: ClerkError | null }>; /** - * Used to perform a ticket-based sign-up. + * Performs a ticket-based sign-up. */ ticket: (params?: SignUpFutureTicketParams) => Promise<{ error: ClerkError | null }>; /** - * Used to perform a Web3-based sign-up. + * Performs a Web3-based sign-up. */ web3: (params: SignUpFutureWeb3Params) => Promise<{ error: ClerkError | null }>; /** - * Used to convert a sign-up with `status === 'complete'` into an active session. Will cause anything observing the - * session state (such as the `useUser()` hook) to update automatically. + * Converts a sign-up with `status === 'complete'` into an active session. Will cause anything observing the session state (such as the `useUser()` hook) to update automatically. */ finalize: (params?: SignUpFutureFinalizeParams) => Promise<{ error: ClerkError | null }>; /** - * Resets the current sign-up attempt by clearing all local state back to null. - * This is useful when you want to allow users to go back to the beginning of - * the sign-up flow (e.g., to change their email address during verification). + * Resets the current sign-up attempt by clearing all local state back to null. This is useful when you want to allow users to go back to the beginning of the sign-up flow (e.g., to change their email address during verification). * - * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change - * to `'fetching'` and does not make any API calls - it only clears local state. + * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change to `'fetching'` and does not make any API calls - it only clears local state. */ reset: () => Promise<{ error: ClerkError | null }>; } From adb9396e6d3473df5f92bc94f9cd6abe95140c92 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Thu, 7 May 2026 21:21:33 -0700 Subject: [PATCH 44/51] fix signupfields --- .typedoc/custom-plugin.mjs | 1 + .typedoc/custom-theme.mjs | 188 +++++++++++++++++++++++- packages/shared/src/types/strategies.ts | 1 + 3 files changed, 189 insertions(+), 1 deletion(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 973e88a66b1..1a814f31e6e 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -70,6 +70,7 @@ const LINK_REPLACEMENTS = [ ['organization-membership-request-resource', '/docs/reference/types/organization-membership-request'], ['o-auth-consent-info', '/docs/reference/types/oauth-consent-info'], ['o-auth-consent-scope', '/docs/reference/types/oauth-consent-scope'], + ['o-auth-strategy', '/docs/reference/types/sso#o-auth-strategy'], ['session', '/docs/reference/backend/types/backend-session'], ['session-activity', '/docs/reference/backend/types/backend-session-activity'], ['organization', '/docs/reference/backend/types/backend-organization'], diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index f02c6d6b7c0..82f0dbf83ac 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -87,6 +87,172 @@ function findNamedTypeDeclaration(project, name) { return undefined; } +/** + * Prefer `packages/shared/src/types/strategies.ts` when multiple type aliases share the name `OAuthStrategy`. + * + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function findOAuthStrategyDeclaration(project) { + if (!project) { + return undefined; + } + /** @param {import('typedoc').Reflection} r */ + const sourcePath = r => { + const sources = /** @type {{ sources?: object[] }} */ (r).sources; + const s = sources?.[0]; + if (!s) { + return ''; + } + const raw = /** @type {{ file?: { fullFileName?: string }; fullFileName?: string }} */ (s); + const p = raw.file?.fullFileName ?? raw.fullFileName ?? ''; + return String(p).replace(/\\/g, '/'); + }; + + const byKind = + typeof project.getReflectionsByKind === 'function' + ? project.getReflectionsByKind(ReflectionKind.TypeAlias).filter(r => r.name === 'OAuthStrategy') + : Object.values(project.reflections ?? {}).filter( + r => + r.name === 'OAuthStrategy' && + /** @type {import('typedoc').Reflection} */ (r).kindOf?.(ReflectionKind.TypeAlias), + ); + if (byKind.length === 0) { + return findNamedTypeDeclaration(project, 'OAuthStrategy'); + } + if (byKind.length === 1) { + return /** @type {import('typedoc').DeclarationReflection} */ (byKind[0]); + } + const fromStrategies = byKind.find(r => sourcePath(r).includes('strategies')); + return /** @type {import('typedoc').DeclarationReflection | undefined} */ (fromStrategies ?? byKind[0]); +} + +/** + * Stock `someType` uses `instanceof UnionType`; duplicate Typedoc copies in the tree break that check and unions + * fall through to `backTicks(model.toString())`, bypassing {@link unionType} entirely (including OAuth collapse). + * + * @param {import('typedoc').Type | undefined} model + * @returns {import('typedoc').UnionType | undefined} + */ +function coerceUnionTypeIfNeeded(model) { + if (!model || typeof model !== 'object') { + return undefined; + } + if (model instanceof UnionType) { + return model; + } + const o = /** @type {{ type?: string; types?: import('typedoc').SomeType[] }} */ (model); + if (o.type === 'union' && Array.isArray(o.types) && o.types.length) { + return new UnionType(o.types); + } + return undefined; +} + +/** + * TypeScript normalizes `OAuthStrategy` to a large union of `oauth_*` string literals plus + * `` `oauth_custom_${string}` ``. That is not a {@link ReferenceType}, so the theme prints every literal. + * Collapse **only** when the union clearly matches that expanded Clerk shape, then render a link to `OAuthStrategy`. + * + * Guards (all must pass): many `oauth_` literals, fingerprint literals present, optional `oauth_custom_` template arm, + * `OAuthStrategy` exists and is not `@inline`. Skips ambiguous cases so other unions are unchanged. + * + * @param {import('typedoc').Type | undefined} t + * @returns {import('typedoc').Type[]} + */ +function flattenUnionTypeMembersForOAuthCollapse(t) { + if (!t || typeof t !== 'object') { + return []; + } + const o = /** @type {{ type?: string; types?: import('typedoc').Type[] }} */ (t); + if (o.type === 'union' && Array.isArray(o.types)) { + /** @type {import('typedoc').Type[]} */ + const acc = []; + for (const inner of o.types) { + acc.push(...flattenUnionTypeMembersForOAuthCollapse(inner)); + } + return acc; + } + return [t]; +} + +/** + * @param {import('typedoc').Type} t + */ +function isExpandedOAuthStrategyUnionArm(t) { + const o = /** @type {{ type?: string; value?: unknown; head?: string; tail?: unknown }} */ (t); + if (o.type === 'literal' && typeof o.value === 'string') { + return o.value.startsWith('oauth_'); + } + if (o.type === 'templateLiteral' && typeof o.head === 'string') { + return o.head === 'oauth_custom_'; + } + return false; +} + +/** Minimum distinct `oauth_*` literal arms before we treat the union as “expanded OAuthStrategy”. */ +const OAUTH_STRATEGY_COLLAPSE_MIN_LITERAL_ARMS = 12; + +/** + * @param {import('typedoc').UnionType} model + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {import('typedoc').UnionType | undefined} + */ +function tryCollapseExpandedOAuthStrategyUnion(model, ctx) { + const project = ctx.page?.project; + if (!project) { + return undefined; + } + const oauthDecl = findOAuthStrategyDeclaration(project); + if (!oauthDecl?.kindOf(ReflectionKind.TypeAlias)) { + return undefined; + } + if (oauthDecl.comment?.hasModifier('@inline')) { + return undefined; + } + + const members = flattenUnionTypeMembersForOAuthCollapse(model); + const oauthArms = members.filter(isExpandedOAuthStrategyUnionArm); + if (oauthArms.length < OAUTH_STRATEGY_COLLAPSE_MIN_LITERAL_ARMS) { + return undefined; + } + + const literalVals = oauthArms + .filter(u => /** @type {{ type?: string }} */ (u).type === 'literal') + .map(u => /** @type {{ value?: unknown }} */ (/** @type {unknown} */ (u)).value) + .filter(/** @return {v is string} */ v => typeof v === 'string'); + const literalSet = new Set(literalVals); + if (!literalSet.has('oauth_google') || (!literalSet.has('oauth_facebook') && !literalSet.has('oauth_github'))) { + return undefined; + } + + const hasCustomTemplateArm = oauthArms.some(u => { + const o = /** @type {{ type?: string; head?: string }} */ (u); + return o.type === 'templateLiteral' && o.head === 'oauth_custom_'; + }); + /** Without the template arm, require an even larger literal set (avoids small hand-written unions). */ + if (!hasCustomTemplateArm && literalVals.length < 20) { + return undefined; + } + + const ref = ReferenceType.createResolvedReference('OAuthStrategy', oauthDecl, project); + /** @type {import('typedoc').Type[]} */ + const out = []; + let i = 0; + while (i < members.length) { + if (isExpandedOAuthStrategyUnionArm(members[i])) { + out.push(ref); + i++; + while (i < members.length && isExpandedOAuthStrategyUnionArm(members[i])) { + i++; + } + } else { + out.push(members[i]); + i++; + } + } + return new UnionType(/** @type {import('typedoc').SomeType[]} */ (/** @type {unknown} */ (out))); +} + /** * Collect documented property reflections from one intersection arm (object literal, type alias, interface, nested `&`). * E.g. `{ a: string } & { b: number }` => `[{ name: 'a', type: 'string' }, { name: 'b', type: 'number' }]` @@ -861,6 +1027,25 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, + /** + * Ensure unions always route through `unionType` (OAuth collapse) even when `instanceof UnionType` fails. + * + * @param {import('typedoc').Type | undefined} model + * @param {Parameters[1]} [options] + */ + someType: (model, options) => { + const ut = coerceUnionTypeIfNeeded(model); + if (ut) { + const collapsed = tryCollapseExpandedOAuthStrategyUnion(ut, this); + const toRender = collapsed ?? ut; + return superPartials.someType.call(this, toRender, options); + } + return superPartials.someType.call( + this, + /** @type {import('typedoc').SomeType | undefined} */ (/** @type {unknown} */ (model)), + options, + ); + }, /** * Stock `comments.comment` prints every {@link Comment.modifierTags} as **`TitleCase`** before the summary. * `@inline` / `@inlineType` are router/type hints only; `@experimental` is SDK-only guidance — none of these @@ -1276,7 +1461,8 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {import('typedoc').UnionType} model */ unionType: model => { - const defaultOutput = superPartials.unionType(model); + const collapsed = tryCollapseExpandedOAuthStrategyUnion(model, this); + const defaultOutput = superPartials.unionType(collapsed ?? model); const output = defaultOutput // Escape stuff that would be turned into markdown diff --git a/packages/shared/src/types/strategies.ts b/packages/shared/src/types/strategies.ts index b97d2c1bf3e..2055c1a5056 100644 --- a/packages/shared/src/types/strategies.ts +++ b/packages/shared/src/types/strategies.ts @@ -30,6 +30,7 @@ export type CustomOAuthStrategy = `oauth_custom_${string}`; /** @inline */ export type EnterpriseSSOStrategy = 'enterprise_sso'; +/** OAuth-related authentication strategies (`oauth_` and custom OAuth). */ export type OAuthStrategy = `oauth_${OAuthProvider}` | CustomOAuthStrategy; /** @inline */ From 83de5883105534bbaf8c3d6d38595f7e28ad9d94 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 8 May 2026 10:41:50 -0700 Subject: [PATCH 45/51] signupfutureresource updates --- .typedoc/custom-plugin.mjs | 4 +++ packages/shared/src/types/signUpFuture.ts | 40 +++++++++++------------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 1a814f31e6e..10047a15c0c 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -267,6 +267,10 @@ function getCatchAllReplacements() { pattern: /(? Promise<{ error: ClerkError | null }>; /** - * Updates the current `SignUp`. + * Updates the current `SignUpFutureResource` instance with the provided parameters. */ update: (params: SignUpFutureUpdateParams) => Promise<{ error: ClerkError | null }>; @@ -492,7 +492,7 @@ export interface SignUpFutureResource { password: (params: SignUpFuturePasswordParams) => Promise<{ error: ClerkError | null }>; /** - * Performs an OAuth-based sign-up. + * Performs an SSO-based sign-up ([Social/OAuth](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview) or [Enterprise](https://clerk.com/docs/guides/configure/auth-strategies/enterprise-connections/overview)). */ sso: (params: SignUpFutureSSOParams) => Promise<{ error: ClerkError | null }>; From a473d6cf7aa78030c16548a48495328c22616d71 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 8 May 2026 10:42:14 -0700 Subject: [PATCH 46/51] param headings need to get wrapped in backticks --- .typedoc/extract-methods.mjs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index c3f9b58348e..6079a794a0e 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -48,6 +48,18 @@ function markdownHeading(level, text) { return `${'#'.repeat(l)} ${text}`; } +/** + * Heading whose visible title is a type/identifier (nominal single-parameter object sections) needs to get wrapped in backticks. + * + * @param {number} level + * @param {string} text + */ +function markdownHeadingInlineCode(level, text) { + const l = Math.min(Math.max(level, 1), 6); + const t = text.trim(); + return `${'#'.repeat(l)} \`${t}\``; +} + /** * Same as typedoc-plugin-markdown `removeLineBreaks` for table cells. * @@ -1218,7 +1230,7 @@ function trySingleNominalParameterTypeSection(sig, ctx) { if (!tableMd?.trim()) { return undefined; } - return [markdownHeading(4, nominal.sectionTitle), '', tableMd, ''].join('\n'); + return [markdownHeadingInlineCode(4, nominal.sectionTitle), '', tableMd, ''].join('\n'); } /** From 4e51dce051dc70d0bff82c328f81fac22eafc625 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 8 May 2026 13:14:50 -0700 Subject: [PATCH 47/51] generate organization docs --- .typedoc/__tests__/file-structure.test.ts | 2 + .typedoc/custom-plugin.mjs | 3 +- .typedoc/reference-objects.mjs | 4 + packages/shared/src/types/billing.ts | 9 +- packages/shared/src/types/organization.ts | 170 ++++++++++++++++++ .../shared/src/types/organizationDomain.ts | 8 +- .../src/types/organizationInvitation.ts | 4 +- 7 files changed, 187 insertions(+), 13 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 55f38270bce..7c5321fee18 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -64,6 +64,8 @@ describe('Typedoc output', () => { "shared/sign-in-future-resource/methods", "shared/sign-up-future-resource", "shared/sign-up-future-resource/methods", + "shared/organization-resource", + "shared/organization-resource/methods", ] `); }); diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 10047a15c0c..2cb3eab76ac 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -62,6 +62,7 @@ const LINK_REPLACEMENTS = [ ['user-resource', '/docs/reference/objects/user'], ['session-status-claim', '/docs/reference/types/session-status'], ['user-organization-invitation-resource', '/docs/reference/types/user-organization-invitation'], + ['organization-custom-role-key', '/docs/reference/types/organization-custom-role-key'], ['organization-membership-resource', '/docs/reference/types/organization-membership'], ['organization-suggestion-resource', '/docs/reference/types/organization-suggestion'], ['organization-resource', '/docs/reference/objects/organization'], @@ -296,7 +297,7 @@ function getCatchAllReplacements() { replace: '[OrganizationPrivateMetadata](/docs/reference/types/metadata#organization-private-metadata)', }, { - pattern: /(? Promise; /** - * Adds a payment method for the user. + * Adds a payment method. + * @returns An [`BillingPaymentMethodResource`](https://clerk.com/docs/reference/types/billing-payment-method-resource) object. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ addPaymentMethod: (params: AddPaymentMethodParams) => Promise; /** - * Retrieves a list of payment methods that have been stored for the user. + * Retrieves a list of payment methods that have been stored. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`BillingPaymentMethodResource`](https://clerk.com/docs/reference/types/billing-payment-method-resource) objects. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPaymentMethods: ( diff --git a/packages/shared/src/types/organization.ts b/packages/shared/src/types/organization.ts index 98ced75e217..9fc96ef5ef1 100644 --- a/packages/shared/src/types/organization.ts +++ b/packages/shared/src/types/organization.ts @@ -36,90 +36,260 @@ declare global { * @interface */ export interface OrganizationResource extends ClerkResource, BillingPayerMethods { + /** + * The unique identifier for the Organization. + */ id: string; + /** + * The name of the Organization. + */ name: string; + /** + * The URL-friendly identifier of the Organization. If supplied, it must be unique for the instance. + */ slug: string | null; + /** + * Holds the Organization's logo. Compatible with Clerk's [Image Optimization](https://clerk.com/docs/guides/development/image-optimization). + */ imageUrl: string; + /** + * Whether the Organization has an image. + */ hasImage: boolean; + /** + * The number of members in the Organization. + */ membersCount: number; + /** + * The number of pending invitations in the Organization. + */ pendingInvitationsCount: number; + /** + * Metadata that can be read from both the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }} and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. Once the user accepts the invitation and signs up, these metadata will end up in the user's public metadata. + */ publicMetadata: OrganizationPublicMetadata; + /** + * Whether the Organization allows admins to delete users. + */ adminDeleteEnabled: boolean; + /** + * The maximum number of memberships allowed in the Organization. + */ maxAllowedMemberships: number; + /** + * The date when the Organization was first created. + */ createdAt: Date; + /** + * The date when the Organization was last updated. + */ updatedAt: Date; + /** + * Updates the current Organization. + */ update: (params: UpdateOrganizationParams) => Promise; + /** + * Retrieves the list of Organization Memberships. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) objects. + */ getMemberships: GetMemberships; + + /** + * Retrieves the list of invitations. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationInvitationResource`](https://clerk.com/docs/reference/types/organization-invitation) objects. + */ getInvitations: (params?: GetInvitationsParams) => Promise>; + /** + * Retrieves the list of [Roles](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) available. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`RoleResource`](https://clerk.com/docs/reference/types/role) objects and a `has_role_set_migration` status. + * + * When `has_role_set_migration` is `true`, updating Organization membership Roles is not allowed. Learn how to [build a custom flow for managing member Roles in an Organization](/docs/guides/development/custom-flows/organizations/manage-roles). + */ getRoles: (params?: GetRolesParams) => Promise; + /** + * Retrieves the list of domains. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) objects. + * + * > [!WARNING] + * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + */ getDomains: (params?: GetDomainsParams) => Promise>; + /** + * Retrieves the list of membership requests. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipRequestResource`](https://clerk.com/docs/reference/types/organization-membership-request) objects. + * + * > [!WARNING] + * > You must have [**Verified domains** and **Automatic suggestion**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + */ getMembershipRequests: ( params?: GetMembershipRequestParams, ) => Promise>; + /** + * Adds a user as a member to an organization. A user can only be added to an organization if they are not already a member of it and if they already exist in the same instance as the organization. Only administrators can add members to an organization. + * @returns An [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) object. + */ addMember: (params: AddMemberParams) => Promise; + /** + * Creates and sends an invitation to the given email address. + * @returns An [`OrganizationInvitationResource`](https://clerk.com/docs/reference/types/organization-invitation) object. + */ inviteMember: (params: InviteMemberParams) => Promise; + /** + * Creates and sends invitations to the given email addresses. + * @returns An array of [`OrganizationInvitationResource`](https://clerk.com/docs/reference/types/organization-invitation) objects. + */ inviteMembers: (params: InviteMembersParams) => Promise; + /** + * Updates a given member. + * @returns An [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) object. + */ updateMember: (params: UpdateMembershipParams) => Promise; + /** + * Removes a member. + * @returns An [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) object. + * @param userId - The unique identifier of the user to remove. + */ removeMember: (userId: string) => Promise; + /** + * Creates a new domain. + * @returns An [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) object. + * + * > [!WARNING] + * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + * @param domainName - The name of the domain to create. + */ createDomain: (domainName: string) => Promise; + /** + * Retrieves a domain for an Organization based on the given domain ID. + * @returns An [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) object. + * + * > [!WARNING] + * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + * @param domainId - The unique identifier of the domain to retrieve. + */ getDomain: ({ domainId }: { domainId: string }) => Promise; + /** + * Deletes the Organization. Only administrators can delete an Organization. + * + * Deleting an Organization will also delete all memberships and invitations. **This is not reversible.** + */ destroy: () => Promise; setLogo: (params: SetOrganizationLogoParams) => Promise; __internal_toSnapshot: () => OrganizationJSONSnapshot; } +/** @document */ export type GetRolesParams = ClerkPaginationParams; export interface GetRolesResponse extends ClerkPaginatedResponse { has_role_set_migration?: boolean; } +/** @document */ export type GetMembersParams = ClerkPaginationParams<{ + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) to filter the users by. + */ role?: OrganizationCustomRoleKey[]; + /** + * The query to filter the users by. + */ query?: string; }>; +/** @document */ export type GetDomainsParams = ClerkPaginationParams<{ + /** + * The [enrollment mode](https://clerk.com/docs/guides/organizations/add-members/verified-domains#enable-verified-domains) will decide how new users join an organization. + */ enrollmentMode?: OrganizationEnrollmentMode; }>; +/** @document */ export type GetInvitationsParams = ClerkPaginationParams<{ + /** + * The status of the invitations to retrieve. + */ status?: OrganizationInvitationStatus[]; }>; +/** @document */ export type GetMembershipRequestParams = ClerkPaginationParams<{ + /** + * The status of the membership requests to retrieve. + */ status?: OrganizationInvitationStatus; }>; +/** @document */ export interface AddMemberParams { + /** + * The unique identifier of the user to add as a member. + */ userId: string; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the user. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface InviteMemberParams { + /** + * The email address of the user to invite. + */ emailAddress: string; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the user. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface InviteMembersParams { + /** + * The email addresses of the users to invite. + */ emailAddresses: string[]; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the users. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface UpdateMembershipParams { + /** + * The unique identifier of the user to update. + */ userId: string; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the user. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface UpdateOrganizationParams { + /** + * The name of the Organization. + */ name: string; + /** + * The URL-friendly identifier of the Organization. If supplied, it must be unique for the instance. + */ slug?: string; } +/** @document */ export interface SetOrganizationLogoParams { + /** + * The file to set as the Organization's logo. + */ file: Blob | File | string | null; } +/** @document */ export type GetMemberships = ( params?: GetMembersParams, ) => Promise>; diff --git a/packages/shared/src/types/organizationDomain.ts b/packages/shared/src/types/organizationDomain.ts index 86b845a1aa7..c64f4e196ba 100644 --- a/packages/shared/src/types/organizationDomain.ts +++ b/packages/shared/src/types/organizationDomain.ts @@ -7,14 +7,10 @@ export interface OrganizationDomainVerification { expiresAt: Date; } -/** - * @inline - */ +/** @inline */ export type OrganizationDomainVerificationStatus = 'unverified' | 'verified'; -/** - * @inline - */ +/** @inline */ export type OrganizationEnrollmentMode = 'manual_invitation' | 'automatic_invitation' | 'automatic_suggestion'; /** diff --git a/packages/shared/src/types/organizationInvitation.ts b/packages/shared/src/types/organizationInvitation.ts index 61581a28e37..0027efc929b 100644 --- a/packages/shared/src/types/organizationInvitation.ts +++ b/packages/shared/src/types/organizationInvitation.ts @@ -34,7 +34,5 @@ export interface OrganizationInvitationResource extends ClerkResource { revoke: () => Promise; } -/** - * @inline - */ +/** @inline */ export type OrganizationInvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired'; From 5c23021abef1c207909c91e7f456f2755ba12a30 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 8 May 2026 13:23:35 -0700 Subject: [PATCH 48/51] remove properties headings from generated files --- .typedoc/custom-plugin.mjs | 2 +- .typedoc/extract-methods.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 2cb3eab76ac..6c541cf12c0 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -416,7 +416,7 @@ function restoreProtectedInlineCodeSpans(text, placeholders) { } /** - * Remove the Properties section (heading + table) from reference object pages (e.g. `shared/clerk/clerk.mdx`); the table is copied into `shared//properties.mdx` by `extract-methods.mjs`. + * Remove the Properties section (heading + table) from reference object pages (e.g. `shared/clerk/clerk.mdx`); the table body (no heading) is copied into `shared//properties.mdx` by `extract-methods.mjs`. * * @param {string} contents */ diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 6079a794a0e..b8f92a7f9ae 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -1,6 +1,6 @@ // @ts-check /** - * For each entry in REFERENCE_OBJECTS_LIST, reads the TypeDoc output (e.g. `shared/clerk/clerk.mdx`), strips **Properties** from the main generated file and copies it into `properties.mdx`, and writes one .mdx per method under `methods/` (alongside the main page in that resource folder). + * For each entry in REFERENCE_OBJECTS_LIST, reads the TypeDoc output (e.g. `shared/clerk/clerk.mdx`), strips **Properties** from the main generated file and copies the section body (table only, no `## Properties` heading) into `properties.mdx`, and writes one .mdx per method under `methods/` (alongside the main page in that resource folder). * * Run after `typedoc` (same cwd as repo root). Uses a second TypeDoc convert pass to read reflections. * @@ -661,7 +661,7 @@ function extractPropertiesAndTrimSourcePage(pageUrl) { fs.mkdirSync(objectDir, { recursive: true }); if (body) { - const propertiesDoc = [`## Properties`, '', body.trimEnd(), ''].join('\n'); + const propertiesDoc = `${body.trimEnd()}\n`; const propertiesPath = path.join(objectDir, 'properties.mdx'); fs.writeFileSync( propertiesPath, From 28ebf79ed87c6ceed7a489365bc381b698a8be27 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 8 May 2026 13:30:48 -0700 Subject: [PATCH 49/51] generate api-keys --- .typedoc/__tests__/file-structure.test.ts | 2 + .typedoc/extract-methods.mjs | 64 ++++++++++++++++------- .typedoc/reference-objects.mjs | 9 +++- packages/shared/src/types/apiKeys.ts | 4 ++ 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 7c5321fee18..867a2c86cea 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -66,6 +66,8 @@ describe('Typedoc output', () => { "shared/sign-up-future-resource/methods", "shared/organization-resource", "shared/organization-resource/methods", + "shared/api-key-resource", + "shared/api-key-resource/methods", ] `); }); diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index b8f92a7f9ae..e1d474f8def 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -1373,6 +1373,41 @@ function processExtractMethodsNamespace(parentDecl, ctx, outDir) { return count; } +/** + * @param {import('typedoc').DeclarationReflection} decl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {string} outDir + */ +function extractCallableMembersFromDeclaration(decl, ctx, outDir) { + let count = 0; + if (!decl.children) { + return 0; + } + for (const child of decl.children) { + if (child.name.startsWith('__')) { + continue; + } + const childDecl = /** @type {import('typedoc').DeclarationReflection} */ (child); + + if (hasExtractMethodsModifier(childDecl)) { + count += processExtractMethodsNamespace(childDecl, ctx, outDir); + continue; + } + + if (shouldExtractCallableMember(childDecl, ctx)) { + const mdx = buildMethodMdx(childDecl, ctx); + if (mdx) { + const fileName = `${toKebabCase(child.name)}.mdx`; + const filePath = path.join(outDir, fileName); + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } + } + } + return count; +} + /** * @param {string} pageUrl * @param {import('typedoc').ProjectReflection} project @@ -1386,6 +1421,7 @@ function extractMethodsForPage(pageUrl, project, app) { } const { symbol, declarationHint } = entry; + const extraMethodInterfaces = 'extraMethodInterfaces' in entry ? entry.extraMethodInterfaces : undefined; const decl = findInterfaceOrClass(project, symbol, declarationHint); if (!decl?.children) { console.warn(`[extract-methods] Could not find interface/class "${symbol}"`); @@ -1401,29 +1437,19 @@ function extractMethodsForPage(pageUrl, project, app) { const outDir = path.join(objectDir, 'methods'); fs.mkdirSync(outDir, { recursive: true }); - let count = 0; - for (const child of decl.children) { - if (child.name.startsWith('__')) { - continue; - } - const childDecl = /** @type {import('typedoc').DeclarationReflection} */ (child); + let count = extractCallableMembersFromDeclaration(decl, ctx, outDir); - if (hasExtractMethodsModifier(childDecl)) { - count += processExtractMethodsNamespace(childDecl, ctx, outDir); - continue; - } - - if (shouldExtractCallableMember(childDecl, ctx)) { - const mdx = buildMethodMdx(childDecl, ctx); - if (mdx) { - const fileName = `${toKebabCase(child.name)}.mdx`; - const filePath = path.join(outDir, fileName); - fs.writeFileSync(filePath, mdx, 'utf-8'); - console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); - count++; + if (Array.isArray(extraMethodInterfaces)) { + for (const extra of extraMethodInterfaces) { + const extraDecl = findInterfaceOrClass(project, extra.symbol, extra.declarationHint); + if (!extraDecl?.children) { + console.warn(`[extract-methods] extraMethodInterfaces: could not find "${extra.symbol}" for ${pageUrl}`); + continue; } + count += extractCallableMembersFromDeclaration(extraDecl, ctx, outDir); } } + return count; } diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 328fb196ebf..257a8aad8f7 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -12,7 +12,9 @@ * * `declarationHint` is a substring of `packages/shared/src/**` file paths (used by `findInterfaceOrClass()` in `extract-methods.mjs` when multiple reflections share the same interface/class name). * - * `extract-methods.mjs` reads each file, writes `properties.mdx` with the same Properties table as TypeDoc, strips Properties from `.mdx`, and writes methods under `methods/`. + * `extract-methods.mjs` reads each file, writes `properties.mdx` with the same Properties table as TypeDoc (no `## Properties` heading), strips Properties from `.mdx`, and writes methods under `methods/`. + * + * Optional **`extraMethodInterfaces`**: extra `interface` / `class` declarations whose callable members (and `@extractMethods` namespaces) are emitted into the same `methods/` folder. Use when the documented resource type and the API surface live on different types (e.g. `APIKeyResource` vs `APIKeysNamespace` on `Clerk.apiKeys`). */ export const REFERENCE_OBJECT_CONFIG = { 'shared/clerk/clerk.mdx': { @@ -43,6 +45,11 @@ export const REFERENCE_OBJECT_CONFIG = { symbol: 'OrganizationResource', declarationHint: 'types/organization', }, + 'shared/api-key-resource/api-key-resource.mdx': { + symbol: 'APIKeyResource', + declarationHint: 'types/apiKeys', + extraMethodInterfaces: [{ symbol: 'APIKeysNamespace', declarationHint: 'types/apiKeys' }], + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts index 280761ee2a9..b4f73bb779c 100644 --- a/packages/shared/src/types/apiKeys.ts +++ b/packages/shared/src/types/apiKeys.ts @@ -2,6 +2,9 @@ import type { CreateAPIKeyParams, GetAPIKeysParams, RevokeAPIKeyParams } from '. import type { ClerkPaginatedResponse } from './pagination'; import type { ClerkResource } from './resource'; +/** + * The `APIKeys` object provides methods for managing API keys that allow your application's users to grant third-party services programmatic access to your application's API endpoints on their behalf. API keys are long-lived, opaque tokens that can be instantly revoked. + */ export interface APIKeyResource extends ClerkResource { /** * A unique identifier for the API key. @@ -69,6 +72,7 @@ export interface APIKeyResource extends ClerkResource { updatedAt: Date; } +/** @document */ export interface APIKeysNamespace { /** * Retrieves a paginated list of API keys for the current user or organization. From 537a7f7f4bbec5266dd9ecf5cb0dc14b81dc4655 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 8 May 2026 14:11:58 -0700 Subject: [PATCH 50/51] generate billing docs; fix file-structure-test --- .typedoc/__tests__/file-structure.test.ts | 14 ++++--- .typedoc/extract-methods.mjs | 43 +++++++++++++++------- .typedoc/reference-objects.mjs | 5 +++ packages/shared/src/types/billing.ts | 45 ++++++++++++++++------- 4 files changed, 74 insertions(+), 33 deletions(-) diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 867a2c86cea..ad1c3770971 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -52,22 +52,24 @@ describe('Typedoc output', () => { expect(nestedFolders).toMatchInlineSnapshot(` [ "react/legacy", + "shared/api-key-resource", + "shared/api-key-resource/methods", + "shared/billing-namespace", + "shared/billing-namespace/methods", "shared/clerk", "shared/clerk/methods", "shared/client-resource", "shared/client-resource/methods", + "shared/organization-resource", + "shared/organization-resource/methods", "shared/session-resource", "shared/session-resource/methods", - "shared/user-resource", - "shared/user-resource/methods", "shared/sign-in-future-resource", "shared/sign-in-future-resource/methods", "shared/sign-up-future-resource", "shared/sign-up-future-resource/methods", - "shared/organization-resource", - "shared/organization-resource/methods", - "shared/api-key-resource", - "shared/api-key-resource/methods", + "shared/user-resource", + "shared/user-resource/methods", ] `); }); diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index e1d474f8def..dfb8efcb710 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -879,9 +879,10 @@ function pickBetterUnionPropertyCandidate(existing, candidate) { * TypeDoc applies `@param parent.prop` descriptions onto property reflections under this declaration. * * @param {import('typedoc').SomeType | undefined} t + * @param {import('typedoc').ProjectReflection | undefined} [project] For resolving references when `ref.reflection` is missing (intersections like `Foo & WithOptionalOrgType<…>`). * @returns {import('typedoc').DeclarationReflection | undefined} */ -function resolveDeclarationWithObjectMembers(t) { +function resolveDeclarationWithObjectMembers(t, project) { if (!t) { return undefined; } @@ -893,11 +894,16 @@ function resolveDeclarationWithObjectMembers(t) { } if (t.type === 'reference') { const ref = /** @type {import('typedoc').ReferenceType} */ (t); - const r = ref.reflection; - if (r && 'type' in r) { - const decl = /** @type {import('typedoc').DeclarationReflection} */ (r); - const typeArgs = ref.typeArguments ?? []; + const typeArgs = ref.typeArguments ?? []; + let decl = + ref.reflection && 'kind' in ref.reflection + ? /** @type {import('typedoc').DeclarationReflection} */ (ref.reflection) + : undefined; + if (!decl && project && ref.name) { + decl = lookupInterfaceOrTypeAliasByName(project, ref.name); + } + if (decl) { /** * Generic aliases like `ClerkPaginationParams<{ status?: … }>` are a reference with `typeArguments`. * TypeDoc often puts pagination fields only on the target alias `children` and omits `decl.type`, so returning `decl` early drops the type argument object. Merge base + each type argument's properties. @@ -906,7 +912,7 @@ function resolveDeclarationWithObjectMembers(t) { /** @type {Map} */ const byName = new Map(); if (decl.type) { - const fromType = resolveDeclarationWithObjectMembers(decl.type); + const fromType = resolveDeclarationWithObjectMembers(decl.type, project); if (fromType?.children?.length) { for (const c of fromType.children) { if (c.kindOf(ReflectionKind.Property)) { @@ -923,7 +929,7 @@ function resolveDeclarationWithObjectMembers(t) { } } for (const ta of typeArgs) { - const fromArg = resolveDeclarationWithObjectMembers(ta); + const fromArg = resolveDeclarationWithObjectMembers(ta, project); if (fromArg?.children?.length) { for (const c of fromArg.children) { if (c.kindOf(ReflectionKind.Property)) { @@ -947,7 +953,7 @@ function resolveDeclarationWithObjectMembers(t) { return decl; } if (decl.type) { - return resolveDeclarationWithObjectMembers(decl.type); + return resolveDeclarationWithObjectMembers(decl.type, project); } } } @@ -956,7 +962,7 @@ function resolveDeclarationWithObjectMembers(t) { /** @type {Map} */ const byName = new Map(); for (const inner of inter.types) { - const res = resolveDeclarationWithObjectMembers(inner); + const res = resolveDeclarationWithObjectMembers(inner, project); if (res?.children?.length) { for (const c of res.children) { if (c.kindOf(ReflectionKind.Property)) { @@ -982,7 +988,7 @@ function resolveDeclarationWithObjectMembers(t) { /** @type {Map} */ const byName = new Map(); for (const inner of u.types) { - const res = resolveDeclarationWithObjectMembers(inner); + const res = resolveDeclarationWithObjectMembers(inner, project); if (!res?.children?.length) { continue; } @@ -1013,7 +1019,7 @@ function resolveDeclarationWithObjectMembers(t) { ); } if (t.type === 'optional') { - return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType); + return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType, project); } return undefined; } @@ -1083,7 +1089,8 @@ function nestedParameterRowsFromDocumentedProperties(param, ctx) { return []; } - const holder = resolveDeclarationWithObjectMembers(param.type); + const project = /** @type {import('typedoc').ProjectReflection | undefined} */ (param.project ?? ctx.page?.project); + const holder = resolveDeclarationWithObjectMembers(param.type, project); if (!holder?.children?.length) { return []; } @@ -1165,7 +1172,7 @@ function resolveNominalObjectTypeForSingleParam(t, project) { if (typeDecl.kindOf(ReflectionKind.TypeAlias)) { // Prefer resolving `typeAlias.type` so intersections and generic instantiations (e.g. `ClerkPaginationParams<{ status?: … }>`) merge every `&` arm into one property list. // Some aliases only attach members on `typeDecl.children` with no object shape on `.type`; keep that fallback (e.g. `SignOutOptions`, `JoinWaitlistParams`). - const fromResolvedType = typeDecl.type ? resolveDeclarationWithObjectMembers(typeDecl.type) : undefined; + const fromResolvedType = typeDecl.type ? resolveDeclarationWithObjectMembers(typeDecl.type, project) : undefined; const holder = fromResolvedType?.children?.length ? fromResolvedType : typeDecl.children?.length @@ -1181,6 +1188,9 @@ function resolveNominalObjectTypeForSingleParam(t, project) { } /** + * Nominal param sections are skipped when there is no prose anywhere — avoids huge undocumented tables. + * Type-only aliases often use `@experimental` / `@deprecated` on the type with an empty summary; intersection params like `GetPaymentAttemptParams` still have documented arms (`id`, pagination) and must inline. + * * @param {import('typedoc').DeclarationReflection} typeDecl * @param {import('typedoc').DeclarationReflection[]} props */ @@ -1188,6 +1198,10 @@ function isNominalParamTypeDocumented(typeDecl, props) { if (typeDecl.comment?.summary?.length) { return true; } + const blockTags = typeDecl.comment?.blockTags ?? []; + if (blockTags.some(t => t.tag !== '@inline')) { + return true; + } return props.some(p => p.comment?.summary?.length); } @@ -1323,7 +1337,8 @@ function hasExtractMethodsModifier(decl) { * @returns {number} Number of files written */ function processExtractMethodsNamespace(parentDecl, ctx, outDir) { - const holder = resolveDeclarationWithObjectMembers(parentDecl.type); + const project = ctx.page?.project; + const holder = resolveDeclarationWithObjectMembers(parentDecl.type, project); const members = holder?.children ?? []; if (members.length === 0) { console.warn( diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs index 257a8aad8f7..ecb196dac00 100644 --- a/.typedoc/reference-objects.mjs +++ b/.typedoc/reference-objects.mjs @@ -50,6 +50,11 @@ export const REFERENCE_OBJECT_CONFIG = { declarationHint: 'types/apiKeys', extraMethodInterfaces: [{ symbol: 'APIKeysNamespace', declarationHint: 'types/apiKeys' }], }, + 'shared/billing-namespace/billing-namespace.mdx': { + symbol: 'BillingNamespace', + declarationHint: 'types/billing', + extraMethodInterfaces: [{ symbol: 'BillingNamespace', declarationHint: 'types/billing' }], + }, }; /** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ diff --git a/packages/shared/src/types/billing.ts b/packages/shared/src/types/billing.ts index 0bdf4507c50..f0446bf7f9a 100644 --- a/packages/shared/src/types/billing.ts +++ b/packages/shared/src/types/billing.ts @@ -6,7 +6,10 @@ import type { ClerkPaginatedResponse, ClerkPaginationParams } from './pagination import type { ClerkResource } from './resource'; import type { ForceNull, RemoveFunctions, Simplify } from './utils'; -type WithOptionalOrgType = T & { +/** + * Intersects `T` with an optional organization scope (`orgId`) for billing and related requests. + */ +export type WithOptionalOrgType = T & { /** * The Organization ID to perform the request on. */ @@ -18,56 +21,64 @@ type WithOptionalOrgType = T & { */ export interface BillingNamespace { /** - * Returns a list of payment attempts for the current user or supplied organization. + * Gets a list of payment attempts for the current user or supplied organization. + * @returns A [`ClerkPaginatedResponse`](/docs/reference/types/clerk-paginated-response) of [`BillingPaymentResource`](/docs/reference/types/billing-payment-resource) objects. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPaymentAttempts: (params: GetPaymentAttemptsParams) => Promise>; /** - * Returns details of a specific payment attempt for the current user or supplied Organization. + * Gets details of a specific payment attempt for the current user or supplied Organization. + * @returns A [`BillingPaymentResource`](https://clerk.com/docs/reference/types/billing-payment-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPaymentAttempt: (params: GetPaymentAttemptParams) => Promise; /** - * Returns a list of all publically visible Billing Plans. + * Gets a list of all publically visible Billing Plans. + * @returns A [`ClerkPaginatedResponse`](/docs/reference/types/clerk-paginated-response) of [`BillingPlanResource`](/docs/reference/types/billing-plan-resource) objects. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPlans: (params?: GetPlansParams) => Promise>; /** - * Returns a Billing Plan by ID. + * Gets a given Billing Plan. + * @returns A [`BillingPlanResource`](/docs/reference/types/billing-plan-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPlan: (params: GetPlanParams) => Promise; /** - * Returns the main Billing Subscription for the current user or supplied Organization. + * Gets the main Billing Subscription for the current user or supplied Organization. + * @returns A [`BillingSubscriptionResource`](/docs/reference/types/billing-subscription-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getSubscription: (params: GetSubscriptionParams) => Promise; /** - * Returns a list of billing statements for the current user or supplied Organization. + * Gets a list of billing statements for the current user or supplied Organization. + * @returns A [`ClerkPaginatedResponse`](/docs/reference/types/clerk-paginated-response) of [`BillingStatementResource`](/docs/reference/types/billing-statement-resource) objects. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getStatements: (params: GetStatementsParams) => Promise>; /** - * Returns a billing statement by ID. + * Gets a given Billing Statement. + * @returns A [`BillingStatementResource`](/docs/reference/types/billing-statement-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getStatement: (params: GetStatementParams) => Promise; /** - * Creates a new billing checkout for the current user or supplied Organization. + * Creates a new Billing checkout for the current user or supplied Organization. + * @returns A [`BillingCheckoutResource`](/docs/reference/types/billing-checkout-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ @@ -119,7 +130,7 @@ export interface BillingPayerMethods { */ addPaymentMethod: (params: AddPaymentMethodParams) => Promise; /** - * Retrieves a list of payment methods that have been stored. + * Gets a list of payment methods that have been stored. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`BillingPaymentMethodResource`](https://clerk.com/docs/reference/types/billing-payment-method-resource) objects. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ @@ -133,7 +144,7 @@ export interface BillingPayerMethods { */ export type GetPlanParams = { /** - * The ID of the Billing Plan to fetch. + * The ID of the Billing Plan to get. */ id: string; }; @@ -563,14 +574,19 @@ export type GetPaymentAttemptsParams = WithOptionalOrgType; +export type GetPaymentAttemptParams = { + /** + * The unique identifier for the payment attempt to get. + */ + id: string; +} & WithOptionalOrgType; /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ export type GetStatementParams = { /** - * The ID of the statement to fetch. + * The ID of the statement to get. */ id: string; } & WithOptionalOrgType; @@ -636,6 +652,9 @@ export interface BillingStatementGroup { * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ export type GetSubscriptionParams = { + /** + * The unique identifier for the Organization to get the subscription for. + */ orgId?: string; }; From f0826e1b0ad59858634c22a1361e08e0eb2314e0 Mon Sep 17 00:00:00 2001 From: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Date: Fri, 8 May 2026 14:14:11 -0700 Subject: [PATCH 51/51] retrieve --> get --- .../backend/src/api/endpoints/SessionApi.ts | 2 +- .../backend/src/tokens/authenticateContext.ts | 2 +- .../clerk-js/src/core/modules/apiKeys/index.ts | 2 +- packages/shared/src/types/apiKeys.ts | 2 +- packages/shared/src/types/clerk.ts | 4 ++-- packages/shared/src/types/organization.ts | 18 +++++++++--------- packages/shared/src/types/session.ts | 2 +- packages/shared/src/types/user.ts | 10 +++++----- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/backend/src/api/endpoints/SessionApi.ts b/packages/backend/src/api/endpoints/SessionApi.ts index 047aeb8fb4f..170408ea5ac 100644 --- a/packages/backend/src/api/endpoints/SessionApi.ts +++ b/packages/backend/src/api/endpoints/SessionApi.ts @@ -72,7 +72,7 @@ export class SessionAPI extends AbstractAPI { } /** - * Retrieves a session token or generates a JWT using a specified template. + * Gets a session token or generates a JWT using a specified template. * * @param sessionId - The ID of the session for which to generate the token * @param template - The name of the JWT template configured in the Clerk Dashboard. diff --git a/packages/backend/src/tokens/authenticateContext.ts b/packages/backend/src/tokens/authenticateContext.ts index 794c9268874..e8d1d682330 100644 --- a/packages/backend/src/tokens/authenticateContext.ts +++ b/packages/backend/src/tokens/authenticateContext.ts @@ -58,7 +58,7 @@ class AuthenticateContext implements AuthenticateContext { private originalFrontendApi: string = ''; /** - * Retrieves the session token from either the cookie or the header. + * Gets the session token from either the cookie or the header. * * @returns {string | undefined} The session token if available, otherwise undefined. */ diff --git a/packages/clerk-js/src/core/modules/apiKeys/index.ts b/packages/clerk-js/src/core/modules/apiKeys/index.ts index 77949b70ae0..58e589c1a2d 100644 --- a/packages/clerk-js/src/core/modules/apiKeys/index.ts +++ b/packages/clerk-js/src/core/modules/apiKeys/index.ts @@ -40,7 +40,7 @@ export class APIKeys implements APIKeysNamespace { } /** - * Retrieves a paginated list of API keys. + * Gets a paginated list of API keys. * * The subject (owner) is resolved in the following order: * 1. Explicit `subject` param (user or organization ID) diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts index b4f73bb779c..dd4ea909ace 100644 --- a/packages/shared/src/types/apiKeys.ts +++ b/packages/shared/src/types/apiKeys.ts @@ -75,7 +75,7 @@ export interface APIKeyResource extends ClerkResource { /** @document */ export interface APIKeysNamespace { /** - * Retrieves a paginated list of API keys for the current user or organization. + * Gets a paginated list of API keys for the current user or organization. */ getAll(params?: GetAPIKeysParams): Promise>; /** diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 5ea0a2a0436..7af56f46670 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -1117,9 +1117,9 @@ export interface Clerk { createOrganization: (params: CreateOrganizationParams) => Promise; /** - * Retrieves a single [Organization](https://clerk.com/docs/reference/objects/organization) by ID. + * Gets a single [Organization](https://clerk.com/docs/reference/objects/organization) by ID. * - * @param organizationId - The ID of the Organization to retrieve. + * @param organizationId - The ID of the Organization to get. */ getOrganization: (organizationId: string) => Promise; diff --git a/packages/shared/src/types/organization.ts b/packages/shared/src/types/organization.ts index 9fc96ef5ef1..00107ff1cd3 100644 --- a/packages/shared/src/types/organization.ts +++ b/packages/shared/src/types/organization.ts @@ -89,25 +89,25 @@ export interface OrganizationResource extends ClerkResource, BillingPayerMethods */ update: (params: UpdateOrganizationParams) => Promise; /** - * Retrieves the list of Organization Memberships. + * Gets the list of Organization Memberships. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) objects. */ getMemberships: GetMemberships; /** - * Retrieves the list of invitations. + * Gets the list of invitations. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationInvitationResource`](https://clerk.com/docs/reference/types/organization-invitation) objects. */ getInvitations: (params?: GetInvitationsParams) => Promise>; /** - * Retrieves the list of [Roles](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) available. + * Gets the list of [Roles](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) available. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`RoleResource`](https://clerk.com/docs/reference/types/role) objects and a `has_role_set_migration` status. * * When `has_role_set_migration` is `true`, updating Organization membership Roles is not allowed. Learn how to [build a custom flow for managing member Roles in an Organization](/docs/guides/development/custom-flows/organizations/manage-roles). */ getRoles: (params?: GetRolesParams) => Promise; /** - * Retrieves the list of domains. + * Gets the list of domains. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) objects. * * > [!WARNING] @@ -115,7 +115,7 @@ export interface OrganizationResource extends ClerkResource, BillingPayerMethods */ getDomains: (params?: GetDomainsParams) => Promise>; /** - * Retrieves the list of membership requests. + * Gets the list of membership requests. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipRequestResource`](https://clerk.com/docs/reference/types/organization-membership-request) objects. * * > [!WARNING] @@ -160,12 +160,12 @@ export interface OrganizationResource extends ClerkResource, BillingPayerMethods */ createDomain: (domainName: string) => Promise; /** - * Retrieves a domain for an Organization based on the given domain ID. + * Gets a domain for an Organization based on the given domain ID. * @returns An [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) object. * * > [!WARNING] * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. - * @param domainId - The unique identifier of the domain to retrieve. + * @param domainId - The unique identifier of the domain to get. */ getDomain: ({ domainId }: { domainId: string }) => Promise; /** @@ -208,7 +208,7 @@ export type GetDomainsParams = ClerkPaginationParams<{ /** @document */ export type GetInvitationsParams = ClerkPaginationParams<{ /** - * The status of the invitations to retrieve. + * The status of the invitations to get. */ status?: OrganizationInvitationStatus[]; }>; @@ -216,7 +216,7 @@ export type GetInvitationsParams = ClerkPaginationParams<{ /** @document */ export type GetMembershipRequestParams = ClerkPaginationParams<{ /** - * The status of the membership requests to retrieve. + * The status of the membership requests to get. */ status?: OrganizationInvitationStatus; }>; diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index 918cd593233..430a798c3eb 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -280,7 +280,7 @@ export interface SessionResource extends ClerkResource { */ touch: (params?: SessionTouchParams) => Promise; /** - * Retrieves the current user's [session token](https://clerk.com/docs/guides/sessions/session-tokens) or a [custom JWT template](https://clerk.com/docs/guides/sessions/jwt-templates). + * Gets the current user's [session token](https://clerk.com/docs/guides/sessions/session-tokens) or a [custom JWT template](https://clerk.com/docs/guides/sessions/jwt-templates). * * This method uses a cache so a network request will only be made if the token in memory has expired. The TTL for a Clerk token is one minute. It retries on transient failures (e.g. network errors); when the browser is offline and retries are exhausted, it throws `ClerkOfflineError`. * diff --git a/packages/shared/src/types/user.ts b/packages/shared/src/types/user.ts index 17f92d96c31..203dca90aab 100644 --- a/packages/shared/src/types/user.ts +++ b/packages/shared/src/types/user.ts @@ -261,7 +261,7 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { */ isPrimaryIdentification: (ident: EmailAddressResource | PhoneNumberResource | Web3WalletResource) => boolean; /** - * Retrieves all **active** sessions for this user. This method uses a cache so a network request will only be triggered only once. + * Gets all **active** sessions for this user. This method uses a cache so a network request will only be triggered only once. * @returns An array of [`SessionWithActivities`](https://clerk.com/docs/reference/types/session-with-activities) objects. */ getSessions: () => Promise; @@ -278,21 +278,21 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { createExternalAccount: (params: CreateExternalAccountParams) => Promise; getOrganizationMemberships: GetOrganizationMemberships; /** - * Retrieves a list of Organization invitations for the user. + * Gets a list of Organization invitations for the user. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`UserOrganizationInvitation`](https://clerk.com/docs/reference/types/user-organization-invitation) objects. */ getOrganizationInvitations: ( params?: GetUserOrganizationInvitationsParams, ) => Promise>; /** - * Retrieves a list of Organization suggestions for the user. + * Gets a list of Organization suggestions for the user. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationSuggestionResource`](https://clerk.com/docs/reference/types/organization-suggestion) objects. */ getOrganizationSuggestions: ( params?: GetUserOrganizationSuggestionsParams, ) => Promise>; /** - * Retrieves organization creation defaults for the current user. + * Gets organization creation defaults for the current user. * @returns A [`OrganizationCreationDefaultsResource`](https://clerk.com/docs/reference/types/organization-creation-defaults) object. */ getOrganizationCreationDefaults: () => Promise; @@ -479,7 +479,7 @@ export type GetUserOrganizationSuggestionsParams = ClerkPaginationParams<{ export type GetUserOrganizationMembershipParams = ClerkPaginationParams; /** - * Retrieves a list of Organization memberships for the user. + * Gets a list of Organization memberships for the user. * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) objects. */ export type GetOrganizationMemberships = (