diff --git a/.changeset/typedoc-marker-slicer.md b/.changeset/typedoc-marker-slicer.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/typedoc-marker-slicer.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.typedoc/__tests__/__snapshots__/api-key-resource-methods-create.mdx b/.typedoc/__tests__/__snapshots__/api-key-resource-methods-create.mdx index 8a60ae22d3d..d1d6a9d49c3 100644 --- a/.typedoc/__tests__/__snapshots__/api-key-resource-methods-create.mdx +++ b/.typedoc/__tests__/__snapshots__/api-key-resource-methods-create.mdx @@ -18,4 +18,4 @@ function create(params: CreateAPIKeyParams): Promise | `description?` | `string` | The description of the API key. | | `name` | `string` | The name of the API key. | | `secondsUntilExpiration?` | `number` | The number of seconds until the API key expires. Set to `null` or omit to create a key that never expires. | -| `subject?` | `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 user or organization ID to associate the API key with. If not provided, defaults to the [Active Organization](!active-organization), then the current user. | diff --git a/.typedoc/__tests__/__snapshots__/clerk-properties.mdx b/.typedoc/__tests__/__snapshots__/clerk-properties.mdx index 571d450cf7a..0db740e0a02 100644 --- a/.typedoc/__tests__/__snapshots__/clerk-properties.mdx +++ b/.typedoc/__tests__/__snapshots__/clerk-properties.mdx @@ -4,11 +4,11 @@ | `billing` | [`BillingNamespace`](/docs/reference/objects/billing) | The `Billing` object used for managing billing. | | `client` | undefined \| [ClientResource](/docs/reference/objects/client) | The `Client` object for the current window. | | `domain` | `string` | The current Clerk app's domain. Prefixed with `clerk.` on production if not already prefixed. Returns `""` when ran on the server. | -| `instanceType` | undefined \| "production" \| "development" | Indicates whether the Clerk instance is running in a production or development environment. | -| `isSatellite` | `boolean` | Indicates whether the instance is a satellite app. | -| `isSignedIn` | `boolean` | Indicates whether the current user has a valid signed-in client session. | -| `isStandardBrowser` | undefined \| boolean | Indicates whether 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. | -| `loaded` | `boolean` | Indicates whether 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"`. | +| `instanceType` | undefined \| "production" \| "development" | Whether the Clerk instance is running in a production or development environment. | +| `isSatellite` | `boolean` | Whether the instance is a satellite app. | +| `isSignedIn` | `boolean` | Whether the current user has a valid signed-in client session. | +| `isStandardBrowser` | undefined \| boolean | Whether 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. | +| `loaded` | `boolean` | Whether 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"`. | | `oauthApplication` | [`OAuthApplicationNamespace`](/docs/reference/types/oauth-application) | OAuth application helpers (e.g. consent metadata for custom consent UIs). | | `organization` | undefined \| null \| [OrganizationResource](/docs/reference/objects/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. | | `proxyUrl` | undefined \| string | **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`). | @@ -20,8 +20,8 @@ | `session` | undefined \| null \| [SignedInSessionResource](/docs/reference/objects/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`. | | `status` | "error" \| "degraded" \| "loading" \| "ready" | The status of the `Clerk` instance. Possible values are: | | `telemetry` | undefined \| \{ isDebug: boolean; isEnabled: boolean; record: void; recordLog: void; \} | [Telemetry](/docs/guides/how-clerk-works/security/clerk-telemetry) configuration. | -| `telemetry.isDebug` | `boolean` | If `true`, telemetry events are only logged to the console and not sent to Clerk. | -| `telemetry.isEnabled` | `boolean` | Indicates whether telemetry is enabled. | +| `telemetry.isDebug` | `boolean` | Whether telemetry events are only logged to the console and not sent to Clerk. | +| `telemetry.isEnabled` | `boolean` | Whether telemetry is enabled. | | `uiVersion` | undefined \| string | The version of `@clerk/ui` that is currently loaded, or `undefined` if the prebuilt UI has not been loaded yet. | | `user` | undefined \| null \| [UserResource](/docs/reference/objects/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. | | `version` | undefined \| string | The Clerk SDK version number. | diff --git a/.typedoc/__tests__/__snapshots__/clerk.mdx b/.typedoc/__tests__/__snapshots__/clerk.mdx index 8ea6508d37a..2bc7c984f96 100644 --- a/.typedoc/__tests__/__snapshots__/clerk.mdx +++ b/.typedoc/__tests__/__snapshots__/clerk.mdx @@ -1 +1,1699 @@ 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. + +## `addListener()` + +Register a listener that triggers a callback whenever a change in the [`Client`](/docs/reference/objects/client), [`Session`](/docs/reference/objects/session), [`User`](/docs/reference/objects/user), or [`Organization`](/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`. + +```typescript +function addListener( + callback: (emission: { + client: ClientResource; + organization?: null | OrganizationResource; + session?: null | SignedInSessionResource; + user?: null | UserResource; + }) => void, + options?: { skipInitialEmit?: boolean }, +): UnsubscribeCallback; +``` + +### Parameters + +| Parameter | Type | Description | +| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| `callback` | (emission: \{ client: [ClientResource](/docs/reference/objects/client); organization?: null \| [OrganizationResource](/docs/reference/objects/organization); session?: null \| [SignedInSessionResource](/docs/reference/objects/session); user?: null \| [UserResource](/docs/reference/objects/user); \}) => void | The function to call when Clerk resources change. | +| `options?` | \{ skipInitialEmit?: boolean; \} | Optional configuration. | +| `options?.skipInitialEmit?` | `boolean` | Whether the callback will not be called immediately after registration. Defaults to `false`. | + +### Returns + +`UnsubscribeCallback` — - An `UnsubscribeCallback` function that can be used to remove the listener from the `Clerk` object. + +## `authenticateWithBase()` + +Starts a sign-in flow that uses Base to authenticate the user using their Web3 wallet address. + +```typescript +function authenticateWithBase( + params?: AuthenticateWithBaseParams, +): Promise; +``` + +### `AuthenticateWithBaseParams` + +| Property | Type | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | +| `legalAccepted?` | `boolean` | A boolean indicating whether the user has agreed to the [legal compliance](/docs/guides/secure/legal-compliance) documents. | +| `redirectUrl?` | `string` | The full URL or path to navigate to after a successful sign-in or sign-up. | +| `signUpContinueUrl?` | `string` | The URL to navigate to if the sign-up process is missing user information. | +| `unsafeMetadata?` | [SignUpUnsafeMetadata](/docs/reference/types/metadata#sign-up-unsafe-metadata) | 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. | + +### Returns + +`Promise`\<`unknown`\> + +## `authenticateWithCoinbaseWallet()` + +Starts a sign-in flow that uses Coinbase Smart Wallet to authenticate the user using their Coinbase wallet address. + +```typescript +function authenticateWithCoinbaseWallet( + params?: AuthenticateWithCoinbaseWalletParams, +): Promise; +``` + +### `AuthenticateWithCoinbaseWalletParams` + +| Property | Type | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | +| `legalAccepted?` | `boolean` | A boolean indicating whether the user has agreed to the [legal compliance](/docs/guides/secure/legal-compliance) documents. | +| `redirectUrl?` | `string` | The full URL or path to navigate to after a successful sign-in or sign-up. | +| `signUpContinueUrl?` | `string` | The URL to navigate to if the sign-up process is missing user information. | +| `unsafeMetadata?` | [SignUpUnsafeMetadata](/docs/reference/types/metadata#sign-up-unsafe-metadata) | 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. | + +### Returns + +`Promise`\<`unknown`\> + +## `authenticateWithGoogleOneTap()` + +Authenticates user using a Google token generated from Google identity services. + +```typescript +function authenticateWithGoogleOneTap( + params: AuthenticateWithGoogleOneTapParams, +): Promise; +``` + +### `AuthenticateWithGoogleOneTapParams` + +| Property | Type | Description | +| ------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------- | +| `legalAccepted?` | `boolean` | A boolean indicating whether the user has agreed to the [legal compliance](/docs/guides/secure/legal-compliance) documents. | +| `token` | `string` | The Google credential token from the Google Identity Services response. | + +### Returns + +`Promise`\<[SignInResource](/docs/reference/objects/sign-in) \| [SignUpResource](/docs/reference/objects/sign-up)\> + +## `authenticateWithMetamask()` + +Starts a sign-in flow that uses MetaMask to authenticate the user using their Metamask wallet address. + +```typescript +function authenticateWithMetamask( + params?: AuthenticateWithMetamaskParams, +): Promise; +``` + +### `AuthenticateWithMetamaskParams` + +| Property | Type | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | +| `legalAccepted?` | `boolean` | A boolean indicating whether the user has agreed to the [legal compliance](/docs/guides/secure/legal-compliance) documents. | +| `redirectUrl?` | `string` | The full URL or path to navigate to after a successful sign-in or sign-up. | +| `signUpContinueUrl?` | `string` | The URL to navigate to if the sign-up process is missing user information. | +| `unsafeMetadata?` | [SignUpUnsafeMetadata](/docs/reference/types/metadata#sign-up-unsafe-metadata) | 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. | + +### Returns + +`Promise`\<`unknown`\> + +## `authenticateWithOKXWallet()` + +Starts a sign-in flow that uses OKX Wallet to authenticate the user using their OKX wallet address. + +```typescript +function authenticateWithOKXWallet( + params?: AuthenticateWithOKXWalletParams, +): Promise; +``` + +### `AuthenticateWithOKXWalletParams` + +| Property | Type | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | +| `legalAccepted?` | `boolean` | A boolean indicating whether the user has agreed to the [legal compliance](/docs/guides/secure/legal-compliance) documents. | +| `redirectUrl?` | `string` | The full URL or path to navigate to after a successful sign-in or sign-up. | +| `signUpContinueUrl?` | `string` | The URL to navigate to if the sign-up process is missing user information. | +| `unsafeMetadata?` | [SignUpUnsafeMetadata](/docs/reference/types/metadata#sign-up-unsafe-metadata) | 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. | + +### Returns + +`Promise`\<`unknown`\> + +## `authenticateWithSolana()` + +Starts a sign-in flow that uses Solana to authenticate the user using their Solana wallet address. + +```typescript +function authenticateWithSolana( + params: AuthenticateWithSolanaParams, +): Promise; +``` + +### `AuthenticateWithSolanaParams` + +| Property | Type | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | +| `legalAccepted?` | `boolean` | A boolean indicating whether the user has agreed to the [legal compliance](/docs/guides/secure/legal-compliance) documents. | +| `redirectUrl?` | `string` | The full URL or path to navigate to after a successful sign-in or sign-up. | +| `signUpContinueUrl?` | `string` | The URL to navigate to if the sign-up process is missing user information. | +| `unsafeMetadata?` | [SignUpUnsafeMetadata](/docs/reference/types/metadata#sign-up-unsafe-metadata) | 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. | +| `walletName` | `string` | The name of the Solana wallet to use for authentication. | + +### Returns + +`Promise`\<`unknown`\> + +## `authenticateWithWeb3()` + +Starts a sign-in flow that uses a Web3 Wallet browser extension to authenticate the user using their Ethereum wallet address. + +```typescript +function authenticateWithWeb3( + params: ClerkAuthenticateWithWeb3Params, +): Promise; +``` + +### `ClerkAuthenticateWithWeb3Params` + +| Property | Type | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | +| `legalAccepted?` | `boolean` | A boolean indicating whether the user has agreed to the [legal compliance](/docs/guides/secure/legal-compliance) documents. | +| `redirectUrl?` | `string` | The full URL or path to navigate to after a successful sign-in or sign-up. | +| `secondFactorUrl?` | `string` | The URL to navigate to if [second factor](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) is required. | +| `signUpContinueUrl?` | `string` | The URL to navigate to if the sign-up process is missing user information. | +| `strategy` | "web3_metamask_signature" \| "web3_base_signature" \| "web3_coinbase_wallet_signature" \| "web3_okx_wallet_signature" \| "web3_solana_signature" | The strategy to use for the sign-in flow. | +| `unsafeMetadata?` | [SignUpUnsafeMetadata](/docs/reference/types/metadata#sign-up-unsafe-metadata) | 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. | +| `walletName?` | `string` | The name of the wallet to use for authentication. | + +### Returns + +`Promise`\<`unknown`\> + +## `buildAfterMultiSessionSingleSignOutUrl()` + +Returns the configured `afterMultiSessionSingleSignOutUrl` of the instance. + +```typescript +function buildAfterMultiSessionSingleSignOutUrl(): string; +``` + +### Returns + +`string` + +## `buildAfterSignInUrl()` + +Returns the configured `afterSignInUrl` of the instance. + +```typescript +function buildAfterSignInUrl(params?: { params?: URLSearchParams }): string; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------------------------------------------- | ----------------------------------------------- | +| `params?` | \{ params?: URLSearchParams; \} | Optional query parameters to append to the URL. | + +### Returns + +`string` + +## `buildAfterSignOutUrl()` + +Returns the configured `afterSignOutUrl` of the instance. + +```typescript +function buildAfterSignOutUrl(): string; +``` + +### Returns + +`string` + +## `buildAfterSignUpUrl()` + +Returns the configured `afterSignUpUrl` of the instance. + +```typescript +function buildAfterSignUpUrl(params?: { params?: URLSearchParams }): string; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------------------------------------------- | ----------------------------------------------- | +| `params?` | \{ params?: URLSearchParams; \} | Optional query parameters to append to the URL. | + +### Returns + +`string` + +## `buildCreateOrganizationUrl()` + +Returns the configured URL where [``](/docs/reference/components/organization/create-organization) is mounted or a custom create-organization page is rendered. + +```typescript +function buildCreateOrganizationUrl(): string; +``` + +### Returns + +`string` + +## `buildNewSubscriptionRedirectUrl()` + +Returns the configured `newSubscriptionRedirectUrl` of the instance. + +```typescript +function buildNewSubscriptionRedirectUrl(): string; +``` + +### Returns + +`string` + +## `buildOrganizationProfileUrl()` + +Returns the configured URL where [``](/docs/reference/components/organization/organization-profile) is mounted or a custom organization-profile page is rendered. + +```typescript +function buildOrganizationProfileUrl(): string; +``` + +### Returns + +`string` + +## `buildSignInUrl()` + +Returns the configured URL where [``](/docs/reference/components/authentication/sign-in) is mounted or a custom sign-in page is rendered. + +```typescript +function buildSignInUrl(opts?: RedirectOptions): string; +``` + +### `RedirectOptions` + +| Property | Type | Description | +| ------------------------------------------------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `redirectUrl?` | string \| null | Full URL or path to navigate to after a successful action. | +| `signInFallbackRedirectUrl?` | string \| null | 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](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. Defaults to `'/'`. | +| `signInForceRedirectUrl?` | string \| null | If provided, this URL will always be redirected to after the user signs in. It's recommended to use the [environment variable](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. | +| `signUpFallbackRedirectUrl?` | string \| null | 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](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. Defaults to `'/'`. | +| `signUpForceRedirectUrl?` | string \| null | If provided, this URL will always be redirected to after the user signs up. It's recommended to use the [environment variable](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. | + +### Returns + +`string` + +## `buildSignUpUrl()` + +Returns the configured URL where [``](/docs/reference/components/authentication/sign-up) is mounted or a custom sign-up page is rendered. + +```typescript +function buildSignUpUrl(opts?: RedirectOptions): string; +``` + +### `RedirectOptions` + +| Property | Type | Description | +| ------------------------------------------------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `redirectUrl?` | string \| null | Full URL or path to navigate to after a successful action. | +| `signInFallbackRedirectUrl?` | string \| null | 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](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. Defaults to `'/'`. | +| `signInForceRedirectUrl?` | string \| null | If provided, this URL will always be redirected to after the user signs in. It's recommended to use the [environment variable](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. | +| `signUpFallbackRedirectUrl?` | string \| null | 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](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. Defaults to `'/'`. | +| `signUpForceRedirectUrl?` | string \| null | If provided, this URL will always be redirected to after the user signs up. It's recommended to use the [environment variable](/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. | + +### Returns + +`string` + +## `buildTasksUrl()` + +Returns the configured URL where [session tasks](/docs/guides/configure/session-tasks) are mounted. + +```typescript +function buildTasksUrl(): string; +``` + +### Returns + +`string` + +## `buildUrlWithAuth()` + +Decorates the provided URL with the auth token for development instances. + +```typescript +function buildUrlWithAuth(to: string): string; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------- | ---------------------------------- | +| `to` | `string` | The route to create a URL towards. | + +### Returns + +`string` + +## `buildUserProfileUrl()` + +Returns the configured URL where [``](/docs/reference/components/user/user-profile) is mounted or a custom user-profile page is rendered. + +```typescript +function buildUserProfileUrl(): string; +``` + +### Returns + +`string` + +## `buildWaitlistUrl()` + +Returns the configured URL where [``](/docs/reference/components/authentication/waitlist) is mounted or a custom waitlist page is rendered. + +```typescript +function buildWaitlistUrl(opts?: { + initialValues?: Record; +}): string; +``` + +### Parameters + +| Parameter | Type | Description | +| ---------------------- | ------------------------------------------------------------ | -------------------------------------------- | +| `opts?` | \{ initialValues?: Record\; \} | Options to control the waitlist URL. | +| `opts?.initialValues?` | `Record`\<`string`, `string`\> | Initial values to prefill the waitlist form. | + +### Returns + +`string` + +## `closeCreateOrganization()` + +Closes the Clerk CreateOrganization modal. + +```typescript +function closeCreateOrganization(): void; +``` + +### Returns + +`void` + +## `closeGoogleOneTap()` + +Opens the Google One Tap component. +If the component is not already open, results in a noop. + +```typescript +function closeGoogleOneTap(): void; +``` + +### Returns + +`void` + +## `closeOrganizationProfile()` + +Closes the Clerk OrganizationProfile modal. + +```typescript +function closeOrganizationProfile(): void; +``` + +### Returns + +`void` + +## `closeSignIn()` + +Closes the Clerk SignIn modal. + +```typescript +function closeSignIn(): void; +``` + +### Returns + +`void` + +## `closeSignUp()` + +Closes the Clerk SignUp modal. + +```typescript +function closeSignUp(): void; +``` + +### Returns + +`void` + +## `closeUserProfile()` + +Closes the Clerk UserProfile modal. + +```typescript +function closeUserProfile(): void; +``` + +### Returns + +`void` + +## `closeWaitlist()` + +Closes the Clerk Waitlist modal. + +```typescript +function closeWaitlist(): void; +``` + +### Returns + +`void` + +## `createOrganization()` + +Creates an Organization programmatically, adding the current user as admin. Returns an [`Organization`](/docs/reference/objects/organization) object. + +> [!NOTE] +> For React-based apps, consider using the [``](/docs/reference/components/organization/create-organization) component. + +```typescript +function createOrganization( + params: CreateOrganizationParams, +): Promise; +``` + +### `CreateOrganizationParams` + +| Property | Type | Description | +| ------------------------- | -------- | ----------------------------- | +| `name` | `string` | The name of the Organization. | +| `slug?` | `string` | The slug of the Organization. | + +### Returns + +`Promise`\<[`OrganizationResource`](/docs/reference/objects/organization)\> + +## `getOrganization()` + +Gets a single [Organization](/docs/reference/objects/organization) by ID. + +```typescript +function getOrganization(organizationId: string): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| ---------------- | -------- | ---------------------------------- | +| `organizationId` | `string` | The ID of the Organization to get. | + +### Returns + +`Promise`\<[`OrganizationResource`](/docs/reference/objects/organization)\> + +## `handleEmailLinkVerification()` + +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. + +```typescript +function handleEmailLinkVerification( + params: { + onVerifiedOnOtherDevice?: () => void; + redirectUrl?: string; + redirectUrlComplete?: string; + }, + customNavigate?: (to: string) => Promise, +): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| --------------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `params` | \{ onVerifiedOnOtherDevice?: () => void; redirectUrl?: string; redirectUrlComplete?: string; \} | 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. | +| `params.onVerifiedOnOtherDevice?` | () => void | Callback function to be executed after successful email link verification on another device. | +| `params.redirectUrl?` | `string` | The full URL to navigate to after successful email link verification on the same device, but without completing sign-in or sign-up. | +| `params.redirectUrlComplete?` | `string` | The full URL to navigate to after successful email link verification on completed sign-up or sign-in on the same device. | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | + +### Returns + +`Promise`\<`unknown`\> + +## `handleGoogleOneTapCallback()` + +Completes a Google One Tap redirection flow started by [`authenticateWithGoogleOneTap()`](/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. + +```typescript +function handleGoogleOneTapCallback( + signInOrUp: SignInResource | SignUpResource, + params: HandleOAuthCallbackParams, + customNavigate?: (to: string) => Promise, +): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| ----------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `signInOrUp` | [SignInResource](/docs/reference/objects/sign-in) \| [SignUpResource](/docs/reference/objects/sign-up) | The resource returned from the initial `authenticateWithGoogleOneTap()` call (before redirect). | +| `params` | [`HandleOAuthCallbackParams`](/docs/reference/types/handle-o-auth-callback-params) | Additional props that define where the user will be redirected to at the end of a successful Google One Tap flow. | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | + +### Returns + +`Promise`\<`unknown`\> + +## `handleRedirectCallback()` + +Completes a custom OAuth or SAML redirect flow that was started by calling [`SignIn.authenticateWithRedirect(params)`](/docs/reference/objects/sign-in) or [`SignUp.authenticateWithRedirect(params)`](/docs/reference/objects/sign-up). + +```typescript +function handleRedirectCallback( + params: HandleOAuthCallbackParams, + customNavigate?: (to: string) => Promise, +): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| ----------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `params` | [`HandleOAuthCallbackParams`](/docs/reference/types/handle-o-auth-callback-params) | Additional props that define where the user will be redirected to at the end of a successful OAuth or SAML flow. | +| `customNavigate?` | (to: string) => Promise\ | A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. | + +### Returns + +`Promise`\<`unknown`\> + +## `handleUnauthenticated()` + +Handles a 401 response from the Frontend API by refreshing the [`Client`](/docs/reference/objects/client) and [`Session`](/docs/reference/objects/session) object accordingly. + +```typescript +function handleUnauthenticated(): Promise; +``` + +### Returns + +`Promise`\<`unknown`\> + +## `joinWaitlist()` + +Create a new waitlist entry programmatically. Requires that you set your app's sign-up mode to [**Waitlist**](/docs/guides/secure/restricting-access#waitlist) in the Clerk Dashboard. + +```typescript +function joinWaitlist(params: JoinWaitlistParams): Promise; +``` + +### `JoinWaitlistParams` + +| Property | Type | Description | +| ---------------------------------------- | -------- | ----------------------------------------------------- | +| `emailAddress` | `string` | The email address of the user to add to the waitlist. | + +### Returns + +`Promise`\<`WaitlistResource`\> + +## `mountAPIKeys()` + +Mount an API keys component at the target element. + +```typescript +function mountAPIKeys(targetNode: HTMLDivElement, props?: APIKeysProps): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | --------------------------------------- | -------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target to mount the APIKeys component. | +| `props?` | [`APIKeysProps`](../api-keys-props.mdx) | Configuration parameters. | + +### Returns + +`void` + +## `mountCreateOrganization()` + +Mount a CreateOrganization component at the target element. + +```typescript +function mountCreateOrganization( + targetNode: HTMLDivElement, + props?: CreateOrganizationProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ------------------------------------------------------------- | ------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target to mount the CreateOrganization component. | +| `props?` | [`CreateOrganizationProps`](../create-organization-props.mdx) | Configuration parameters. | + +### Returns + +`void` + +## `mountOAuthConsent()` + +Mounts a OAuth consent component at the target element. + +```typescript +function mountOAuthConsent( + targetNode: HTMLDivElement, + oauthConsentProps?: OAuthConsentProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| -------------------- | ------------------- | ------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to mount the OAuth consent component. | +| `oauthConsentProps?` | `OAuthConsentProps` | OAuth consent configuration parameters. | + +### Returns + +`void` + +## `mountOrganizationList()` + +Mount an Organization list component at the target element. + +```typescript +function mountOrganizationList( + targetNode: HTMLDivElement, + props?: OrganizationListProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | --------------------------------------------------------- | ----------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target to mount the OrganizationList component. | +| `props?` | [`OrganizationListProps`](../organization-list-props.mdx) | Configuration parameters. | + +### Returns + +`void` + +## `mountOrganizationProfile()` + +Mount an Organization profile component at the target element. + +```typescript +function mountOrganizationProfile( + targetNode: HTMLDivElement, + props?: OrganizationProfileProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | --------------------------------------------------------------- | -------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target to mount the OrganizationProfile component. | +| `props?` | [`OrganizationProfileProps`](../organization-profile-props.mdx) | Configuration parameters. | + +### Returns + +`void` + +## `mountOrganizationSwitcher()` + +Mount an Organization switcher component at the target element. + +```typescript +function mountOrganizationSwitcher( + targetNode: HTMLDivElement, + props?: OrganizationSwitcherProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ----------------------------------------------------------------- | --------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target to mount the OrganizationSwitcher component. | +| `props?` | [`OrganizationSwitcherProps`](../organization-switcher-props.mdx) | Configuration parameters. | + +### Returns + +`void` + +## `mountPricingTable()` + +Mounts a pricing table component at the target element. + +```typescript +function mountPricingTable( + targetNode: HTMLDivElement, + props?: PricingTableProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ------------------------------------------------- | ------------------------------------------------ | +| `targetNode` | `HTMLDivElement` | Target node to mount the PricingTable component. | +| `props?` | [`PricingTableProps`](../pricing-table-props.mdx) | configuration parameters. | + +### Returns + +`void` + +## `mountSignIn()` + +Mounts a sign in flow component at the target element. + +```typescript +function mountSignIn( + targetNode: HTMLDivElement, + signInProps?: SignInProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| -------------- | ------------------------------------- | ------------------------------------------ | +| `targetNode` | `HTMLDivElement` | Target node to mount the SignIn component. | +| `signInProps?` | [`SignInProps`](../sign-in-props.mdx) | sign in configuration parameters. | + +### Returns + +`void` + +## `mountSignUp()` + +Mounts a sign up flow component at the target element. + +```typescript +function mountSignUp( + targetNode: HTMLDivElement, + signUpProps?: SignUpProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| -------------- | ------------------------------------- | ------------------------------------------ | +| `targetNode` | `HTMLDivElement` | Target node to mount the SignUp component. | +| `signUpProps?` | [`SignUpProps`](../sign-up-props.mdx) | sign up configuration parameters. | + +### Returns + +`void` + +## `mountTaskChooseOrganization()` + +Mounts a TaskChooseOrganization component at the target element. + +```typescript +function mountTaskChooseOrganization( + targetNode: HTMLDivElement, + props?: TaskChooseOrganizationProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------------------------------------------------------------- | ---------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to mount the TaskChooseOrganization component. | +| `props?` | [`TaskChooseOrganizationProps`](../task-choose-organization-props.mdx) | configuration parameters. | + +### Returns + +`void` + +## `mountTaskResetPassword()` + +Mounts a TaskResetPassword component at the target element. + +```typescript +function mountTaskResetPassword( + targetNode: HTMLDivElement, + props?: TaskResetPasswordProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ------------------------------------------------------------ | ----------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to mount the TaskResetPassword component. | +| `props?` | [`TaskResetPasswordProps`](../task-reset-password-props.mdx) | configuration parameters. | + +### Returns + +`void` + +## `mountTaskSetupMFA()` + +Mounts a TaskSetupMFA component at the target element. +This component allows users to set up multi-factor authentication. + +```typescript +function mountTaskSetupMFA( + targetNode: HTMLDivElement, + props?: TaskSetupMFAProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | -------------------------------------------------- | ------------------------------------------------ | +| `targetNode` | `HTMLDivElement` | Target node to mount the TaskSetupMFA component. | +| `props?` | [`TaskSetupMFAProps`](../task-setup-mfa-props.mdx) | configuration parameters. | + +### Returns + +`void` + +## `mountUserAvatar()` + +Mount a user avatar component at the target element. + +```typescript +function mountUserAvatar( + targetNode: HTMLDivElement, + userAvatarProps?: UserAvatarProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------------ | ----------------- | ---------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to mount the UserAvatar component. | +| `userAvatarProps?` | `UserAvatarProps` | - | + +### Returns + +`void` + +## `mountUserButton()` + +Mount a user button component at the target element. + +```typescript +function mountUserButton( + targetNode: HTMLDivElement, + userButtonProps?: UserButtonProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------------ | ----------------- | ---------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to mount the UserButton component. | +| `userButtonProps?` | `UserButtonProps` | User button configuration parameters. | + +### Returns + +`void` + +## `mountUserProfile()` + +Mount a user profile component at the target element. + +```typescript +function mountUserProfile( + targetNode: HTMLDivElement, + userProfileProps?: UserProfileProps, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------------- | ----------------------------------------------- | ------------------------------------------ | +| `targetNode` | `HTMLDivElement` | Target to mount the UserProfile component. | +| `userProfileProps?` | [`UserProfileProps`](../user-profile-props.mdx) | User profile configuration parameters. | + +### Returns + +`void` + +## `mountWaitlist()` + +Mount a waitlist at the target element. + +```typescript +function mountWaitlist(targetNode: HTMLDivElement, props?: WaitlistProps): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------------------------------- | --------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target to mount the Waitlist component. | +| `props?` | [`WaitlistProps`](../waitlist-props.mdx) | Configuration parameters. | + +### Returns + +`void` + +## `navigate()` + +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. + +```typescript +function navigate( + to: string, + options?: { metadata?: RouterMetadata; replace?: boolean }, +): Promise | void; +``` + +### Parameters + +| Parameter | Type | Description | +| -------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `to` | `string` | The URL or relative path to navigate to. | +| `options?` | \{ metadata?: RouterMetadata; replace?: boolean; \} | Optional configuration. | +| `options?.metadata?` | `RouterMetadata` | Router metadata. | +| `options?.replace?` | `boolean` | Whether to replace the current history entry instead of pushing a new one. | + +### Returns + +Promise\ \| void + +## `off()` + +Removes an event handler for a specific Clerk event. + +```typescript +function off(event: E, handler: EventHandler): void; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | --------------------- | ----------------------------------- | +| `event` | `E` | The event name to unsubscribe from. | +| `handler` | `EventHandler`\<`E`\> | The callback function to remove. | + +### Returns + +`void` + +## `on()` + +Registers an event handler for a specific Clerk event. + +```typescript +function on( + event: E, + handler: EventHandler, + opt?: { notify: boolean }, +): void; +``` + +### Parameters + +| Parameter | Type | Description | +| -------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `event` | `E` | The event name to subscribe to. | +| `handler` | `EventHandler`\<`E`\> | The callback function to execute when the event is dispatched. | +| `opt?` | \{ notify: boolean; \} | Optional configuration. | +| `opt?.notify?` | `boolean` | If true and the event was previously dispatched, handler will be called immediately with the latest payload. | + +### Returns + +`void` + +## `openCreateOrganization()` + +Opens the Clerk CreateOrganization modal. + +```typescript +function openCreateOrganization(props?: CreateOrganizationModalProps): void; +``` + +### `CreateOrganizationModalProps` + +| Property | Type | Description | +| --------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `getContainer?` | () => HTMLElement \| null | Function that returns the container element where portals should be rendered. This allows Clerk components to render inside external dialogs/popovers (e.g., Radix Dialog, React Aria Components) instead of document.body. | + +### Returns + +`void` + +## `openGoogleOneTap()` + +Opens the Google One Tap component. + +```typescript +function openGoogleOneTap(props?: GoogleOneTapProps): void; +``` + +### `GoogleOneTapProps` + +| Property | Type | Description | +| --------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `appearance?` | [`ClerkAppearanceTheme`](../clerk-appearance-theme.mdx) | 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` configuration (if one is provided). See the [`Appearance`](/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. | +| `cancelOnTapOutside?` | `boolean` | Whether to cancel the Google One Tap request if a user clicks outside the prompt. Defaults to `true`. | +| `fedCmSupport?` | `boolean` | FedCM enables more private sign-in flows without requiring the use of third-party cookies. The browser controls user settings, displays user prompts, and only contacts an Identity Provider such as Google after explicit user consent is given. Backwards compatible with browsers that still support third-party cookies. Defaults to `true`. | +| `itpSupport?` | `boolean` | Enables upgraded One Tap UX on ITP browsers. Turning this options off, would hide any One Tap UI in such browsers. Defaults to `true`. | + +### Returns + +`void` + +## `openOrganizationProfile()` + +Opens the Clerk OrganizationProfile modal. + +```typescript +function openOrganizationProfile(props?: OrganizationProfileModalProps): void; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------------------------------- | ------------------------------------------------------------------------ | +| `props?` | `OrganizationProfileModalProps` | Optional props that will be passed to the OrganizationProfile component. | + +### Returns + +`void` + +## `openSignIn()` + +Opens the Clerk SignIn component in a modal. + +```typescript +function openSignIn(props?: SignInModalProps): void; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------------------ | ------------------------------------------ | +| `props?` | `SignInModalProps` | Optional sign in configuration parameters. | + +### Returns + +`void` + +## `openSignUp()` + +Opens the Clerk SignUp component in a modal. + +```typescript +function openSignUp(props?: SignUpModalProps): void; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ------------------ | ----------------------------------------------------------- | +| `props?` | `SignUpModalProps` | Optional props that will be passed to the SignUp component. | + +### Returns + +`void` + +## `openUserProfile()` + +Opens the Clerk UserProfile modal. + +```typescript +function openUserProfile(props?: UserProfileModalProps): void; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ----------------------- | ---------------------------------------------------------------- | +| `props?` | `UserProfileModalProps` | Optional props that will be passed to the UserProfile component. | + +### Returns + +`void` + +## `openWaitlist()` + +Opens the Clerk Waitlist modal. + +```typescript +function openWaitlist(props?: WaitlistModalProps): void; +``` + +### `WaitlistModalProps` + +| Property | Type | Description | +| --------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `afterJoinWaitlistUrl?` | `string` | Full URL or path to navigate to after join waitlist. | +| `appearance?` | [`ClerkAppearanceTheme`](../clerk-appearance-theme.mdx) | 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` configuration (if one is provided). See the [`Appearance`](/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. | +| `getContainer?` | () => HTMLElement \| null | Function that returns the container element where portals should be rendered. This allows Clerk components to render inside external dialogs/popovers (e.g., Radix Dialog, React Aria Components) instead of document.body. | +| `signInUrl?` | `string` | Full URL or path where the SignIn component is mounted. | + +### Returns + +`void` + +## `redirectToAfterSignIn()` + +Redirects to the configured `afterSignIn` URL. + +```typescript +function redirectToAfterSignIn(): void; +``` + +### Returns + +`void` + +## `redirectToAfterSignOut()` + +Redirects to the configured `afterSignOut` URL. + +```typescript +function redirectToAfterSignOut(): void; +``` + +### Returns + +`void` + +## `redirectToAfterSignUp()` + +Redirects to the configured `afterSignUp` URL. + +```typescript +function redirectToAfterSignUp(): void; +``` + +### Returns + +`void` + +## `redirectToCreateOrganization()` + +Redirects to the configured URL where [``](/docs/reference/components/organization/create-organization) is mounted. This method uses the [`navigate()`](/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. + +```typescript +function redirectToCreateOrganization(): Promise; +``` + +### Returns + +`Promise`\<`unknown`\> + +## `redirectToOrganizationProfile()` + +Redirects to the configured URL where [``](/docs/reference/components/organization/organization-profile) is mounted. This method uses the [`navigate()`](/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. + +```typescript +function redirectToOrganizationProfile(): Promise; +``` + +### Returns + +`Promise`\<`unknown`\> + +## `redirectToSignIn()` + +Redirects to the sign-in URL, as configured in your application's instance settings. This method uses the [`navigate()`](/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. + +```typescript +function redirectToSignIn(opts?: SignInRedirectOptions): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ----------------------------------------------------------------------- | -------------------------------- | +| `opts?` | [SignInRedirectOptions](/docs/reference/types/sign-in-redirect-options) | Options to control the redirect. | + +### Returns + +`Promise`\<`unknown`\> + +## `redirectToSignUp()` + +Redirects to the sign-up URL, as configured in your application's instance settings. This method uses the [`navigate()`](/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. + +```typescript +function redirectToSignUp(opts?: SignUpRedirectOptions): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ----------------------------------------------------------------------- | -------------------------------- | +| `opts?` | [SignUpRedirectOptions](/docs/reference/types/sign-up-redirect-options) | Options to control the redirect. | + +### Returns + +`Promise`\<`unknown`\> + +## `redirectToTasks()` + +Redirects to the configured URL where [session tasks](/docs/reference/objects/session) are mounted. + +```typescript +function redirectToTasks(opts?: TasksRedirectOptions): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `opts?` | [TasksRedirectOptions](/docs/reference/types/redirect-options) | Options to control the redirect (e.g. redirect URL after tasks are complete). | + +### Returns + +`Promise`\<`unknown`\> + +## `redirectToUserProfile()` + +Redirects to the configured URL where [``](/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. + +```typescript +function redirectToUserProfile(): Promise; +``` + +### Returns + +`Promise`\<`unknown`\> + +## `redirectToWaitlist()` + +Redirects to the configured URL where [``](/docs/reference/components/authentication/waitlist) is mounted. + +```typescript +function redirectToWaitlist(): void; +``` + +### Returns + +`void` + +## `redirectWithAuth()` + +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. + +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. + +```typescript +function redirectWithAuth(to: string): Promise; +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | -------- | ----------------------- | +| `to` | `string` | The URL to redirect to. | + +### Returns + +`Promise`\<`unknown`\> + +## `setActive()` + +A method used to set the current session and/or Organization for the client. Accepts a [`SetActiveParams`](/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. + +```typescript +function setActive(setActiveParams: SetActiveParams): Promise; +``` + +### `SetActiveParams` + +| Property | Type | Description | +| ----------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `navigate?` | `SetActiveNavigate` | 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](/docs/reference/objects/clerk#using-the-navigate-parameter) for more details. | +| `organization?` | null \| string \| [OrganizationResource](/docs/reference/objects/organization) | The Organization resource or Organization ID/slug (string version) to be set as active in the current session. If `null`, the currently Active Organization is removed as active. | +| `redirectUrl?` | `string` | The full URL or path to redirect to just before the session and/or organization is set. | +| `session?` | null \| string \| [SignedInSessionResource](/docs/reference/objects/session) | The session resource or session ID (string version) to be set as active. If `null`, the current session is deleted. | + +### Returns + +`Promise`\<`void`\> + +## `signOut()` + +Signs out the current user on single-session instances, or all users on multi-session instances. + +```typescript +function signOut(options?: SignOutOptions): Promise; +``` + +### `SignOutOptions` + +| Property | Type | Description | +| --------------------------------------- | -------- | ------------------------------------------------------------------------------ | +| `redirectUrl?` | `string` | Specify a redirect URL to navigate to after sign-out is complete. | +| `sessionId?` | `string` | Specify a specific session to sign out. Useful for multi-session applications. | + +### Returns + +`Promise`\<`void`\> + +## `unmountAPIKeys()` + +Unmount an API keys component from the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountAPIKeys(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | -------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the APIKeys component from. | + +### Returns + +`void` + +## `unmountCreateOrganization()` + +Unmount the CreateOrganization component from the target node. + +```typescript +function unmountCreateOrganization(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ------------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the CreateOrganization component from. | + +### Returns + +`void` + +## `unmountOAuthConsent()` + +Unmounts a OAuth consent component from the target element. + +```typescript +function unmountOAuthConsent(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | -------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the OAuth consent component from. | + +### Returns + +`void` + +## `unmountOrganizationList()` + +Unmount the Organization list component from the target node.\* + +```typescript +function unmountOrganizationList(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ----------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the OrganizationList component from. | + +### Returns + +`void` + +## `unmountOrganizationProfile()` + +Unmount the Organization profile component from the target node. + +```typescript +function unmountOrganizationProfile(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | -------------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the OrganizationProfile component from. | + +### Returns + +`void` + +## `unmountOrganizationSwitcher()` + +Unmount the Organization switcher component from the target node.\* + +```typescript +function unmountOrganizationSwitcher(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | --------------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the OrganizationSwitcher component from. | + +### Returns + +`void` + +## `unmountPricingTable()` + +Unmount a pricing table component from the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountPricingTable(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the PricingTable component from. | + +### Returns + +`void` + +## `unmountSignIn()` + +Unmount a sign in flow component from the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountSignIn(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the SignIn component from. | + +### Returns + +`void` + +## `unmountSignUp()` + +Unmount a sign up flow component from the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountSignUp(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the SignUp component from. | + +### Returns + +`void` + +## `unmountTaskChooseOrganization()` + +Unmount a TaskChooseOrganization component from the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountTaskChooseOrganization(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ----------------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the TaskChooseOrganization component from. | + +### Returns + +`void` + +## `unmountTaskResetPassword()` + +Unmount a TaskResetPassword component from the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountTaskResetPassword(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ------------------------------------------------------------ | +| `targetNode` | `HTMLDivElement` | Target node to unmount the TaskResetPassword component from. | + +### Returns + +`void` + +## `unmountTaskSetupMFA()` + +Unmount a TaskSetupMFA component from the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountTaskSetupMFA(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ------------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the TaskSetupMFA component from. | + +### Returns + +`void` + +## `unmountUserAvatar()` + +Unmount a user avatar component at the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountUserAvatar(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ----------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the UserAvatar component from. | + +### Returns + +`void` + +## `unmountUserButton()` + +Unmount a user button component at the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountUserButton(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ----------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the UserButton component from. | + +### Returns + +`void` + +## `unmountUserProfile()` + +Unmount a user profile component at the target element. +If there is no component mounted at the target node, results in a noop. + +```typescript +function unmountUserProfile(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | ------------------------------------------------------ | +| `targetNode` | `HTMLDivElement` | Target node to unmount the UserProfile component from. | + +### Returns + +`void` + +## `unmountWaitlist()` + +Unmount the Waitlist component from the target node. + +```typescript +function unmountWaitlist(targetNode: HTMLDivElement): void; +``` + +### Parameters + +| Parameter | Type | Description | +| ------------ | ---------------- | --------------------------------------------------- | +| `targetNode` | `HTMLDivElement` | Target node to unmount the Waitlist component from. | + +### Returns + +`void` diff --git a/.typedoc/__tests__/__snapshots__/user-resource-properties.mdx b/.typedoc/__tests__/__snapshots__/user-resource-properties.mdx index 31a3f8a43a5..c6e180901fd 100644 --- a/.typedoc/__tests__/__snapshots__/user-resource-properties.mdx +++ b/.typedoc/__tests__/__snapshots__/user-resource-properties.mdx @@ -1,17 +1,17 @@ | Property | Type | Description | | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `backupCodeEnabled` | `boolean` | Indicates whether the user has enabled backup codes. | +| `backupCodeEnabled` | `boolean` | Whether the user has enabled backup codes. | | `createdAt` | null \| Date | The date and time when the user was created. | -| `createOrganizationEnabled` | `boolean` | Indicates whether the user can create organizations. | +| `createOrganizationEnabled` | `boolean` | Whether the user can create organizations. | | `createOrganizationsLimit` | null \| number | The maximum number of organizations the user can create. | -| `deleteSelfEnabled` | `boolean` | Indicates whether the user can delete their own account. | +| `deleteSelfEnabled` | `boolean` | Whether the user can delete their own account. | | `emailAddresses` | [EmailAddressResource](/docs/reference/types/email-address)[] | An array of all the `EmailAddress` objects associated with the user. Includes the primary. | | `enterpriseAccounts` | [EnterpriseAccountResource](/docs/reference/types/enterprise-account)[] | An array of all the `EnterpriseAccount` objects associated with the user via enterprise SSO. | | `externalAccounts` | [ExternalAccountResource](/docs/reference/types/external-account)[] | An array of all the `ExternalAccount` objects associated with the user via OAuth. | | `externalId` | null \| string | The user's ID as used in your external systems. Must be unique across your instance. | | `firstName` | null \| string | The user's first name. | | `fullName` | null \| string | The user's full name. | -| `hasImage` | `boolean` | 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` | Whether the user has uploaded an image or one was copied from OAuth. Returns `false` if Clerk is displaying an avatar for the user. | | `id` | `string` | The unique identifier of the user. | | `imageUrl` | `string` | Holds the default avatar or user's uploaded profile image. Compatible with Clerk's [Image Optimization](/docs/guides/development/image-optimization). | | `lastName` | null \| string | The user's last name. | @@ -19,7 +19,7 @@ | `legalAcceptedAt` | null \| Date | The date and time when the user accepted the legal compliance documents. `null` if [**Require express consent to legal documents**](/docs/guides/secure/legal-compliance) is not enabled. | | `organizationMemberships` | [OrganizationMembershipResource](/docs/reference/types/organization-membership)[] | An array of all the `OrganizationMembership` objects associated with the user. | | `passkeys` | [PasskeyResource](/docs/reference/types/passkey-resource)[] | An array of all the `Passkey` objects associated with the user. | -| `passwordEnabled` | `boolean` | Indicates whether the user has a password on their account. | +| `passwordEnabled` | `boolean` | Whether the user has a password on their account. | | `phoneNumbers` | [PhoneNumberResource](/docs/reference/types/phone-number)[] | An array of all the `PhoneNumber` objects associated with the user. Includes the primary. | | `primaryEmailAddress` | null \| [EmailAddressResource](/docs/reference/types/email-address) | The user's primary email address. | | `primaryEmailAddressId` | null \| string | The ID of the user's primary email address. | @@ -28,8 +28,8 @@ | `primaryWeb3Wallet` | null \| [Web3WalletResource](/docs/reference/types/web3-wallet) | The user's primary Web3 wallet. | | `primaryWeb3WalletId` | null \| string | The ID of the user's primary Web3 wallet. | | `publicMetadata` | [UserPublicMetadata](/docs/reference/types/metadata#user-public-metadata) | Metadata that can be read from the Frontend API and Backend API and can be set only from the Backend API. | -| `totpEnabled` | `boolean` | Indicates whether the user has enabled TOTP. | -| `twoFactorEnabled` | `boolean` | Indicates whether the user has enabled two-factor authentication. | +| `totpEnabled` | `boolean` | Whether the user has enabled TOTP. | +| `twoFactorEnabled` | `boolean` | Whether the user has enabled two-factor authentication. | | `unsafeMetadata` | [UserUnsafeMetadata](/docs/reference/types/metadata#user-unsafe-metadata) | 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`](/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. | | `updatedAt` | null \| Date | The date and time when the user was last updated. | | `username` | null \| string | The user's username. | diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index ee160a04d23..90e88fc251d 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -183,219 +183,99 @@ export function applyRelativeLinkReplacements(contents) { return out; } +/** + * Bare-identifier → docs-link data table for {@link getCatchAllReplacements}. + * Each entry is `[name, url]` or `[name, url, linkText]` (defaults to `name`). + * The regex is derived from `name` via {@link catchAllBareSymbolRegex}. + * @type {ReadonlyArray} + */ +const CATCH_ALL_TYPE_LINKS = [ + ['APIKeysNamespace', '/docs/reference/objects/api-keys', 'APIKeys'], + ['ClerkAPIResponseError', '/docs/reference/types/clerk-api-response-error'], + ['EmailAddressResource', '/docs/reference/types/email-address'], + ['EnterpriseAccountResource', '/docs/reference/types/enterprise-account'], + ['ExternalAccountResource', '/docs/reference/types/external-account'], + ['LastAuthenticationStrategy', '/docs/reference/types/last-authentication-strategy'], + ['LoadedClerk', '/docs/reference/objects/clerk', 'Clerk'], + ['LocalizationResource', '/docs/guides/customizing-clerk/localization'], + ['Machine', '/docs/reference/backend/types/backend-machine'], + ['OAuthProvider', '/docs/reference/types/sso#o-auth-provider'], + ['OAuthStrategy', '/docs/reference/types/sso#o-auth-strategy'], + ['OrganizationInvitationPrivateMetadata', '/docs/reference/types/metadata#organization-invitation-private-metadata'], + ['OrganizationInvitationPublicMetadata', '/docs/reference/types/metadata#organization-invitation-public-metadata'], + ['OrganizationMembershipPrivateMetadata', '/docs/reference/types/metadata#organization-membership-private-metadata'], + ['OrganizationMembershipPublicMetadata', '/docs/reference/types/metadata#organization-membership-public-metadata'], + ['OrganizationPrivateMetadata', '/docs/reference/types/metadata#organization-private-metadata'], + ['OrganizationPublicMetadata', '/docs/reference/types/metadata#organization-public-metadata'], + ['OrganizationResource', '/docs/reference/objects/organization'], + ['PasskeyResource', '/docs/reference/types/passkey-resource'], + ['PhoneNumberResource', '/docs/reference/types/phone-number'], + ['SessionStatusClaim', '/docs/reference/types/session-status'], + ['SetActiveParams', '/docs/reference/types/set-active-params'], + ['SignedInSessionResource', '/docs/reference/objects/session'], + ['SignInErrors', '/docs/reference/types/errors'], + ['SignInFirstFactor', '/docs/reference/types/sign-in-first-factor'], + ['SignInFutureResource', '/docs/reference/objects/sign-in-future'], + ['SignInRedirectOptions', '/docs/reference/types/sign-in-redirect-options'], + ['SignInResource', '/docs/reference/objects/sign-in'], + ['SignInSecondFactor', '/docs/reference/types/sign-in-second-factor'], + ['SignUpErrors', '/docs/reference/types/errors'], + ['SignUpFutureResource', '/docs/reference/objects/sign-up-future'], + ['SignUpRedirectOptions', '/docs/reference/types/sign-up-redirect-options'], + ['SignUpResource', '/docs/reference/objects/sign-up'], + ['SignUpUnsafeMetadata', '/docs/reference/types/metadata#sign-up-unsafe-metadata'], + ['SignUpVerificationResource', '/docs/reference/types/sign-up-verification-resource'], + ['TasksRedirectOptions', '/docs/reference/types/redirect-options'], + ['UserPrivateMetadata', '/docs/reference/types/metadata#user-private-metadata'], + ['UserPublicMetadata', '/docs/reference/types/metadata#user-public-metadata'], + ['UserResource', '/docs/reference/objects/user'], + ['UserUnsafeMetadata', '/docs/reference/types/metadata#user-unsafe-metadata'], + ['VerificationResource', '/docs/reference/types/verification-resource'], + ['Web3WalletResource', '/docs/reference/types/web3-wallet'], +]; + +/** + * Match a bare identifier not already inside a link (`[…]`), inline code (`` `…` ``), heading anchor (`#…`), or word-character run. Optional wrapping backticks on the match are consumed so the replacement produces a clean `[Name](url)` link. + * @param {string} name + */ +function catchAllBareSymbolRegex(name) { + return new RegExp(`(? string) }[]} */ + const linkRules = CATCH_ALL_TYPE_LINKS.map(([name, url, linkText]) => ({ + pattern: catchAllBareSymbolRegex(name), + replace: `[${linkText ?? name}](${url})`, + })); return [ + ...linkRules, { - pattern: /(?/g, replace: '[Appearance](/docs/guides/customizing-clerk/appearance-prop/overview)', }, { + // In-page anchor for `CreateOrganizationParams` — only inside parentheses (prose lists). pattern: /(? `[${type}](/docs/reference/types/errors)`, - }, - { - pattern: /(? { - return `Examples: ${capturedGroup.split(' ').join(', ')}.`; - }, + replace: (/** @type {string} */ _match, /** @type {string} */ capturedGroup) => + `Examples: ${capturedGroup.split(' ').join(', ')}.`, }, ]; } @@ -434,19 +314,6 @@ 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 body (no heading) 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. diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index c2c0d1c79c3..b292bc892f3 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1,57 +1,25 @@ // @ts-check -import { ArrayType, i18n, IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; -import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; +import { + ArrayType, + Comment, + i18n, + IntersectionType, + OptionalType, + ReferenceType, + ReflectionKind, + ReflectionType, + UnionType, +} from 'typedoc'; +import { MarkdownPageEvent, MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; import { applyTodoStrippingToComment } from './comment-utils.mjs'; +import { applyCatchAllMdReplacements, applyRelativeLinkReplacements } from './custom-plugin.mjs'; import { backTicks, heading, htmlTable, removeLineBreaks, table } from './markdown-helpers.mjs'; -import { REFERENCE_OBJECTS_LIST } from './reference-objects.mjs'; +import { BACKEND_API_CONFIG, REFERENCE_OBJECT_CONFIG, REFERENCE_OBJECTS_LIST } from './reference-objects.mjs'; +import { toFileSlug } from './slug.mjs'; import { isInlineModifierWithoutStandalonePage } from './standalone-page-tag.mjs'; import { unwrapOptional } from './type-utils.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 - * @returns {boolean} - */ -function isUnionTypeDoc(/** @type {import('typedoc').Type | undefined} */ t) { - const o = /** @type {{ type?: string; types?: import('typedoc').Type[] } | null} */ (t); - return Boolean(o && typeof o === 'object' && o.type === 'union' && Array.isArray(o.types)); -} - /** * Stock `typedoc-plugin-markdown` `arrayType` only wraps `elementType.type === 'union'`. * For `T | T[]` where `T` is an `@inline` alias to a union, the element is still a `reference` in the model but renders as `"a" \| "b"`, producing `"a" \| "b"[]` (wrong binding). Instead, parens the array type whenever the reference inlines to a union RHS so it produces `("a" \| "b")[]`. @@ -61,21 +29,17 @@ function isUnionTypeDoc(/** @type {import('typedoc').Type | undefined} */ t) { * @returns {boolean} */ function isArrayElementReferenceInliningToUnion(elementType) { - if (!isReferenceTypeDoc(elementType)) { - return false; - } - const ref = /** @type {import('typedoc').ReferenceType} */ (elementType); - if (!ref.reflection) { + if (!(elementType instanceof ReferenceType) || !elementType.reflection) { return false; } - if (!isInlineModifierWithoutStandalonePage(ref.reflection)) { + if (!isInlineModifierWithoutStandalonePage(elementType.reflection)) { return false; } - const decl = /** @type {import('typedoc').DeclarationReflection} */ (ref.reflection); + const decl = /** @type {import('typedoc').DeclarationReflection} */ (elementType.reflection); if (!decl.kindOf?.(ReflectionKind.TypeAlias) || !decl.type) { return false; } - return isUnionTypeDoc(decl.type); + return decl.type instanceof UnionType; } /** @@ -101,7 +65,7 @@ function findNamedTypeDeclaration(project, name) { } /** - * Prefer `packages/shared/src/types/strategies.ts` when multiple type aliases share the name `OAuthStrategy`. + * Prefer `packages/shared/src/types/strategies.ts` when multiple type aliases share the name `OAuthStrategy` (also declared in `packages/backend`). * * @param {import('typedoc').ProjectReflection | undefined} project * @returns {import('typedoc').DeclarationReflection | undefined} @@ -110,54 +74,16 @@ 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); + const candidates = /** @type {import('typedoc').DeclarationReflection[]} */ ( + project.getReflectionsByKind(ReflectionKind.TypeAlias).filter(r => r.name === 'OAuthStrategy') + ); + if (candidates.length <= 1) { + return candidates[0]; } - return undefined; + const fromStrategies = candidates.find(r => + (r.sources?.[0]?.fileName ?? '').replace(/\\/g, '/').includes('strategies'), + ); + return fromStrategies ?? candidates[0]; } /** @@ -169,14 +95,13 @@ function coerceUnionTypeIfNeeded(model) { * @returns {import('typedoc').Type[]} */ function flattenUnionTypeMembersForOAuthCollapse(t) { - if (!t || typeof t !== 'object') { + if (!t) { return []; } - const o = /** @type {{ type?: string; types?: import('typedoc').Type[] }} */ (t); - if (o.type === 'union' && Array.isArray(o.types)) { + if (t instanceof UnionType) { /** @type {import('typedoc').Type[]} */ const acc = []; - for (const inner of o.types) { + for (const inner of t.types) { acc.push(...flattenUnionTypeMembersForOAuthCollapse(inner)); } return acc; @@ -277,7 +202,7 @@ function collectPropertyReflectionsFromIntersectionArm(t, visitedReflectionIds, return []; } - if (isReflectionTypeDoc(unwrapped)) { + if (unwrapped instanceof ReflectionType) { const decl = unwrapped.declaration; if (!decl) { return []; @@ -288,7 +213,7 @@ function collectPropertyReflectionsFromIntersectionArm(t, visitedReflectionIds, return (decl.children ?? []).filter(c => c.kind === ReflectionKind.Property); } - if (isReferenceTypeDoc(unwrapped)) { + if (unwrapped instanceof ReferenceType) { let ref = unwrapped.reflection; if (!ref && unwrapped.name && project) { ref = findNamedTypeDeclaration(project, unwrapped.name); @@ -339,7 +264,7 @@ function collectPropertyReflectionsFromIntersectionArm(t, visitedReflectionIds, return []; } - if (isIntersectionTypeDoc(unwrapped)) { + if (unwrapped instanceof IntersectionType) { /** @type {import('typedoc').DeclarationReflection[]} */ const out = []; for (const arm of unwrapped.types) { @@ -348,7 +273,7 @@ function collectPropertyReflectionsFromIntersectionArm(t, visitedReflectionIds, return out; } - if (isUnionTypeDoc(unwrapped)) { + if (unwrapped instanceof UnionType) { return collectPropertyReflectionsFromUnionObjectArms(unwrapped, visitedReflectionIds, project); } @@ -1065,11 +990,650 @@ function clerkDeclaration(model, options = { headingLevel: 2, nested: false }) { return md.join('\n\n'); } +/** + * `'reference'` (default): each `methods/.mdx` opens with `### foo()` and uses an H4 `#### Parameters` heading — matches the reference-object pages that aggregate many methods. + * `'page'`: skip the method-name title and use an H2 `## Parameters` heading — for one-method-per-docs-page surfaces, like the backend API endpoints. + * @typedef {'reference' | 'page'} MethodFormat + */ + +/** @param {string} pageUrl */ +function methodFormatForPageUrl(pageUrl) { + return pageUrl in BACKEND_API_CONFIG + ? /** @type {MethodFormat} */ ('page') + : /** @type {MethodFormat} */ ('reference'); +} + +/** + * @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]; +} + +/** + * 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 + */ +function shouldExtractCallableMember(decl, ctx) { + if (decl.kind === ReflectionKind.Method) { + return true; + } + if ( + decl.kind === ReflectionKind.Property || + decl.kind === ReflectionKind.Accessor || + decl.kind === ReflectionKind.Variable + ) { + return isCallableInterfaceProperty(decl, ctx.helpers); + } + return false; +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + */ +function hasExtractMethodsModifier(decl) { + return Boolean(decl.comment?.hasModifier('@extractMethods')); +} + +/** + * @param {import('typedoc-plugin-markdown').MarkdownPageEvent} output + * @returns {string | undefined} + */ +function matchReferenceObjectPageUrl(output) { + if (!output.url) { + return undefined; + } + const normalized = output.url.replace(/\\/g, '/'); + if (normalized in REFERENCE_OBJECT_CONFIG) return normalized; + if (normalized in BACKEND_API_CONFIG) return normalized; + return undefined; +} + +/** @param {string} pageUrl */ +function configEntryForPageUrl(pageUrl) { + return /** @type {import('./reference-objects.mjs').REFERENCE_OBJECT_CONFIG[keyof typeof REFERENCE_OBJECT_CONFIG]} */ ( + REFERENCE_OBJECT_CONFIG[/** @type {keyof typeof REFERENCE_OBJECT_CONFIG} */ (pageUrl)] ?? + BACKEND_API_CONFIG[/** @type {keyof typeof BACKEND_API_CONFIG} */ (pageUrl)] + ); +} + +/** + * Wrap the name portion of bare method headings on a parent page in backticks: + * `^## methodName()` → `` ^## `methodName()` ``. Applies to H2–H4 headings whose full text is + * an identifier followed by `()` with no other content — matches typedoc's natural rendering of + * callable signature titles on aggregator pages (e.g. `agent-task-api.mdx`). Idempotent: headings + * that already contain a backtick are left alone. + * + * @param {string} contents + * @returns {string} + */ +function addBackticksToBareMethodHeadings(contents) { + if (!contents) return contents; + return contents.replace(/^(#{2,4}) ([A-Za-z_$][A-Za-z0-9_$]*)\(\)\s*$/gm, '$1 `$2()`'); +} + +/** + * Replace each method's natural `### Parameters` (or `## Parameters` on single-callable pages) + * section with the canonical `parametersMarkdownTable` output for the same signature at the same + * heading level. Routes every parameters table through the same code path: + * + * - single nominal param (e.g. `create(params: CreateAgentTaskParams)`): section becomes + * `` `TypeName` `` (backticked) + the propertiesTable-based expanded rows. + * - multi-arg or non-nominal (e.g. `revoke(agentTaskId: string)`): section stays `Parameters` + * + parametersTable output. + * + * Handles two page shapes: + * 1. **Aggregator pages** (backend API parents, namespaces like `api-keys-namespace.mdx`): each + * method is a `## `methodName()` ` section with `### Parameters` inside. Iterates `decl.children`. + * 2. **Single-callable pages** (`server-get-token.mdx`, React hooks): no method heading, `## Parameters` + * at top-level. Uses `decl`'s own signature. + * + * Runs uniformly across all pages. `extract-returns-and-params.mjs` is responsible for renaming + * `` ## `TypeName` `` back to `## Parameters` on the react-package pages it post-processes (so the + * sibling `-params.mdx` extraction still finds the section) — this keeps theme output uniform and + * pushes downstream-consumer knowledge into the downstream tool. + * + * Applies link replacements manually because this listener runs after `custom-plugin.mjs`'s pass. + * + * @param {string} contents + * @param {import('typedoc').DeclarationReflection | import('typedoc').Reflection} decl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {string} + */ +function swapParentPageParametersSections(contents, decl, ctx) { + if (!contents) return contents; + let result = contents; + + /** @param {import('typedoc').SignatureReflection} sig @param {number} level */ + const renderParams = (sig, level) => { + const skip = + Boolean(/** @type {any} */ (sig).comment?.hasModifier?.('@skipParametersSection')) || + Boolean(/** @type {any} */ (sig).parent?.comment?.hasModifier?.('@skipParametersSection')); + if (skip) return undefined; + overlayParamCommentsFromSignatureBlockTags(sig); + const parent = /** @type {import('typedoc').DeclarationReflection} */ (sig.parent); + const instantiationMap = parent ? getGenericInstantiationMapFromCallableProperty(parent) : undefined; + const md = parametersMarkdownTable(sig, ctx, instantiationMap, level); + if (!md) return undefined; + return applyCatchAllMdReplacements(applyRelativeLinkReplacements(md)); + }; + + const declWithChildren = /** @type {import('typedoc').DeclarationReflection} */ (decl); + const callableChildren = (declWithChildren.children ?? []).filter( + /** @param {import('typedoc').DeclarationReflection} c */ c => { + if (c.name.startsWith('__')) return false; + return shouldExtractCallableMember(c, ctx); + }, + ); + + if (callableChildren.length > 0) { + // Aggregator page: iterate `## `methodName()` ` blocks, swap the nested params section. + for (const child of callableChildren) { + const sig = getPrimaryCallSignature(/** @type {import('typedoc').DeclarationReflection} */ (child)); + if (!sig) continue; + const heading = `## \`${child.name}()\``; + const startIdx = result.indexOf(heading); + if (startIdx === -1) continue; + const rest = result.slice(startIdx); + const paramsMatch = rest.match(/\n(##+) Parameters\n[\s\S]*?(?=\n(?:##+ |---)|$)/); + if (!paramsMatch || paramsMatch.index === undefined) continue; + const level = paramsMatch[1].length; + const newParamsMd = renderParams(sig, level); + if (!newParamsMd) continue; + const absStart = startIdx + paramsMatch.index; + const absEnd = absStart + paramsMatch[0].length; + result = `${result.slice(0, absStart)}\n${newParamsMd.trimEnd()}\n${result.slice(absEnd)}`; + } + return result; + } + + // Single-callable page: `decl` is (or wraps) the signature. Find the top-level Parameters section. + /** @type {import('typedoc').SignatureReflection | undefined} */ + const sig = + /** @type {any} */ (decl).signatures?.[0] ?? + /** @type {any} */ (decl.kind && getPrimaryCallSignature(declWithChildren)) ?? + undefined; + if (!sig) return result; + const paramsMatch = result.match(/(^|\n)(##+) Parameters\n[\s\S]*?(?=\n(?:##+ |---)|$)/); + if (!paramsMatch || paramsMatch.index === undefined) return result; + const level = paramsMatch[2].length; + const newParamsMd = renderParams(sig, level); + if (!newParamsMd) return result; + const leadingNl = paramsMatch[1]; + const absStart = paramsMatch.index + leadingNl.length; + const absEnd = paramsMatch.index + paramsMatch[0].length; + result = `${result.slice(0, absStart)}${newParamsMd.trimEnd()}\n${result.slice(absEnd)}`; + return result; +} + +/** + * Check whether the page contents already include a `## methodName()` H2 heading (either bare or + * backticked) for the given method name. Used to avoid duplicating a synthesized aggregator section + * on pages where typedoc-plugin-markdown already emits per-method headings naturally (backend + * class-based API pages). + * + * @param {string} contents + * @param {string} name + * @returns {boolean} + */ +function contentsHaveMethodHeading(contents, name) { + if (!contents) return false; + const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const re = new RegExp(`(^|\\n)## \`?${escaped}\`?\\(\\)\`?[ \\t]*(\\n|$)`); + return re.test(contents); +} + +/** + * Render the canonical H2 property-table section for a parent page. Shape: + * + * ## `name` + * + * + * + * `namespacePropertyTable` entries list the non-callable members of an `@extractMethods` namespace + * (e.g. `## `verifications`` lists `emailAddress`, `phoneNumber`, ...). `memberPropertyTable` + * entries expand one non-callable member's object shape (e.g. `## `emailLink.verification``). + * + * Sub-doc equivalents produced by the marker-block path use H3 (`### `name``); text-slice demotes + * the H2 back to H3 on reshape, so the sub-doc is byte-identical to the marker-block output. + * + * @param {Extract} entry + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {string} + */ +function renderAggregatorPropertyTableSection(entry, ctx) { + const displayName = entry.kind === 'namespacePropertyTable' ? entry.decl.name : entry.qualifiedName; + const description = commentSummaryAndBody(entry.decl.comment); + /** @type {import('typedoc').DeclarationReflection[]} */ + let propsUnsorted; + if (entry.kind === 'namespacePropertyTable') { + propsUnsorted = entry.nonCallableMembers; + } else { + propsUnsorted = resolveObjectShapeMembersForPropertyTable(entry.decl.type) ?? []; + } + if (!propsUnsorted.length) return ''; + const props = [...propsUnsorted].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 title = `## \`${displayName}\``; + const raw = [title, '', description, '', tableMd].filter(Boolean).join('\n\n'); + return `${applyCatchAllMdReplacements(applyRelativeLinkReplacements(raw)).trim()}\n`; +} + +/** + * Render one method's canonical "aggregator" H2 section for a parent page — the shape every parent + * page uses so `extract-methods.mjs` can slice it and reshape into the per-method extracted file + * with pure text ops (no reflection access needed downstream): + * + * ## `name()` + * (summary + non-@returns block tags) + * ```typescript + * function name(...): ReturnType + * ``` + * ### `TypeName` (nominal single-arg) OR ### Parameters + * + * ### Returns + * `Type` — (from ctx.partials.signatureReturns) + * + * Used to synthesize sections for callable members typedoc doesn't render as separate H2s — + * function-typed properties on interfaces (`end: () => Promise`) and members declared on + * `extraMethodInterfaces`. Sections synthesized here are indistinguishable from natural sections + * on the same page after all reshapers have run. + * + * Applies link replacements manually because this listener runs after `custom-plugin.mjs`'s pass. + * For property-table entries, delegates to {@link renderAggregatorPropertyTableSection}. + * + * @param {AggregatorMethodEntry} entry + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {string} + */ +function renderAggregatorMethodSection(entry, ctx) { + if (entry.kind === 'namespacePropertyTable' || entry.kind === 'memberPropertyTable') { + return renderAggregatorPropertyTableSection(entry, ctx); + } + const { decl, qualifiedName } = entry; + const sig = getPrimaryCallSignature(decl); + if (!sig) return ''; + const displayName = qualifiedName ?? decl.name; + const title = `## \`${displayName}()\``; + const comment = decl.comment ?? sig.comment; + const c = comment ? (applyTodoStrippingToComment(comment) ?? comment) : undefined; + const summary = c ? 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 description = [summary, block].filter(Boolean).join('\n\n'); + const instantiationMap = getGenericInstantiationMapFromCallableProperty(decl); + const signatureBlock = ['```typescript', formatTypeScriptSignature(sig, displayName, instantiationMap), '```'].join( + '\n', + ); + const skipParametersSection = + Boolean(decl.comment?.hasModifier('@skipParametersSection')) || + Boolean(sig.comment?.hasModifier('@skipParametersSection')); + let paramsMd = ''; + if (!skipParametersSection) { + overlayParamCommentsFromSignatureBlockTags(sig); + paramsMd = parametersMarkdownTable(sig, ctx, instantiationMap, 3); + } + // Function-typed properties (`create: (params) => Promise`) carry `@returns` on the property + // comment, not on the signature comment. `signatureReturns` reads sig.comment.@returns; overlay + // the decl comment's @returns onto sig so the rendered `### Returns` section carries the + // description text. + const declReturnsTag = decl.comment?.getTag('@returns'); + const sigReturnsTag = sig.comment?.getTag('@returns'); + if (declReturnsTag?.content?.length && !sigReturnsTag?.content?.length) { + if (!sig.comment) sig.comment = new Comment(); + sig.comment.blockTags = [...(sig.comment.blockTags ?? []), declReturnsTag]; + } + const returnsMd = ctx.partials.signatureReturns(sig, { headingLevel: 3 }); + // Apply link replacements to the prose chunks only. The typescript signature block must stay + // raw — `applyRelativeLinkReplacements` is line-based and would rewrite bare type identifiers + // inside the fenced code block into `[Type](...)` markdown link syntax, breaking downstream + // slicing. + const proseChunks = [title, description, paramsMd.trimEnd(), returnsMd] + .filter(s => s && s.length > 0) + .map(s => applyCatchAllMdReplacements(applyRelativeLinkReplacements(s))); + const chunks = [proseChunks[0], proseChunks[1], signatureBlock, ...proseChunks.slice(2)].filter( + s => s && s.length > 0, + ); + return chunks.join('\n\n'); +} + +/** + * Delete a `## methodName()` H2 section (heading through the next `## ` or end-of-file) from the + * page contents. Used when a natural typedoc-emitted section needs to be replaced by a synthesized + * one — e.g. overloaded backend-class methods where typedoc emits `### Call Signature` sub-headings + * per overload, but the aggregator synthesizes a single section for the primary signature only + * (via `getPrimaryCallSignature`). + * + * @param {string} contents + * @param {string} name + * @returns {string} + */ +function removeMethodH2Section(contents, name) { + if (!contents) return contents; + const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const headingRe = new RegExp(`(^|\\n)## \`?${escaped}\`?\\(\\)\`?[ \\t]*\\n`, ''); + const m = contents.match(headingRe); + if (!m || m.index === undefined) return contents; + const startIdx = m.index + (m[1] === '\n' ? 1 : 0); + const afterHeading = m.index + m[0].length; + const rest = contents.slice(afterHeading); + const nextH2 = rest.search(/\n## /); + const endIdx = nextH2 === -1 ? contents.length : afterHeading + nextH2 + 1; + // Also trim the trailing `***` typedoc separator that natural sections use between overloads. + let sliceEnd = endIdx; + const trailingSep = contents.slice(sliceEnd).match(/^\*+\s*\n+/); + if (trailingSep) sliceEnd += trailingSep[0].length; + return contents.slice(0, startIdx) + contents.slice(sliceEnd); +} + +/** + * Insert a `` ```typescript ... ``` `` signature code block into each natural (typedoc-emitted) + * `## methodName()` H2 section on the parent page. Positioned after the section's description + * prose and before the first `### ` subheading — matching the position `renderAggregatorMethodSection` + * uses for synthesized sections. This is what makes the parent's H2 sections a self-contained + * source for `extract-methods.mjs`: the extracted file's signature can be sliced out of the parent + * verbatim rather than regenerated downstream. + * + * Idempotent-ish: relies on `contentsHaveMethodHeading` matching the (possibly-backticked) title, + * and looks for the absence of a fenced ```typescript block already inside the section. + * + * @param {string} contents + * @param {import('typedoc').DeclarationReflection[]} callableDecls - direct callable members that + * may have natural sections. Callers pass the same list they enumerate for synthesis-vs-natural + * decisions. + * @returns {string} + */ +function insertSignatureBlocksIntoNaturalMethodSections(contents, callableDecls) { + if (!contents) return contents; + let result = contents; + for (const decl of callableDecls) { + const sig = getPrimaryCallSignature(decl); + if (!sig) continue; + const escaped = decl.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const headingRe = new RegExp(`(^|\\n)## \`?${escaped}\`?\\(\\)\`?[ \\t]*\\n`); + const headingMatch = result.match(headingRe); + if (!headingMatch || headingMatch.index === undefined) continue; + const sectionStart = headingMatch.index + headingMatch[0].length; + const rest = result.slice(sectionStart); + const nextH2 = rest.search(/\n## /); + const sectionEnd = nextH2 === -1 ? result.length : sectionStart + nextH2; + const section = result.slice(sectionStart, sectionEnd); + if (/```typescript\n/.test(section)) continue; + const firstSubheading = section.search(/\n### /); + const descriptionEnd = firstSubheading === -1 ? section.length : firstSubheading; + const description = section.slice(0, descriptionEnd); + const remainder = section.slice(descriptionEnd); + const instantiationMap = getGenericInstantiationMapFromCallableProperty(decl); + const signatureBlock = ['```typescript', formatTypeScriptSignature(sig, decl.name, instantiationMap), '```'].join( + '\n', + ); + const before = result.slice(0, sectionStart); + const rebuilt = `${description.trimEnd()}\n\n${signatureBlock}\n${remainder.startsWith('\n') ? remainder : `\n${remainder}`}`; + result = `${before}${rebuilt}${result.slice(sectionEnd)}`; + } + return result; +} + +/** + * @typedef {( + * | { kind?: 'method', decl: import('typedoc').DeclarationReflection, qualifiedName?: string } + * | { kind: 'namespacePropertyTable', decl: import('typedoc').DeclarationReflection, nonCallableMembers: import('typedoc').DeclarationReflection[] } + * | { kind: 'memberPropertyTable', decl: import('typedoc').DeclarationReflection, qualifiedName: string } + * )} AggregatorMethodEntry + */ + +/** + * Enumerate per-H2-section entries for the given parent page. Three shapes: + * - `method`: direct callable children, callables from `extraMethodInterfaces`, and callables + * inside `@extractMethods` namespaces (qualified names like `emailCode.sendCode`). + * - `namespacePropertyTable`: an `@extractMethods` namespace with at least one non-callable + * object-like member. Rendered as a `## `namespace`` H2 listing those non-callable members. + * - `memberPropertyTable`: a non-callable, object-like member inside an `@extractMethods` + * namespace. Rendered as a `## `namespace.member`` H2 expanding that member's object shape. + * + * @param {import('typedoc').DeclarationReflection} decl + * @param {import('typedoc').ProjectReflection | undefined} project + * @param {ReturnType} entry + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {AggregatorMethodEntry[]} + */ +function enumerateAggregatorMethodDecls(decl, project, entry, ctx) { + /** @type {AggregatorMethodEntry[]} */ + const out = []; + /** @param {import('typedoc').DeclarationReflection} d */ + const pushCallables = d => { + for (const child of d.children ?? []) { + if (child.name.startsWith('__')) continue; + const cd = /** @type {import('typedoc').DeclarationReflection} */ (child); + if (hasExtractMethodsModifier(cd)) { + // `@extractMethods` namespace: enumerate its object-like members. Callables produce + // qualified-name method entries (`emailCode.sendCode`). Non-callable members with a + // resolvable object shape produce `memberPropertyTable` entries; if any such members + // exist, a `namespacePropertyTable` entry is also emitted for the namespace itself. + const members = resolveDeclarationWithObjectMembers(cd.type, project) ?? []; + /** @type {import('typedoc').DeclarationReflection[]} */ + const nonCallableMembers = []; + for (const nested of members) { + if (nested.name.startsWith('__')) continue; + const nd = /** @type {import('typedoc').DeclarationReflection} */ (nested); + if (shouldExtractCallableMember(nd, ctx)) { + out.push({ kind: 'method', decl: nd, qualifiedName: `${cd.name}.${nd.name}` }); + continue; + } + nonCallableMembers.push(nd); + if (resolveObjectShapeMembersForPropertyTable(nd.type)?.length) { + out.push({ kind: 'memberPropertyTable', decl: nd, qualifiedName: `${cd.name}.${nd.name}` }); + } + } + if (nonCallableMembers.length) { + out.push({ kind: 'namespacePropertyTable', decl: cd, nonCallableMembers }); + } + continue; + } + if (shouldExtractCallableMember(cd, ctx)) out.push({ kind: 'method', decl: cd }); + } + }; + pushCallables(decl); + const extraMethodInterfaces = entry && 'extraMethodInterfaces' in entry ? entry.extraMethodInterfaces : undefined; + if (Array.isArray(extraMethodInterfaces) && project) { + for (const extra of extraMethodInterfaces) { + const extraDecl = findInterfaceOrClass(project, extra.symbol, extra.declarationHint); + if (!extraDecl) { + console.warn(`[custom-theme] extraMethodInterfaces: could not find "${extra.symbol}"`); + continue; + } + pushCallables(extraDecl); + } + } + return out; +} + +/** + * Wrap the `## Properties` section body in the page contents with HTML-comment markers so the slicer can extract it after prettier runs. + * Idempotent: no-op if markers already exist. + * + * @param {string} contents + * @returns {string} + */ +function wrapPropertiesSectionWithMarkers(contents) { + if (!contents) return contents; + if (contents.includes('')) return contents; + const normalized = contents.replace(/\r\n/g, '\n'); + // Match `## Properties` whether it's at start-of-file or after a newline. The slicer's strip + // regex (in `extract-methods.mjs`) intentionally requires a leading `\n` so pages where + // `## Properties` is the very first line (e.g. `billing-namespace.mdx`) do NOT have the section + // stripped from the parent page — matching the pre-refactor `stripReferenceObjectPropertiesSection` + // behavior. Wrapping still happens (so `properties.mdx` gets written), which also matches + // pre-refactor (`extractPropertiesSectionBody` used the same `(^|\n)` prefix). + const m = normalized.match(/(^|\n)## Properties\n+/); + if (!m || m.index === undefined) return contents; + const headingEnd = m.index + m[0].length; + const rest = normalized.slice(headingEnd); + const nextH2 = rest.search(/\n## /); + const bodyEnd = nextH2 === -1 ? normalized.length : headingEnd + nextH2; + const before = normalized.slice(0, headingEnd); + const body = normalized.slice(headingEnd, bodyEnd); + const after = normalized.slice(bodyEnd); + return `${before}\n${body.trimEnd()}\n\n${after}`; +} + +/** + * Strip inline-code markers (backticks, `` tags) from a type-rendering partial's output and rewrap the whole span in a single ``. When rendering inside a function signature (`signatureTitle`), the outer code block wraps the whole span so return the stripped text unwrapped. + * + * @param {string} output + * @param {boolean} insideFunctionSignature + */ +function wrapInlineTypeAsCode(output, insideFunctionSignature) { + const stripped = output + .replace(/`/g, '') + .replace(//g, '') + .replace(/<\/code>/g, ''); + return insideFunctionSignature ? stripped : `${stripped}`; +} + /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ export function load(app) { app.renderer.defineTheme('clerkTheme', ClerkMarkdownTheme); + + // Negative priority ensures this fires AFTER `custom-plugin.mjs`'s `MarkdownPageEvent.END` + // listener (which applies `applyRelativeLinkReplacements` + `applyCatchAllMdReplacements` at the + // default priority of 0). If we emitted first, custom-plugin's second pass would re-apply link + // replacements to synthesized aggregator sections we already processed — mangling the + // ```typescript``` signature fences (custom-plugin's replacements are line-based and do not + // skip fenced code blocks). `extract-methods.mjs`'s slicer registers at an even lower priority + // so it fires after this emit. + app.renderer.on( + MarkdownPageEvent.END, + output => { + const decl = /** @type {import('typedoc').DeclarationReflection | undefined} */ (output.model); + if (!decl) return; + const theme = /** @type {InstanceType | undefined} */ (app.renderer.theme); + if (!theme || typeof theme.getRenderContext !== 'function') return; + const ctx = /** @type {import('typedoc-plugin-markdown').MarkdownThemeContext} */ ( + theme.getRenderContext(output) + ); + + output.contents = addBackticksToBareMethodHeadings(output.contents ?? ''); + output.contents = swapParentPageParametersSections(output.contents ?? '', decl, ctx); + + const pageUrl = matchReferenceObjectPageUrl(output); + // Reference-object / backend-api specific work: + // 1. Synthesize per-H2 aggregator sections for callable and property-table members that + // typedoc-plugin-markdown doesn't render on its own (function-typed interface properties, + // `extraMethodInterfaces`, `@extractMethods` namespace members). + // 2. Wrap the `## Properties` section for post-prettier extraction into `properties.mdx`. + // `extract-methods.mjs` slices the resulting parent page into `methods/*.mdx` sub-docs. + if (pageUrl) { + const entry = configEntryForPageUrl(pageUrl); + const methodFormat = methodFormatForPageUrl(pageUrl); + const project = output.project; + if (decl.children && project) { + const allEntries = enumerateAggregatorMethodDecls(decl, project, entry, ctx); + // Property-table entries are always synthesized (typedoc emits nothing for them); + // handle them alongside `missing` methods below. Only the method entries participate + // in the natural-heading dance. + const methodEntries = allEntries.filter(e => !e.kind || e.kind === 'method'); + const propertyTableEntries = allEntries.filter( + e => e.kind === 'namespacePropertyTable' || e.kind === 'memberPropertyTable', + ); + // Overloaded methods on backend classes render natively as `### Call Signature` + // sub-sections per overload. To keep the parent-page shape uniform across single and + // multi-overload methods (so `extract-methods.mjs` can slice with one code path), remove + // the overloaded natural section and treat the method as missing — it'll be re-synthesized + // below using `getPrimaryCallSignature`. + for (const e of methodEntries) { + const nm = e.qualifiedName ?? e.decl.name; + if ((e.decl.signatures?.length ?? 0) > 1 && contentsHaveMethodHeading(output.contents ?? '', nm)) { + output.contents = removeMethodH2Section(output.contents ?? '', nm); + } + } + const naturalCallables = methodEntries.filter(e => + contentsHaveMethodHeading(output.contents ?? '', e.qualifiedName ?? e.decl.name), + ); + const missing = [ + ...methodEntries.filter( + e => !contentsHaveMethodHeading(output.contents ?? '', e.qualifiedName ?? e.decl.name), + ), + ...propertyTableEntries, + ]; + // Backend class pages: typedoc-plugin-markdown already emits `## methodName()` H2s. + // Inject a `` ```typescript ``` `` signature block after each natural section's + // description so the parent's section is self-contained (matches shape of synthesized + // sections below). Only applies to unqualified members — qualified-name entries from + // `@extractMethods` namespaces are never natural on the parent page. + const naturalUnqualified = naturalCallables.filter(e => !e.qualifiedName).map(e => e.decl); + if (naturalUnqualified.length) { + output.contents = insertSignatureBlocksIntoNaturalMethodSections(output.contents ?? '', naturalUnqualified); + } + if (missing.length) { + const sections = missing.map(e => renderAggregatorMethodSection(e, ctx)).filter(Boolean); + if (sections.length) { + // Insert synthesized sections BEFORE `## Properties` so extract-methods' strip + // (which drops `## Properties` and everything after during the transitional refactor) + // preserves them. Falls back to appending at end when the page has no properties + // section (backend class pages have methods only). + const block = sections.join('\n\n'); + const currentContents = output.contents ?? ''; + const propsIdx = currentContents.search(/(^|\n)## Properties\n/); + if (propsIdx === -1) { + output.contents = `${currentContents.trimEnd()}\n\n${block}\n`; + } else { + const before = currentContents.slice(0, propsIdx); + const after = currentContents.slice(propsIdx); + output.contents = `${before.trimEnd()}\n\n${block}\n\n${after.replace(/^\n+/, '')}`; + } + } + } + } + output.contents = wrapPropertiesSectionWithMarkers(output.contents ?? ''); + } + }, + -100, + ); } class ClerkMarkdownTheme extends MarkdownTheme { @@ -1130,23 +1694,17 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, /** - * Ensure unions always route through `unionType` (OAuth collapse) even when `instanceof UnionType` fails. + * Run the OAuth-strategy collapse before delegating to the stock partial when the model is a union. * - * @param {import('typedoc').Type | undefined} model + * @param {import('typedoc').SomeType | 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); + if (model instanceof UnionType) { + const collapsed = tryCollapseExpandedOAuthStrategyUnion(model, this); + return superPartials.someType.call(this, collapsed ?? model, options); } - return superPartials.someType.call( - this, - /** @type {import('typedoc').SomeType | undefined} */ (/** @type {unknown} */ (model)), - options, - ); + return superPartials.someType.call(this, model, options); }, /** * Stock `comments.comment` prints every {@link Comment.modifierTags} as **`TitleCase`** before the summary (it does not consult `notRenderedTags`; that option only filters block tags). `@inline` / `@inlineType` are router/type hints; `@experimental` is SDK-only guidance — none of these must appear in property tables or prose. @@ -1525,11 +2083,8 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { 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 (!opts.nested && model.type instanceof IntersectionType) { + const merged = mergeIntersectionPropertyReflections(model.type, model.project); if (merged.length > 0) { const output = renderMergedIntersectionDeclaration(this, customizedModel, opts, merged, superPartials); return output.replace(/^## Type declaration$/gm, ''); @@ -1559,19 +2114,7 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { ? defaultOutput.replace('\\{', '\\{ ') : defaultOutput; - const output = withCorrectWhitespaceAtStart - // Remove any backticks - .replace(/`/g, '') - // Remove any `` and `` tags - .replace(//g, '') - .replace(/<\/code>/g, ''); - - // Only wrap in if NOT inside a function signature - if (this._insideFunctionSignature) { - return output; - } - - return `${output}`; + return wrapInlineTypeAsCode(withCorrectWhitespaceAtStart, this._insideFunctionSignature); }, /** * This modifies the output of union types by wrapping everything in a single `foo | bar` tag instead of doing `foo` | `bar` @@ -1579,23 +2122,11 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { */ unionType: model => { const collapsed = tryCollapseExpandedOAuthStrategyUnion(model, this); - const defaultOutput = superPartials.unionType(collapsed ?? model); - - const output = defaultOutput - // Escape stuff that would be turned into markdown - .replace(/__experimental_/g, '\\_\\_experimental\\_') - // Remove any backticks - .replace(/`/g, '') - // Remove any `` and `` tags - .replace(//g, '') - .replace(/<\/code>/g, ''); - - // Only wrap in if NOT inside a function signature - if (this._insideFunctionSignature) { - return output; - } - - return `${output}`; + // Escape `__experimental_` before wrap-as-code so the double-underscore isn't parsed as markdown bold. + const escaped = superPartials + .unionType(collapsed ?? model) + .replace(/__experimental_/g, '\\_\\_experimental\\_'); + return wrapInlineTypeAsCode(escaped, this._insideFunctionSignature); }, /** * This ensures that everything is wrapped in a single codeblock @@ -1603,27 +2134,7 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {{ forceParameterType?: boolean; typeSeparator?: string }} [options] */ functionType: (model, options) => { - const defaultOutput = superPartials.functionType(model, options); - const delimiter = this.options.getValue('useCodeBlocks') ? ';\n' : '; '; - - const output = defaultOutput - .split(delimiter) - .map(fn => - fn - // Remove any backticks - .replace(/`/g, '') - // Remove any `` and `` tags - .replace(//g, '') - .replace(/<\/code>/g, ''), - ) - .join(delimiter); - - // Only wrap in if NOT inside a function signature - if (this._insideFunctionSignature) { - return output; - } - - return `${output}`; + return wrapInlineTypeAsCode(superPartials.functionType(model, options), this._insideFunctionSignature); }, /** * Copied from original theme. @@ -1730,41 +2241,14 @@ ${tabs} const theType = this.partials.someType(el); const needsParens = el.type === 'union' || isArrayElementReferenceInliningToUnion(el); const defaultOutput = needsParens ? `(${theType})[]` : `${theType}[]`; - - const output = defaultOutput - // Remove any backticks - .replace(/`/g, '') - // Remove any `` and `` tags - .replace(//g, '') - .replace(/<\/code>/g, ''); - - // Only wrap in if NOT inside a function signature - if (this._insideFunctionSignature) { - return output; - } - - return `${output}`; + return wrapInlineTypeAsCode(defaultOutput, this._insideFunctionSignature); }, /** * Ensures that reflection types (like Simplify wrapped types) are wrapped in a single codeblock * @param {import('typedoc').ReflectionType} model */ reflectionType: model => { - const defaultOutput = superPartials.reflectionType(model); - - const output = defaultOutput - // Remove any backticks - .replace(/`/g, '') - // Remove any `` and `` tags - .replace(//g, '') - .replace(/<\/code>/g, ''); - - // Only wrap in if NOT inside a function signature - if (this._insideFunctionSignature) { - return output; - } - - return `${output}`; + return wrapInlineTypeAsCode(superPartials.reflectionType(model), this._insideFunctionSignature); }, /** * Hide "Extends" and "Extended by" sections @@ -1860,35 +2344,1067 @@ function swap(arr, i, j) { return arr; } +// --------------------------------------------------------------------------- +// Extracted-methods helpers (moved from extract-methods.mjs). +// --------------------------------------------------------------------------- + /** - * @param {import('typedoc').DeclarationReflection} prop - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + * @param {number} level + * @param {string} text */ -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()); +function markdownHeading(level, text) { + const l = Math.min(Math.max(level, 1), 6); + return `${'#'.repeat(l)} ${text}`; } /** - * 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 + * Heading whose visible title is a type/identifier (nominal single-parameter object sections) needs to get wrapped in backticks. * - * @param {import('typedoc').Type | undefined} t - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers - * @param {Set} seenReflectionIds - * @returns {boolean} + * @param {number} level + * @param {string} text */ -function isCallablePropertyValueType(t, helpers, seenReflectionIds) { - if (!t) { - return false; +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. + * + * @param {string | undefined} str + */ +function removeLineBreaksForTableCell(str) { + return str?.replace(/\r?\n/g, ' ').replace(/ {2,}/g, ' '); +} + +/** + * 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; } - if (t.type === 'optional' && 'elementType' in t) { - return isCallablePropertyValueType( - /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType, + return `\`${text}\``; +} + +/** + * Build the description cell for a nested `param.field` row: summary text plus a leading `**Deprecated.**` marker (with the `@deprecated` tag's body) when that tag is present. Matches what typedoc-plugin-markdown's `parseParams` emits for inline `{ … }` params — fields documented only via `@deprecated` (no summary) otherwise rendered as `—` here, hiding important guidance. + * Returns `'—'` when both summary and `@deprecated` are empty. + * + * @param {import('typedoc').Comment | undefined} comment + */ +function renderNestedRowDescription(comment) { + const summaryText = comment?.summary?.length ? displayPartsToString(comment.summary).trim() : ''; + const deprecatedTag = comment?.blockTags?.find(t => t.tag === '@deprecated'); + const deprecatedText = deprecatedTag ? displayPartsToString(deprecatedTag.content).trim() : ''; + const deprecatedMd = deprecatedTag ? `**Deprecated.**${deprecatedText ? ` ${deprecatedText}` : ''}` : ''; + const combined = [summaryText, deprecatedMd].filter(Boolean).join(' '); + return combined || '—'; +} + +/** + * @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(''); +} + +/** + * 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').SignatureReflection} sig + */ +function signatureIsDeprecated(sig) { + const c = sig.comment; + if (!c) return false; + if (typeof c.hasModifier === 'function' && c.hasModifier('@deprecated')) return true; + return c.blockTags?.some(t => t.tag === '@deprecated') ?? false; +} + +/** + * typedoc maps `@param paramName description` to `parameter.comment` during conversion of the + * signature that physically owns the JSDoc. With overloads + an implementation, the impl's `@param` + * tags don't transfer to overload parameters — even when typedoc copies the impl's comment onto + * each overload's `sig.comment.blockTags`. Without descriptions on `param.comment`, typedoc-plugin-markdown + * drops the Description column from the parameters table. + * + * Overlay missing parameter comments from any matching `@param` block tag on the signature comment + * so the rendered table shows the descriptions the author wrote on the implementation's JSDoc. + * + * @param {import('typedoc').SignatureReflection} sig + */ +function overlayParamCommentsFromSignatureBlockTags(sig) { + const params = sig.parameters; + if (!params?.length) return; + const blockTags = sig.comment?.blockTags; + if (!blockTags?.length) return; + for (const p of params) { + if (p.comment?.summary?.length) continue; + const tag = blockTags.find(t => t.tag === '@param' && t.name === p.name); + if (!tag?.content?.length) continue; + p.comment = new Comment(tag.content); + } +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + * @returns {import('typedoc').SignatureReflection | undefined} + */ +function getPrimaryCallSignature(decl) { + if (decl.signatures?.length) { + // Prefer the first non-`@deprecated` overload. typedoc treats each overload as a separate + // signature, and selecting a deprecated one usually means rendering the form users shouldn't + // use — and (often) one whose JSDoc isn't where the canonical `@param` descriptions live. + const firstActive = decl.signatures.find(s => !signatureIsDeprecated(s)); + return firstActive ?? decl.signatures[0]; + } + const t = decl.type; + 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]; + } + } + // `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; +} + +/** + * 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 = unwrapOptional(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 = unwrapOptional(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; +} + +/** + * Resolve a property's type to its declared default when it references a `TypeParameter` reflection. + * Used when a generic alias is inlined into an intersection (e.g. `CreateParams = { … } & MetadataParams` where the `& MetadataParams` arm gets inlined as a reflection, losing the named reference) so the property table surfaces the resolved default type instead of the open type-parameter name (e.g. `TPublic` → `OrganizationPublicMetadata`). + * + * @param {import('typedoc').Type | undefined} t + * @returns {import('typedoc').Type | undefined} + */ +function substituteTypeParameterDefaultsInType(t) { + if (!t) { + return t; + } + if (/** @type {{ type?: string }} */ (t).type === 'optional' && 'elementType' in t) { + const el = /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + const next = substituteTypeParameterDefaultsInType(el); + if (next && next !== el) { + return new OptionalType(/** @type {import('typedoc').SomeType} */ (/** @type {unknown} */ (next))); + } + return t; + } + if (t instanceof ReferenceType) { + const tp = /** @type {{ kindOf?: (k: number) => boolean, default?: import('typedoc').Type }} */ (t.reflection); + if (tp?.kindOf?.(ReflectionKind.TypeParameter) && tp.default) { + return tp.default; + } + } + return t; +} + +/** + * Clone each property and apply `substituteTypeParameterDefaultsInType` to its type — see comment on `substituteTypeParameterDefaultsInType` for the motivating case. + * + * @param {import('typedoc').DeclarationReflection[]} children + */ +function substituteTypeParameterDefaultsInChildren(children) { + return children.map(child => { + const next = substituteTypeParameterDefaultsInType(child.type); + if (next === child.type) { + return child; + } + return Object.assign(Object.create(Object.getPrototypeOf(child)), child, { type: next }); + }); +} + +/** + * @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; +} + +/** + * 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 = unwrapOptional(valueType, { deep: true }); + 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, + ); +} + +/** + * 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, 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 t = substituteGenericParamRefsInType(p.type, instantiationMap) ?? p.type; + const typeStr = typeStringForTypeScriptFence(t); + return `${rest}${p.name}${opt}: ${typeStr}`; + }) ?? []; + const retT = substituteGenericParamRefsInType(sig.type, instantiationMap) ?? sig.type; + const ret = retT ? typeStringForTypeScriptFence(retT) : 'void'; + // Qualified names (`emailCode.sendCode`) aren't valid in `function foo.bar()` syntax; use the bare last segment — the parent is already in the heading above. + const displayName = memberName.includes('.') ? memberName.split('.').pop() : memberName; + return `function ${displayName}${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 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 = + c.blockTags + ?.filter(t => t.tag === '@returns') + .map(t => formatReturnsLineFromTag(t)) + .filter(Boolean) ?? []; + return [summary, block, ...returnsLines].filter(Boolean).join('\n\n'); +} + +/** + * @param {import('typedoc').DeclarationReflection} prop + */ +function propertyReflectionTypeIsNever(prop) { + const ty = unwrapOptional(prop.type, { deep: true }); + 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; +} + +/** + * Collect the set of string-literal keys from a type used as the second argument to `Omit` or `Pick` — a single literal (`'organizationId'`) or a union of literals (`'a' | 'b'`). Returns `undefined` if the type isn't a literal/literal-union (e.g. a `keyof` reference we can't resolve here), in which case callers should fall through to the generic-instantiation path. + * + * @param {import('typedoc').Type | undefined} t + * @returns {Set | undefined} + */ +function collectLiteralStringKeys(t) { + if (!t) { + return undefined; + } + if (t.type === 'literal' && typeof (/** @type {{ value: unknown }} */ (t).value) === 'string') { + return new Set([/** @type {{ value: string }} */ (t).value]); + } + if (t.type === 'union') { + const u = /** @type {import('typedoc').UnionType} */ (t); + /** @type {Set} */ + const keys = new Set(); + for (const inner of u.types) { + const got = collectLiteralStringKeys(inner); + if (!got) { + return undefined; + } + for (const k of got) keys.add(k); + } + return keys.size ? keys : undefined; + } + return undefined; +} + +/** + * Filter each arm to `Property` reflections and dedupe by name, returning a single sorted list + * (or `undefined` if every arm was empty). Used for intersection / union / generic-instantiation + * arm merges in {@link resolveDeclarationWithObjectMembers}. + * + * Default behavior is "later arm wins" overwrite (right for intersections + generic instantiations + * where every arm's properties are part of the final shape). For unions, set + * `{ skipNever: true, pickBetter: true }`: union arms often use `prop?: never` as a discriminator, + * so we drop those and keep the documentable branch when names collide. + * + * @param {Array} arms + * @param {{ skipNever?: boolean, pickBetter?: boolean }} [options] + * @returns {import('typedoc').DeclarationReflection[] | undefined} + */ +function mergePropertyArms(arms, options) { + /** @type {Map} */ + const byName = new Map(); + for (const arm of arms) { + if (!arm?.length) { + continue; + } + for (const c of arm) { + if (!c.kindOf(ReflectionKind.Property)) { + continue; + } + if (options?.skipNever && propertyReflectionTypeIsNever(c)) { + continue; + } + const existing = byName.get(c.name); + if (!existing) { + byName.set(c.name, c); + continue; + } + byName.set(c.name, options?.pickBetter ? pickBetterUnionPropertyCandidate(existing, c) : c); + } + } + if (byName.size === 0) { + return undefined; + } + const merged = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); + return substituteTypeParameterDefaultsInChildren(merged); +} + +/** + * Resolve a parameter / property type to the list of `Property` reflections that should populate + * a nested rows table. TypeDoc applies `@param parent.prop` descriptions onto these reflections. + * + * Cases: + * - `reflection` (inline `{...}`): the declaration's own children. + * - `reference` to a named interface/alias: the target's children, or — for generic instantiations + * like `ClerkPaginationParams<{ status?: … }>` — the base properties merged with each typeArg's. + * - `intersection`: every `&` arm's properties combined (later arm wins on name collision). + * - `union`: every `|` arm's properties combined, dropping `prop?: never` discriminators and + * preferring the branch with more documentation on collisions. + * - `optional`: unwrap and recurse. + * + * Returns `undefined` when nothing resolves (so callers can `if (!children?.length)` cheaply). + * The children list may include non-`Property` kinds for direct `reflection` / `reference` cases — + * callers that need only `Property` should filter; merge cases (typeArgs / intersection / union) + * pre-filter via {@link mergePropertyArms}. + * + * @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, project) { + if (!t) { + return undefined; + } + if (t.type === 'optional') { + return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType, project); + } + if (t.type === 'array') { + return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').ArrayType} */ (t).elementType, project); + } + if (t.type === 'reflection') { + const children = t.declaration?.children; + return children?.length ? children : undefined; + } + if (t.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + /** + * `Omit` / `Pick` — TypeScript built-in utilities. They have no project reflection to look up, and falling through to the generic-instantiation path below would merge K's properties (zero, since K is a literal type) without applying the filter — Omit/Pick would silently behave like an identity. Resolve `X` to its property list, then keep/drop by the literal-string keys in `K`. Without this, `Array>` shows no nested rows because `decl` is undefined. + */ + if ((ref.name === 'Omit' || ref.name === 'Pick') && (ref.typeArguments?.length ?? 0) === 2) { + const baseChildren = resolveDeclarationWithObjectMembers(ref.typeArguments[0], project); + const keys = collectLiteralStringKeys(ref.typeArguments[1]); + if (baseChildren?.length && keys) { + const keep = ref.name === 'Pick' ? c => keys.has(c.name) : c => !keys.has(c.name); + return baseChildren.filter(keep); + } + } + 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) { + return undefined; + } + /** + * Generic instantiation: TypeDoc often attaches pagination fields only to the target alias's + * own `children` and omits `decl.type`, so returning the base early drops the type argument + * object. Merge the base (`decl.type` if present, else `decl.children` as a fallback) with + * each type argument's properties. + */ + const typeArgs = ref.typeArguments ?? []; + if (typeArgs.length > 0) { + const baseFromType = decl.type ? resolveDeclarationWithObjectMembers(decl.type, project) : undefined; + const base = baseFromType ?? (decl.children?.length ? decl.children : undefined); + const argArms = typeArgs.map(ta => resolveDeclarationWithObjectMembers(ta, project)); + return mergePropertyArms([base, ...argArms]); + } + if (decl.children?.length) { + return substituteTypeParameterDefaultsInChildren(decl.children); + } + if (decl.type) { + return resolveDeclarationWithObjectMembers(decl.type, project); + } + return undefined; + } + if (t.type === 'intersection') { + const inter = /** @type {import('typedoc').IntersectionType} */ (t); + return mergePropertyArms(inter.types.map(inner => resolveDeclarationWithObjectMembers(inner, project))); + } + if (t.type === 'union') { + const u = /** @type {import('typedoc').UnionType} */ (t); + return mergePropertyArms( + u.types.map(inner => resolveDeclarationWithObjectMembers(inner, project)), + { skipNever: true, pickBetter: true }, + ); + } + return undefined; +} + +/** + * Build the name cell for a nominal-nested row. Uses `?.` when the parent param is optional (so `options?.foo` mirrors how it would be accessed at runtime) and `.` when required — same rule as `clerkParametersTable.flattenParams` in `custom-theme.mjs`. Appends `?` to the child name when the child property itself is optional, matching how the inline-flatten path renders `params.field?` via the standard parametersTable. + * + * @param {import('typedoc').ParameterReflection} parentParam + * @param {import('typedoc').DeclarationReflection} child + */ +function formatNestedParamNameColumn(parentParam, child) { + const sep = parentParam.flags?.isOptional ? '?.' : '.'; + const childOptional = child.flags?.isOptional ? '?' : ''; + return `\`${parentParam.name}${sep}${child.name}${childOptional}\``; +} + +/** + * 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 unless `@standalonePage` is set (`standalone-page-tag.mjs`). + * + * @param {import('typedoc').SomeType | undefined} t + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + */ +function parameterTypeLinksToStandaloneMdxPage(t, ctx) { + const bare = unwrapOptional(t); + if (!bare) { + return false; + } + // `Array`: the standalone page documents the element shape, not the array signature. + // Surface both — the Type column links to the element page, and nested `params.` rows flatten the element so readers don't have to navigate just to see field names. + if (bare.type === 'array') { + return false; + } + if (bare.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (bare); + if (isInlineModifierWithoutStandalonePage(ref.reflection)) { + 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 + */ +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 []; + } + } + + if (parameterTypeLinksToStandaloneMdxPage(param.type, ctx)) { + return []; + } + + const project = /** @type {import('typedoc').ProjectReflection | undefined} */ (param.project ?? ctx.page?.project); + const children = resolveDeclarationWithObjectMembers(param.type, project); + if (!children?.length) { + return []; + } + const props = 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 typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; + const nestedNameCol = formatNestedParamNameColumn(param, child); + const nestedDescRaw = renderNestedRowDescription(child.comment); + // Strip line breaks so multi-line `
    ` / paragraph descriptions don't shatter the markdown + // table row (which must be a single line). Matches the treatment already applied to typeCell. + const nestedDesc = removeLineBreaksForTableCell(nestedDescRaw) ?? '—'; + rows.push(`| ${nestedNameCol} | ${typeCell} | ${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 the section title (the type's name), the resolved property list, and the + * source `typeDecl` for `@experimental` / `@deprecated` checks. + * + * @param {import('typedoc').SomeType | undefined} t + * @param {import('typedoc').ProjectReflection} project + * @returns {{ sectionTitle: string, children: 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') { + return undefined; + } + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + const 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, children: typeDecl.children, typeDecl }; + } + 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, project) : undefined; + const children = fromResolvedType?.length ? fromResolvedType : typeDecl.children; + if (!children?.length) { + return undefined; + } + return { sectionTitle: typeDecl.name, children, typeDecl }; + } + return undefined; +} + +/** + * 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 + */ +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); +} + +/** + * `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; + } + } +} + +/** 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').SignatureReflection} sig + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {number} [headingLevel] Defaults to 4 (reference-object format); pass 2 for page format. + */ +function trySingleNominalParameterTypeSection(sig, ctx, headingLevel = 4) { + const params = sig.parameters ?? []; + if (params.length !== 1) { + return undefined; + } + const p = params[0]; + const project = sig.project ?? ctx.page?.project; + const nominal = resolveNominalObjectTypeForSingleParam(p.type, project); + if (!nominal) { + return undefined; + } + const props = nominal.children.filter(c => c.kindOf(ReflectionKind.Property)); + if (props.length === 0) { + return undefined; + } + if (!isNominalParamTypeDocumented(nominal.typeDecl, props)) { + return undefined; + } + const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => + ctx.partials.propertiesTable( + props, + /** @type {Parameters[1]} */ + ({ + kind: nominal.typeDecl.kind, + isEventProps: false, + applyAllowlistedPropertyTableRowFilters: false, + }), + ), + ); + if (!tableMd?.trim()) { + return undefined; + } + return [markdownHeadingInlineCode(headingLevel, nominal.sectionTitle), '', tableMd, ''].join('\n'); +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {Map | undefined} instantiationMap + * @param {number} [headingLevel] Defaults to 4 (reference-object format); pass 2 for page format. + */ +function parametersMarkdownTable(sig, ctx, instantiationMap, headingLevel = 4) { + const sigForDisplay = signatureWithInstantiation(sig, instantiationMap); + const params = sigForDisplay.parameters ?? []; + if (params.length === 0) { + return ''; + } + + const singleNominal = trySingleNominalParameterTypeSection(sigForDisplay, ctx, headingLevel); + if (singleNominal) { + return singleNominal; + } + + let tableMd = renderMemberTableOmittingExampleBlocks(params, ctx, () => ctx.partials.parametersTable(params)); + /** @type {string[]} */ + const nested = []; + for (const p of params) { + nested.push(...nestedParameterRowsFromDocumentedProperties(p, ctx)); + } + if (nested.length) { + // Nested rows are always 3-column (`| name | type | description |`). If the base table came + // back 2-column (no direct param has a comment, so `clerkParametersTable` omits the Description + // column), pad the base to 3 columns so the appended rows don't malform the table. + if (!parameterTableHasDescriptionColumn(tableMd)) { + tableMd = padParameterTableWithEmptyDescriptionColumn(tableMd); + } + tableMd = `${tableMd.trimEnd()}\n${nested.join('\n')}\n`; + } + + return [markdownHeading(headingLevel, ReflectionKind.pluralString(ReflectionKind.Parameter)), '', tableMd, ''].join( + '\n', + ); +} + +/** + * Detects whether a markdown parameter table (pipe-format) already has a `Description` column. + * HTML tables render on a single line — treat those as "already correct" (they carry their own + * column structure via `` cells and aren't concatenated with pipe-row nested output). + * + * @param {string} tableMd + */ +function parameterTableHasDescriptionColumn(tableMd) { + if (!tableMd) return true; + const firstLine = tableMd.split('\n', 1)[0].trim(); + if (!firstLine.startsWith('|')) return true; + return /\|\s*Description\s*\|/.test(firstLine); +} + +/** + * Append an empty Description column to a 2-column pipe-format parameter table so that appending + * 3-column nested-row output doesn't malform it. Header gets ` Description |`, divider gets + * ` ------ |`, data rows get ` - |`. + * + * @param {string} tableMd + */ +function padParameterTableWithEmptyDescriptionColumn(tableMd) { + const lines = tableMd.split('\n'); + let sawDivider = false; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (!line.startsWith('|') || !line.endsWith('|')) continue; + if (i === 0) { + lines[i] = line.replace(/\|\s*$/, ' Description |'); + } else if (!sawDivider && /^\|[\s\-|:]+\|$/.test(line)) { + lines[i] = line.replace(/\|\s*$/, ' ------ |'); + sawDivider = true; + } else { + lines[i] = line.replace(/\|\s*$/, ' - |'); + } + } + return lines.join('\n'); +} + +/** + * @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. + * E.g. `navigate: CustomNavigation` in clerk.ts + * + * @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, ); @@ -1953,5 +3469,3 @@ function isCallablePropertyValueType(t, helpers, seenReflectionIds) { } return false; } - -export { isCallableInterfaceProperty }; diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs index 731606fcd42..c9d01408e5d 100644 --- a/.typedoc/extract-methods.mjs +++ b/.typedoc/extract-methods.mjs @@ -1,1470 +1,367 @@ // @ts-check /** - * TypeDoc plugin that runs during the markdown render pass. For each reference-object page listed in {@link REFERENCE_OBJECT_CONFIG} (e.g. `shared/clerk/clerk.mdx`), this listener: + * TypeDoc plugin that runs during the markdown render pass. Acts as a text slicer for the + * parent-page shape that `custom-theme.mjs` produces: * - * - copies the body of the page's `## Properties` section (table only, no heading) into a sibling `properties.mdx`, - * - mutates `output.contents` to drop the `## Properties` section from the main page, - * - writes one `methods/.mdx` per callable child on the reflection (and on any `extraMethodInterfaces`), alongside the main page in that resource folder. + * - Synchronously (before prettier) slices `## `methodName()`` and `## `namespace`` H2 sections + * from the parent's `output.contents` and reshapes each into `methods/.mdx`. The parent's + * sections stay in place — the slicer only reads. + * - Queues a `preWriteAsyncJob` to run **after** typedoc-plugin-markdown's prettier job. Once + * `output.contents` is prettier-formatted, extract the Properties body from between the + * `` markers that `custom-theme.mjs` wraps around `## + * Properties`, and write it to sibling `properties.mdx` — inheriting prettier's column + * alignment. Strip the entire `## Properties` region + markers from the parent page contents. * - * Must load **after** `custom-plugin.mjs` so its `MarkdownPageEvent.END` listener — which applies link replacements to `output.contents` — runs first. The Properties body we copy out is then already in its final, replaced form. + * Must load **after** `custom-plugin.mjs` so its `MarkdownPageEvent.END` listener — which applies + * link replacements to `output.contents` — runs first (link replacements pass through HTML comment + * markers unchanged). * - * 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). The theme context comes from `theme.getRenderContext(output)` on the live page event — no second TypeDoc convert pass. - * - * 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`. + * Has zero reflection access: all generation lives in `custom-theme.mjs`. */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { - Comment, - IntersectionType, - OptionalType, - ReferenceType, - ReflectionKind, - ReflectionType, - UnionType, -} from 'typedoc'; import { MarkdownPageEvent } from 'typedoc-plugin-markdown'; -import { applyTodoStrippingToComment } from './comment-utils.mjs'; -import { - applyCatchAllMdReplacements, - applyRelativeLinkReplacements, - stripReferenceObjectPropertiesSection, -} from './custom-plugin.mjs'; -import { isCallableInterfaceProperty } from './custom-theme.mjs'; -import { removeLineBreaks } from './markdown-helpers.mjs'; import { BACKEND_API_CONFIG, REFERENCE_OBJECT_CONFIG } from './reference-objects.mjs'; - -/** - * `'reference'` (default): each `methods/.mdx` opens with `### foo()` and uses an H4 `#### Parameters` heading — matches the reference-object pages that aggregate many methods. - * `'page'`: skip the method-name title and use an H2 `## Parameters` heading — for one-method-per-docs-page surfaces, like the backend API endpoints. - * @typedef {'reference' | 'page'} MethodFormat - */ - -/** @param {string} pageUrl */ -function methodFormatForPageUrl(pageUrl) { - return pageUrl in BACKEND_API_CONFIG - ? /** @type {MethodFormat} */ ('page') - : /** @type {MethodFormat} */ ('reference'); -} import { toFileSlug } from './slug.mjs'; -import { isInlineModifierWithoutStandalonePage } from './standalone-page-tag.mjs'; -import { unwrapOptional } from './type-utils.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}`; -} - -/** - * Heading whose visible title is a type/identifier (nominal single-parameter object sections) needs to get wrapped in backticks. + * Parse a parent-page `## `methodName()`` H2 section into its logical parts. Handles both natural + * typedoc-plugin-markdown sections (backend classes) and synthesized aggregator sections (shared + * interfaces, extraMethodInterfaces). * - * @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. + * Expected shape (mixture across natural/synthesized — reshape logic tolerates either): * - * @param {string | undefined} str - */ -function removeLineBreaksForTableCell(str) { - return str?.replace(/\r?\n/g, ' ').replace(/ {2,}/g, ' '); -} - -/** - * TypeDoc `code` display parts often already include backticks (same as {@link Comment.combineDisplayParts}). - * Wrapping again would produce `` `Client` `` in MDX. + * ## `name()` + * + * ```typescript + * function name(...) + * ``` + * ### `TypeName` (nominal single-arg) OR ### Parameters + * + * ### Returns + * `Type` — (or just `Type` with no ` — `) * - * @param {string} text + * @param {string} section */ -function codeDisplayPartToMarkdown(text) { - const trimmed = text.trim(); - if (trimmed.length >= 2 && trimmed.startsWith('`') && trimmed.endsWith('`')) { - return trimmed; - } - return `\`${text}\``; -} +function parseParentAggregatorSection(section) { + const lines = section.split('\n'); + // Method titles are either bare (`## methodName()`) or qualified (`## `emailCode.sendCode()``). + // Qualified names use `.` as separator (from `@extractMethods` namespaces). + const titleMatch = lines[0]?.match(/^## `?([A-Za-z_$][A-Za-z0-9_$.]*)`?\(\)`?/); + const name = titleMatch?.[1] ?? ''; -/** - * Build the description cell for a nested `param.field` row: summary text plus a leading `**Deprecated.**` marker (with the `@deprecated` tag's body) when that tag is present. Matches what typedoc-plugin-markdown's `parseParams` emits for inline `{ … }` params — fields documented only via `@deprecated` (no summary) otherwise rendered as `—` here, hiding important guidance. - * Returns `'—'` when both summary and `@deprecated` are empty. - * - * @param {import('typedoc').Comment | undefined} comment - */ -function renderNestedRowDescription(comment) { - const summaryText = comment?.summary?.length ? displayPartsToString(comment.summary).trim() : ''; - const deprecatedTag = comment?.blockTags?.find(t => t.tag === '@deprecated'); - const deprecatedText = deprecatedTag ? displayPartsToString(deprecatedTag.content).trim() : ''; - const deprecatedMd = deprecatedTag ? `**Deprecated.**${deprecatedText ? ` ${deprecatedText}` : ''}` : ''; - const combined = [summaryText, deprecatedMd].filter(Boolean).join(' '); - return combined || '—'; -} + const sigStart = lines.findIndex((l, i) => i > 0 && l.startsWith('```typescript')); + const sigEnd = sigStart === -1 ? -1 : lines.findIndex((l, i) => i > sigStart && l === '```'); -/** - * @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(''); -} + const descBeforeSig = sigStart === -1 ? lines.length : sigStart; + const descLines = lines.slice(1, descBeforeSig); + trimBlankBoundariesInPlace(descLines); + const description = descLines.join('\n'); -/** - * @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]; -} + const signature = sigStart !== -1 && sigEnd !== -1 ? lines.slice(sigStart, sigEnd + 1).join('\n') : ''; -/** - * 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').SignatureReflection} sig - */ -function signatureIsDeprecated(sig) { - const c = sig.comment; - if (!c) return false; - if (typeof c.hasModifier === 'function' && c.hasModifier('@deprecated')) return true; - return c.blockTags?.some(t => t.tag === '@deprecated') ?? false; -} - -/** - * typedoc maps `@param paramName description` to `parameter.comment` during conversion of the - * signature that physically owns the JSDoc. With overloads + an implementation, the impl's `@param` - * tags don't transfer to overload parameters — even when typedoc copies the impl's comment onto - * each overload's `sig.comment.blockTags`. Without descriptions on `param.comment`, typedoc-plugin-markdown - * drops the Description column from the parameters table. - * - * Overlay missing parameter comments from any matching `@param` block tag on the signature comment - * so the rendered table shows the descriptions the author wrote on the implementation's JSDoc. - * - * @param {import('typedoc').SignatureReflection} sig - */ -function overlayParamCommentsFromSignatureBlockTags(sig) { - const params = sig.parameters; - if (!params?.length) return; - const blockTags = sig.comment?.blockTags; - if (!blockTags?.length) return; - for (const p of params) { - if (p.comment?.summary?.length) continue; - const tag = blockTags.find(t => t.tag === '@param' && t.name === p.name); - if (!tag?.content?.length) continue; - p.comment = new Comment(tag.content); - } -} - -/** - * @param {import('typedoc').DeclarationReflection} decl - * @returns {import('typedoc').SignatureReflection | undefined} - */ -function getPrimaryCallSignature(decl) { - if (decl.signatures?.length) { - // Prefer the first non-`@deprecated` overload. typedoc treats each overload as a separate - // signature, and selecting a deprecated one usually means rendering the form users shouldn't - // use — and (often) one whose JSDoc isn't where the canonical `@param` descriptions live. - const firstActive = decl.signatures.find(s => !signatureIsDeprecated(s)); - return firstActive ?? decl.signatures[0]; - } - const t = decl.type; - 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]; - } - } - // `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; -} - -/** - * 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 = unwrapOptional(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 = unwrapOptional(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; -} - -/** - * Resolve a property's type to its declared default when it references a `TypeParameter` reflection. - * Used when a generic alias is inlined into an intersection (e.g. `CreateParams = { … } & MetadataParams` where the `& MetadataParams` arm gets inlined as a reflection, losing the named reference) so the property table surfaces the resolved default type instead of the open type-parameter name (e.g. `TPublic` → `OrganizationPublicMetadata`). - * - * @param {import('typedoc').Type | undefined} t - * @returns {import('typedoc').Type | undefined} - */ -function substituteTypeParameterDefaultsInType(t) { - if (!t) { - return t; - } - if (/** @type {{ type?: string }} */ (t).type === 'optional' && 'elementType' in t) { - const el = /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; - const next = substituteTypeParameterDefaultsInType(el); - if (next && next !== el) { - return new OptionalType(/** @type {import('typedoc').SomeType} */ (/** @type {unknown} */ (next))); - } - return t; - } - if (t instanceof ReferenceType) { - const tp = /** @type {{ kindOf?: (k: number) => boolean, default?: import('typedoc').Type }} */ (t.reflection); - if (tp?.kindOf?.(ReflectionKind.TypeParameter) && tp.default) { - return tp.default; - } - } - return t; -} - -/** - * Clone each property and apply `substituteTypeParameterDefaultsInType` to its type — see comment on `substituteTypeParameterDefaultsInType` for the motivating case. - * - * @param {import('typedoc').DeclarationReflection[]} children - */ -function substituteTypeParameterDefaultsInChildren(children) { - return children.map(child => { - const next = substituteTypeParameterDefaultsInType(child.type); - if (next === child.type) { - return child; - } - return Object.assign(Object.create(Object.getPrototypeOf(child)), child, { type: next }); - }); -} - -/** - * @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` (`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 - */ -function shouldExtractCallableMember(decl, ctx) { - if (decl.kind === ReflectionKind.Method) { - return true; - } - if ( - decl.kind === ReflectionKind.Property || - decl.kind === ReflectionKind.Accessor || - decl.kind === ReflectionKind.Variable - ) { - return isCallableInterfaceProperty(decl, ctx.helpers); - } - return false; -} - -/** - * 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 = unwrapOptional(valueType, { deep: true }); - 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 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, - /** @type {Parameters[1]} */ - ({ - kind: ReflectionKind.Interface, - isEventProps: false, - applyAllowlistedPropertyTableRowFilters: false, - }), - ), + const afterSig = sigEnd === -1 ? 1 + descBeforeSig : sigEnd + 1; + const returnsIdx = lines.findIndex((l, i) => i >= afterSig && /^### Returns\b/.test(l)); + const searchEnd = returnsIdx === -1 ? lines.length : returnsIdx; + const paramsIdx = lines.findIndex( + (l, i) => i >= afterSig && i < searchEnd && /^### /.test(l) && !/^### Returns\b/.test(l), ); - const chunks = [title, '', description, '', tableMd].filter(Boolean); - const raw = chunks.join('\n\n'); - 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 - */ -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; -} - -/** - * Split the `## Properties` section out of page contents, returning the body (no heading) and the page contents with the Properties section removed. - * - * Operates on the in-memory `output.contents` of a `MarkdownPageEvent`; the caller writes `properties.mdx` and assigns the stripped string back to `output.contents`. The page's own END pipeline (link replacements) has already run by the time we get called, so the Properties body is in its final, replaced form — no re-application needed. - * - * @param {string} contents - * @returns {{ propertiesBody: string | undefined, stripped: string }} - */ -function splitPropertiesFromContents(contents) { - if (!contents) { - return { propertiesBody: undefined, stripped: contents }; - } - const propertiesBody = extractPropertiesSectionBody(contents); - const stripped = stripReferenceObjectPropertiesSection(contents); - return { propertiesBody, stripped }; -} - -/** - * 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, 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 t = substituteGenericParamRefsInType(p.type, instantiationMap) ?? p.type; - const typeStr = typeStringForTypeScriptFence(t); - return `${rest}${p.name}${opt}: ${typeStr}`; - }) ?? []; - const retT = substituteGenericParamRefsInType(sig.type, instantiationMap) ?? sig.type; - const ret = retT ? typeStringForTypeScriptFence(retT) : 'void'; - // Qualified names (`emailCode.sendCode`) aren't valid in `function foo.bar()` syntax; use the bare last segment — the parent is already in the heading above. - const displayName = memberName.includes('.') ? memberName.split('.').pop() : memberName; - return `function ${displayName}${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 - */ -/** - * `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; - } + let paramsHeadingLine = ''; + let paramsBody = ''; + if (paramsIdx !== -1) { + paramsHeadingLine = lines[paramsIdx]; + const paramsBodyLines = lines.slice(paramsIdx + 1, searchEnd); + trimBlankBoundariesInPlace(paramsBodyLines); + paramsBody = paramsBodyLines.join('\n'); } -} - -/** 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 - */ -function commentSummaryAndBody(comment) { - if (!comment) { - return ''; + let returnsBody = ''; + if (returnsIdx !== -1) { + const returnsLines = lines.slice(returnsIdx + 1); + trimBlankBoundariesInPlace(returnsLines); + returnsBody = returnsLines.join('\n'); } - 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 = - c.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); + return { name, description, signature, paramsHeadingLine, paramsBody, returnsBody }; } -/** - * @param {import('typedoc').DeclarationReflection} prop - */ -function propertyReflectionTypeIsNever(prop) { - const ty = unwrapOptional(prop.type, { deep: true }); - return ty?.type === 'intrinsic' && ty.name === 'never'; +/** @param {string[]} arr */ +function trimBlankBoundariesInPlace(arr) { + while (arr.length && arr[0].trim() === '') arr.shift(); + while (arr.length && arr[arr.length - 1].trim() === '') arr.pop(); } /** - * Union discriminators often use `otherProp?: never`. Prefer the branch with a documentable type. + * Take the `### Returns` body and produce the extracted-file `Returns .` + * prose that `buildMethodMdx` writes in via `formatReturnsLineFromTag(@returns)`. Two shapes are + * possible depending on the theme's `signatureReturns` partial: * - * @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; -} - -/** - * Collect the set of string-literal keys from a type used as the second argument to `Omit` or `Pick` — a single literal (`'organizationId'`) or a union of literals (`'a' | 'b'`). Returns `undefined` if the type isn't a literal/literal-union (e.g. a `keyof` reference we can't resolve here), in which case callers should fall through to the generic-instantiation path. + * - Condensed: `` `Type` — `` — single line with em-dash separator. Description can + * extend across multiple lines (embedded `> [!WARNING]` callouts from `@returns` source). + * - Uncondensed: `` `Type`\n\n `` — default typedoc-plugin-markdown format when the + * theme's condense heuristic doesn't apply (typically when the return type prints on multiple + * lines, e.g. object literals). * - * @param {import('typedoc').Type | undefined} t - * @returns {Set | undefined} - */ -function collectLiteralStringKeys(t) { - if (!t) { - return undefined; - } - if (t.type === 'literal' && typeof (/** @type {{ value: unknown }} */ (t).value) === 'string') { - return new Set([/** @type {{ value: string }} */ (t).value]); - } - if (t.type === 'union') { - const u = /** @type {import('typedoc').UnionType} */ (t); - /** @type {Set} */ - const keys = new Set(); - for (const inner of u.types) { - const got = collectLiteralStringKeys(inner); - if (!got) { - return undefined; - } - for (const k of got) keys.add(k); - } - return keys.size ? keys : undefined; - } - return undefined; -} - -/** - * Filter each arm to `Property` reflections and dedupe by name, returning a single sorted list - * (or `undefined` if every arm was empty). Used for intersection / union / generic-instantiation - * arm merges in {@link resolveDeclarationWithObjectMembers}. + * Empty string when the returns body only shows a type (no `@returns` text) — matches + * `buildMethodMdx` which only emits `Returns …` when `@returns` tag content is present. * - * Default behavior is "later arm wins" overwrite (right for intersections + generic instantiations - * where every arm's properties are part of the final shape). For unions, set - * `{ skipNever: true, pickBetter: true }`: union arms often use `prop?: never` as a discriminator, - * so we drop those and keep the documentable branch when names collide. - * - * @param {Array} arms - * @param {{ skipNever?: boolean, pickBetter?: boolean }} [options] - * @returns {import('typedoc').DeclarationReflection[] | undefined} + * @param {string} returnsBody */ -function mergePropertyArms(arms, options) { - /** @type {Map} */ - const byName = new Map(); - for (const arm of arms) { - if (!arm?.length) { - continue; - } - for (const c of arm) { - if (!c.kindOf(ReflectionKind.Property)) { - continue; - } - if (options?.skipNever && propertyReflectionTypeIsNever(c)) { - continue; - } - const existing = byName.get(c.name); - if (!existing) { - byName.set(c.name, c); - continue; - } - byName.set(c.name, options?.pickBetter ? pickBetterUnionPropertyCandidate(existing, c) : c); +function extractReturnsProseFromReturnsBody(returnsBody) { + if (!returnsBody) return ''; + const dashIdx = returnsBody.indexOf(' — '); + let rightSide = ''; + if (dashIdx !== -1) { + rightSide = returnsBody.slice(dashIdx + 3); + } else { + const paragraphs = returnsBody.split('\n\n'); + if (paragraphs.length >= 2) { + rightSide = paragraphs.slice(1).join('\n\n'); } } - if (byName.size === 0) { - return undefined; - } - const merged = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); - return substituteTypeParameterDefaultsInChildren(merged); + if (!rightSide) return ''; + // Strip leading `- ` or `* ` prefix (source `@returns - foo` renders with a leading dash) and + // lowercase the first character — matches custom-theme's `formatReturnsLineFromTag` / + // `normalizeReturnsBody`. + const normalized = rightSide.replace(/^\s*[-*]\s+/, '').trim(); + return `Returns ${normalized.charAt(0).toLowerCase()}${normalized.slice(1)}`; } /** - * Resolve a parameter / property type to the list of `Property` reflections that should populate - * a nested rows table. TypeDoc applies `@param parent.prop` descriptions onto these reflections. - * - * Cases: - * - `reflection` (inline `{...}`): the declaration's own children. - * - `reference` to a named interface/alias: the target's children, or — for generic instantiations - * like `ClerkPaginationParams<{ status?: … }>` — the base properties merged with each typeArg's. - * - `intersection`: every `&` arm's properties combined (later arm wins on name collision). - * - `union`: every `|` arm's properties combined, dropping `prop?: never` discriminators and - * preferring the branch with more documentation on collisions. - * - `optional`: unwrap and recurse. - * - * Returns `undefined` when nothing resolves (so callers can `if (!children?.length)` cheaply). - * The children list may include non-`Property` kinds for direct `reflection` / `reference` cases — - * callers that need only `Property` should filter; merge cases (typeArgs / intersection / union) - * pre-filter via {@link mergePropertyArms}. + * Reshape a parent-page property-table H2 section into the sub-doc shape produced by the + * marker-block path (`buildExtractMethodsNamespacePropertyTableMdx` / + * `buildPropertyTableDocMdx`). The parent's `## `name`` heading is demoted to `### `name``; + * everything after the heading (description + table body) is preserved verbatim so whitespace + * matches the marker-block output byte-for-byte. * - * @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} + * @param {string} section - full section text as returned by {@link findMethodH2Sections} + * @returns {string} */ -function resolveDeclarationWithObjectMembers(t, project) { - if (!t) { - return undefined; - } - if (t.type === 'optional') { - return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType, project); - } - if (t.type === 'array') { - return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').ArrayType} */ (t).elementType, project); - } - if (t.type === 'reflection') { - const children = t.declaration?.children; - return children?.length ? children : undefined; - } - if (t.type === 'reference') { - const ref = /** @type {import('typedoc').ReferenceType} */ (t); - /** - * `Omit` / `Pick` — TypeScript built-in utilities. They have no project reflection to look up, and falling through to the generic-instantiation path below would merge K's properties (zero, since K is a literal type) without applying the filter — Omit/Pick would silently behave like an identity. Resolve `X` to its property list, then keep/drop by the literal-string keys in `K`. Without this, `Array>` shows no nested rows because `decl` is undefined. - */ - if ((ref.name === 'Omit' || ref.name === 'Pick') && (ref.typeArguments?.length ?? 0) === 2) { - const baseChildren = resolveDeclarationWithObjectMembers(ref.typeArguments[0], project); - const keys = collectLiteralStringKeys(ref.typeArguments[1]); - if (baseChildren?.length && keys) { - const keep = ref.name === 'Pick' ? c => keys.has(c.name) : c => !keys.has(c.name); - return baseChildren.filter(keep); - } - } - 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) { - return undefined; - } - /** - * Generic instantiation: TypeDoc often attaches pagination fields only to the target alias's - * own `children` and omits `decl.type`, so returning the base early drops the type argument - * object. Merge the base (`decl.type` if present, else `decl.children` as a fallback) with - * each type argument's properties. - */ - const typeArgs = ref.typeArguments ?? []; - if (typeArgs.length > 0) { - const baseFromType = decl.type ? resolveDeclarationWithObjectMembers(decl.type, project) : undefined; - const base = baseFromType ?? (decl.children?.length ? decl.children : undefined); - const argArms = typeArgs.map(ta => resolveDeclarationWithObjectMembers(ta, project)); - return mergePropertyArms([base, ...argArms]); - } - if (decl.children?.length) { - return substituteTypeParameterDefaultsInChildren(decl.children); - } - if (decl.type) { - return resolveDeclarationWithObjectMembers(decl.type, project); - } - return undefined; - } - if (t.type === 'intersection') { - const inter = /** @type {import('typedoc').IntersectionType} */ (t); - return mergePropertyArms(inter.types.map(inner => resolveDeclarationWithObjectMembers(inner, project))); - } - if (t.type === 'union') { - const u = /** @type {import('typedoc').UnionType} */ (t); - return mergePropertyArms( - u.types.map(inner => resolveDeclarationWithObjectMembers(inner, project)), - { skipNever: true, pickBetter: true }, - ); - } - return undefined; +function reshapeAggregatorPropertyTableSectionToExtractedFile(section) { + const nlIdx = section.indexOf('\n'); + if (nlIdx === -1) return `${section.replace(/^## /, '### ').trimEnd()}\n`; + const rest = section.slice(nlIdx + 1); + const demotedTitle = section.slice(0, nlIdx).replace(/^## /, '### '); + return `${demotedTitle}\n${rest.trimEnd()}\n`; } /** - * Build the name cell for a nominal-nested row. Uses `?.` when the parent param is optional (so `options?.foo` mirrors how it would be accessed at runtime) and `.` when required — same rule as `clerkParametersTable.flattenParams` in `custom-theme.mjs`. Appends `?` to the child name when the child property itself is optional, matching how the inline-flatten path renders `params.field?` via the standard parametersTable. + * Reshape a parent-page `## `name()`` H2 section into the per-method extracted-file shape produced + * by `buildMethodMdx` (which the marker-block path historically wrote). The output is intended to + * be byte-identical to the marker-block output so we can drop the marker block emission entirely. * - * @param {import('typedoc').ParameterReflection} parentParam - * @param {import('typedoc').DeclarationReflection} child - */ -function formatNestedParamNameColumn(parentParam, child) { - const sep = parentParam.flags?.isOptional ? '?.' : '.'; - const childOptional = child.flags?.isOptional ? '?' : ''; - return `\`${parentParam.name}${sep}${child.name}${childOptional}\``; -} - -/** - * 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 unless `@standalonePage` is set (`standalone-page-tag.mjs`). + * - **`reference` format** (shared reference-object pages): keeps an `### `name()`` H3 title. + * Params heading is demoted to H4 (`#### `TypeName`` or `#### Parameters`). + * - **`page` format** (backend API pages, one method per docs page): no title. Params heading + * is promoted to H2 (`## `TypeName`` or `## Parameters`). * - * @param {import('typedoc').SomeType | undefined} t - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - */ -function parameterTypeLinksToStandaloneMdxPage(t, ctx) { - const bare = unwrapOptional(t); - if (!bare) { - return false; - } - // `Array`: the standalone page documents the element shape, not the array signature. - // Surface both — the Type column links to the element page, and nested `params.` rows flatten the element so readers don't have to navigate just to see field names. - if (bare.type === 'array') { - return false; - } - if (bare.type === 'reference') { - const ref = /** @type {import('typedoc').ReferenceType} */ (bare); - if (isInlineModifierWithoutStandalonePage(ref.reflection)) { - 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). + * Params heading is always followed by `\n\n` (blank line + blank line, i.e. `\n\n\n`) before the + * table body — matches the buildMethodMdx output. Every consecutive chunk is separated by a + * single blank line. * - * @param {import('typedoc').ParameterReflection} param - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {ReturnType} parts + * @param {'reference' | 'page'} methodFormat + * @returns {string} */ -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 []; - } - } +function reshapeAggregatorSectionToExtractedFile(parts, methodFormat) { + const returnsProse = extractReturnsProseFromReturnsBody(parts.returnsBody); + const descriptionWithReturns = returnsProse + ? parts.description + ? `${parts.description}\n\n${returnsProse}` + : returnsProse + : parts.description; - if (parameterTypeLinksToStandaloneMdxPage(param.type, ctx)) { - return []; + let outputParamsHeading = ''; + if (parts.paramsHeadingLine) { + outputParamsHeading = + methodFormat === 'reference' + ? parts.paramsHeadingLine.replace(/^### /, '#### ') + : parts.paramsHeadingLine.replace(/^### /, '## '); } - const project = /** @type {import('typedoc').ProjectReflection | undefined} */ (param.project ?? ctx.page?.project); - const children = resolveDeclarationWithObjectMembers(param.type, project); - if (!children?.length) { - return []; - } - const props = 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 typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; - const nestedNameCol = formatNestedParamNameColumn(param, child); - const nestedDescRaw = renderNestedRowDescription(child.comment); - // Strip line breaks so multi-line `
      ` / paragraph descriptions don't shatter the markdown - // table row (which must be a single line). Matches the treatment already applied to typeCell. - const nestedDesc = removeLineBreaksForTableCell(nestedDescRaw) ?? '—'; - rows.push(`| ${nestedNameCol} | ${typeCell} | ${nestedDesc} |`); + const chunks = []; + if (methodFormat === 'reference') chunks.push(`### \`${parts.name}()\``); + if (descriptionWithReturns) chunks.push(descriptionWithReturns); + if (parts.signature) chunks.push(parts.signature); + if (outputParamsHeading && parts.paramsBody) { + // Match buildMethodMdx: params heading is followed by a blank + blank before the table body. + chunks.push(`${outputParamsHeading}\n\n\n${parts.paramsBody}`); + } else if (outputParamsHeading) { + chunks.push(outputParamsHeading); } - return rows; + return chunks.join('\n\n').trim() + '\n'; } /** - * Merged / external references sometimes leave {@link ReferenceType.reflection} unset; resolve by name. + * Iterate `## `name()`` (method) and `## `name`` (property-table) H2 sections in the parent + * contents, returning each as `{ name, section, shape }` in document order. Sections extend from + * a `## ` heading until the next `## ` heading or end-of-file. Skips `## Properties` and any + * other H2s that aren't identifier-shaped headings (e.g. `## Overview`). * - * @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 the section title (the type's name), the resolved property list, and the - * source `typeDecl` for `@experimental` / `@deprecated` checks. + * A section is classified `propertyTable` when its title has no `()` — matches what + * `renderAggregatorPropertyTableSection` emits for `@extractMethods` namespace and non-callable + * object-shape members. * - * @param {import('typedoc').SomeType | undefined} t - * @param {import('typedoc').ProjectReflection} project - * @returns {{ sectionTitle: string, children: 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') { - return undefined; - } - const ref = /** @type {import('typedoc').ReferenceType} */ (t); - const 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; + * @param {string} contents + * @returns {{ name: string, section: string, shape: 'method' | 'propertyTable' }[]} + */ +function findMethodH2Sections(contents) { + if (!contents) return []; + const lines = contents.split('\n'); + /** @type {{ start: number, name: string, shape: 'method' | 'propertyTable' }[]} */ + const starts = []; + for (let i = 0; i < lines.length; i++) { + const method = lines[i].match(/^## `?([A-Za-z_$][A-Za-z0-9_$.]*)`?\(\)`?[ \t]*$/); + if (method) { + starts.push({ start: i, name: method[1], shape: 'method' }); + continue; } - return { sectionTitle: typeDecl.name, children: typeDecl.children, typeDecl }; - } - 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, project) : undefined; - const children = fromResolvedType?.length ? fromResolvedType : typeDecl.children; - if (!children?.length) { - return undefined; + const propTable = lines[i].match(/^## `([A-Za-z_$][A-Za-z0-9_$.]*)`[ \t]*$/); + if (propTable) { + starts.push({ start: i, name: propTable[1], shape: 'propertyTable' }); + } + } + /** @type {{ name: string, section: string, shape: 'method' | 'propertyTable' }[]} */ + const out = []; + for (let i = 0; i < starts.length; i++) { + const startLine = starts[i].start; + const nextInList = i + 1 < starts.length ? starts[i + 1].start : -1; + // Also stop at any other `## ` H2 that isn't in our list (e.g. `## Properties`). + let endLine = lines.length; + for (let j = startLine + 1; j < lines.length; j++) { + if (lines[j].startsWith('## ')) { + endLine = j; + break; + } } - return { sectionTitle: typeDecl.name, children, typeDecl }; + if (nextInList !== -1 && nextInList < endLine) endLine = nextInList; + // Trim trailing separator lines like `***` typedoc places between natural sections. + let sectionLines = lines.slice(startLine, endLine); + while (sectionLines.length && /^(\s|\*+\s*)*$/.test(sectionLines[sectionLines.length - 1])) sectionLines.pop(); + out.push({ name: starts[i].name, section: sectionLines.join('\n'), shape: starts[i].shape }); } - return undefined; -} - -/** - * 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 - */ -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); + return out; } /** - * 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. + * Extract the Properties body from between markers. Called AFTER prettier so the returned body + * is column-aligned, matching pre-refactor behavior where the properties.mdx was sliced from the + * already-prettified page. * - * @param {import('typedoc').SignatureReflection} sig - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {string} contents * @returns {string | undefined} */ -/** - * @param {import('typedoc').SignatureReflection} sig - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @param {number} [headingLevel] Defaults to 4 (reference-object format); pass 2 for page format. - */ -function trySingleNominalParameterTypeSection(sig, ctx, headingLevel = 4) { - const params = sig.parameters ?? []; - if (params.length !== 1) { - return undefined; - } - const p = params[0]; - const project = sig.project ?? ctx.page?.project; - const nominal = resolveNominalObjectTypeForSingleParam(p.type, project); - if (!nominal) { - return undefined; - } - const props = nominal.children.filter(c => c.kindOf(ReflectionKind.Property)); - if (props.length === 0) { - return undefined; - } - if (!isNominalParamTypeDocumented(nominal.typeDecl, props)) { - return undefined; - } - const tableMd = renderMemberTableOmittingExampleBlocks(props, ctx, () => - ctx.partials.propertiesTable( - props, - /** @type {Parameters[1]} */ - ({ - kind: nominal.typeDecl.kind, - isEventProps: false, - applyAllowlistedPropertyTableRowFilters: false, - }), - ), - ); - if (!tableMd?.trim()) { - return undefined; - } - return [markdownHeadingInlineCode(headingLevel, nominal.sectionTitle), '', tableMd, ''].join('\n'); -} - -/** - * @param {import('typedoc').SignatureReflection} sig - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @param {Map | undefined} instantiationMap - * @param {number} [headingLevel] Defaults to 4 (reference-object format); pass 2 for page format. - */ -function parametersMarkdownTable(sig, ctx, instantiationMap, headingLevel = 4) { - const sigForDisplay = signatureWithInstantiation(sig, instantiationMap); - const params = sigForDisplay.parameters ?? []; - if (params.length === 0) { - return ''; - } - - const singleNominal = trySingleNominalParameterTypeSection(sigForDisplay, ctx, headingLevel); - if (singleNominal) { - return singleNominal; - } - - let tableMd = renderMemberTableOmittingExampleBlocks(params, ctx, () => ctx.partials.parametersTable(params)); - /** @type {string[]} */ - const nested = []; - for (const p of params) { - nested.push(...nestedParameterRowsFromDocumentedProperties(p, ctx)); - } - if (nested.length) { - tableMd = `${tableMd.trimEnd()}\n${nested.join('\n')}\n`; - } - - return [markdownHeading(headingLevel, ReflectionKind.pluralString(ReflectionKind.Parameter)), '', tableMd, ''].join( - '\n', - ); -} - -/** - * @param {import('typedoc').DeclarationReflection} decl - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @param {{ qualifiedName?: string, methodFormat?: MethodFormat }} [options] Nested namespace methods use `parent.child` for headings / signatures. - */ -function buildMethodMdx(decl, ctx, options = {}) { - const name = options.qualifiedName ?? decl.name; - const methodFormat = options.methodFormat ?? 'reference'; - const sig = getPrimaryCallSignature(decl); - if (!sig) { - return ''; - } - // `'page'` format renders one method per docs page, so the page is anchored by a containing file/heading upstream — no in-page `### foo()` title needed. `'reference'` format aggregates many methods on one page and uses `### foo()` to disambiguate them. - const title = methodFormat === 'page' ? '' : `### \`${name}()\``; - const paramsHeadingLevel = methodFormat === 'page' ? 2 : 4; - /** 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 = comment === sig.comment ? '' : appendSignatureOnlyReturns(decl.comment, sig.comment); - if (sigReturns) { - description = [description, sigReturns].filter(Boolean).join('\n\n'); - } - const instantiationMap = getGenericInstantiationMapFromCallableProperty(decl); - const ts = ['```typescript', formatTypeScriptSignature(sig, name, instantiationMap), '```'].join('\n'); - const skipParametersSection = - Boolean(decl.comment?.hasModifier('@skipParametersSection')) || - Boolean(sig.comment?.hasModifier('@skipParametersSection')); - if (!skipParametersSection) { - overlayParamCommentsFromSignatureBlockTags(sig); - } - const paramsMd = skipParametersSection ? '' : parametersMarkdownTable(sig, ctx, instantiationMap, paramsHeadingLevel); - - // 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 headSource = title ? [title, '', description].join('\n') : description; - const head = applyCatchAllMdReplacements(applyRelativeLinkReplacements(headSource)); - const paramsProcessed = paramsMd ? applyCatchAllMdReplacements(applyRelativeLinkReplacements(paramsMd)) : ''; - const chunks = head ? [head, ts] : [ts]; - if (paramsProcessed) { - chunks.push(paramsProcessed); - } - return chunks.join('\n\n').trim() + '\n'; -} - -/** - * @param {import('typedoc').DeclarationReflection} decl - */ -function hasExtractMethodsModifier(decl) { - return Boolean(decl.comment?.hasModifier('@extractMethods')); +function extractPropertiesBetweenMarkers(contents) { + const m = contents.match(/\n?([\s\S]*?)\n?/); + if (!m) return undefined; + const body = m[1].trim(); + return body.length ? body : undefined; } /** - * @typedef {{ filePath: string, content: string }} ExtractedFile - */ - -/** - * Collect `methods/-.mdx` content for each direct member of an `@extractMethods` object-like type: callables via {@link buildMethodMdx}, non-callables with a resolvable object shape via {@link buildPropertyTableDocMdx}. Plus a `.mdx` index for non-callable members. + * Remove the entire `## Properties` region plus its markers, producing output equivalent to the + * pre-refactor `stripReferenceObjectPropertiesSection` (which did + * `contents.replace(/\n## Properties\n+[\s\S]*$/, '').trimEnd() + '\n'`). * - * Supports inline object literals and named references (`interface` / object-like `type` aliases) via {@link resolveDeclarationWithObjectMembers}. + * Pages where `## Properties` is the very first line (no leading `\n`) intentionally do NOT get + * their section stripped — matches pre-refactor behavior. In that case we still remove the wrapper + * markers themselves so they don't leak into the final MDX. * - * @param {import('typedoc').DeclarationReflection} parentDecl - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @param {string} outDir - * @returns {ExtractedFile[]} - */ -function processExtractMethodsNamespace(parentDecl, ctx, outDir) { - const project = ctx.page?.project; - const members = resolveDeclarationWithObjectMembers(parentDecl.type, project) ?? []; - if (members.length === 0) { - console.warn( - `[extract-methods] @extractMethods on "${parentDecl.name}" requires an object-like type with members; skipping nested extraction`, + * @param {string} contents + * @returns {string} + */ +function stripPropertiesSectionWithMarkers(contents) { + if (!contents.includes('')) { + return contents; + } + // Case 1: leading `\n## Properties` — strip the entire section (heading + markers + body) and + // any trailing content, then re-add a single trailing newline. Mirrors the pre-refactor regex. + if (/\n## Properties\n+/.test(contents)) { + const stripped = contents.replace( + /\n## Properties\n+[\s\S]*?[\s\S]*$/, + '', ); - return []; - } - const parentName = parentDecl.name; - /** @type {ExtractedFile[]} */ - const collected = []; - /** @type {import('typedoc').DeclarationReflection[]} */ - const nonCallableMembers = []; - for (const nested of members) { - if (nested.name.startsWith('__')) { - continue; - } - const nd = /** @type {import('typedoc').DeclarationReflection} */ (nested); - const fileSlug = `${toFileSlug(parentName)}-${toFileSlug(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; - } - collected.push({ filePath, content: mdx }); - continue; - } - nonCallableMembers.push(nd); - const propTableMdx = buildPropertyTableDocMdx(parentName, nd, ctx); - if (!propTableMdx) { - continue; - } - collected.push({ filePath, content: propTableMdx }); - } - if (nonCallableMembers.length) { - const namespaceMdx = buildExtractMethodsNamespacePropertyTableMdx(parentDecl, nonCallableMembers, ctx); - if (namespaceMdx) { - const namespacePath = path.join(outDir, `${toFileSlug(parentName)}.mdx`); - collected.push({ filePath: namespacePath, content: namespaceMdx }); - } - } - return collected; -} - -/** - * Collect (path, content) pairs for each callable/`@extractMethods` child on `decl`. Callers are responsible for writing — see {@link load} which prettifies then writes. - * - * @param {import('typedoc').DeclarationReflection} decl - * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx - * @param {string} outDir - * @param {MethodFormat} [methodFormat] - * @returns {ExtractedFile[]} - */ -function extractCallableMembersFromDeclaration(decl, ctx, outDir, methodFormat = 'reference') { - if (!decl.children) { - return []; - } - /** @type {ExtractedFile[]} */ - const collected = []; - for (const child of decl.children) { - if (child.name.startsWith('__')) { - continue; - } - const childDecl = /** @type {import('typedoc').DeclarationReflection} */ (child); - - if (hasExtractMethodsModifier(childDecl)) { - collected.push(...processExtractMethodsNamespace(childDecl, ctx, outDir)); - continue; - } - - if (shouldExtractCallableMember(childDecl, ctx)) { - const mdx = buildMethodMdx(childDecl, ctx, { methodFormat }); - if (mdx) { - const fileName = `${toFileSlug(child.name)}.mdx`; - const filePath = path.join(outDir, fileName); - collected.push({ filePath, content: mdx }); - } - } - } - return collected; -} - -/** - * @param {import('typedoc-plugin-markdown').MarkdownPageEvent} output - * @returns {string | undefined} - */ -function matchReferenceObjectPageUrl(output) { - if (!output.url) { - return undefined; + return stripped.trimEnd() + '\n'; } - const normalized = output.url.replace(/\\/g, '/'); - if (normalized in REFERENCE_OBJECT_CONFIG) return normalized; - if (normalized in BACKEND_API_CONFIG) return normalized; - return undefined; -} - -/** @param {string} pageUrl */ -function configEntryForPageUrl(pageUrl) { - return /** @type {import('./reference-objects.mjs').REFERENCE_OBJECT_CONFIG[keyof typeof REFERENCE_OBJECT_CONFIG]} */ ( - REFERENCE_OBJECT_CONFIG[/** @type {keyof typeof REFERENCE_OBJECT_CONFIG} */ (pageUrl)] ?? - BACKEND_API_CONFIG[/** @type {keyof typeof BACKEND_API_CONFIG} */ (pageUrl)] - ); + // Case 2: `## Properties` at start-of-file (billing-namespace pattern). Pre-refactor left the + // section in place. Strip just the marker comments plus any adjacent blank lines prettier may + // have inserted around them. + return contents + .replace(/\n+/g, '') + .replace(/\n+\n*/g, '\n'); } /** - * Plugin entry: registers a `MarkdownPageEvent.END` listener that, for each page in {@link REFERENCE_OBJECT_CONFIG}, queues a `preWriteAsyncJob` to extract Properties + methods. - * - * The job runs **after** typedoc-plugin-markdown's own prettier job (also a `preWriteAsyncJob`, queued during `renderDocument`) — so by the time we read `output.contents`, the Properties table is already prettier-formatted, and our `properties.mdx` inherits that formatting. Method files are written raw (matching the pre-refactor behavior, where extract-methods.mjs also bypassed prettier for `methods/*.mdx`). - * - * Must be loaded **after** `custom-plugin.mjs` so its END listener (link replacements + heading filtering) runs first. + * Plugin entry: registers a `MarkdownPageEvent.END` listener that text-slices per-H2 sections + * from the parent contents synchronously (pre-prettier), then defers Properties extraction / + * marker stripping until after prettier via `preWriteAsyncJobs`. * * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ export function load(app) { - app.renderer.on(MarkdownPageEvent.END, output => { - const pageUrl = matchReferenceObjectPageUrl(output); - if (!pageUrl) { - return; - } - const entry = configEntryForPageUrl(pageUrl); - const methodFormat = methodFormatForPageUrl(pageUrl); - const decl = /** @type {import('typedoc').DeclarationReflection | undefined} */ (output.model); - if (!decl?.children) { - console.warn(`[extract-methods] No children on reflection for ${pageUrl}, skipping`); - return; - } - const project = output.project; - if (!project) { - console.warn(`[extract-methods] No project on page event for ${pageUrl}, skipping`); - return; - } - const theme = /** @type {InstanceType | undefined} */ ( - app.renderer.theme - ); - if (!theme || typeof theme.getRenderContext !== 'function') { - console.warn(`[extract-methods] Renderer theme not ready for ${pageUrl}, skipping`); - return; - } - const ctx = /** @type {import('typedoc-plugin-markdown').MarkdownThemeContext} */ (theme.getRenderContext(output)); - - const objectDir = path.dirname(output.filename); - const outDir = path.join(objectDir, 'methods'); - - /** @type {ExtractedFile[]} */ - const methodFiles = extractCallableMembersFromDeclaration(decl, ctx, outDir, methodFormat); - const extraMethodInterfaces = 'extraMethodInterfaces' in entry ? entry.extraMethodInterfaces : undefined; - 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; + // Priority -200: fire after `custom-theme.mjs`'s emit listener (registered at -100), which in + // turn fires after `custom-plugin.mjs`'s link replacements (default priority 0). By the time + // this slicer runs, aggregator sections have been synthesized and the Properties section has + // been wrapped — and no other END listener will touch our contents after us. + app.renderer.on( + MarkdownPageEvent.END, + output => { + const contents = output.contents ?? ''; + const normUrl = (output.url ?? '').replace(/\\/g, '/'); + const pageUrl = output.url ?? '(unknown)'; + const objectDir = path.dirname(output.filename); + const methodsDir = path.join(objectDir, 'methods'); + + // Slice `## `methodName()`` and `## `namespace`` H2 sections from the parent's pre-prettier + // content and reshape into per-method / per-property-table extracted files. Handles every + // callable member (including `@extractMethods` namespace callables emitted with qualified + // names) and every `@extractMethods` namespace / non-callable-with-object-shape property. + const methodFormat = + normUrl in REFERENCE_OBJECT_CONFIG ? 'reference' : normUrl in BACKEND_API_CONFIG ? 'page' : undefined; + if (methodFormat) { + const h2Sections = findMethodH2Sections(contents); + if (h2Sections.length) { + fs.mkdirSync(methodsDir, { recursive: true }); + for (const { name, section, shape } of h2Sections) { + const extracted = + shape === 'propertyTable' + ? reshapeAggregatorPropertyTableSectionToExtractedFile(section) + : reshapeAggregatorSectionToExtractedFile(parseParentAggregatorSection(section), methodFormat); + const filename = `${name.split('.').map(toFileSlug).join('-')}.mdx`; + fs.writeFileSync(path.join(methodsDir, filename), extracted, 'utf-8'); + } + console.log(`[extract-methods] ${pageUrl}: text-slice wrote ${h2Sections.length} method file(s)`); } - methodFiles.push(...extractCallableMembersFromDeclaration(extraDecl, ctx, outDir, methodFormat)); - } - } - - output.preWriteAsyncJobs.push(() => { - fs.mkdirSync(objectDir, { recursive: true }); - - // `output.contents` is already prettier-formatted by typedoc-plugin-markdown's earlier - // pre-write job. Extract the Properties body from it (also formatted), write it out, - // then strip the section so the main page no longer ships it. - const { propertiesBody, stripped } = splitPropertiesFromContents(output.contents ?? ''); - if (propertiesBody) { - const propertiesPath = path.join(objectDir, 'properties.mdx'); - fs.writeFileSync(propertiesPath, `${propertiesBody.trimEnd()}\n`, 'utf-8'); - console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), propertiesPath)}`); - } - if (stripped && stripped !== output.contents) { - output.contents = stripped; } - if (methodFiles.length === 0) { + if (!contents.includes('')) { return; } - fs.mkdirSync(outDir, { recursive: true }); - for (const { filePath, content } of methodFiles) { - fs.writeFileSync(filePath, content, 'utf-8'); - console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); - } - console.log(`[extract-methods] ${pageUrl}: wrote ${methodFiles.length} method file(s)`); - }); - }); + + output.preWriteAsyncJobs.push(() => { + const post = output.contents ?? ''; + + // Extract the (now prettier-formatted) Properties body from between its markers, write it + // out, then strip the whole `## Properties` region (heading + markers + body) from the + // parent page. + const propertiesBody = extractPropertiesBetweenMarkers(post); + if (propertiesBody) { + fs.mkdirSync(objectDir, { recursive: true }); + const propertiesPath = path.join(objectDir, 'properties.mdx'); + fs.writeFileSync(propertiesPath, `${propertiesBody.trimEnd()}\n`, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), propertiesPath)}`); + } + output.contents = stripPropertiesSectionWithMarkers(post); + }); + }, + -200, + ); } diff --git a/.typedoc/extract-returns-and-params.mjs b/.typedoc/extract-returns-and-params.mjs index 4cec2a53cee..28c39dcc65c 100644 --- a/.typedoc/extract-returns-and-params.mjs +++ b/.typedoc/extract-returns-and-params.mjs @@ -6,8 +6,36 @@ import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +/** + * Packages whose pages this script post-processes (Returns + Parameters extracted into sibling + * `-return.mdx` / `-params.mdx` files). Pages under these packages get their nominal-type + * parameter heading normalized back to the generic `## Parameters` before extraction — see + * {@link normalizeNominalParametersHeading}. + */ +const EXTRACT_RETURNS_AND_PARAMS_PACKAGES = ['react']; + const LEGACY_HOOK_NAMES = new Set(['use-sign-in-1', 'use-sign-up-1']); +/** + * `custom-theme.mjs` uniformly swaps a method's `## Parameters` heading to `` ## `TypeName` `` + * when the sole argument is a nominal object type — so every parameters table in the docs uses + * one code path. On the pages this script processes, callers still expect the sibling + * `-params.mdx` extraction to find a section keyed on the literal `## Parameters` heading, so we + * rename the nominal H2 heading back to `## Parameters` in place before extraction. + * + * Only H2 headings without parentheses are treated as params-type headings: `` ## `TypeName` `` ✔, + * `` ## `methodName()` `` ✘ (that's a method name on an aggregator page). Rewrites the first match + * only — hook/single-callable pages have at most one such heading per file. + * + * @param {string} content + * @returns {string} + */ +function normalizeNominalParametersHeading(content) { + return content.replace(/(^|\n)## `([A-Za-z_$][A-Za-z0-9_$]*)`(\s*)(\n|$)/, (_m, before, _name, trailing, after) => { + return `${before}## Parameters${trailing}${after}`; + }); +} + /** * Returns legacy hook output info or null if not a legacy hook. * @param {string} filePath @@ -203,8 +231,7 @@ function getAllMdxFiles(dir) { * Main function to process all files from the react package */ function main() { - const packages = ['react']; - const dirs = packages.map(folder => path.join(__dirname, 'temp-docs', folder)); + const dirs = EXTRACT_RETURNS_AND_PARAMS_PACKAGES.map(folder => path.join(__dirname, 'temp-docs', folder)); for (const dir of dirs) { if (!fs.existsSync(dir)) { @@ -219,7 +246,14 @@ function main() { let paramsCount = 0; for (const filePath of mdxFiles) { - const content = fs.readFileSync(filePath, 'utf-8'); + const original = fs.readFileSync(filePath, 'utf-8'); + // Normalize the theme's uniform `` ## `TypeName` `` back to `## Parameters` so both the + // sibling extraction below AND downstream consumers reading the page see the generic + // heading. Persist the rewrite when it changed anything. + const content = normalizeNominalParametersHeading(original); + if (content !== original) { + fs.writeFileSync(filePath, content, 'utf-8'); + } const legacyTarget = getLegacyHookTarget(filePath); // Extract Returns sections diff --git a/packages/backend/src/api/endpoints/BillingApi.ts b/packages/backend/src/api/endpoints/BillingApi.ts index 6011d0c9a26..6a8eea4305b 100644 --- a/packages/backend/src/api/endpoints/BillingApi.ts +++ b/packages/backend/src/api/endpoints/BillingApi.ts @@ -13,25 +13,19 @@ const userBasePath = '/users'; /** @generateWithEmptyComment */ export type GetPlanListParams = ClerkPaginationRequest<{ - /** - * Filters plans by the type of payer. - */ + /** Filters plans by the type of payer. */ payerType: 'org' | 'user'; }>; /** @inline */ export type CancelSubscriptionItemParams = { - /** - * Whether the Subscription Item should be canceled immediately. If `false`, the Subscription Item will be canceled at the end of the current billing period. - */ + /** Whether the Subscription Item should be canceled immediately. If `false`, the Subscription Item will be canceled at the end of the current billing period. */ endNow?: boolean; }; /** @inline */ export type ExtendSubscriptionItemFreeTrialParams = { - /** - * The date to extend the free trial to. Must be in the future and not more than 365 days from the current trial end date. - */ + /** The date to extend the free trial to. Must be in the future and not more than 365 days from the current trial end date. */ extendTo: Date; }; diff --git a/packages/backend/src/api/endpoints/EnterpriseConnectionApi.ts b/packages/backend/src/api/endpoints/EnterpriseConnectionApi.ts index ccbcc15d0d1..ccfecd53255 100644 --- a/packages/backend/src/api/endpoints/EnterpriseConnectionApi.ts +++ b/packages/backend/src/api/endpoints/EnterpriseConnectionApi.ts @@ -9,13 +9,9 @@ const basePath = '/enterprise_connections'; /** @generateWithEmptyComment */ export type EnterpriseConnectionListParams = ClerkPaginationRequest<{ - /** - * Filters enterprise connections by Organization ID. - */ + /** Filters enterprise connections by Organization ID. */ organizationId?: string; - /** - * Filters enterprise connections by active status. If `true`, only active connections are returned. If `false`, only inactive connections are returned. If omitted, all connections are returned. - */ + /** Filters enterprise connections by active status. If `true`, only active connections are returned. If `false`, only inactive connections are returned. If omitted, all connections are returned. */ active?: boolean; }>; diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index d3053261556..24deb85a0a5 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -320,9 +320,9 @@ export type UpdateUserParams = { legalAcceptedAt?: Date; /** The locale of the user in BCP-47 format (e.g. `'en-US'`). */ locale?: string; - /** If `true`, the user can delete themselves with the Frontend API. */ + /** Whether the user can delete themselves with the Frontend API. */ deleteSelfEnabled?: boolean; - /** If `true`, the user can create Organizations with the Frontend API. */ + /** Whether the user can create Organizations with the Frontend API. */ createOrganizationEnabled?: boolean; /** The maximum number of Organizations the user can create. `0` means unlimited. */ createOrganizationsLimit?: number; diff --git a/packages/backend/src/webhooks.ts b/packages/backend/src/webhooks.ts index 3f1e7c69392..c45733c7b65 100644 --- a/packages/backend/src/webhooks.ts +++ b/packages/backend/src/webhooks.ts @@ -4,13 +4,9 @@ import { Webhook } from 'standardwebhooks'; import type { WebhookEvent } from './api/resources/Webhooks'; import { errorThrower } from './util/shared'; -/** - * @inline - */ +/** @inline */ export type VerifyWebhookOptions = { - /** - * The signing secret for the webhook. It's recommended to use the [`CLERK_WEBHOOK_SIGNING_SECRET` environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#webhooks) instead. - */ + /** The signing secret for the webhook. It's recommended to use the [`CLERK_WEBHOOK_SIGNING_SECRET` environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#webhooks) instead. */ signingSecret?: string; }; diff --git a/packages/expo/src/local-credentials/useLocalCredentials/useLocalCredentials.ts b/packages/expo/src/local-credentials/useLocalCredentials/useLocalCredentials.ts index 1705353f808..77a819adc68 100644 --- a/packages/expo/src/local-credentials/useLocalCredentials/useLocalCredentials.ts +++ b/packages/expo/src/local-credentials/useLocalCredentials/useLocalCredentials.ts @@ -190,13 +190,9 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { * @return A promise that will reject if value cannot be stored on the device. */ setCredentials, - /** - * Indicates whether there are any credentials stored on the device. - */ + /** Whether there are any credentials stored on the device. */ hasCredentials: hasLocalAuthCredentials, - /** - * Indicates whether the stored credentials belong to the signed in user. When there is no signed-in user the value will always be `false`. - */ + /** Whether the stored credentials belong to the signed in user. When there is no signed-in user the value will always be `false`. */ userOwnsCredentials, /** * Removes the stored credentials from the device. diff --git a/packages/nextjs/src/server/createGetAuth.ts b/packages/nextjs/src/server/createGetAuth.ts index dfd60fa861b..1a866bd733b 100644 --- a/packages/nextjs/src/server/createGetAuth.ts +++ b/packages/nextjs/src/server/createGetAuth.ts @@ -14,13 +14,14 @@ import { detectClerkMiddleware, getHeader } from './headers-utils'; import type { RequestLike } from './types'; import { assertAuthStatus } from './utils'; +/** @generateWithEmptyComment */ export type GetAuthOptions = { + /** The token types that the `getAuth()` function will accept. */ acceptsToken?: GetAuthDataFromRequestOptions['acceptsToken']; } & PendingSessionOptions; /** - * The async variant of our old `createGetAuth` allows for asynchronous code inside its callback. - * Should be used with function like `auth()` that are already asynchronous. + * The async variant of our old `createGetAuth` allows for asynchronous code inside its callback. Should be used with function like `auth()` that are already asynchronous. */ export const createAsyncGetAuth = ({ debugLoggerName, @@ -94,8 +95,8 @@ 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 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`. + * @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](!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. * * @example diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index 4957f7b6796..647d71908d8 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -17,9 +17,7 @@ import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvid import { useAuthBase } from './useAuthBase'; import { createGetToken, createSignOut } from './utils'; -/** - * @inline - */ +/** @inline */ type UseAuthOptions = PendingSessionOptions | undefined | null; /** @@ -35,7 +33,7 @@ type UseAuthOptions = PendingSessionOptions | undefined | null; * @unionReturnHeadings * ["Loading", "Signed out", "Signed in (no active organization)", "Signed in (with active organization)"] * - * @param [options] - An object containing options for the `useAuth()` hook. `treatPendingAsSignedOut` indicates whether pending sessions are considered as signed out or not. Defaults to `true`. + * @param options - An object containing options for the `useAuth()` hook. * * @function * diff --git a/packages/shared/src/react/billing/payment-element.tsx b/packages/shared/src/react/billing/payment-element.tsx index ea63015df5c..2c493622847 100644 --- a/packages/shared/src/react/billing/payment-element.tsx +++ b/packages/shared/src/react/billing/payment-element.tsx @@ -93,27 +93,15 @@ type internalStripeAppearance = { spacingUnit: string; }; -/** - * @interface - */ +/** @interface */ export type PaymentElementProviderProps = { - /** - * A 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 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; - /** - * Specifies whether to fetch for the current user or Organization. - * - * @default 'user' - */ + /** Specifies whether to fetch for the current user or Organization. Defaults to `'user'`. */ for?: ForPayerType; - /** - * A 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; }; @@ -293,22 +281,16 @@ export type UsePaymentElementReturn = { * A function that resets the payment form to its initial, empty state. */ reset: () => Promise; - /** - * Indicates whether the payment form UI has been rendered and is ready for user input. This is useful for disabling a submit button until the form is interactive. - */ + /** Whether the payment form UI has been rendered and is ready for user input. This is useful for disabling a submit button until the form is interactive. */ isFormReady: boolean; } & ( | { - /** - * An object containing information about the initialized payment provider. It is `undefined` until `isProviderReady` is `true`. - */ + /** An object containing information about the initialized payment provider. It is `undefined` until `isProviderReady` is `true`. */ provider: { /** The name of the payment provider. */ name: 'stripe'; }; - /** - * Indicates whether the underlying payment provider (e.g. Stripe) has been fully initialized. - */ + /** Whether the underlying payment provider (e.g. Stripe) has been fully initialized. */ isProviderReady: true; } | { diff --git a/packages/shared/src/react/contexts.tsx b/packages/shared/src/react/contexts.tsx index 0b35abc54e2..903cbb7bc0c 100644 --- a/packages/shared/src/react/contexts.tsx +++ b/packages/shared/src/react/contexts.tsx @@ -55,31 +55,17 @@ export function useInitialStateContext(): InitialState | undefined { const OptionsContext = React.createContext({}); -/** - * @interface - */ +/** @interface */ export type UseCheckoutOptions = { - /** - * Specifies if the checkout is for an Organization. - * - * @default 'user' - */ + /** Specifies if the checkout is for an Organization. Defaults to `'user'`. */ for?: ForPayerType; - /** - * The billing period for the Plan. - */ + /** The billing period for the Plan. */ planPeriod: BillingSubscriptionPlanPeriod; - /** - * The ID of the Subscription Plan to check out (e.g. `cplan_xxx`). - */ + /** The ID of the Subscription Plan to check out (e.g. `cplan_xxx`). */ planId: string; - /** - * The number of total seats to check out for - */ + /** The number of total seats to check out for */ seatsQuantity?: number; - /** - * The specific price ID to check out for, used when the desired price ID is not the current default price - */ + /** The specific price ID to check out for, used when the desired price ID is not the current default price */ priceId?: string; }; @@ -89,9 +75,7 @@ const __experimental_CheckoutProvider = ({ children, ...rest }: PropsWithChildre return {children}; }; -/** - * @internal - */ +/** @internal */ function useOptionsContext(): ClerkOptions { const context = React.useContext(OptionsContext); if (context === undefined) { @@ -100,9 +84,7 @@ function useOptionsContext(): ClerkOptions { return context; } -/** - * @internal - */ +/** @internal */ function useAssertWrappedByClerkProvider(displayNameOrFn: string | (() => void)): void { const ctx = React.useContext(ClerkInstanceContext); diff --git a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx index e6ecc3137e6..fe24f786472 100644 --- a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx +++ b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx @@ -29,11 +29,7 @@ type BillingHookConfig { - /** - * Specifies whether to fetch for the current user or Organization. - * - * @default 'user' - */ + /** Specifies whether to fetch for the current user or Organization. Defaults to `'user'`. */ for?: ForPayerType; } diff --git a/packages/shared/src/react/hooks/useAPIKeys.tsx b/packages/shared/src/react/hooks/useAPIKeys.tsx index 69bd41b552b..158e42e2d25 100644 --- a/packages/shared/src/react/hooks/useAPIKeys.tsx +++ b/packages/shared/src/react/hooks/useAPIKeys.tsx @@ -6,23 +6,15 @@ import type { PaginatedHookConfig, PaginatedResources } from '../types'; import { createCacheKeys } from './createCacheKeys'; import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite'; -/** - * @interface - */ +/** @interface */ export type UseAPIKeysParams = PaginatedHookConfig< GetAPIKeysParams & { - /** - * If `true`, a request will be triggered when the hook is mounted. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted. Defaults to `true`. */ enabled?: boolean; } >; -/** - * @interface - */ +/** @interface */ export type UseAPIKeysReturn = PaginatedResources< APIKeyResource, T extends { infinite: true } ? true : false diff --git a/packages/shared/src/react/hooks/useOAuthConsent.types.ts b/packages/shared/src/react/hooks/useOAuthConsent.types.ts index 659157bbe57..dfc00e1aa10 100644 --- a/packages/shared/src/react/hooks/useOAuthConsent.types.ts +++ b/packages/shared/src/react/hooks/useOAuthConsent.types.ts @@ -1,42 +1,22 @@ import type { ClerkAPIResponseError } from '../../errors/clerkApiResponseError'; import type { GetOAuthConsentInfoParams, OAuthConsentInfo } from '../../types'; -/** - * @interface - */ +/** @interface */ export type UseOAuthConsentParams = Pick & { - /** - * If `true`, the previous data will be kept in the cache until new data is fetched. - * - * @default true - */ + /** Whether the previous data will be kept in the cache until new data is fetched. Defaults to `true`. */ keepPreviousData?: boolean; - /** - * If `true`, a request will be triggered when the hook is mounted and the user is signed in. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted and the user is signed in. Defaults to `true`. */ enabled?: boolean; }; -/** - * @interface - */ +/** @interface */ export type UseOAuthConsentReturn = { - /** - * The OAuth consent screen metadata returned by Clerk, or `undefined` before the first successful fetch. - */ + /** The OAuth consent screen metadata returned by Clerk, or `undefined` before the first successful fetch. */ data: OAuthConsentInfo | undefined; - /** - * Any error that occurred during the data fetch, or `null` if no error occurred. - */ + /** Any error that occurred during the data fetch, or `null` if no error occurred. */ error: ClerkAPIResponseError | null; - /** - * Whether the initial consent metadata fetch is still in progress. - */ + /** Whether the initial consent metadata fetch is still in progress. */ isLoading: boolean; - /** - * Whether any request is still in flight, including background updates. - */ + /** Whether any request is still in flight, including background updates. */ isFetching: boolean; }; diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx index 127c622cc54..973db7e2c6d 100644 --- a/packages/shared/src/react/hooks/useOrganization.tsx +++ b/packages/shared/src/react/hooks/useOrganization.tsx @@ -20,9 +20,7 @@ import { createCacheKeys } from './createCacheKeys'; import { useAttemptToEnableOrganizations } from './useAttemptToEnableOrganizations'; import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite'; -/** - * @interface - */ +/** @interface */ export type UseOrganizationParams = { /** * If set to `true`, all default properties will be used.
      @@ -63,38 +61,22 @@ export type UseOrganizationParams = { invitations?: true | PaginatedHookConfig; }; -/** - * @interface - */ +/** @interface */ export type UseOrganizationReturn = | { - /** - * Indicates whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). - */ + /** Whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; - /** - * The currently Active Organization. - */ + /** The currently Active Organization. */ organization: undefined; - /** - * The current Organization membership. - */ + /** The current Organization membership. */ membership: undefined; - /** - * Includes a paginated list of the Organization's domains. - */ + /** Includes a paginated list of the Organization's domains. */ domains: PaginatedResourcesWithDefault; - /** - * Includes a paginated list of the Organization's membership requests. - */ + /** Includes a paginated list of the Organization's membership requests. */ membershipRequests: PaginatedResourcesWithDefault; - /** - * Includes a paginated list of the Organization's memberships. - */ + /** Includes a paginated list of the Organization's memberships. */ memberships: PaginatedResourcesWithDefault; - /** - * Includes a paginated list of the Organization's invitations. - */ + /** Includes a paginated list of the Organization's invitations. */ invitations: PaginatedResourcesWithDefault; } | { diff --git a/packages/shared/src/react/hooks/useOrganizationCreationDefaults.types.ts b/packages/shared/src/react/hooks/useOrganizationCreationDefaults.types.ts index f968b333741..43d3ac20c80 100644 --- a/packages/shared/src/react/hooks/useOrganizationCreationDefaults.types.ts +++ b/packages/shared/src/react/hooks/useOrganizationCreationDefaults.types.ts @@ -1,42 +1,22 @@ import type { ClerkAPIResponseError } from '../../errors/clerkApiResponseError'; import type { OrganizationCreationDefaultsResource } from '../../types'; -/** - * @interface - */ +/** @interface */ export type UseOrganizationCreationDefaultsParams = { - /** - * If true, the previous data will be kept in the cache until new data is fetched. - * - * @default true - */ + /** Whether the previous data will be kept in the cache until new data is fetched. Defaults to `true`. */ keepPreviousData?: boolean; - /** - * If `true`, a request will be triggered when the hook is mounted. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted. Defaults to `true`. */ enabled?: boolean; }; -/** - * @interface - */ +/** @interface */ export type UseOrganizationCreationDefaultsReturn = { - /** - * The organization creation defaults resource, `undefined` before the first fetch, or `null` if not available. - */ + /** The organization creation defaults resource, `undefined` before the first fetch, or `null` if not available. */ data: OrganizationCreationDefaultsResource | undefined | null; - /** - * Any error that occurred during the data fetch, or `null` if no error occurred. - */ + /** Any error that occurred during the data fetch, or `null` if no error occurred. */ error: ClerkAPIResponseError | null; - /** - * Indicates whether the initial data is still being fetched. - */ + /** Whether the initial data is still being fetched. */ isLoading: boolean; - /** - * Indicates whether any request is still in flight, including background updates. - */ + /** Whether any request is still in flight, including background updates. */ isFetching: boolean; }; diff --git a/packages/shared/src/react/hooks/useOrganizationList.tsx b/packages/shared/src/react/hooks/useOrganizationList.tsx index 38eb4176b86..04b43716806 100644 --- a/packages/shared/src/react/hooks/useOrganizationList.tsx +++ b/packages/shared/src/react/hooks/useOrganizationList.tsx @@ -71,34 +71,20 @@ const undefinedPaginatedResource = { setData: undefined, } as const; -/** - * @interface - */ +/** @interface */ export type UseOrganizationListReturn = | { - /** - * Indicates whether Clerk has loaded the current authentication state and there is an authenticated user. Initially `false`, becomes `true` once Clerk loads with a user, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). - */ + /** Whether Clerk has loaded the current authentication state and there is an authenticated user. Initially `false`, becomes `true` once Clerk loads with a user, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; - /** - * A function that returns a `Promise` which resolves to the newly created `Organization`. - */ + /** A function that returns a `Promise` which resolves to the newly created `Organization`. */ createOrganization: undefined; - /** - * A function that sets the active session and/or Organization. - */ + /** A function that sets the active session and/or Organization. */ setActive: undefined; - /** - * Returns `PaginatedResources` which includes a list of the user's Organization memberships. - */ + /** Returns `PaginatedResources` which includes a list of the user's Organization memberships. */ userMemberships: PaginatedResourcesWithDefault; - /** - * Returns `PaginatedResources` which includes a list of the user's Organization invitations. - */ + /** Returns `PaginatedResources` which includes a list of the user's Organization invitations. */ userInvitations: PaginatedResourcesWithDefault; - /** - * Returns `PaginatedResources` which includes a list of suggestions for Organizations that the user can join. - */ + /** Returns `PaginatedResources` which includes a list of suggestions for Organizations that the user can join. */ userSuggestions: PaginatedResourcesWithDefault; } | { diff --git a/packages/shared/src/react/hooks/usePaymentAttemptQuery.types.ts b/packages/shared/src/react/hooks/usePaymentAttemptQuery.types.ts index e35e4964319..c7233969c42 100644 --- a/packages/shared/src/react/hooks/usePaymentAttemptQuery.types.ts +++ b/packages/shared/src/react/hooks/usePaymentAttemptQuery.types.ts @@ -1,52 +1,26 @@ import type { ClerkAPIResponseError } from '../../error'; import type { BillingPaymentResource, ForPayerType } from '../../types'; -/** - * @internal - */ +/** @internal */ export type UsePaymentAttemptQueryParams = { - /** - * The payment attempt ID to fetch. - */ + /** The payment attempt ID to fetch. */ paymentAttemptId: string; - /** - * Specifies whether to fetch the payment attempt for an organization or a user. - * - * @default 'user' - */ + /** Specifies whether to fetch the payment attempt for an organization or a user. Defaults to `'user'`. */ for?: ForPayerType; - /** - * If true, the previous data will be kept in the cache until new data is fetched. - * - * @default false - */ + /** Whether the previous data will be kept in the cache until new data is fetched. Defaults to `false`. */ keepPreviousData?: boolean; - /** - * If `true`, a request will be triggered when the hook is mounted. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted. Defaults to `true`. */ enabled?: boolean; }; -/** - * @internal - */ +/** @internal */ export type PaymentAttemptQueryResult = { - /** - * The payment attempt object, `undefined` before the first fetch, or `null` if no payment attempt exists. - */ + /** The payment attempt object, `undefined` before the first fetch, or `null` if no payment attempt exists. */ data: BillingPaymentResource | undefined | null; - /** - * Any error that occurred during the data fetch, or `undefined` if no error occurred. - */ + /** Any error that occurred during the data fetch, or `undefined` if no error occurred. */ error: ClerkAPIResponseError | null; - /** - * Indicates whether the initial data is still being fetched. - */ + /** Whether the initial data is still being fetched. */ isLoading: boolean; - /** - * Indicates whether any request is still in flight, including background updates. - */ + /** Whether any request is still in flight, including background updates. */ isFetching: boolean; }; diff --git a/packages/shared/src/react/hooks/usePlanDetailsQuery.types.ts b/packages/shared/src/react/hooks/usePlanDetailsQuery.types.ts index 46113540c73..da36fcbe837 100644 --- a/packages/shared/src/react/hooks/usePlanDetailsQuery.types.ts +++ b/packages/shared/src/react/hooks/usePlanDetailsQuery.types.ts @@ -1,50 +1,26 @@ import type { ClerkAPIResponseError } from '../../errors/clerkApiResponseError'; import type { BillingPlanResource } from '../../types'; -/** - * @internal - */ +/** @internal */ export type UsePlanDetailsQueryParams = { - /** - * The plan ID to fetch. - */ + /** The plan ID to fetch. */ planId?: string | null; - /** - * Initial plan data to use before fetching. - */ + /** Initial plan data to use before fetching. */ initialPlan?: BillingPlanResource | null; - /** - * If true, the previous data will be kept in the cache until new data is fetched. - * - * @default true - */ + /** Whether the previous data will be kept in the cache until new data is fetched. Defaults to `true`. */ keepPreviousData?: boolean; - /** - * If `true`, a request will be triggered when the hook is mounted. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted. Defaults to `true`. */ enabled?: boolean; }; -/** - * @internal - */ +/** @internal */ export type PlanDetailsQueryResult = { - /** - * The plan object, `undefined` before the first fetch, or `null` if no plan exists. - */ + /** The plan object, `undefined` before the first fetch, or `null` if no plan exists. */ data: BillingPlanResource | undefined | null; - /** - * Any error that occurred during the data fetch, or `undefined` if no error occurred. - */ + /** Any error that occurred during the data fetch, or `undefined` if no error occurred. */ error: ClerkAPIResponseError | null; - /** - * Indicates whether the initial data is still being fetched. - */ + /** Whether the initial data is still being fetched. */ isLoading: boolean; - /** - * Indicates whether any request is still in flight, including background updates. - */ + /** Whether any request is still in flight, including background updates. */ isFetching: boolean; }; diff --git a/packages/shared/src/react/hooks/useStatementQuery.types.ts b/packages/shared/src/react/hooks/useStatementQuery.types.ts index 0ee92ab87d1..0546a5e7c3a 100644 --- a/packages/shared/src/react/hooks/useStatementQuery.types.ts +++ b/packages/shared/src/react/hooks/useStatementQuery.types.ts @@ -1,51 +1,25 @@ import type { BillingStatementResource, ClerkAPIResponseError, ForPayerType } from '../../types'; -/** - * @internal - */ +/** @internal */ export type UseStatementQueryParams = { - /** - * The statement ID to fetch. - */ + /** The statement ID to fetch. */ statementId?: string | null; - /** - * Specifies whether to fetch the statement for an organization or a user. - * - * @default 'user' - */ + /** Specifies whether to fetch the statement for an organization or a user. Defaults to `'user'`. */ for?: ForPayerType; - /** - * If true, the previous data will be kept in the cache until new data is fetched. - * - * @default false - */ + /** Whether the previous data will be kept in the cache until new data is fetched. Defaults to `false`. */ keepPreviousData?: boolean; - /** - * If `true`, a request will be triggered when the hook is mounted. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted. Defaults to `true`. */ enabled?: boolean; }; -/** - * @internal - */ +/** @internal */ export type StatementQueryResult = { - /** - * The statement object, `undefined` before the first fetch, or `null` if no statement exists. - */ + /** The statement object, `undefined` before the first fetch, or `null` if no statement exists. */ data: BillingStatementResource | undefined | null; - /** - * Any error that occurred during the data fetch, or `undefined` if no error occurred. - */ + /** Any error that occurred during the data fetch, or `undefined` if no error occurred. */ error: ClerkAPIResponseError | null; - /** - * Indicates whether the initial data is still being fetched. - */ + /** Whether the initial data is still being fetched. */ isLoading: boolean; - /** - * Indicates whether any request is still in flight, including background updates. - */ + /** Whether any request is still in flight, including background updates. */ isFetching: boolean; }; diff --git a/packages/shared/src/react/hooks/useSubscription.types.ts b/packages/shared/src/react/hooks/useSubscription.types.ts index 11637dd4c11..9df61ca8d1c 100644 --- a/packages/shared/src/react/hooks/useSubscription.types.ts +++ b/packages/shared/src/react/hooks/useSubscription.types.ts @@ -1,51 +1,25 @@ import type { BillingSubscriptionResource, ForPayerType } from '../../types'; -/** - * @interface - */ +/** @interface */ export type UseSubscriptionParams = { - /** - * Specifies whether to fetch the Subscription for an Organization or a user. - * - * @default 'user' - */ + /** Specifies whether to fetch the Subscription for an Organization or a user. Defaults to `'user'`. */ for?: ForPayerType; - /** - * If true, the previous data will be kept in the cache until new data is fetched. - * - * @default false - */ + /** Whether the previous data will be kept in the cache until new data is fetched. Defaults to `false`. */ keepPreviousData?: boolean; - /** - * If `true`, a request will be triggered when the hook is mounted. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted. Defaults to `true`. */ enabled?: boolean; }; -/** - * @interface - */ +/** @interface */ export type SubscriptionResult = { - /** - * The subscription object, `undefined` before the first fetch, or `null` if no subscription exists. - */ + /** The subscription object, `undefined` before the first fetch, or `null` if no subscription exists. */ data: BillingSubscriptionResource | undefined | null; - /** - * Any error that occurred during the data fetch, or `undefined` if no error occurred. - */ + /** Any error that occurred during the data fetch, or `undefined` if no error occurred. */ error: Error | undefined; - /** - * Indicates whether the initial data is still being fetched. - */ + /** Whether the initial data is still being fetched. */ isLoading: boolean; - /** - * Indicates whether any request is still in flight, including background updates. - */ + /** Whether any request is still in flight, including background updates. */ isFetching: boolean; - /** - * Function to manually revalidate or refresh the subscription data. - */ + /** Function to manually revalidate or refresh the subscription data. */ revalidate: () => Promise | void; }; diff --git a/packages/shared/src/react/types.ts b/packages/shared/src/react/types.ts index a5d3a3adb09..b6fd5388ed8 100644 --- a/packages/shared/src/react/types.ts +++ b/packages/shared/src/react/types.ts @@ -7,70 +7,37 @@ export type CacheSetter = ( data?: CData | ((currentData?: CData) => Promise | undefined | CData), ) => Promise; -/** - * @interface - */ +/** @interface */ export type PaginatedResources = { - /** - * An array that contains the fetched data. For example, for the `memberships` attribute, data will be an array of [`OrganizationMembership`](https://clerk.com/docs/reference/types/organization-membership) objects. - */ + /** An array that contains the fetched data. For example, for the `memberships` attribute, data will be an array of [`OrganizationMembership`](https://clerk.com/docs/reference/types/organization-membership) objects. */ data: T[]; - /** - * The total count of data that exist remotely. - */ + /** The total count of data that exist remotely. */ count: number; - /** - * Clerk's API response error object. - */ + /** Clerk's API response error object. */ error: ClerkAPIResponseError | null; - /** - * Indicates whether there is an ongoing request and there is no fetched data. - */ + /** Whether there is an ongoing request and there is no fetched data. */ isLoading: boolean; - /** - * Indicates whether there is an ongoing request or a revalidation. - */ + /** Whether there is an ongoing request or a revalidation. */ isFetching: boolean; - /** - * Indicates whether the request failed. - */ + /** Whether the request failed. */ isError: boolean; - /** - * The current page. - */ + /** The current page. */ page: number; - /** - * The total amount of pages. It is calculated based on `count`, `initialPage`, and `pageSize`. - */ + /** The total amount of pages. It is calculated based on `count`, `initialPage`, and `pageSize`. */ pageCount: number; - /** - * A function that triggers a specific page to be loaded. - */ + /** A function that triggers a specific page to be loaded. */ fetchPage: ValueOrSetter; - /** - * - * A function that triggers the previous page to be loaded. This is the same as `fetchPage(page => Math.max(0, page - 1))`. - */ + /** A function that triggers the previous page to be loaded. This is the same as `fetchPage(page => Math.max(0, page - 1))`. */ fetchPrevious: () => void; - /** - * A function that triggers the next page to be loaded. This is the same as `fetchPage(page => Math.min(pageCount, page + 1))`. - */ + /** A function that triggers the next page to be loaded. This is the same as `fetchPage(page => Math.min(pageCount, page + 1))`. */ fetchNext: () => void; - /** - * Indicates whether there are available pages to be fetched. - */ + /** Whether there are available pages to be fetched. */ hasNextPage: boolean; - /** - * Indicates whether there are available pages to be fetched. - */ + /** Whether there are available pages to be fetched. */ hasPreviousPage: boolean; - /** - * A function that triggers a revalidation of the current page. - */ + /** A function that triggers a revalidation of the current page. */ revalidate: () => Promise; - /** - * A function that allows you to set the data manually. - */ + /** A function that allows you to set the data manually. */ setData: Infinite extends true ? // Array of pages of data CacheSetter<(ClerkPaginatedResponse | undefined)[]> @@ -88,26 +55,14 @@ export type PaginatedResourcesWithDefault = { * @standalonePage */ export type PaginatedHookConfig = T & { - /** - * If `true`, newly fetched data will be appended to the existing list rather than replacing it. Useful for implementing infinite scroll functionality. - * - * @default false - */ + /** Whether newly fetched data will be appended to the existing list rather than replacing it. Useful for implementing infinite scroll functionality. Defaults to `false`. */ infinite?: boolean; - /** - * If `true`, the previous data will be kept in the cache until new data is fetched. - * - * @default false - */ + /** Whether the previous data will be kept in the cache until new data is fetched. Defaults to `false`. */ keepPreviousData?: boolean; }; export type PagesOrInfiniteConfig = PaginatedHookConfig<{ - /** - * If `true`, a request will be triggered when the hook is mounted. - * - * @default true - */ + /** Whether a request will be triggered when the hook is mounted. Defaults to `true`. */ enabled?: boolean; /** * On `cache` mode, no request will be triggered when the hook is mounted and the data will be fetched from the cache. @@ -128,20 +83,10 @@ export type PagesOrInfiniteConfig = PaginatedHookConfig<{ isSignedIn?: boolean; }>; -/** - * @interface - */ +/** @interface */ export type PagesOrInfiniteOptions = { - /** - * A number that specifies which page to fetch. For example, if `initialPage` is set to 10, it will skip the first 9 pages and fetch the 10th page. - * - * @default 1 - */ + /** A number that specifies which page to fetch. For example, if `initialPage` is set to 10, it will skip the first 9 pages and fetch the 10th page. Defaults to `1`. */ initialPage?: number; - /** - * A number that specifies the maximum number of results to return per page. - * - * @default 10 - */ + /** A number that specifies the maximum number of results to return per page. Defaults to `10`. */ pageSize?: number; }; diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts index 652cf83a9f2..a1f4d74b6cf 100644 --- a/packages/shared/src/types/apiKeys.ts +++ b/packages/shared/src/types/apiKeys.ts @@ -6,69 +6,37 @@ 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. - */ + /** A unique identifier for the API key. */ id: string; - /** - * The type of the API key. - */ + /** The type of the API key. */ type: string; - /** - * The name of the API key. - */ + /** The name of the API key. */ name: string; - /** - * The user or organization ID that the API key is associated with. - */ + /** 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. - */ + /** 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. - */ + /** Custom claims associated with the API key, or `null` if none. */ claims: Record | null; - /** - * Indicates whether the API key has been revoked. - */ + /** Whether the API key has been revoked. */ revoked: boolean; - /** - * The reason the API key was revoked, or `null` if not revoked. - */ + /** The reason the API key was revoked, or `null` if not revoked. */ revocationReason: string | null; - /** - * Indicates whether the API key has expired. - */ + /** Whether the API key has expired. */ expired: boolean; - /** - * The expiration date and time for the API key, or `null` if the key never expires. - */ + /** 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. - */ + /** The ID of the user that created the API key. */ createdBy: string | null; - /** - * A description for the API key. - */ + /** A 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.** - */ + /** 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. - */ + /** 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. - */ + /** The date and time when the API key was created. */ createdAt: Date; - /** - * The date and time when the API key was last updated. - */ + /** 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 a81d46bcd00..808eddd7c56 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -84,32 +84,19 @@ export type __experimental_CheckoutOptions = { }; export type CheckoutErrors = { - /** - * The raw, unparsed errors from the Clerk API. - */ + /** The raw, unparsed errors from the Clerk API. */ raw: unknown[] | null; - /** - * Parsed errors that are not related to any specific field. - * Does not include any errors that could be parsed as a field error - */ + /** Parsed errors that are not related to any specific field. Does not include any errors that could be parsed as a field error */ global: ClerkGlobalHookError[] | null; }; -/** - * @interface - */ +/** @interface */ export interface CheckoutSignalValue { - /** - * Represents the errors that occurred during the last fetch of the parent resource. - */ + /** Represents the errors that occurred during the last fetch of the parent resource. */ errors: CheckoutErrors; - /** - * The fetch status of the underlying `Checkout` resource. - */ + /** The fetch status of the underlying `Checkout` resource. */ fetchStatus: 'idle' | 'fetching'; - /** - * An instance representing the currently active `Checkout`. - */ + /** An instance representing the currently active `Checkout`. */ checkout: CheckoutFlowResource; } @@ -119,21 +106,13 @@ export interface CheckoutSignal { type __experimental_CheckoutFunction = (options: __experimental_CheckoutOptions) => CheckoutSignalValue; -/** - * @inline - */ +/** @inline */ export type SDKMetadata = { - /** - * The npm package name of the SDK. - */ + /** The npm package name of the SDK. */ name: string; - /** - * The npm package version of the SDK. - */ + /** The npm package version of the SDK. */ version: string; - /** - * Typically this will be the `NODE_ENV` that the SDK is currently running in. - */ + /** Typically this will be the `NODE_ENV` that the SDK is currently running in. */ environment?: string; }; @@ -144,7 +123,7 @@ export type SDKMetadata = { 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`. + * @param skipInitialEmit - Whether the callback will not be called immediately after registration. Defaults to `false`. * @inline */ export type ListenerOptions = { skipInitialEmit?: boolean }; @@ -188,7 +167,8 @@ export type SetActiveNavigate = (params: { /** * A callback that runs after sign out completes. - * @inline */ + * @inline + */ export type SignOutCallback = () => void | Promise; /** @@ -205,9 +185,7 @@ export type SignOutOptions = { redirectUrl?: string; }; -/** - * @inline - */ +/** @inline */ export interface SignOut { (options?: SignOutOptions): Promise; @@ -242,23 +220,17 @@ export type OnEventListener = ( */ export type OffEventListener = (event: E, handler: EventHandler) => void; -/** - * @inline - */ +/** @inline */ export type ClerkStatus = 'degraded' | 'error' | 'loading' | 'ready'; /** * 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 { - /** - * The Clerk SDK version number. - */ + /** The Clerk SDK version number. */ version: string | undefined; - /** - * The version of `@clerk/ui` that is currently loaded, or `undefined` if the prebuilt UI has not been loaded yet. - */ + /** The version of `@clerk/ui` that is currently loaded, or `undefined` if the prebuilt UI has not been loaded yet. */ uiVersion: string | undefined; /** @@ -268,7 +240,7 @@ export interface Clerk { sdkMetadata: SDKMetadata | undefined; /** - * Indicates whether 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"`. + * Whether 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; @@ -283,9 +255,7 @@ export interface Clerk { */ status: ClerkStatus; - /** - * @internal - */ + /** @internal */ __internal_getOption(key: K): ClerkOptions[K]; /** @@ -309,21 +279,19 @@ export interface Clerk { /** The current Clerk app's domain. Prefixed with `clerk.` on production if not already prefixed. Returns `""` when ran on the server. */ domain: string; - /** Indicates whether the instance is a satellite app. */ + /** Whether the instance is a satellite app. */ isSatellite: boolean; - /** Indicates whether the Clerk instance is running in a production or development environment. */ + /** Whether the Clerk instance is running in a production or development environment. */ instanceType: InstanceType | undefined; /** - * Indicates whether 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. + * Whether 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. - */ + /** Whether the current user has a valid signed-in client session. */ isSignedIn: boolean; /** The `Client` object for the current window. */ @@ -850,7 +818,7 @@ export interface Clerk { * * @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`. + * @param options.skipInitialEmit - Whether 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; @@ -861,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 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. + * @param opt.notify - Whether the handler will be called immediately with the latest payload. */ on: OnEventListener; @@ -1281,7 +1249,7 @@ export type HandleSamlCallbackParams = HandleOAuthCallbackParams; * * @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.replace? - Whether to 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; @@ -1303,7 +1271,7 @@ export type ClerkOptionsNavigation = */ routerReplace?: never; /** - * If `true`, the router will log debug information to the console. + * Whether the router will log debug information to the console. */ routerDebug?: boolean; } @@ -1317,7 +1285,7 @@ export type ClerkOptionsNavigation = */ routerReplace: RouterFn; /** - * If `true`, the router will log debug information to the console. + * Whether the router will log debug information to the console. */ routerDebug?: boolean; }; @@ -1374,7 +1342,7 @@ export type ClerkOptions = ClerkOptionsNavigation & */ localization?: LocalizationResource; /** - * Indicates whether Clerk should poll against Clerk's backend every 5 minutes. + * Whether Clerk should poll against Clerk's backend every 5 minutes. * @default true */ polling?: boolean; @@ -1383,7 +1351,7 @@ export type ClerkOptions = ClerkOptionsNavigation & */ selectInitialSession?: (client: ClientResource) => SignedInSessionResource | null; /** - * 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`. + * 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; /** @@ -1411,7 +1379,7 @@ export type ClerkOptions = ClerkOptionsNavigation & */ allowedRedirectProtocols?: Array; /** - * Indicates whether the current application should behave as a satellite app. Unlike `domain`, which must be set in the [`Clerk` constructor](https://clerk.com/docs/reference/objects/clerk), `isSatellite` must be set in [`load()`](https://clerk.com/docs/reference/objects/clerk#load). + * Whether the current application should behave as a satellite app. Unlike `domain`, which must be set in the [`Clerk` constructor](https://clerk.com/docs/reference/objects/clerk), `isSatellite` must be set in [`load()`](https://clerk.com/docs/reference/objects/clerk#load). */ isSatellite?: boolean | ((url: URL) => boolean); /** @@ -1521,7 +1489,7 @@ export type ClerkOptions = ClerkOptionsNavigation & /** @inline */ export interface NavigateOptions { /** - * If `true`, replace the current history entry instead of pushing a new one. + * Whether to replace the current history entry instead of pushing a new one. */ replace?: boolean; /** @@ -1755,7 +1723,7 @@ export type SignInProps = RoutingOptions & { */ 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. + * 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 */ @@ -2375,45 +2343,29 @@ export type ConfigureSSOProps = { /** @generateWithEmptyComment */ 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. - */ + /** 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. - */ + /** A search query to filter API keys by name. */ query?: string; }>; /** @generateWithEmptyComment */ export type CreateAPIKeyParams = { - /** - * The name of the API key. - */ + /** 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. - */ + /** 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. - */ + /** 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. - */ + /** The description of the API key. */ description?: string; }; /** @generateWithEmptyComment */ export type RevokeAPIKeyParams = { - /** - * The ID of the API key to revoke. - */ + /** The ID of the API key to revoke. */ apiKeyID: string; - /** - * The reason for revoking the API key. - */ + /** The reason for revoking the API key. */ revocationReason?: string; }; diff --git a/packages/shared/src/types/client.ts b/packages/shared/src/types/client.ts index aeae7ce8142..7c4c04ece56 100644 --- a/packages/shared/src/types/client.ts +++ b/packages/shared/src/types/client.ts @@ -32,7 +32,7 @@ export interface ClientResource extends ClerkResource { */ signIn: SignInResource; /** - * Indicates whether this client hasn't been saved (created) yet in the Frontend API. + * Whether this client hasn't been saved (created) yet in the Frontend API. */ isNew: () => boolean; /** @@ -60,7 +60,7 @@ export interface ClientResource extends ClerkResource { */ resetSignUp: () => void; /** - * Indicates whether the client cookie is due to expire in 8 days or less. + * Whether the client cookie is due to expire in 8 days or less. */ isEligibleForTouch: () => boolean; /** @@ -78,7 +78,7 @@ export interface ClientResource extends ClerkResource { */ lastAuthenticationStrategy: LastAuthenticationStrategy | null; /** - * Indicates whether CAPTCHA checks are skipped for this client. + * Whether CAPTCHA checks are skipped for this client. */ captchaBypass: boolean; /** diff --git a/packages/shared/src/types/factors.ts b/packages/shared/src/types/factors.ts index 71088a89629..51e7f26920a 100644 --- a/packages/shared/src/types/factors.ts +++ b/packages/shared/src/types/factors.ts @@ -30,7 +30,7 @@ export type EmailCodeFactor = { */ safeIdentifier: string; /** - * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + * Whether the email address is set as the primary email address, as multiple can be added to a user's profile. */ primary?: boolean; }; @@ -50,7 +50,7 @@ export type EmailLinkFactor = { */ safeIdentifier: string; /** - * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + * Whether the email address is set as the primary email address, as multiple can be added to a user's profile. */ primary?: boolean; }; @@ -70,11 +70,11 @@ export type PhoneCodeFactor = { */ safeIdentifier: string; /** - * Indicates whether the phone number is set as the primary phone number, as multiple can be added to a user's profile. + * 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. + * Whether the phone number is set as the default identifier. */ default?: boolean; /** @@ -94,7 +94,7 @@ export type Web3SignatureFactor = { */ web3WalletId: string; /** - * Indicates whether the Web3 Wallet is set as the primary Web3 Wallet, as multiple can be added to a user's profile. + * Whether the Web3 Wallet is set as the primary Web3 Wallet, as multiple can be added to a user's profile. */ primary?: boolean; /** @@ -161,7 +161,7 @@ export type ResetPasswordPhoneCodeFactor = { */ safeIdentifier: string; /** - * Indicates whether the phone number is set as the primary phone number, as multiple can be added to a user's profile. + * Whether the phone number is set as the primary phone number, as multiple can be added to a user's profile. */ primary?: boolean; }; @@ -181,7 +181,7 @@ export type ResetPasswordEmailCodeFactor = { */ safeIdentifier: string; /** - * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + * Whether the email address is set as the primary email address, as multiple can be added to a user's profile. */ primary?: boolean; }; diff --git a/packages/shared/src/types/hooks.ts b/packages/shared/src/types/hooks.ts index 9e377663e44..ca7e3bc5d1e 100644 --- a/packages/shared/src/types/hooks.ts +++ b/packages/shared/src/types/hooks.ts @@ -26,11 +26,11 @@ type CheckAuthorizationSignedOut = CheckAuthorizationWithoutOrgOrUser; export type UseAuthReturn = | { /** - * Indicates whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). + * Whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; /** - * Indicates whether a user is currently signed in. + * Whether a user is currently signed in. */ isSignedIn: undefined; /** @@ -123,7 +123,7 @@ export type UseAuthReturn = export type UseSignInReturn = | { /** - * Indicates whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). + * Whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; /** @@ -147,7 +147,7 @@ export type UseSignInReturn = export type UseSignUpReturn = | { /** - * Indicates whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). + * Whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; /** @@ -171,11 +171,11 @@ export type UseSignUpReturn = export type UseSessionReturn = | { /** - * Indicates whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). + * Whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; /** - * Indicates whether a user is currently signed in. + * Whether a user is currently signed in. */ isSignedIn: undefined; /** @@ -200,7 +200,7 @@ export type UseSessionReturn = export type UseSessionListReturn = | { /** - * Indicates whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). + * Whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; /** @@ -224,11 +224,11 @@ export type UseSessionListReturn = export type UseUserReturn = | { /** - * Indicates whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). + * Whether Clerk has loaded the current authentication state. Initially `false`, becomes `true` once Clerk loads, and can revert to `false` while auth state is updating (for example, when switching organizations via [`setActive()`](https://clerk.com/docs/reference/objects/clerk#set-active)). */ isLoaded: false; /** - * Indicates whether the user is signed in. + * Whether the user is signed in. */ isSignedIn: undefined; /** diff --git a/packages/shared/src/types/multiDomain.ts b/packages/shared/src/types/multiDomain.ts index 83ef6b4487a..2c8b863d20f 100644 --- a/packages/shared/src/types/multiDomain.ts +++ b/packages/shared/src/types/multiDomain.ts @@ -11,7 +11,7 @@ import type { ClerkOptions } from './clerk'; export type MultiDomainAndOrProxy = | { /** - * Indicates whether the application is a satellite application. + * Whether the application is a satellite application. */ isSatellite?: never; /** @@ -37,7 +37,7 @@ export type MultiDomainAndOrProxy = export type MultiDomainAndOrProxyPrimitives = | { /** - * Indicates whether the application is a satellite application. + * Whether the application is a satellite application. */ isSatellite?: never; /** diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index 5e148977474..9e71adb9e6c 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -30,15 +30,9 @@ import type { TokenResource } from './token'; import type { UserResource } from './user'; import type { Autocomplete } from './utils'; -/** - * @inline - */ +/** @inline */ export type PendingSessionOptions = { - /** - * Indicates whether pending sessions are considered as signed out or not. - * - * @default true - */ + /** Whether pending sessions are considered as signed out or not. Defaults to `true`. */ treatPendingAsSignedOut?: boolean; }; @@ -462,7 +456,7 @@ export interface PublicUserData { */ imageUrl: string; /** - * Indicates whether the user has a profile picture. + * Whether the user has a profile picture. */ hasImage: boolean; /** @@ -478,7 +472,7 @@ export interface PublicUserData { */ username?: string; /** - * Indicates whether the user is banned. + * Whether the user is banned. */ banned?: boolean; } diff --git a/packages/shared/src/types/signInFuture.ts b/packages/shared/src/types/signInFuture.ts index 3bb6798c6c8..3666bae34c9 100644 --- a/packages/shared/src/types/signInFuture.ts +++ b/packages/shared/src/types/signInFuture.ts @@ -8,68 +8,43 @@ import type { Web3Provider } from './web3'; /** @generateWithEmptyComment */ 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; - /** - * The full URL or path that the OAuth provider should redirect to after successful authorization on their part. - */ + /** The full URL or path that the OAuth provider should redirect to after successful authorization on their part. */ 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; - /** - * **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. - */ + /** **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; } /** Parameters for submitting a password to sign-in. */ export type SignInFuturePasswordParams = { - /** - * 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 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. - */ + /** 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; phoneNumber?: never; } | { - /** - * 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`. - */ + /** 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; phoneNumber?: never; } | { - /** - * 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`. - */ + /** 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; emailAddress?: never; @@ -84,39 +59,28 @@ export type SignInFuturePasswordParams = { /** Parameters for sending a sign-in email verification code. */ export type SignInFutureEmailCodeSendParams = | { - /** - * 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. - */ + /** 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 one-time authentication code. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists. - */ + /** The ID for the user's email address that will receive an email with the one-time authentication code. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists. */ emailAddressId?: string; 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. - */ + /** The full URL that the user will be redirected to when they visit the email link. */ verificationUrl: 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. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists. - */ + /** 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. Omit both when a sign-in already exists. - */ + /** 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; } @@ -124,51 +88,36 @@ export type SignInFutureEmailLinkSendParams = { /** @generateWithEmptyComment */ export interface SignInFutureEmailCodeVerifyParams { - /** - * The one-time code that was sent to the user. - */ + /** The one-time code that was sent to the user. */ code: string; } /** @generateWithEmptyComment */ export interface SignInFutureResetPasswordSubmitParams { - /** - * The new password for the user. - */ + /** The new password for the user. */ password: string; - /** - * If `true`, signs the user out of all other authenticated sessions. - */ + /** Whether to sign the user out of all other authenticated sessions. */ signOutOfOtherSessions?: boolean; } /** @generateWithEmptyComment */ export interface SignInFutureResetPasswordPhoneCodeSendParams { - /** - * 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. */ phoneNumber?: string; } /** @generateWithEmptyComment */ export type SignInFuturePhoneCodeSendParams = { - /** - * The mechanism to use to send the code to the provided phone number. Defaults to `'sms'`. - */ + /** The mechanism to use to send the code to the provided phone number. Defaults to `'sms'`. */ channel?: PhoneCodeChannel; } & ( | { - /** - * 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. - */ + /** 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. Provide either `phoneNumber` or `phoneNumberId`, not both. Omit both when a sign-in already exists. - */ + /** 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; } @@ -176,29 +125,21 @@ export type SignInFuturePhoneCodeSendParams = { /** @generateWithEmptyComment */ export interface SignInFuturePhoneCodeVerifyParams { - /** - * The one-time code that was sent to the user. - */ + /** The one-time code that was sent to the user. */ code: string; } /** @generateWithEmptyComment */ export interface SignInFutureResetPasswordPhoneCodeVerifyParams { - /** - * The one-time code that was sent to the user. - */ + /** The one-time code that was sent to the user. */ code: string; } /** @generateWithEmptyComment */ export interface SignInFutureSSOParams { - /** - * The strategy to use for authentication. - */ + /** The strategy to use for authentication. */ strategy: OAuthStrategy | 'enterprise_sso'; - /** - * The URL to redirect to after the user has completed the SSO flow. - */ + /** The URL to redirect to after the user has completed the SSO flow. */ redirectUrl: string; /** * The URL to redirect to if a session was not created, and needs additional information. @@ -218,74 +159,54 @@ export interface SignInFutureSSOParams { * ``` */ popup?: Window; - /** - * 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; - /** - * The unique identifier of the user. Only supported with the `enterprise_sso` strategy. - */ + /** The unique identifier of the user. Only supported with the `enterprise_sso` strategy. */ identifier?: string; } /** @generateWithEmptyComment */ export interface SignInFutureMFAPhoneCodeVerifyParams { - /** - * The one-time code that was sent to the user. - */ + /** The one-time code that was sent to the user. */ code: string; } /** @generateWithEmptyComment */ export interface SignInFutureMFAEmailCodeVerifyParams { - /** - * The one-time code that was sent to the user. - */ + /** The one-time code that was sent to the user. */ code: string; } /** @generateWithEmptyComment */ export interface SignInFutureTOTPVerifyParams { - /** - * The TOTP generated by the user's authenticator app. - */ + /** The TOTP generated by the user's authenticator app. */ code: string; } /** @generateWithEmptyComment */ export interface SignInFutureBackupCodeVerifyParams { - /** - * The backup code that was provided to the user when they set up backup codes. - */ + /** The backup code that was provided to the user when they set up backup codes. */ code: string; } /** @generateWithEmptyComment */ 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; } /** @generateWithEmptyComment */ export interface SignInFutureWeb3Params { - /** - * The verification strategy to validate the user's sign-in request. - */ + /** The verification strategy to validate the user's sign-in request. */ strategy: Web3Strategy; - /** - * The Web3 wallet provider to use for the sign-in. - */ + /** The Web3 wallet provider to use for the sign-in. */ provider: Web3Provider; - /** - * **Required** when `provider` is set to `'solana'`. The name of the wallet to use for Solana sign-ins. - */ + /** **Required** when `provider` is set to `'solana'`. The name of the wallet to use for Solana sign-ins. */ walletName?: string; } @@ -314,19 +235,13 @@ 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. */ export interface SignInFutureResource { - /** - * The unique identifier for the current sign-in attempt. - */ + /** The unique identifier for the current sign-in attempt. */ 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[]; /** @@ -342,39 +257,25 @@ 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; - /** - * Indicates 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?: { - /** - * The ID of the existing session. - */ + /** 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. - */ + /** 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 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'`. - */ + /** 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; /** @@ -407,152 +308,102 @@ export interface SignInFutureResource { */ create: (params: SignInFutureCreateParams) => Promise<{ error: ClerkError | null }>; - /** - * Submits a password to sign-in. - */ + /** Submits a password to sign-in. */ password: (params: SignInFuturePasswordParams) => Promise<{ error: ClerkError | null }>; /** @extractMethods */ emailCode: { - /** - * Sends an email code to sign-in. - */ + /** Sends an email code to sign-in. */ sendCode: (params?: SignInFutureEmailCodeSendParams) => Promise<{ error: ClerkError | null }>; - /** - * Verifies a code sent with the [`emailCode.sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#email-code-send-code) method. - */ + /** Verifies a code sent with the [`emailCode.sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#email-code-send-code) method. */ verifyCode: (params: SignInFutureEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; /** @extractMethods */ emailLink: { - /** - * Sends an email link to sign in with. - */ + /** Sends an email link to sign in with. */ sendLink: (params: SignInFutureEmailLinkSendParams) => Promise<{ error: ClerkError | null }>; - /** - * Waits for email link 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. */ verification: { - /** - * The verification status. - */ + /** The verification status. */ status: 'verified' | 'expired' | 'failed' | 'client_mismatch'; - /** - * The ID of the session that was created upon completion of the current sign-in. - */ + /** The ID of the session that was created upon completion of the current sign-in. */ createdSessionId: string; - /** - * Whether the verification was from the same client. - */ + /** Whether the verification was from the same client. */ verifiedFromTheSameClient: boolean; } | null; }; /** @extractMethods */ phoneCode: { - /** - * Sends a phone code to sign in with. - */ + /** Sends a phone code to sign in with. */ sendCode: (params?: SignInFuturePhoneCodeSendParams) => Promise<{ error: ClerkError | null }>; - /** - * Verifies a code sent with the [`phoneCode.sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#phone-code-send-code) method. - */ + /** Verifies a code sent with the [`phoneCode.sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#phone-code-send-code) method. */ verifyCode: (params: SignInFuturePhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; /** @extractMethods */ resetPasswordEmailCode: { - /** - * Sends 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 }>; - /** - * Verifies a password reset code sent with the [`resetPasswordEmailCode.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. - */ + /** Verifies a password reset code sent with the [`resetPasswordEmailCode.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 }>; - /** - * Submits a new password and moves the sign-in status to `'complete'`. - */ + /** Submits a new password and moves the sign-in status to `'complete'`. */ submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: ClerkError | null }>; }; /** @extractMethods */ resetPasswordPhoneCode: { - /** - * Sends 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 }>; - /** - * Verifies a password reset code sent with the [`resetPasswordPhoneCode.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. - */ + /** Verifies a password reset code sent with the [`resetPasswordPhoneCode.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 }>; - /** - * Submits a new password and moves the sign-in status to `'complete'`. - */ + /** Submits a new password and moves the sign-in status to `'complete'`. */ submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: ClerkError | null }>; }; - /** - * Performs an SSO-based sign-in (Social/OAuth or Enterprise). - */ + /** Performs an SSO-based sign-in (Social/OAuth or Enterprise). */ sso: (params: SignInFutureSSOParams) => Promise<{ error: ClerkError | null }>; /** @extractMethods */ mfa: { - /** - * Sends a phone code to sign in with as a second factor. - */ + /** Sends a phone code to sign in with as a second factor. */ sendPhoneCode: () => Promise<{ error: ClerkError | null }>; - /** - * Verifies a phone code sent with the [`mfa.sendPhoneCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-phone-code) method. - */ + /** Verifies a phone code sent with the [`mfa.sendPhoneCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-phone-code) method. */ verifyPhoneCode: (params: SignInFutureMFAPhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; - /** - * Sends an email code to sign in with as a second factor. - */ + /** Sends an email code to sign in with as a second factor. */ sendEmailCode: () => Promise<{ error: ClerkError | null }>; - /** - * Verifies an email code sent with the [`mfa.sendEmailCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-email-code) method. - */ + /** Verifies an email code sent with the [`mfa.sendEmailCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-email-code) method. */ verifyEmailCode: (params: SignInFutureMFAEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; - /** - * Verifies an authenticator app (TOTP) code to sign in with as a second factor. - */ + /** Verifies an authenticator app (TOTP) code to sign in with as a second factor. */ verifyTOTP: (params: SignInFutureTOTPVerifyParams) => Promise<{ error: ClerkError | null }>; - /** - * Verifies a backup code to sign in with as a second factor. - */ + /** Verifies a backup code to sign in with as a second factor. */ verifyBackupCode: (params: SignInFutureBackupCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; - /** - * Performs a ticket-based sign-in. - */ + /** Performs a ticket-based sign-in. */ ticket: (params?: SignInFutureTicketParams) => Promise<{ error: ClerkError | null }>; - /** - * Performs a Web3-based sign-in. - */ + /** Performs a Web3-based sign-in. */ web3: (params: SignInFutureWeb3Params) => Promise<{ error: ClerkError | null }>; /** diff --git a/packages/shared/src/types/signUpFuture.ts b/packages/shared/src/types/signUpFuture.ts index 27a47c5756f..c35e64b0f4a 100644 --- a/packages/shared/src/types/signUpFuture.ts +++ b/packages/shared/src/types/signUpFuture.ts @@ -28,7 +28,7 @@ export interface SignUpFutureAdditionalParams { */ unsafeMetadata?: SignUpUnsafeMetadata; /** - * Indicates whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + * Whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. */ legalAccepted?: boolean; /** diff --git a/packages/shared/src/types/telemetry.ts b/packages/shared/src/types/telemetry.ts index 02512197d43..9666f0581a3 100644 --- a/packages/shared/src/types/telemetry.ts +++ b/packages/shared/src/types/telemetry.ts @@ -2,9 +2,7 @@ import type { InstanceType } from './instance'; type JSONValue = string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }; -/** - * @internal - */ +/** @internal */ export type TelemetryEvent = { event: string; /** @@ -34,9 +32,7 @@ export type TelemetryEvent = { payload: Record; }; -/** - * @internal - */ +/** @internal */ export type TelemetryEventRaw = { event: TelemetryEvent['event']; eventSamplingRate?: number; @@ -57,24 +53,14 @@ export interface TelemetryLogEntry { readonly userId?: string; } -/** - * @inline - */ +/** @inline */ export interface TelemetryCollector { - /** - * Indicates whether telemetry is enabled. - */ + /** Whether telemetry is enabled. */ isEnabled: boolean; - /** - * If `true`, telemetry events are only logged to the console and not sent to Clerk. - */ + /** Whether telemetry events are only logged to the console and not sent to Clerk. */ isDebug: boolean; - /** - * Records a telemetry event. - */ + /** Records a telemetry event. */ record(event: TelemetryEventRaw): void; - /** - * Records a telemetry log entry. - */ + /** Records a telemetry log entry. */ recordLog(entry: TelemetryLogEntry): void; } diff --git a/packages/shared/src/types/user.ts b/packages/shared/src/types/user.ts index d606a01d480..706bd5a8b9f 100644 --- a/packages/shared/src/types/user.ts +++ b/packages/shared/src/types/user.ts @@ -115,7 +115,7 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { */ 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. + * 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; /** @@ -147,19 +147,19 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { */ organizationMemberships: OrganizationMembershipResource[]; /** - * Indicates whether the user has a password on their account. + * Whether the user has a password on their account. */ passwordEnabled: boolean; /** - * Indicates whether the user has enabled TOTP. + * Whether the user has enabled TOTP. */ totpEnabled: boolean; /** - * Indicates whether the user has enabled backup codes. + * Whether the user has enabled backup codes. */ backupCodeEnabled: boolean; /** - * Indicates whether the user has enabled two-factor authentication. + * Whether the user has enabled two-factor authentication. */ twoFactorEnabled: boolean; /** @@ -181,7 +181,7 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { */ legalAcceptedAt: Date | null; /** - * Indicates whether the user can create organizations. + * Whether the user can create organizations. */ createOrganizationEnabled: boolean; /** @@ -189,7 +189,7 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { */ createOrganizationsLimit: number | null; /** - * Indicates whether the user can delete their own account. + * Whether the user can delete their own account. */ deleteSelfEnabled: boolean; /**