UNPKG

@clerk/shared

Version:

Internal package utils used by the Clerk SDKs

1 lines 5.55 kB
{"version":3,"file":"getToken.mjs","names":[],"sources":["../src/getToken.ts"],"sourcesContent":["import { inBrowser } from './browser';\nimport { ClerkRuntimeError } from './errors/clerkRuntimeError';\nimport type { GetTokenOptions, LoadedClerk } from './types';\n\nconst TIMEOUT_MS = 10000; // 10 second timeout for Clerk to load\n\n/**\n * A promise that includes resolve/reject callbacks for external resolution.\n * Used for coordination between getToken() and clerk-js initialization.\n */\ntype ClerkReadyPromise = Promise<LoadedClerk> & {\n __resolve?: (clerk: LoadedClerk) => void;\n __reject?: (error: Error) => void;\n};\n\n/**\n * Local Window type extension for __clerk_internal_ready coordination.\n * Avoids global augmentation to prevent declaration collisions for consumers.\n */\ninterface ClerkWindow extends Window {\n __clerk_internal_ready?: ClerkReadyPromise;\n}\n\nfunction getWindowClerk(): LoadedClerk | undefined {\n if (inBrowser() && 'Clerk' in window) {\n const clerk = (window as unknown as { Clerk?: LoadedClerk }).Clerk;\n if (clerk && (clerk.status === 'ready' || clerk.status === 'degraded')) {\n return clerk;\n }\n // Legacy fallback for older clerk-js versions without status\n if (clerk?.loaded && !clerk.status) {\n return clerk;\n }\n }\n return undefined;\n}\n\nasync function waitForClerk(): Promise<LoadedClerk> {\n if (!inBrowser()) {\n throw new ClerkRuntimeError(\n 'getToken can only be used in browser environments. To access auth data server-side, see the Auth object reference doc: https://clerk.com/docs/reference/backend/types/auth-object',\n {\n code: 'clerk_runtime_not_browser',\n },\n );\n }\n\n const clerk = getWindowClerk();\n if (clerk) {\n return clerk;\n }\n\n const clerkWindow = window as ClerkWindow;\n\n // Get or create the coordination promise\n if (!clerkWindow.__clerk_internal_ready) {\n let resolve!: (clerk: LoadedClerk) => void;\n let reject!: (error: Error) => void;\n const promise = new Promise<LoadedClerk>((res, rej) => {\n resolve = res;\n reject = rej;\n }) as ClerkReadyPromise;\n promise.__resolve = resolve;\n promise.__reject = reject;\n clerkWindow.__clerk_internal_ready = promise;\n }\n\n const readyPromise = clerkWindow.__clerk_internal_ready;\n\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () =>\n reject(\n new ClerkRuntimeError('Timeout waiting for Clerk to load.', {\n code: 'clerk_runtime_load_timeout',\n }),\n ),\n TIMEOUT_MS,\n );\n });\n\n return Promise.race([readyPromise, timeoutPromise]).finally(() => {\n clearTimeout(timeoutId);\n });\n}\n\n/**\n * Retrieves the current session token, waiting for Clerk to initialize if necessary.\n *\n * This function is safe to call from anywhere in the browser, such as API interceptors,\n * data fetching layers, or vanilla JavaScript code.\n *\n * **Note:** In frameworks with concurrent rendering (e.g., React 18+), a global token read\n * may not correspond to the currently committed UI during transitions. This is a coherence\n * consideration, not an auth safety issue.\n *\n * @param options - Optional configuration for token retrieval\n * @param options.template - The name of a JWT template to use\n * @param options.organizationId - Organization ID to include in the token\n * @param options.skipCache - Whether to skip the token cache\n * @returns A Promise that resolves to the session token, or `null` if the user is not signed in\n *\n * @throws {ClerkRuntimeError} When called in a non-browser environment (code: `clerk_runtime_not_browser`)\n *\n * @throws {ClerkRuntimeError} When Clerk fails to load within timeout (code: `clerk_runtime_load_timeout`)\n *\n * @throws {ClerkOfflineError} When the browser is offline and unable to fetch a token (code: `clerk_offline`).\n * Use `ClerkOfflineError.is(error)` to check for this error type.\n *\n * @example\n * ```typescript\n * // In an Axios interceptor\n * import { getToken } from '@clerk/nextjs';\n *\n * axios.interceptors.request.use(async (config) => {\n * const token = await getToken();\n * if (token) {\n * config.headers.Authorization = `Bearer ${token}`;\n * }\n * return config;\n * });\n * ```\n */\nexport async function getToken(options?: GetTokenOptions): Promise<string | null> {\n const clerk = await waitForClerk();\n\n if (!clerk.session) {\n return null;\n }\n\n return clerk.session.getToken(options);\n}\n"],"mappings":";;;;AAIA,MAAM,aAAa;AAmBnB,SAAS,iBAA0C;CACjD,IAAI,UAAU,KAAK,WAAW,QAAQ;EACpC,MAAM,QAAS,OAA8C;EAC7D,IAAI,UAAU,MAAM,WAAW,WAAW,MAAM,WAAW,aACzD,OAAO;EAGT,IAAI,OAAO,UAAU,CAAC,MAAM,QAC1B,OAAO;CAEX;AAEF;AAEA,eAAe,eAAqC;CAClD,IAAI,CAAC,UAAU,GACb,MAAM,IAAI,kBACR,qLACA,EACE,MAAM,4BACR,CACF;CAGF,MAAM,QAAQ,eAAe;CAC7B,IAAI,OACF,OAAO;CAGT,MAAM,cAAc;CAGpB,IAAI,CAAC,YAAY,wBAAwB;EACvC,IAAI;EACJ,IAAI;EACJ,MAAM,UAAU,IAAI,SAAsB,KAAK,QAAQ;GACrD,UAAU;GACV,SAAS;EACX,CAAC;EACD,QAAQ,YAAY;EACpB,QAAQ,WAAW;EACnB,YAAY,yBAAyB;CACvC;CAEA,MAAM,eAAe,YAAY;CAEjC,IAAI;CAEJ,MAAM,iBAAiB,IAAI,SAAgB,GAAG,WAAW;EACvD,YAAY,iBAER,OACE,IAAI,kBAAkB,sCAAsC,EAC1D,MAAM,6BACR,CAAC,CACH,GACF,UACF;CACF,CAAC;CAED,OAAO,QAAQ,KAAK,CAAC,cAAc,cAAc,CAAC,CAAC,CAAC,cAAc;EAChE,aAAa,SAAS;CACxB,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,eAAsB,SAAS,SAAmD;CAChF,MAAM,QAAQ,MAAM,aAAa;CAEjC,IAAI,CAAC,MAAM,SACT,OAAO;CAGT,OAAO,MAAM,QAAQ,SAAS,OAAO;AACvC"}