diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 2f6422db630..f24be77f269 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -19,6 +19,20 @@ const FILES_WITHOUT_HEADINGS = [ 'verify-token-options.mdx', 'public-organization-data-json.mdx', 'organization-membership-public-user-data.mdx', + 'use-checkout-return.mdx', + 'use-checkout-options.mdx', + 'use-payment-element-return.mdx', + 'use-payment-element-return.mdx', + 'use-payment-methods-return.mdx', + 'use-payment-attempts-return.mdx', + 'use-plans-return.mdx', + 'use-statements-return.mdx', + 'hook-params.mdx', + 'use-subscription-params.mdx', + 'use-subscription-return.mdx', + 'needs-reverification-parameters.mdx', + 'use-reverification-options.mdx', + 'use-reverification-params,mdx', ]; /** @@ -29,6 +43,10 @@ const LINK_REPLACEMENTS = [ ['set-active-params', '/docs/reference/javascript/types/set-active-params'], ['clerk-paginated-response', '/docs/reference/javascript/types/clerk-paginated-response'], ['paginated-resources', '#paginated-resources'], + ['use-checkout-options', '#use-checkout-options'], + ['use-reverification-options', '#use-reverification-options'], + ['needs-reverification-parameters', '#needs-reverification-parameters'], + ['create-organization-params', '#create-organization-params'], ['session-resource', '/docs/reference/javascript/session'], ['signed-in-session-resource', '/docs/reference/javascript/session'], ['sign-in-resource', '/docs/reference/javascript/sign-in'], @@ -57,14 +75,19 @@ const LINK_REPLACEMENTS = [ ['verify-token-options', '#verify-token-options'], ['localization-resource', '/docs/guides/customizing-clerk/localization'], ['confirm-checkout-params', '/docs/reference/javascript/types/billing-checkout-resource#parameters'], + ['billing-payment-resource', '/docs/reference/javascript/types/billing-payment-resource'], ['billing-payment-source-resource', '/docs/reference/javascript/types/billing-payment-source-resource'], ['billing-payer-resource', '/docs/reference/javascript/types/billing-payer-resource'], ['billing-plan-resource', '/docs/reference/javascript/types/billing-plan-resource'], ['billing-checkout-totals', '/docs/reference/javascript/types/billing-checkout-totals'], + ['billing-checkout-resource', '/docs/reference/javascript/types/billing-checkout-resource'], ['billing-money-amount', '/docs/reference/javascript/types/billing-money-amount'], ['billing-subscription-item-resource', '/docs/reference/javascript/types/billing-subscription-item-resource'], ['feature-resource', '/docs/reference/javascript/types/feature-resource'], ['billing-statement-group', '/docs/reference/javascript/types/billing-statement-group'], + ['billing-statement-resource', '/docs/reference/javascript/types/billing-statement-resource'], + ['billing-subscription-resource', '/docs/reference/javascript/types/billing-subscription-resource'], + ['clerk-api-response-error', '/docs/reference/javascript/types/clerk-api-response-error'], ]; /** diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 1ce9c403d22..6d2458e6bbb 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -296,6 +296,7 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { }, /** * This hides the "Type parameters" section, the declaration title, and the "Type declaration" heading from the output + * Unless the @includeType tag is present, in which case it shows the type in a parameter table format * @param {import('typedoc').DeclarationReflection} model * @param {{ headingLevel: number, nested?: boolean }} options */ diff --git a/.typedoc/extract-returns-and-params.mjs b/.typedoc/extract-returns-and-params.mjs new file mode 100644 index 00000000000..3dbda8cfdfc --- /dev/null +++ b/.typedoc/extract-returns-and-params.mjs @@ -0,0 +1,164 @@ +// @ts-check +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Extracts the "## Returns" section from a markdown file and writes it to a separate file. + * @param {string} filePath - The path to the markdown file + * @returns {boolean} True if a file was created + */ +function extractReturnsSection(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + + // Find the "## Returns" section + const returnsStart = content.indexOf('## Returns'); + + if (returnsStart === -1) { + return false; // No Returns section found + } + + // Find the next heading after "## Returns" (or end of file) + const afterReturns = content.slice(returnsStart + 10); // Skip past "## Returns" + const nextHeadingMatch = afterReturns.match(/\n## /); + const returnsEnd = + nextHeadingMatch && typeof nextHeadingMatch.index === 'number' + ? returnsStart + 10 + nextHeadingMatch.index + : content.length; + + // Extract the Returns section and trim trailing whitespace + const returnsContent = content.slice(returnsStart, returnsEnd).trimEnd(); + + // Generate the new filename: use-auth.mdx -> use-auth-return.mdx + const fileName = path.basename(filePath, '.mdx'); + const dirName = path.dirname(filePath); + const newFilePath = path.join(dirName, `${fileName}-return.mdx`); + + // Write the extracted Returns section to the new file + fs.writeFileSync(newFilePath, returnsContent, 'utf-8'); + + console.log(`[extract-returns] Created ${path.relative(process.cwd(), newFilePath)}`); + return true; +} + +/** + * Extracts the "## Parameters" section from a markdown file and writes it to a separate file. + * @param {string} filePath - The path to the markdown file + * @param {string} dirName - The directory containing the files + * @returns {boolean} True if a file was created + */ +function extractParametersSection(filePath, dirName) { + const content = fs.readFileSync(filePath, 'utf-8'); + const fileName = path.basename(filePath, '.mdx'); + + // Always use -params suffix + const suffix = '-params'; + const targetFileName = `${fileName}${suffix}.mdx`; + const propsFileName = `${fileName}-props.mdx`; + + // Delete any existing -props file (TypeDoc-generated) + const propsFilePath = path.join(dirName, propsFileName); + if (fs.existsSync(propsFilePath)) { + fs.unlinkSync(propsFilePath); + console.log(`[extract-returns] Deleted ${path.relative(process.cwd(), propsFilePath)}`); + } + + // Find the "## Parameters" section + const paramsStart = content.indexOf('## Parameters'); + + if (paramsStart === -1) { + return false; // No Parameters section found + } + + // Find the next heading after "## Parameters" (or end of file) + const afterParams = content.slice(paramsStart + 13); // Skip past "## Parameters" + const nextHeadingMatch = afterParams.match(/\n## /); + const paramsEnd = + nextHeadingMatch && typeof nextHeadingMatch.index === 'number' + ? paramsStart + 13 + nextHeadingMatch.index + : content.length; + + // Extract the Parameters section and trim trailing whitespace + const paramsContent = content.slice(paramsStart, paramsEnd).trimEnd(); + + // Write to new file + const newFilePath = path.join(dirName, targetFileName); + fs.writeFileSync(newFilePath, paramsContent, 'utf-8'); + + console.log(`[extract-returns] Created ${path.relative(process.cwd(), newFilePath)}`); + return true; +} + +/** + * Recursively reads all .mdx files in a directory, excluding generated files + * @param {string} dir - The directory to read + * @returns {string[]} Array of file paths + */ +function getAllMdxFiles(dir) { + /** @type {string[]} */ + const files = []; + + if (!fs.existsSync(dir)) { + return files; + } + + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + files.push(...getAllMdxFiles(fullPath)); + } else if (entry.isFile() && entry.name.endsWith('.mdx')) { + // Exclude generated files + const isGenerated = + entry.name.endsWith('-return.mdx') || entry.name.endsWith('-params.mdx') || entry.name.endsWith('-props.mdx'); + if (!isGenerated) { + files.push(fullPath); + } + } + } + + return files; +} + +/** + * Main function to process all clerk-react files + */ +function main() { + const packages = ['clerk-react']; + const dirs = packages.map(folder => path.join(__dirname, 'temp-docs', folder)); + + for (const dir of dirs) { + if (!fs.existsSync(dir)) { + console.log(`[extract-returns] ${dir} directory not found, skipping extraction`); + continue; + } + + const mdxFiles = getAllMdxFiles(dir); + console.log(`[extract-returns] Processing ${mdxFiles.length} files in ${dir}/`); + + let returnsCount = 0; + let paramsCount = 0; + + for (const filePath of mdxFiles) { + // Extract Returns sections + if (extractReturnsSection(filePath)) { + returnsCount++; + } + + // Extract Parameters sections + if (extractParametersSection(filePath, dir)) { + paramsCount++; + } + } + + console.log(`[extract-returns] Extracted ${returnsCount} Returns sections`); + console.log(`[extract-returns] Extracted ${paramsCount} Parameters sections`); + } +} + +main(); diff --git a/package.json b/package.json index 1320877a2a8..a4f9e4239e8 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "test:typedoc": "pnpm typedoc:generate && cd ./.typedoc && vitest run", "turbo:clean": "turbo daemon clean", "typedoc:generate": "pnpm build:declarations && pnpm typedoc:generate:skip-build", - "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && rm -rf .typedoc/docs && mv .typedoc/temp-docs .typedoc/docs", + "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && node .typedoc/extract-returns-and-params.mjs && rm -rf .typedoc/docs && mv .typedoc/temp-docs .typedoc/docs", "version-packages": "changeset version && pnpm install --lockfile-only --engine-strict=false", "version-packages:canary": "./scripts/canary.mjs", "version-packages:snapshot": "./scripts/snapshot.mjs", diff --git a/packages/react/typedoc.json b/packages/react/typedoc.json index 2d417a9b83f..4eab6484506 100644 --- a/packages/react/typedoc.json +++ b/packages/react/typedoc.json @@ -1,4 +1,4 @@ { "$schema": "https://typedoc.org/schema.json", - "entryPoints": ["./src/index.ts", "./src/experimental.ts"] + "entryPoints": ["./src/index.ts", "./src/experimental.ts", "./src/hooks/*.{ts,tsx}"] } diff --git a/packages/shared/src/errors/apiResponseError.ts b/packages/shared/src/errors/apiResponseError.ts index e3541649735..50740ba9099 100644 --- a/packages/shared/src/errors/apiResponseError.ts +++ b/packages/shared/src/errors/apiResponseError.ts @@ -13,6 +13,9 @@ interface ClerkAPIResponseOptions { retryAfter?: number; } +/** + * Interface representing a Clerk API Response Error. + */ export class ClerkAPIResponseError extends Error implements ClerkAPIResponseErrorInterface { clerkError: true; diff --git a/packages/shared/src/react/commerce.tsx b/packages/shared/src/react/commerce.tsx index 72fd978f4b1..af78370d0cf 100644 --- a/packages/shared/src/react/commerce.tsx +++ b/packages/shared/src/react/commerce.tsx @@ -6,7 +6,6 @@ import useSWR from 'swr'; import useSWRMutation from 'swr/mutation'; import { createContextAndHook } from './hooks/createContextAndHook'; -import type { useCheckout } from './hooks/useCheckout'; import { useClerk } from './hooks/useClerk'; import { useOrganization } from './hooks/useOrganization'; import { useUser } from './hooks/useUser'; @@ -139,15 +138,27 @@ type internalStripeAppearance = { spacingUnit: string; }; -type PaymentElementProviderProps = { - checkout?: BillingCheckoutResource | ReturnType['checkout']; +/** + * @interface + */ +export type PaymentElementProviderProps = { + /** + * An optional checkout resource object. When provided, the payment element is scoped to the specific checkout session. + */ + checkout?: BillingCheckoutResource; + /** + * An optional object to customize the appearance of the Stripe Payment Element. This allows you to match the form's styling to your application's theme. + */ stripeAppearance?: internalStripeAppearance; /** - * Default to `user` if not provided. + * Specifies whether to fetch for the current user or organization. * * @default 'user' */ for?: ForPayerType; + /** + * An optional description to display to the user within the payment element UI. + */ paymentDescription?: string; }; @@ -229,7 +240,17 @@ const PaymentElementInternalRoot = (props: PropsWithChildren) => { return {props.children}; }; -const PaymentElement = ({ fallback }: { fallback?: ReactNode }) => { +/** + * @interface + */ +export type PaymentElementProps = { + /** + * Optional fallback content, such as a loading skeleton, to display while the payment form is being initialized. + */ + fallback?: ReactNode; +}; + +const PaymentElement = ({ fallback }: PaymentElementProps) => { const { setIsPaymentElementReady, paymentMethodOrder, @@ -296,7 +317,13 @@ const throwLibsMissingError = () => { ); }; -type UsePaymentElementReturn = { +/** + * @interface + */ +export type UsePaymentElementReturn = { + /** + * A function that submits the payment form data to the payment provider. It returns a promise that resolves with either a `data` object containing a payment token on success, or an `error` object on failure. + */ submit: () => Promise< | { data: { gateway: 'stripe'; paymentToken: string }; @@ -307,13 +334,25 @@ type UsePaymentElementReturn = { error: PaymentElementError; } >; + /** + * A function that resets the payment form to its initial, empty state. + */ reset: () => Promise; + /** + * A boolean that indicates if 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`. + */ provider: { name: 'stripe'; }; + /** + * A boolean that indicates if 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 c54ea6f7a2a..f33bced6233 100644 --- a/packages/shared/src/react/contexts.tsx +++ b/packages/shared/src/react/contexts.tsx @@ -25,9 +25,23 @@ const [SessionContext, useSessionContext] = createContextAndHook({}); -type UseCheckoutOptions = { +/** + * @interface + */ +export type UseCheckoutOptions = { + /** + * Specifies if the checkout is for an organization. + * + * @default 'user' + */ for?: ForPayerType; + /** + * The billing period for the plan. + */ planPeriod: BillingSubscriptionPlanPeriod; + /** + * The ID of the subscription plan to check out (e.g. `cplan_xxx`). + */ planId: string; }; diff --git a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx index 9d9bb6c2b28..9e51b9bd5d0 100644 --- a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx +++ b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx @@ -24,6 +24,18 @@ type BillingHookConfig & { + /** + * Specifies whether to fetch for the current user or organization. + * + * @default 'user' + */ + for?: ForPayerType; +}; + /** * A hook factory that creates paginated data fetching hooks for commerce-related resources. * It provides a standardized way to create hooks that can fetch either user or organization resources @@ -44,10 +56,6 @@ export function createBillingPaginatedHook) { - type HookParams = PaginatedHookConfig & { - for?: ForPayerType; - }; - return function useBillingHook( params?: T, ): PaginatedResources { diff --git a/packages/shared/src/react/hooks/useCheckout.ts b/packages/shared/src/react/hooks/useCheckout.ts index 507553b0c6b..68561a43662 100644 --- a/packages/shared/src/react/hooks/useCheckout.ts +++ b/packages/shared/src/react/hooks/useCheckout.ts @@ -27,11 +27,23 @@ type ForceNull = { [K in keyof T]: null; }; +/** + * @inline + */ type CheckoutProperties = Omit, 'pathRoot' | 'status'>; +/** + * @inline + */ type FetchStatusAndError = | { + /** + * Returns an error object if any part of the checkout process fails. + */ error: ClerkAPIResponseError; + /** + * The data fetching status. + */ fetchStatus: 'error'; } | { @@ -40,34 +52,73 @@ type FetchStatusAndError = }; /** - * @internal + * @inline * On status === 'needs_initialization', all properties are null. * On status === 'needs_confirmation' or 'completed', all properties are defined the same as the CommerceCheckoutResource. */ type CheckoutPropertiesPerStatus = | ({ + /** + * @inline + * The current status of the checkout session. The following statuses are possible: + *
    + *
  • `needs_initialization`: The checkout hasn't started but the hook is mounted. Call `start()` to continue.
  • + *
  • `needs_confirmation`: The checkout has been initialized and is awaiting confirmation. Call `confirm()` to continue.
  • + *
  • `completed`: The checkout has been successfully confirmed. Call `finalize()` to complete the checkout.
  • + *
+ */ status: Extract<__experimental_CheckoutCacheState['status'], 'needs_initialization'>; } & ForceNull) | ({ status: Extract<__experimental_CheckoutCacheState['status'], 'needs_confirmation' | 'completed'>; } & CheckoutProperties); +/** + * @interface + */ +export type UseCheckoutReturn = FetchStatusAndError & + CheckoutPropertiesPerStatus & { + /** + * A function that confirms and finalizes the checkout process, usually after the user has provided and validated payment information. + */ + confirm: __experimental_CheckoutInstance['confirm']; + /** + * A function that initializes the checkout process by creating a checkout resource on the server. + */ + start: __experimental_CheckoutInstance['start']; + /** + * A function that clears the current checkout state from the cache. + */ + clear: () => void; + /** + * A function that finalizes the checkout process. Can optionally accept a `redirectUrl` to navigate the user to upon completion. + */ + finalize: (params?: { navigate?: SetActiveNavigate }) => void; + getState: () => __experimental_CheckoutCacheState; + /** + * A boolean that indicates if the `start()` method is in progress. + */ + isStarting: boolean; + /** + * A boolean that indicates if the `confirm()` method is in progress. + */ + isConfirming: boolean; + }; + type __experimental_UseCheckoutReturn = { - checkout: FetchStatusAndError & - CheckoutPropertiesPerStatus & { - confirm: __experimental_CheckoutInstance['confirm']; - start: __experimental_CheckoutInstance['start']; - clear: () => void; - finalize: (params?: { navigate?: SetActiveNavigate }) => void; - getState: () => __experimental_CheckoutCacheState; - isStarting: boolean; - isConfirming: boolean; - }; + checkout: UseCheckoutReturn; }; -type Params = Parameters[0]; +type UseCheckoutParams = Parameters[0]; -export const useCheckout = (options?: Params): __experimental_UseCheckoutReturn => { +/** + * @function + * + * @param [options] - An object containing the configuration for the checkout flow. + * + * **Required** if the hook is used without a `` wrapping the component tree. + */ +export const useCheckout = (options?: UseCheckoutParams): __experimental_UseCheckoutReturn => { const contextOptions = useCheckoutContext(); const { for: forOrganization, planId, planPeriod } = options || contextOptions; diff --git a/packages/shared/src/react/hooks/usePaymentAttempts.tsx b/packages/shared/src/react/hooks/usePaymentAttempts.tsx index f74ba4c09cc..f260aba8ce2 100644 --- a/packages/shared/src/react/hooks/usePaymentAttempts.tsx +++ b/packages/shared/src/react/hooks/usePaymentAttempts.tsx @@ -17,3 +17,8 @@ export const usePaymentAttempts = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/usePaymentMethods.tsx b/packages/shared/src/react/hooks/usePaymentMethods.tsx index 8537f32caa9..fbcb0561f7f 100644 --- a/packages/shared/src/react/hooks/usePaymentMethods.tsx +++ b/packages/shared/src/react/hooks/usePaymentMethods.tsx @@ -19,3 +19,8 @@ export const usePaymentMethods = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/usePlans.tsx b/packages/shared/src/react/hooks/usePlans.tsx index 77a45213908..0eae3071952 100644 --- a/packages/shared/src/react/hooks/usePlans.tsx +++ b/packages/shared/src/react/hooks/usePlans.tsx @@ -23,3 +23,8 @@ export const usePlans = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/useReverification.ts b/packages/shared/src/react/hooks/useReverification.ts index dadbe438104..164c82462f0 100644 --- a/packages/shared/src/react/hooks/useReverification.ts +++ b/packages/shared/src/react/hooks/useReverification.ts @@ -11,6 +11,9 @@ import { useSafeLayoutEffect } from './useSafeLayoutEffect'; const CLERK_API_REVERIFICATION_ERROR_CODE = 'session_reverification_required'; +/** + * + */ async function resolveResult(result: Promise | T): Promise> { try { const r = await result; @@ -34,24 +37,31 @@ type ExcludeClerkError = T extends { clerk_error: any } ? never : T; /** * @interface */ -type NeedsReverificationParameters = { +export type NeedsReverificationParameters = { + /** + * Marks the reverification process as complete and retries the original request. + */ cancel: () => void; + /** + * Marks the reverification process as cancelled and rejects the original request. + */ complete: () => void; + /** + * The verification level required for the reverification process. + */ level: SessionVerificationLevel | undefined; }; /** - * The optional options object. * @interface */ -type UseReverificationOptions = { +export type UseReverificationOptions = { /** * A handler that is called when reverification is needed, this will opt-out of using the default UI when provided. * * @param cancel - A function that will cancel the reverification process. * @param complete - A function that will retry the original request after reverification. * @param level - The level returned with the reverification hint. - * */ onNeedsReverification?: (properties: NeedsReverificationParameters) => void; }; @@ -66,12 +76,15 @@ type UseReverificationResult Promise | /** * @interface */ -type UseReverification = < - Fetcher extends (...args: any[]) => Promise | undefined, - Options extends UseReverificationOptions = UseReverificationOptions, ->( - fetcher: Fetcher, - options?: Options, +export type UseReverificationParams = Promise | undefined>( + /** + * A function that returns a promise. + */ + fetcher: (...args: any[]) => Promise, + /** + * The optional options object. + */ + options?: UseReverificationOptions, ) => UseReverificationResult; type CreateReverificationHandlerParams = UseReverificationOptions & { @@ -79,7 +92,12 @@ type CreateReverificationHandlerParams = UseReverificationOptions & { telemetry: Clerk['telemetry']; }; +/** + */ function createReverificationHandler(params: CreateReverificationHandlerParams) { + /** + * + */ function assertReverification Promise | undefined>( fetcher: Fetcher, ): (...args: Parameters) => Promise>>> { @@ -191,9 +209,8 @@ function createReverificationHandler(params: CreateReverificationHandlerParams) * return * } * ``` - * */ -export const useReverification: UseReverification = (fetcher, options) => { +export const useReverification: UseReverificationParams = (fetcher, options) => { const { __internal_openReverification, telemetry } = useClerk(); const fetcherRef = useRef(fetcher); const optionsRef = useRef(options); diff --git a/packages/shared/src/react/hooks/useSession.ts b/packages/shared/src/react/hooks/useSession.ts index 7427e53baf3..df656dc6042 100644 --- a/packages/shared/src/react/hooks/useSession.ts +++ b/packages/shared/src/react/hooks/useSession.ts @@ -15,7 +15,6 @@ const hookName = `useSession`; * @function * * @param [options] - An object containing options for the `useSession()` hook. - * * @example * ### Access the `Session` object * diff --git a/packages/shared/src/react/hooks/useStatements.tsx b/packages/shared/src/react/hooks/useStatements.tsx index 78817ffebc1..91c39017971 100644 --- a/packages/shared/src/react/hooks/useStatements.tsx +++ b/packages/shared/src/react/hooks/useStatements.tsx @@ -17,3 +17,8 @@ export const useStatements = createBillingPaginatedHook; diff --git a/packages/shared/src/react/hooks/useSubscription.tsx b/packages/shared/src/react/hooks/useSubscription.tsx index 53c48df8c08..5acbf2880c9 100644 --- a/packages/shared/src/react/hooks/useSubscription.tsx +++ b/packages/shared/src/react/hooks/useSubscription.tsx @@ -1,4 +1,4 @@ -import type { EnvironmentResource, ForPayerType } from '@clerk/types'; +import type { BillingSubscriptionResource, EnvironmentResource, ForPayerType } from '@clerk/types'; import { useCallback } from 'react'; import { eventMethodCalled } from '../../telemetry/events'; @@ -12,10 +12,18 @@ import { const hookName = 'useSubscription'; -type UseSubscriptionParams = { +/** + * @interface + */ +export type UseSubscriptionParams = { + /** + * Specifies whether to fetch subscription for an organization or user. + * + * @default 'user' + */ for?: ForPayerType; /** - * If `true`, the previous data will be kept in the cache until new data is fetched. + * If `true`, the previous data will be kept in the cache until new data is fetched. This helps prevent layout shifts. * * @default false */ @@ -23,7 +31,51 @@ type UseSubscriptionParams = { }; /** - * @internal + * @interface + */ +interface BaseSubscriptionReturn { + /** + * Function to manually trigger a refresh of the subscription data. + */ + revalidate: () => Promise; +} + +/** + * @interface + */ +export type UseSubscriptionReturn = + | (BaseSubscriptionReturn & { + /** + * The subscription object, or `null` if the data hasn't been loaded yet. + */ + data: null; + /** + * A boolean that indicates whether the initial data is still being fetched. + */ + isLoading: false; + /** + * A boolean that indicates whether any request is still in flight, including background updates. + */ + isFetching: false; + /** + * Any error that occurred during the data fetch, or `null` if no error occurred. + */ + error: null; + }) + | (BaseSubscriptionReturn & { + data: BillingSubscriptionResource; + isLoading: true; + isFetching: true; + error: Error; + }) + | (BaseSubscriptionReturn & { + data: BillingSubscriptionResource | null; + isLoading: boolean; + isFetching: boolean; + error: Error | null; + }); + +/** * * Fetches subscription data for the current user or organization. * diff --git a/packages/shared/src/react/types.ts b/packages/shared/src/react/types.ts index ddc2603fa59..262b66f1796 100644 --- a/packages/shared/src/react/types.ts +++ b/packages/shared/src/react/types.ts @@ -115,6 +115,7 @@ export type PagesOrInfiniteConfig = PaginatedHookConfig<{ * @default undefined * * @hidden + * * @experimental */ __experimental_mode?: 'cache'; diff --git a/packages/shared/typedoc.json b/packages/shared/typedoc.json index 332cb7df315..4e3d9edadcb 100644 --- a/packages/shared/typedoc.json +++ b/packages/shared/typedoc.json @@ -1,6 +1,12 @@ { "$schema": "https://typedoc.org/schema.json", - "entryPoints": ["./src/index.ts", "./src/react/types.ts", "./src/react/hooks/*.{ts,tsx}"], + "entryPoints": [ + "./src/index.ts", + "./src/react/types.ts", + "./src/react/hooks/*.{ts,tsx}", + "./src/react/commerce.tsx", + "./src/react/contexts.tsx" + ], "compilerOptions": { "noImplicitAny": false } diff --git a/packages/types/src/billing.ts b/packages/types/src/billing.ts index 76727fca7f2..7a1f9b29946 100644 --- a/packages/types/src/billing.ts +++ b/packages/types/src/billing.ts @@ -758,7 +758,7 @@ export interface BillingCheckoutResource extends ClerkResource { */ planPeriod: BillingSubscriptionPlanPeriod; /** - * Unix timestamp (milliseconds) of when the current period starts. + * The start date of the plan period, represented as a Unix timestamp. */ planPeriodStart?: number; /** diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index fcf84df542f..b3c49d076af 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -79,6 +79,9 @@ export type __experimental_CheckoutOptions = { planId: string; }; +/** + * @inline + */ type CheckoutResult = | { data: BillingCheckoutResource; diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 6c814689fe3..d460c75d027 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -142,7 +142,7 @@ export type UseSignInReturn = }; /** - * @inline + * @inline */ export type UseSignUpReturn = | { diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index a189d85c73c..d873a48f739 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -43,10 +43,14 @@ type DisallowSystemPermissions

= P extends `${OrganizationSyst ? 'System permissions are not included in session claims and cannot be used on the server-side' : P; -/** @inline */ +/** + * @inline + */ export type CheckAuthorizationFn = (isAuthorizedParams: Params) => boolean; -/** @inline */ +/** + * @inline + */ export type CheckAuthorizationWithCustomPermissions = CheckAuthorizationFn;