diff --git a/.typedoc/__tests__/file-structure.test.ts b/.typedoc/__tests__/file-structure.test.ts index 983a0972d5b..ad1c3770971 100644 --- a/.typedoc/__tests__/file-structure.test.ts +++ b/.typedoc/__tests__/file-structure.test.ts @@ -47,11 +47,29 @@ describe('Typedoc output', () => { it('should only have these nested folders', async () => { const folders = await scanDirectory('directory'); - const nestedFolders = folders.filter(folder => !isTopLevelPath(folder)); + const nestedFolders = folders.filter(folder => !isTopLevelPath(folder)).sort((a, b) => a.localeCompare(b)); expect(nestedFolders).toMatchInlineSnapshot(` [ "react/legacy", + "shared/api-key-resource", + "shared/api-key-resource/methods", + "shared/billing-namespace", + "shared/billing-namespace/methods", + "shared/clerk", + "shared/clerk/methods", + "shared/client-resource", + "shared/client-resource/methods", + "shared/organization-resource", + "shared/organization-resource/methods", + "shared/session-resource", + "shared/session-resource/methods", + "shared/sign-in-future-resource", + "shared/sign-in-future-resource/methods", + "shared/sign-up-future-resource", + "shared/sign-up-future-resource/methods", + "shared/user-resource", + "shared/user-resource/methods", ] `); }); diff --git a/.typedoc/comment-utils.mjs b/.typedoc/comment-utils.mjs new file mode 100644 index 00000000000..842047b19d7 --- /dev/null +++ b/.typedoc/comment-utils.mjs @@ -0,0 +1,93 @@ +// @ts-check +import { Comment } from 'typedoc'; + +const TODO_WORD = /\bTODO\b/i; + +/** + * @param {import('typedoc').Comment | undefined} comment + */ +export function commentContainsTodo(comment) { + if (!comment) { + return false; + } + const chunks = []; + if (comment.summary?.length) { + chunks.push(Comment.combineDisplayParts(comment.summary)); + } + for (const tag of comment.blockTags ?? []) { + if (tag.content?.length) { + chunks.push(Comment.combineDisplayParts(tag.content)); + } + } + return chunks.some(text => TODO_WORD.test(text)); +} + +/** + * Truncate at the first word "TODO" (case-insensitive). Used when flattening display parts to a string. + * + * @param {string} text + */ +export function stripTextAfterTodo(text) { + if (!text) { + return ''; + } + const m = TODO_WORD.exec(text); + if (!m) { + return text; + } + return text.slice(0, m.index).trimEnd(); +} + +/** + * Drop display parts from the first `TODO` onward; truncate the containing text part if `TODO` appears mid-string. + * + * @param {import('typedoc').CommentDisplayPart[] | undefined} parts + * @returns {import('typedoc').CommentDisplayPart[]} + */ +function stripTodoFromDisplayParts(parts) { + if (!parts?.length) { + return parts ?? []; + } + /** @type {import('typedoc').CommentDisplayPart[]} */ + const out = []; + for (const p of parts) { + if (p.kind === 'text' && 'text' in p && typeof p.text === 'string') { + const match = TODO_WORD.exec(p.text); + if (match) { + const before = p.text.slice(0, match.index).trimEnd(); + if (before.length) { + out.push(/** @type {import('typedoc').CommentDisplayPart} */ ({ kind: 'text', text: before })); + } + return out; + } + } + out.push(p); + } + return out; +} + +/** + * Returns a clone with `TODO` and everything after it removed from the summary and from any block tag that contains `TODO`. + * Comments without `TODO` are returned unchanged (same reference). Undefined in, undefined out. + * + * @param {import('typedoc').Comment | undefined} comment + * @returns {import('typedoc').Comment | undefined} + */ +export function applyTodoStrippingToComment(comment) { + if (!comment) { + return undefined; + } + if (!commentContainsTodo(comment)) { + return comment; + } + const c = comment.clone(); + if (c.summary?.length && TODO_WORD.test(Comment.combineDisplayParts(c.summary))) { + c.summary = stripTodoFromDisplayParts(c.summary); + } + for (const tag of c.blockTags ?? []) { + if (tag.content?.length && TODO_WORD.test(Comment.combineDisplayParts(tag.content))) { + tag.content = stripTodoFromDisplayParts(tag.content); + } + } + return c; +} diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index e7132279b63..6c541cf12c0 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -62,6 +62,7 @@ const LINK_REPLACEMENTS = [ ['user-resource', '/docs/reference/objects/user'], ['session-status-claim', '/docs/reference/types/session-status'], ['user-organization-invitation-resource', '/docs/reference/types/user-organization-invitation'], + ['organization-custom-role-key', '/docs/reference/types/organization-custom-role-key'], ['organization-membership-resource', '/docs/reference/types/organization-membership'], ['organization-suggestion-resource', '/docs/reference/types/organization-suggestion'], ['organization-resource', '/docs/reference/objects/organization'], @@ -70,6 +71,7 @@ const LINK_REPLACEMENTS = [ ['organization-membership-request-resource', '/docs/reference/types/organization-membership-request'], ['o-auth-consent-info', '/docs/reference/types/oauth-consent-info'], ['o-auth-consent-scope', '/docs/reference/types/oauth-consent-scope'], + ['o-auth-strategy', '/docs/reference/types/sso#o-auth-strategy'], ['session', '/docs/reference/backend/types/backend-session'], ['session-activity', '/docs/reference/backend/types/backend-session-activity'], ['organization', '/docs/reference/backend/types/backend-organization'], @@ -82,7 +84,10 @@ const LINK_REPLACEMENTS = [ ['enterprise-account-connection', '/docs/reference/backend/types/backend-enterprise-account-connection'], ['enterprise-connection', '/docs/reference/backend/types/backend-enterprise-connection'], ['enterprise-connection-oauth-config', '/docs/reference/backend/types/backend-enterprise-connection-oauth-config'], - ['enterprise-connection-saml-connection', '/docs/reference/backend/types/backend-enterprise-connection-saml-connection'], + [ + 'enterprise-connection-saml-connection', + '/docs/reference/backend/types/backend-enterprise-connection-saml-connection', + ], ['external-account', '/docs/reference/backend/types/backend-external-account'], ['phone-number', '/docs/reference/backend/types/backend-phone-number'], ['saml-account', '/docs/reference/backend/types/backend-saml-account'], @@ -114,6 +119,13 @@ const LINK_REPLACEMENTS = [ ['deleted-object-resource', '/docs/reference/types/deleted-object-resource'], ['checkout-flow-resource', '/docs/reference/hooks/use-checkout#checkout-flow-resource'], ['organization-creation-defaults-resource', '#organization-creation-defaults-resource'], + ['billing-namespace', '/docs/reference/objects/billing'], + ['client-resource', '/docs/reference/objects/client'], + ['redirect-options', '/docs/reference/types/redirect-options'], + ['handle-o-auth-callback-params', '/docs/reference/types/handle-o-auth-callback-params'], + ['session-task', '/docs/reference/types/session-task'], + ['public-user-data', '/docs/reference/types/public-user-data'], + ['session-status', '/docs/reference/types/session-status'], ]; /** @@ -132,14 +144,33 @@ const LINK_REPLACEMENTS = [ function getRelativeLinkReplacements() { return LINK_REPLACEMENTS.map(([fileName, newPath]) => { return { - // Match both path and optional anchor - pattern: new RegExp(`\\((?:(?:\\.{1,2}\\/)+[^()]*?|)${fileName}\\.mdx(#[^)]+)?\\)`, 'g'), + // Match both flat links and nested object-doc links + // Also matches optional anchors (#) + pattern: new RegExp(`\\((?:(?:\\.{1,2}\\/)+[^()]*?|)(?:${fileName}\\/)?${fileName}\\.mdx(#[^)]+)?\\)`, 'g'), // Preserve the anchor in replacement if it exists replace: (/** @type {string} */ _match, anchor = '') => `(${newPath}${anchor})`, }; }); } +/** + * First pass of `MarkdownPageEvent.END`: rewrite `(foo.mdx)` / relative paths to `/docs/...` (see {@link LINK_REPLACEMENTS}). + * Used by `extract-methods.mjs`, which does not go through the renderer hook. + * + * @param {string} contents + */ +export function applyRelativeLinkReplacements(contents) { + if (!contents) { + return contents; + } + let out = contents; + for (const { pattern, replace } of getRelativeLinkReplacements()) { + // @ts-ignore — string | function + out = out.replace(pattern, replace); + } + return out; +} + function getCatchAllReplacements() { return [ { @@ -147,96 +178,176 @@ function getCatchAllReplacements() { replace: '[ClerkAPIResponseError](/docs/reference/types/clerk-api-response-error)', }, { - pattern: /(?/g, - replace: '[`Appearance`](/docs/guides/customizing-clerk/appearance-prop/overview)', + pattern: /(?/g, + replace: '[Appearance](/docs/guides/customizing-clerk/appearance-prop/overview)', + }, + { + pattern: /(? `[\`${type}\`](/docs/reference/types/errors)`, }, { - pattern: /(? { + if (!inner.includes('|')) { + return full; + } + const id = placeholders.length; + placeholders.push(full); + return `\uE000${id}\uE001`; + }); + return { text, placeholders }; +} + +/** + * @param {string} text + * @param {string[]} placeholders + */ +function restoreProtectedInlineCodeSpans(text, placeholders) { + return text.replace(PIPE_CODE_PH, (_, /** @type {string} */ i) => placeholders[Number(i)] ?? ''); +} + +/** + * 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. + * + * Skips ATX heading lines (`#` … `######`) so titles like `#### SetActiveParams` are never linkified. + * (A lone `(? { + if (ATX_HEADING_LINE.test(line.replace(/\r$/, ''))) { + return line; + } + const { text: withPh, placeholders } = protectPipeDelimitedInlineCodeSpans(line); + let out = withPh; + for (const { pattern, replace } of getCatchAllReplacements()) { + // @ts-ignore — string | function + out = out.replace(pattern, replace); + } + return restoreProtectedInlineCodeSpans(out, placeholders); + }, + ) + .join('\n'); +} + /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ export function load(app) { app.renderer.on(MarkdownPageEvent.END, output => { const fileName = output.url.split('/').pop(); - const linkReplacements = getRelativeLinkReplacements(); - for (const { pattern, replace } of linkReplacements) { - if (output.contents) { - output.contents = output.contents.replace(pattern, replace); - } + if (output.contents) { + output.contents = applyRelativeLinkReplacements(output.contents); } - const catchAllReplacements = getCatchAllReplacements(); - - for (const { pattern, replace } of catchAllReplacements) { - if (output.contents) { - // @ts-ignore - Mixture of string and function replacements - output.contents = output.contents.replace(pattern, replace); - } + if (output.contents) { + output.contents = applyCatchAllMdReplacements(output.contents); } if (fileName) { diff --git a/.typedoc/custom-router.mjs b/.typedoc/custom-router.mjs index 97cf8acef8d..c7aabc36813 100644 --- a/.typedoc/custom-router.mjs +++ b/.typedoc/custom-router.mjs @@ -1,6 +1,12 @@ // @ts-check +import { ReflectionKind } from 'typedoc'; import { MemberRouter } from 'typedoc-plugin-markdown'; +import { REFERENCE_OBJECT_PAGE_SYMBOLS } from './reference-objects.mjs'; + +/** @type {Set} */ +const REFERENCE_OBJECT_SYMBOL_NAMES = new Set(Object.values(REFERENCE_OBJECT_PAGE_SYMBOLS)); + /** * From a filepath divided by `/` only keep the first and last part * @param {string} filePath @@ -47,7 +53,24 @@ class ClerkRouter extends MemberRouter { const isExactMatch = page.url.toLocaleLowerCase().endsWith('readme.mdx'); const isMatchWithNumber = page.url.toLocaleLowerCase().match(/readme-\d+\.mdx$/); - return !(isExactMatch || isMatchWithNumber); + if (isExactMatch || isMatchWithNumber) { + return false; + } + + /** + * `@inline` marks types that should be expanded at use sites, not documented as their own page. + * TypeDoc still assigns `fullUrls` for exported aliases, so we also strip links in the theme's `referenceType` partial (`custom-theme.mjs`). + */ + const model = page.model; + if ( + model && + 'comment' in model && + /** @type {{ comment?: import('typedoc').Comment | undefined }} */ (model).comment?.hasModifier('@inline') + ) { + return false; + } + + return true; }); return modifiedPages; @@ -72,6 +95,22 @@ class ClerkRouter extends MemberRouter { */ filePath = flattenDirName(filePath); + /** + * Put each reference object in its own folder alongside `properties.mdx` and `methods/` from `extract-methods.mjs`. + * E.g. `shared/clerk.mdx` -> `shared/clerk/clerk.mdx`, `shared/clerk/properties.mdx`, and `shared/clerk/methods/`. + */ + if ( + (reflection.kind === ReflectionKind.Interface || reflection.kind === ReflectionKind.Class) && + REFERENCE_OBJECT_SYMBOL_NAMES.has(reflection.name) + ) { + const kebab = toKebabCase(reflection.name); + const m = filePath.match(/^([^/]+)\/([^/]+)$/); + if (m) { + const [, pkg] = m; + return `${pkg}/${kebab}/${kebab}`; + } + } + return filePath; } } diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index ec1c32fa2dd..82f0dbf83ac 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -1,6 +1,967 @@ // @ts-check -import { ReflectionKind, ReflectionType, UnionType } from 'typedoc'; +import { ArrayType, i18n, IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc'; import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown'; +import { + backTicks, + heading, + htmlTable, + table, +} from '../node_modules/typedoc-plugin-markdown/dist/libs/markdown/index.js'; +import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; +import { TypeDeclarationVisibility } from '../node_modules/typedoc-plugin-markdown/dist/options/maps.js'; + +import { applyTodoStrippingToComment } from './comment-utils.mjs'; +import { REFERENCE_OBJECTS_LIST } from './reference-objects.mjs'; + +export { REFERENCE_OBJECTS_LIST }; + +/** + * Unwrap optional TypeDoc types so referenced object shapes are still found. + * + * @param {import('typedoc').Type} t + * @returns {import('typedoc').Type} + */ +/** + * Prefer structural checks over `instanceof` so we still match when multiple TypeDoc copies are loaded + * (otherwise `instanceof IntersectionType` is false at render time). + * + * @param {import('typedoc').Type | undefined} t + * @returns {t is import('typedoc').IntersectionType} + */ +function isIntersectionTypeDoc(t) { + const o = /** @type {{ type?: string; types?: import('typedoc').Type[] } | null} */ (t); + return Boolean(o && typeof o === 'object' && o.type === 'intersection' && Array.isArray(o.types)); +} + +/** + * @param {import('typedoc').Type | undefined} t + * @returns {t is import('typedoc').ReferenceType} + */ +function isReferenceTypeDoc(/** @type {import('typedoc').Type | undefined} */ t) { + return Boolean(t && typeof t === 'object' && /** @type {{ type?: string }} */ (t).type === 'reference'); +} + +/** + * @param {import('typedoc').Type | undefined} t + * @returns {t is import('typedoc').ReflectionType} + */ +function isReflectionTypeDoc(/** @type {import('typedoc').Type | undefined} */ t) { + return Boolean(t && typeof t === 'object' && /** @type {{ type?: string }} */ (t).type === 'reflection'); +} + +/** + * @param {import('typedoc').Type | undefined} t + */ +function unwrapOptionalType(t) { + if ( + t && + typeof t === 'object' && + 'type' in t && + /** @type {{ type: string }} */ (t).type === 'optional' && + 'elementType' in t + ) { + return /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + } + return t; +} + +/** + * When `ReferenceType.reflection` is unset (common for imported aliases), resolve by name in the converted project. + * + * @param {import('typedoc').ProjectReflection | undefined} project + * @param {string} name + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function findNamedTypeDeclaration(project, name) { + if (!project?.reflections) { + return undefined; + } + for (const r of Object.values(project.reflections)) { + if (r.name !== name) { + continue; + } + if (r.kind === ReflectionKind.TypeAlias || r.kind === ReflectionKind.Interface) { + return /** @type {import('typedoc').DeclarationReflection} */ (r); + } + } + return undefined; +} + +/** + * Prefer `packages/shared/src/types/strategies.ts` when multiple type aliases share the name `OAuthStrategy`. + * + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function findOAuthStrategyDeclaration(project) { + if (!project) { + return undefined; + } + /** @param {import('typedoc').Reflection} r */ + const sourcePath = r => { + const sources = /** @type {{ sources?: object[] }} */ (r).sources; + const s = sources?.[0]; + if (!s) { + return ''; + } + const raw = /** @type {{ file?: { fullFileName?: string }; fullFileName?: string }} */ (s); + const p = raw.file?.fullFileName ?? raw.fullFileName ?? ''; + return String(p).replace(/\\/g, '/'); + }; + + const byKind = + typeof project.getReflectionsByKind === 'function' + ? project.getReflectionsByKind(ReflectionKind.TypeAlias).filter(r => r.name === 'OAuthStrategy') + : Object.values(project.reflections ?? {}).filter( + r => + r.name === 'OAuthStrategy' && + /** @type {import('typedoc').Reflection} */ (r).kindOf?.(ReflectionKind.TypeAlias), + ); + if (byKind.length === 0) { + return findNamedTypeDeclaration(project, 'OAuthStrategy'); + } + if (byKind.length === 1) { + return /** @type {import('typedoc').DeclarationReflection} */ (byKind[0]); + } + const fromStrategies = byKind.find(r => sourcePath(r).includes('strategies')); + return /** @type {import('typedoc').DeclarationReflection | undefined} */ (fromStrategies ?? byKind[0]); +} + +/** + * Stock `someType` uses `instanceof UnionType`; duplicate Typedoc copies in the tree break that check and unions + * fall through to `backTicks(model.toString())`, bypassing {@link unionType} entirely (including OAuth collapse). + * + * @param {import('typedoc').Type | undefined} model + * @returns {import('typedoc').UnionType | undefined} + */ +function coerceUnionTypeIfNeeded(model) { + if (!model || typeof model !== 'object') { + return undefined; + } + if (model instanceof UnionType) { + return model; + } + const o = /** @type {{ type?: string; types?: import('typedoc').SomeType[] }} */ (model); + if (o.type === 'union' && Array.isArray(o.types) && o.types.length) { + return new UnionType(o.types); + } + return undefined; +} + +/** + * TypeScript normalizes `OAuthStrategy` to a large union of `oauth_*` string literals plus + * `` `oauth_custom_${string}` ``. That is not a {@link ReferenceType}, so the theme prints every literal. + * Collapse **only** when the union clearly matches that expanded Clerk shape, then render a link to `OAuthStrategy`. + * + * Guards (all must pass): many `oauth_` literals, fingerprint literals present, optional `oauth_custom_` template arm, + * `OAuthStrategy` exists and is not `@inline`. Skips ambiguous cases so other unions are unchanged. + * + * @param {import('typedoc').Type | undefined} t + * @returns {import('typedoc').Type[]} + */ +function flattenUnionTypeMembersForOAuthCollapse(t) { + if (!t || typeof t !== 'object') { + return []; + } + const o = /** @type {{ type?: string; types?: import('typedoc').Type[] }} */ (t); + if (o.type === 'union' && Array.isArray(o.types)) { + /** @type {import('typedoc').Type[]} */ + const acc = []; + for (const inner of o.types) { + acc.push(...flattenUnionTypeMembersForOAuthCollapse(inner)); + } + return acc; + } + return [t]; +} + +/** + * @param {import('typedoc').Type} t + */ +function isExpandedOAuthStrategyUnionArm(t) { + const o = /** @type {{ type?: string; value?: unknown; head?: string; tail?: unknown }} */ (t); + if (o.type === 'literal' && typeof o.value === 'string') { + return o.value.startsWith('oauth_'); + } + if (o.type === 'templateLiteral' && typeof o.head === 'string') { + return o.head === 'oauth_custom_'; + } + return false; +} + +/** Minimum distinct `oauth_*` literal arms before we treat the union as “expanded OAuthStrategy”. */ +const OAUTH_STRATEGY_COLLAPSE_MIN_LITERAL_ARMS = 12; + +/** + * @param {import('typedoc').UnionType} model + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {import('typedoc').UnionType | undefined} + */ +function tryCollapseExpandedOAuthStrategyUnion(model, ctx) { + const project = ctx.page?.project; + if (!project) { + return undefined; + } + const oauthDecl = findOAuthStrategyDeclaration(project); + if (!oauthDecl?.kindOf(ReflectionKind.TypeAlias)) { + return undefined; + } + if (oauthDecl.comment?.hasModifier('@inline')) { + return undefined; + } + + const members = flattenUnionTypeMembersForOAuthCollapse(model); + const oauthArms = members.filter(isExpandedOAuthStrategyUnionArm); + if (oauthArms.length < OAUTH_STRATEGY_COLLAPSE_MIN_LITERAL_ARMS) { + return undefined; + } + + const literalVals = oauthArms + .filter(u => /** @type {{ type?: string }} */ (u).type === 'literal') + .map(u => /** @type {{ value?: unknown }} */ (/** @type {unknown} */ (u)).value) + .filter(/** @return {v is string} */ v => typeof v === 'string'); + const literalSet = new Set(literalVals); + if (!literalSet.has('oauth_google') || (!literalSet.has('oauth_facebook') && !literalSet.has('oauth_github'))) { + return undefined; + } + + const hasCustomTemplateArm = oauthArms.some(u => { + const o = /** @type {{ type?: string; head?: string }} */ (u); + return o.type === 'templateLiteral' && o.head === 'oauth_custom_'; + }); + /** Without the template arm, require an even larger literal set (avoids small hand-written unions). */ + if (!hasCustomTemplateArm && literalVals.length < 20) { + return undefined; + } + + const ref = ReferenceType.createResolvedReference('OAuthStrategy', oauthDecl, project); + /** @type {import('typedoc').Type[]} */ + const out = []; + let i = 0; + while (i < members.length) { + if (isExpandedOAuthStrategyUnionArm(members[i])) { + out.push(ref); + i++; + while (i < members.length && isExpandedOAuthStrategyUnionArm(members[i])) { + i++; + } + } else { + out.push(members[i]); + i++; + } + } + return new UnionType(/** @type {import('typedoc').SomeType[]} */ (/** @type {unknown} */ (out))); +} + +/** + * Collect documented property reflections from one intersection arm (object literal, type alias, interface, nested `&`). + * E.g. `{ a: string } & { b: number }` => `[{ name: 'a', type: 'string' }, { name: 'b', type: 'number' }]` + * + * @param {import('typedoc').Type} t + * @param {Set} visitedReflectionIds + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection[]} + */ +function collectPropertyReflectionsFromIntersectionArm(t, visitedReflectionIds, project) { + const unwrapped = unwrapOptionalType(t); + if (!unwrapped) { + return []; + } + + if (isReflectionTypeDoc(unwrapped)) { + const decl = unwrapped.declaration; + if (!decl) { + return []; + } + if (decl.signatures?.length && !decl.children?.length) { + return []; + } + return (decl.children ?? []).filter(c => c.kind === ReflectionKind.Property); + } + + if (isReferenceTypeDoc(unwrapped)) { + let ref = unwrapped.reflection; + if (!ref && unwrapped.name && project) { + ref = findNamedTypeDeclaration(project, unwrapped.name); + } + if (!ref) { + return []; + } + const declRef = /** @type {import('typedoc').DeclarationReflection | undefined} */ ( + 'kind' in ref ? ref : undefined + ); + if (!declRef) { + return []; + } + const id = declRef.id; + if (id != null) { + if (visitedReflectionIds.has(id)) { + return []; + } + visitedReflectionIds.add(id); + } + try { + if (declRef.kind === ReflectionKind.TypeAlias) { + if (declRef.children?.length) { + return declRef.children.filter( + /** @param {import('typedoc').DeclarationReflection} c */ + c => c.kind === ReflectionKind.Property, + ); + } + if (declRef.type) { + return collectPropertyReflectionsFromIntersectionArm(declRef.type, visitedReflectionIds, project); + } + return []; + } + if ( + (declRef.kind === ReflectionKind.Interface || declRef.kind === ReflectionKind.Class) && + declRef.children?.length + ) { + return declRef.children.filter( + /** @param {import('typedoc').DeclarationReflection} c */ + c => c.kind === ReflectionKind.Property, + ); + } + } finally { + if (id != null) { + visitedReflectionIds.delete(id); + } + } + return []; + } + + if (isIntersectionTypeDoc(unwrapped)) { + /** @type {import('typedoc').DeclarationReflection[]} */ + const out = []; + for (const arm of unwrapped.types) { + out.push(...collectPropertyReflectionsFromIntersectionArm(arm, visitedReflectionIds, project)); + } + return out; + } + + return []; +} + +/** + * Merge intersection arms into one property list (later duplicate names override earlier ones, then sort by name). + * type A = type B & type C; type A's properties will be the union of B's and C's properties. + * E.g. `ClerkOptions` in clerk.ts + * + * @param {import('typedoc').IntersectionType} intersection + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection[]} + */ +function mergeIntersectionPropertyReflections(intersection, project) { + /** @type {Map} */ + const byName = new Map(); + const visited = new Set(); + for (const arm of intersection.types) { + for (const p of collectPropertyReflectionsFromIntersectionArm(arm, visited, project)) { + byName.set(p.name, p); + } + } + return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); +} + +/** + * For properties typed something like `false \| { a?: … }`, `getFlattenedDeclarations` does not walk the union, so nested keys never become table rows. Collect object members from each union arm (primitives/literals yield nothing). + * E.g. `telemetry` prop in clerk.ts + * + * @param {import('typedoc').Type | undefined} t + * @param {Set} visitedReflectionIds + * @param {import('typedoc').ProjectReflection | undefined} project + * @returns {import('typedoc').DeclarationReflection[]} + */ +function collectPropertyReflectionsFromUnionObjectArms(t, visitedReflectionIds, project) { + const unwrapped = unwrapOptionalType(t); + if (!unwrapped || /** @type {{ type?: string }} */ (unwrapped).type !== 'union') { + return []; + } + const union = /** @type {import('typedoc').UnionType} */ (unwrapped); + /** @type {Map} */ + const byName = new Map(); + for (const arm of union.types) { + for (const p of collectPropertyReflectionsFromIntersectionArm(arm, visitedReflectionIds, project)) { + byName.set(p.name, p); + } + } + return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)); +} + +/** + * Appends `parent.child` rows for union object arms (e.g. `false \| { disabled?: … }`). **Only** used when building {@link clerkTypeDeclarationTable}; we intentionally do **not** hook `helpers.getFlattenedDeclarations` globally — otherwise top-level `propertiesTable` output (e.g. `Clerk`) would gain synthetic rows like `client.*` for every property whose type is a union such as `ClientResource \| undefined`. + * + * @template {import('typedoc').DeclarationReflection} T + * @param {T[]} base + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + * @param {import('typedoc').ProjectReflection} project + * @returns {T[]} + */ +function appendUnionObjectChildPropertyRows(base, helpers, project) { + /** @type {T[]} */ + const out = []; + for (const prop of base) { + out.push(prop); + if (prop.name.includes('.')) { + continue; + } + const nested = collectPropertyReflectionsFromUnionObjectArms(helpers.getDeclarationType(prop), new Set(), project); + for (const child of nested) { + out.push( + /** @type {T} */ ( + /** @type {unknown} */ ({ + ...child, + name: `${prop.name}.${child.name}`, + getFullName: () => prop.getFullName(), + getFriendlyFullName: () => prop.getFriendlyFullName(), + }) + ), + ); + } + } + return out; +} + +/** + * @param {import('typedoc').ParameterReflection[]} parameters + */ +function hasDefaultValuesForParameters(parameters) { + const defaultValues = parameters.map( + param => param.defaultValue !== '{}' && param.defaultValue !== '...' && !!param.defaultValue, + ); + return !defaultValues.every(value => !value); +} + +/** + * Object shape for a parameter: inline `{ … }`, optional-wrapped, or reference to a type alias / interface. + * + * @param {import('typedoc').Type | undefined} t + * @returns {import('typedoc').DeclarationReflection | undefined} + */ +function getParameterObjectShapeDeclaration(t) { + if (!t || typeof t !== 'object') { + return undefined; + } + const o = + /** @type {{ type?: string; declaration?: import('typedoc').DeclarationReflection; elementType?: import('typedoc').Type }} */ ( + /** @type {unknown} */ (t) + ); + if (o.type === 'optional' && o.elementType) { + return getParameterObjectShapeDeclaration(o.elementType); + } + if (o.type === 'reflection' && o.declaration) { + const d = o.declaration; + if (d.kind === ReflectionKind.TypeLiteral || d.kind === ReflectionKind.Interface) { + return d; + } + } + if (o.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + const sym = ref.reflection; + if (!sym) { + return undefined; + } + const target = /** @type {import('typedoc').DeclarationReflection} */ (sym); + if (sym.kind === ReflectionKind.TypeAlias && target.type) { + return getParameterObjectShapeDeclaration(target.type); + } + if (sym.kind === ReflectionKind.Interface) { + return target; + } + } + return undefined; +} + +/** + * Same as typedoc-plugin-markdown `member.parametersTable`, but avoids useless duplicate rows when a one-property object has **no** property-level JSDoc (the description lives only on `@param`). When the sole property **does** have its own documentation, we flatten so both rows appear. + * + * @param {import('typedoc').DeclarationReflection | undefined} decl + */ +function shouldFlattenInlineObjectParameter(decl) { + if (!decl?.children?.length) { + return false; + } + if (decl.kind !== ReflectionKind.TypeLiteral && decl.kind !== ReflectionKind.Interface) { + return false; + } + if (decl.children.length > 1) { + return true; + } + const only = decl.children[0]; + return Boolean(only?.comment?.hasVisibleComponent()); +} + +/** + * Same as typedoc-plugin-markdown `member.parametersTable`, with `shouldFlattenInlineObjectParameter` and `getParameterObjectShapeDeclaration`. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').ParameterReflection[]} model + */ +function clerkParametersTable(model) { + const tableColumnsOptions = /** @type {{ leftAlignHeaders?: boolean; hideDefaults?: boolean }} */ ( + this.options.getValue('tableColumnSettings') ?? {} + ); + const leftAlignHeadings = tableColumnsOptions.leftAlignHeaders; + /** + * @param {import('typedoc').ParameterReflection} current + * @param {import('typedoc').ParameterReflection[]} acc + * @returns {import('typedoc').ParameterReflection[]} + */ + const parseParams = (current, acc) => { + const decl = getParameterObjectShapeDeclaration(current.type); + const shouldFlatten = shouldFlattenInlineObjectParameter(decl); + return shouldFlatten ? [...acc, current, ...flattenParams(current)] : [...acc, current]; + }; + /** + * @param {import('typedoc').ParameterReflection} current + * @returns {import('typedoc').ParameterReflection[]} + */ + const flattenParams = current => { + const decl = getParameterObjectShapeDeclaration(current.type); + return ( + decl?.children?.reduce( + /** + * @param {import('typedoc').ParameterReflection[]} acc + * @param {import('typedoc').DeclarationReflection} child + * @returns {import('typedoc').ParameterReflection[]} + */ + (acc, child) => { + const childObj = { + ...child, + name: `${current.name}.${child.name}`, + }; + return parseParams( + /** @type {import('typedoc').ParameterReflection} */ (/** @type {unknown} */ (childObj)), + acc, + ); + }, + /** @type {import('typedoc').ParameterReflection[]} */ ([]), + ) ?? [] + ); + }; + const showDefaults = !tableColumnsOptions.hideDefaults && hasDefaultValuesForParameters(model); + const parsedParams = /** @type {import('typedoc').ParameterReflection[]} */ ( + model.reduce( + (acc, current) => parseParams(current, acc), + /** @type {import('typedoc').ParameterReflection[]} */ ([]), + ) + ); + const hasComments = parsedParams.some(param => Boolean(param.comment)); + const theme = /** @type {Record string>} */ (/** @type {unknown} */ (i18n)); + const headers = [ReflectionKind.singularString(ReflectionKind.Parameter), theme.theme_type()]; + if (showDefaults) { + headers.push(theme.theme_default_value()); + } + if (hasComments) { + headers.push(theme.theme_description()); + } + const firstOptionalParamIndex = model.findIndex(parameter => parameter.flags.isOptional); + /** @type {string[][]} */ + const rows = []; + parsedParams.forEach((parameter, i) => { + const row = []; + const isOptional = parameter.flags.isOptional || (firstOptionalParamIndex !== -1 && i > firstOptionalParamIndex); + const rest = parameter.flags?.isRest ? '...' : ''; + const optional = isOptional ? '?' : ''; + row.push(`${rest}${backTicks(`${parameter.name}${optional}`)}`); + if (parameter.type) { + const displayType = + parameter.type instanceof ReflectionType + ? this.partials.reflectionType(parameter.type, { + forceCollapse: true, + }) + : this.partials.someType(parameter.type); + row.push(removeLineBreaks(displayType)); + } + if (showDefaults) { + row.push(backTicks(this.helpers.getParameterDefaultValue(parameter))); + } + if (hasComments) { + if (parameter.comment) { + row.push(this.partials.comment(parameter.comment, { isTableColumn: true })); + } else { + row.push('-'); + } + } + rows.push(row); + }); + return this.options.getValue('parametersFormat') == 'table' + ? table(headers, rows, leftAlignHeadings) + : htmlTable(headers, rows, leftAlignHeadings); +} + +/** + * Used in `clerkTypeDeclarationTable()` to determine if the table should be displayed as an HTML table. + * + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} context + * @param {import('typedoc').ReflectionKind | undefined} kind + */ +function clerkShouldDisplayHtmlTable(context, kind) { + if ( + kind && + [ReflectionKind.CallSignature, ReflectionKind.Variable, ReflectionKind.TypeAlias].includes(kind) && + context.options.getValue('typeDeclarationFormat') == 'htmlTable' + ) { + return true; + } + if (kind === ReflectionKind.Property && context.options.getValue('propertyMembersFormat') == 'htmlTable') { + return true; + } + return false; +} + +/** + * Same rules as typedoc-plugin-markdown `shouldDisplayHTMLTable` in `member.propertiesTable.js` (that helper is not exported). Drives **property-style** tables: `propertiesFormat`, `interfacePropertiesFormat`, etc. + * + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} context + * @param {import('typedoc').ReflectionKind | undefined} kind + */ +function propertiesTableUsesHtmlTable(context, kind) { + if (context.options.getValue('propertiesFormat') === 'htmlTable') { + return true; + } + if (kind === ReflectionKind.Interface && context.options.getValue('interfacePropertiesFormat') === 'htmlTable') { + return true; + } + if (kind === ReflectionKind.Class && context.options.getValue('classPropertiesFormat') === 'htmlTable') { + return true; + } + if (kind === ReflectionKind.TypeAlias && context.options.getValue('typeAliasPropertiesFormat') === 'htmlTable') { + return true; + } + return false; +} + +/** + * Renders a pipe or HTML table using {@link propertiesTableUsesHtmlTable} and `tableColumnSettings.leftAlignHeaders`, matching `member.propertiesTable` output conventions. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {{ + * headers: string[]; + * rows: string[][]; + * kind?: import('typedoc').ReflectionKind; + * }} args + */ +function renderPropertiesFormatTable(args) { + const tableColumnsOptions = /** @type {{ leftAlignHeaders?: boolean }} */ ( + this.options.getValue('tableColumnSettings') ?? {} + ); + const leftAlignHeadings = tableColumnsOptions.leftAlignHeaders; + const kind = args.kind ?? ReflectionKind.TypeAlias; + const useHtml = propertiesTableUsesHtmlTable(this, kind); + return useHtml + ? htmlTable(args.headers, args.rows, leftAlignHeadings) + : table(args.headers, args.rows, leftAlignHeadings); +} + +/** + * Same logic as typedoc-plugin-markdown `member.typeDeclarationTable`, but **always** runs `getFlattenedDeclarations` and then {@link appendUnionObjectChildPropertyRows} (union-object arm rows like `telemetry.*`). The default plugin skips flattening in `compact` mode, which hides nested keys like `telemetry.disabled`. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').DeclarationReflection[]} model + * @param {{ kind?: import('typedoc').ReflectionKind }} options + */ +function clerkTypeDeclarationTable(model, options) { + const tableColumnsOptions = /** @type {{ leftAlignHeaders?: boolean; hideSources?: boolean }} */ ( + this.options.getValue('tableColumnSettings') ?? {} + ); + const leftAlignHeadings = tableColumnsOptions.leftAlignHeaders; + const isCompact = this.options.getValue('typeDeclarationVisibility') === TypeDeclarationVisibility.Compact; + const hasSources = !tableColumnsOptions.hideSources && !this.options.getValue('disableSources'); + const headers = []; + const baseDeclarations = this.helpers.getFlattenedDeclarations(model, { + includeSignatures: true, + }); + const project = this.page?.project ?? this.page?.model?.project; + const declarations = project + ? appendUnionObjectChildPropertyRows(baseDeclarations, this.helpers, project) + : baseDeclarations; + const hasDefaultValues = declarations.some( + declaration => Boolean(declaration.defaultValue) && declaration.defaultValue !== '...', + ); + const hasComments = declarations.some(declaration => Boolean(declaration.comment)); + const theme = /** @type {Record string>} */ (/** @type {unknown} */ (i18n)); + headers.push(theme.theme_name()); + headers.push(theme.theme_type()); + if (hasDefaultValues) { + headers.push(theme.theme_default_value()); + } + if (hasComments) { + headers.push(theme.theme_description()); + } + if (hasSources) { + headers.push(theme.theme_defined_in()); + } + /** @type {string[][]} */ + const rows = []; + declarations.forEach(declaration => { + const declType = /** @type {{ declaration?: import('typedoc').DeclarationReflection } | undefined} */ ( + /** @type {unknown} */ (declaration.type) + ); + const optional = declaration.flags.isOptional ? '?' : ''; + const isSignature = declType?.declaration?.signatures?.length || declaration.signatures?.length; + const row = []; + const nameColumn = []; + if (this.router.hasUrl(declaration) && this.router.getAnchor(declaration)) { + nameColumn.push(``); + } + const name = backTicks(`${declaration.name}${isSignature ? '()' : ''}${optional}`); + nameColumn.push(name); + row.push(nameColumn.join(' ')); + if (isCompact && declaration.type instanceof ReflectionType) { + row.push( + this.partials.reflectionType(declaration.type, { + forceCollapse: isCompact, + }), + ); + } else { + const type = []; + const signatures = declaration.signatures; + if (signatures?.length) { + signatures.forEach(sig => { + type.push(`${this.partials.signatureParameters(sig.parameters || [])} => `); + }); + type.push(this.partials.someType(declaration.type)); + } else { + type.push(this.partials.someType(declaration.type)); + } + row.push(type.join('')); + } + if (hasDefaultValues) { + row.push( + !declaration.defaultValue || declaration.defaultValue === '...' ? '-' : backTicks(declaration.defaultValue), + ); + } + if (hasComments) { + const commentsOut = []; + if (declaration.comment) { + commentsOut.push( + this.partials.comment(declaration.comment, { + isTableColumn: true, + }), + ); + } + if (declType?.declaration?.signatures?.length) { + for (const sig of declType.declaration.signatures) { + if (sig.comment) { + commentsOut.push( + this.partials.comment(sig.comment, { + isTableColumn: true, + }), + ); + } + } + } + if (commentsOut.length) { + row.push(commentsOut.join('\n\n')); + } else { + row.push('-'); + } + } + if (hasSources) { + row.push(this.partials.sources(declaration, { hideLabel: true })); + } + rows.push(row); + }); + return clerkShouldDisplayHtmlTable(this, options?.kind) + ? htmlTable(headers, rows, leftAlignHeadings) + : table(headers, rows, leftAlignHeadings); +} + +/** + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {import('typedoc').DeclarationReflection} model + * @param {{ nested?: boolean; headingLevel?: number }} opts + * @param {import('typedoc').DeclarationReflection[]} mergedChildren + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['partials']} superPartials + */ +function renderMergedIntersectionDeclaration(ctx, model, opts, mergedChildren, superPartials) { + /** @type {string[]} */ + const md = []; + const headingLevel = opts.headingLevel ?? 2; + const nested = opts.nested ?? false; + + if (!nested && model.sources && !ctx.options.getValue('disableSources')) { + md.push(superPartials.sources(model)); + } + if (model?.documents) { + md.push(superPartials.documents(model, { headingLevel })); + } + if (model.comment) { + md.push( + superPartials.comment(model.comment, { + headingLevel, + showSummary: true, + showTags: false, + }), + ); + } + + const synthetic = /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: mergedChildren, + parent: model, + kind: ReflectionKind.TypeLiteral, + }) + ); + md.push(superPartials.typeDeclaration(synthetic, { headingLevel })); + + if (model.comment) { + md.push( + superPartials.comment(model.comment, { + headingLevel, + showSummary: false, + showTags: true, + showReturns: true, + }), + ); + } + md.push(superPartials.inheritance(model, { headingLevel: opts.headingLevel ?? headingLevel })); + return md.filter(Boolean).join('\n\n'); +} + +/** + * Union of literals with JSDoc on each `|` arm: **## Properties** + a table via {@link renderPropertiesFormatTable} (same formatting rules as `member.propertiesTable`). + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').DeclarationReflection} model + * @param {{ headingLevel?: number }} options + */ +function clerkLiteralUnionAsPropertiesTable(model, options) { + const unionType = /** @type {import('typedoc').UnionType} */ (model.type); + const headingLevel = options.headingLevel ?? 2; + const ti = /** @type {{ theme_type: () => string; theme_description: () => string }} */ ( + /** @type {unknown} */ (i18n) + ); + + const headers = [ReflectionKind.singularString(ReflectionKind.Property), ti.theme_type(), ti.theme_description()]; + + /** @type {string[][]} */ + const rows = []; + unionType.types.forEach((type, i) => { + const typeStr = removeLineBreaks(this.partials.someType(type)); + const anchorRaw = typeStr.replace(/^`|`$/g, '').replace(/"/g, '').trim(); + const anchorId = anchorRaw.toLowerCase().replace(/[^a-z0-9]/g, '') || `member-${i}`; + + const propertyCell = [``, backTicks(anchorRaw || `member-${i}`)].join(' '); + + const summary = unionType.elementSummaries?.[i]; + const description = summary ? this.helpers.getCommentParts(summary) : '-'; + + rows.push([propertyCell, typeStr, description]); + }); + + const tableBody = renderPropertiesFormatTable.call(this, { + headers, + rows, + kind: ReflectionKind.TypeAlias, + }); + + return [heading(headingLevel, ReflectionKind.pluralString(ReflectionKind.Property)), tableBody].join('\n\n'); +} + +/** + * Same as typedoc-plugin-markdown `member.declaration`, but type aliases whose value is a **union of only literals** (no `ReflectionType` members) still get a "Type declaration" section when TypeDoc populated {@link UnionType.elementSummaries} from JSDoc before each `|` arm. + * + * Stock `hasTypeDeclaration` required `types.some(t => t instanceof ReflectionType)`, so `SessionStatus` never reached {@link MarkdownThemeContext.partials.typeDeclarationUnionContainer} and only the summary line was emitted. + * + * @this {import('typedoc-plugin-markdown').MarkdownThemeContext} + * @param {import('typedoc').DeclarationReflection} model + * @param {{ headingLevel?: number; nested?: boolean }} [options] + */ +function clerkDeclaration(model, options = { headingLevel: 2, nested: false }) { + const md = []; + const opts = { + nested: false, + ...options, + }; + const headingLevel = opts.headingLevel ?? 2; + const optionsWithHeading = { ...options, headingLevel }; + + if (!opts.nested && model.sources && !this.options.getValue('disableSources')) { + md.push(this.partials.sources(model)); + } + if (model?.documents) { + md.push(this.partials.documents(model, { headingLevel })); + } + /** @type {import('typedoc').DeclarationReflection | undefined} */ + let typeDeclaration = + model.type && 'declaration' in model.type + ? /** @type {{ declaration?: import('typedoc').DeclarationReflection }} */ (model.type).declaration + : undefined; + if (model.type instanceof ArrayType && model.type?.elementType instanceof ReflectionType) { + typeDeclaration = model.type?.elementType?.declaration; + } + const hasTypeDeclaration = + Boolean(typeDeclaration) || + (model.type instanceof UnionType && + (model.type.types.some(type => type instanceof ReflectionType) || Boolean(model.type.elementSummaries?.length))); + if (model.comment) { + md.push( + this.partials.comment(model.comment, { + headingLevel, + showSummary: true, + showTags: false, + }), + ); + } + if (model.type instanceof IntersectionType) { + model.type?.types?.forEach(intersectionType => { + if ( + intersectionType instanceof ReflectionType && + !intersectionType.declaration.signatures && + intersectionType.declaration.children + ) { + md.push(heading(headingLevel, i18n.theme_type_declaration())); + md.push( + this.partials.typeDeclaration(intersectionType.declaration, { + headingLevel, + }), + ); + } + }); + } + if (model.typeParameters) { + md.push(heading(headingLevel, ReflectionKind.pluralString(ReflectionKind.TypeParameter))); + if (this.helpers.useTableFormat('parameters')) { + md.push(this.partials.typeParametersTable(model.typeParameters)); + } else { + md.push( + this.partials.typeParametersList(model.typeParameters, { + headingLevel, + }), + ); + } + } + if (hasTypeDeclaration) { + if (model.type instanceof UnionType) { + if (this.helpers.hasUsefulTypeDetails(model.type)) { + md.push(heading(headingLevel, i18n.theme_type_declaration())); + md.push(this.partials.typeDeclarationUnionContainer(model, optionsWithHeading)); + } + } else if (typeDeclaration) { + const useHeading = + typeDeclaration.children?.length && + (model.kind !== ReflectionKind.Property || this.helpers.useTableFormat('properties')); + if (useHeading) { + md.push(heading(headingLevel, i18n.theme_type_declaration())); + } + md.push(this.partials.typeDeclarationContainer(model, typeDeclaration, optionsWithHeading)); + } + } + if (model.comment) { + md.push( + this.partials.comment(model.comment, { + headingLevel, + showSummary: false, + showTags: true, + showReturns: true, + }), + ); + } + md.push(this.partials.inheritance(model, { headingLevel })); + return md.join('\n\n'); +} /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app @@ -29,6 +990,24 @@ class ClerkMarkdownTheme extends MarkdownTheme { */ const unionCommentMap = new Map(); +/** + * Only for the specified pages do we remove function-valued members from property tables in the "Properties" section. + * (Created for the extract-methods script as these methods will be extracted to separate files.) + * + * @param {string | undefined} pageUrl - The URL of the page to check. + * @param {readonly string[]} allowlist - The list of pages to check. + */ +function pageMatchesAllowlist(pageUrl, allowlist) { + if (!pageUrl) { + return false; + } + const normalized = pageUrl.replace(/\\/g, '/').replace(/^\.\//, ''); + return allowlist.some(entry => { + const e = entry.replace(/\\/g, '/').replace(/^\/+/, ''); + return normalized === e || normalized.endsWith(`/${e}`) || normalized.endsWith(e); + }); +} + /** * Our custom Clerk theme * @extends MarkdownThemeContext @@ -48,6 +1027,112 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { this.partials = { ...superPartials, + /** + * Ensure unions always route through `unionType` (OAuth collapse) even when `instanceof UnionType` fails. + * + * @param {import('typedoc').Type | undefined} model + * @param {Parameters[1]} [options] + */ + someType: (model, options) => { + const ut = coerceUnionTypeIfNeeded(model); + if (ut) { + const collapsed = tryCollapseExpandedOAuthStrategyUnion(ut, this); + const toRender = collapsed ?? ut; + return superPartials.someType.call(this, toRender, options); + } + return superPartials.someType.call( + this, + /** @type {import('typedoc').SomeType | undefined} */ (/** @type {unknown} */ (model)), + options, + ); + }, + /** + * Stock `comments.comment` prints every {@link Comment.modifierTags} as **`TitleCase`** before the summary. + * `@inline` / `@inlineType` are router/type hints only; `@experimental` is SDK-only guidance — none of these + * must appear in property tables or prose. + * + * @param {import('typedoc').Comment} model + * @param {Parameters[1]} [options] + */ + comment: (model, options) => { + if (!model) { + return superPartials.comment.call(this, model, options); + } + const modelToRender = applyTodoStrippingToComment(model) ?? model; + const hidden = new Set(['@inline', '@inlineType', '@experimental']); + const modTags = Array.from(modelToRender.modifierTags ?? []); + if (modTags.some(/** @param {string} t */ t => hidden.has(t))) { + const clone = Object.assign(Object.create(Object.getPrototypeOf(modelToRender)), modelToRender, { + modifierTags: new Set(modTags.filter(/** @param {string} t */ t => !hidden.has(t))), + }); + return superPartials.comment.call(this, clone, options); + } + return superPartials.comment.call(this, modelToRender, options); + }, + /** + * Remove the blockquote signature line. + * + * @returns {string} + */ + declarationTitle: () => '', + /** + * TypeDoc's default links every {@link ReferenceType} to a URL. Types marked `@inline` are expanded at use sites and (via the router) have no standalone page — linking produces broken relative `.mdx` paths in extracted method docs. Render the **aliased type** (RHS) so literals and unions show as `'phone_code'`, not `PhoneCodeStrategy`. + * + * @param {import('typedoc').ReferenceType} model + */ + referenceType: model => { + if (model.reflection?.comment?.hasModifier('@inline')) { + const decl = /** @type {import('typedoc').DeclarationReflection} */ (model.reflection); + // Generic instantiation, e.g. `Fn` — let `someType` apply type arguments. + if (model.typeArguments?.length) { + return removeLineBreaks(this.partials.someType(model)); + } + if (decl.kindOf(ReflectionKind.TypeAlias) && decl.type) { + return removeLineBreaks(this.partials.someType(decl.type)); + } + return backTicks(decl.name); + } + return superPartials.referenceType.call(this, model); + }, + /** + * On allowlisted reference-object pages, drop function-valued members and `@extractMethods` namespace parents from property tables (they are documented under `methods/`). + * `extract-methods.mjs` reuses this partial for nominal param tables and nested `@extractMethods` docs on the same URL; pass `applyAllowlistedPropertyTableRowFilters: false` so rows are not stripped. + * + * @param {import('typedoc').DeclarationReflection[]} model + * @param {Parameters[1] & { applyAllowlistedPropertyTableRowFilters?: boolean }} [options] + */ + propertiesTable: (model, options) => { + if (!Array.isArray(model)) { + return superPartials.propertiesTable(/** @type {any} */ (model), options); + } + + const allowlisted = pageMatchesAllowlist(this.page?.url, REFERENCE_OBJECTS_LIST); + const applyAllowlistFilters = allowlisted && options?.applyAllowlistedPropertyTableRowFilters !== false; + const filtered = applyAllowlistFilters + ? model.filter( + prop => !isCallableInterfaceProperty(prop, this.helpers) && !prop.comment?.hasModifier('@extractMethods'), + ) + : model; + return superPartials.propertiesTable(filtered, options); + }, + /** + * Parameter tables: same as the stock partial except single-property inline object params are not expanded to nested rows. + * + * @param {import('typedoc').ParameterReflection[]} model + */ + parametersTable: model => { + return clerkParametersTable.call(this, model); + }, + /** + * In `compact` mode the default plugin skips `getFlattenedDeclarations`, so union object members never get rows. + * Delegate to {@link clerkTypeDeclarationTable} which always flattens and applies {@link appendUnionObjectChildPropertyRows}. + * + * @param {import('typedoc').DeclarationReflection[]} model + * @param {{ kind?: import('typedoc').ReflectionKind }} options + */ + typeDeclarationTable: (model, options) => { + return clerkTypeDeclarationTable.call(this, model, options); + }, /** * This hides the "Type parameters" section and the signature title from the output (by default). Shows the signature title if the `@displayFunctionSignature` tag is present. * @param {import('typedoc').SignatureReflection} model @@ -319,20 +1404,22 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {{ headingLevel: number, nested?: boolean }} options */ declaration: (model, options = { headingLevel: 2, nested: false }) => { - // Create a local override - const localPartials = { - ...this.partials, - declarationTitle: () => '', - }; - // Store original so that we can restore it later - const originalPartials = this.partials; - + const opts = { nested: false, ...options }; const customizedModel = model; customizedModel.typeParameters = undefined; - this.partials = localPartials; - const output = superPartials.declaration(customizedModel, options); - this.partials = originalPartials; + if (!opts.nested && model.type && isIntersectionTypeDoc(model.type)) { + const merged = mergeIntersectionPropertyReflections( + /** @type {import('typedoc').IntersectionType} */ (model.type), + model.project, + ); + if (merged.length > 0) { + const output = renderMergedIntersectionDeclaration(this, customizedModel, opts, merged, superPartials); + return output.replace(/^## Type declaration$/gm, ''); + } + } + + const output = clerkDeclaration.call(this, customizedModel, options); // Remove the "Type declaration" heading from the output // This heading is generated by the original declaration partial @@ -374,7 +1461,8 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {import('typedoc').UnionType} model */ unionType: model => { - const defaultOutput = superPartials.unionType(model); + const collapsed = tryCollapseExpandedOAuthStrategyUnion(model, this); + const defaultOutput = superPartials.unionType(collapsed ?? model); const output = defaultOutput // Escape stuff that would be turned into markdown @@ -430,6 +1518,20 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext { * @param {{ headingLevel: number }} options */ typeDeclarationUnionContainer: (model, options) => { + const optionsWithHeading = { ...options, headingLevel: options?.headingLevel ?? 2 }; + + if (model.type instanceof UnionType) { + const ut = model.type; + const unionReturnHeadings = model.comment?.getTag('@unionReturnHeadings'); + if ( + !ut.types.some(arm => arm instanceof ReflectionType) && + ut.elementSummaries?.length && + !unionReturnHeadings + ) { + return clerkLiteralUnionAsPropertiesTable.call(this, model, optionsWithHeading); + } + } + /** * @type {string[]} */ @@ -637,3 +1739,101 @@ function swap(arr, i, j) { arr[j] = t; return arr; } + +/** + * @param {import('typedoc').DeclarationReflection} prop + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers + */ +function isCallableInterfaceProperty(prop, helpers) { + // Use the declared value type for properties. `getDeclarationType` mirrors accessor/parameter behavior and can return the wrong node when TypeDoc attaches signatures to the property (same class of bug as TypeAlias + `decl.type`). + const t = + (prop.kind === ReflectionKind.Property || prop.kind === ReflectionKind.Variable) && prop.type + ? prop.type + : helpers.getDeclarationType(prop); + return isCallablePropertyValueType(t, helpers, new Set()); +} + +/** + * True when the property's value type is callable (function type, union/intersection of callables, or reference to a type alias of a function type). Object types with properties (e.g. namespaces) stay false. + * 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, + ); + } + if (t instanceof UnionType) { + const nonNullish = t.types.filter( + u => !(u.type === 'intrinsic' && ['undefined', 'null'].includes(/** @type {{ name: string }} */ (u).name)), + ); + if (nonNullish.length === 0) { + return false; + } + return nonNullish.every(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); + } + if (t instanceof IntersectionType) { + return t.types.some(u => isCallablePropertyValueType(u, helpers, seenReflectionIds)); + } + if (t instanceof ReflectionType) { + const decl = t.declaration; + const callSigs = decl.signatures?.length ?? 0; + const hasProps = (decl.children?.length ?? 0) > 0; + const hasIndex = (decl.indexSignatures?.length ?? 0) > 0; + return callSigs > 0 && !hasProps && !hasIndex; + } + if (t instanceof ReferenceType) { + /** + * Unresolved reference (`reflection` missing): TypeDoc did not link the symbol (not in entry graph, external, filtered, etc.). We cannot tell a function alias from an interface, so we only treat a few **name** patterns as callable (`*Function`, `*Listener`). For anything else, ensure the type is part of the documented program so `reflection` resolves and the structural checks above apply — do not add one-off type names here. + * E.g. `CustomNavigation`, `RouterFn`, etc. + */ + if (!t.reflection && typeof t.name === 'string' && /(?:Function|Listener)$/.test(t.name)) { + return true; + } + const ref = t.reflection; + if (!ref) { + return false; + } + const refId = ref.id; + if (refId != null && seenReflectionIds.has(refId)) { + return false; + } + if (refId != null) { + seenReflectionIds.add(refId); + } + try { + const decl = /** @type {import('typedoc').DeclarationReflection} */ (ref); + /** + * For `type Fn = (a: T) => U`, TypeDoc may attach call signatures to the TypeAlias reflection. + * `getDeclarationType` then returns `signatures[0].type` (here `U`), not the full function type, so we + * mis-classify properties typed as that alias (e.g. `navigate: CustomNavigation`) as non-callable. + * Prefer `decl.type` (the full RHS) for type aliases. + */ + const typeToCheck = + decl.kind === ReflectionKind.TypeAlias && decl.type + ? decl.type + : (helpers.getDeclarationType(decl) ?? decl.type); + if (typeToCheck) { + return isCallablePropertyValueType(typeToCheck, helpers, seenReflectionIds); + } + } finally { + if (refId != null) { + seenReflectionIds.delete(refId); + } + } + return false; + } + return false; +} + +export { isCallableInterfaceProperty }; diff --git a/.typedoc/extract-methods.mjs b/.typedoc/extract-methods.mjs new file mode 100644 index 00000000000..dfb8efcb710 --- /dev/null +++ b/.typedoc/extract-methods.mjs @@ -0,0 +1,1496 @@ +// @ts-check +/** + * For each entry in REFERENCE_OBJECTS_LIST, reads the TypeDoc output (e.g. `shared/clerk/clerk.mdx`), strips **Properties** from the main generated file and copies the section body (table only, no `## Properties` heading) into `properties.mdx`, and writes one .mdx per method under `methods/` (alongside the main page in that resource folder). + * + * Run after `typedoc` (same cwd as repo root). Uses a second TypeDoc convert pass to read reflections. + * + * Like `extract-returns-and-params.mjs`, parameter tables are not hand-built: they use the same `MarkdownThemeContext.partials` as TypeDoc markdown output (`parametersTable` / `propertiesTable`, which call `someType` and therefore pick up `custom-theme.mjs` union/`<code>` behavior). Router + theme are prepared via `prepare-markdown-renderer.mjs` (same idea as `typedoc-plugin-markdown` `render()`). + * + * Inline object namespaces tagged **`@extractMethods`** on the parent property are omitted from the main Properties table (see `custom-theme.mjs`). For each direct member: callables become `methods/-.mdx` via `buildMethodMdx`; non-callables become a heading + property table via `buildPropertyTableDocMdx`. + */ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + Application, + Comment, + IntersectionType, + OptionalType, + PageKind, + ReferenceType, + ReflectionKind, + ReflectionType, + UnionType, +} from 'typedoc'; +import { MarkdownPageEvent, MarkdownTheme } from 'typedoc-plugin-markdown'; +import { removeLineBreaks } from '../node_modules/typedoc-plugin-markdown/dist/libs/utils/index.js'; + +import typedocConfig from '../typedoc.config.mjs'; +import { isCallableInterfaceProperty } from './custom-theme.mjs'; +import { + applyCatchAllMdReplacements, + applyRelativeLinkReplacements, + stripReferenceObjectPropertiesSection, +} from './custom-plugin.mjs'; +import { prepareMarkdownRenderer } from './prepare-markdown-renderer.mjs'; +import { applyTodoStrippingToComment } from './comment-utils.mjs'; +import { REFERENCE_OBJECTS_LIST, REFERENCE_OBJECT_CONFIG } from './reference-objects.mjs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * @param {number} level + * @param {string} text + */ +function markdownHeading(level, text) { + const l = Math.min(Math.max(level, 1), 6); + return `${'#'.repeat(l)} ${text}`; +} + +/** + * Heading whose visible title is a type/identifier (nominal single-parameter object sections) needs to get wrapped in backticks. + * + * @param {number} level + * @param {string} text + */ +function markdownHeadingInlineCode(level, text) { + const l = Math.min(Math.max(level, 1), 6); + const t = text.trim(); + return `${'#'.repeat(l)} \`${t}\``; +} + +/** + * Same as typedoc-plugin-markdown `removeLineBreaks` for table cells. + * + * @param {string | undefined} str + */ +function removeLineBreaksForTableCell(str) { + return str?.replace(/\r?\n/g, ' ').replace(/ {2,}/g, ' '); +} + +/** + * Append data rows to a markdown table string (header + separator + rows). + * + * @param {string} tableMd + * @param {string[]} rowLines Lines like `| a | b | c |` + */ +function appendMarkdownTableRows(tableMd, rowLines) { + if (!rowLines.length) { + return tableMd; + } + return `${tableMd.trimEnd()}\n${rowLines.join('\n')}\n`; +} + +/** + * Post-process the theme’s parameters markdown table. TypeDoc flattens object params as `parent.child` and may interleave those rows with other parameters. Here we (1) move each `parent.*` block directly under `parent`, and (2) rewrite dotted paths in the name column to optional-chaining (`parent?.child`, `a?.b?.c`). Top-level names are unchanged (`foo?`, `exa`). + * + * @param {string} tableMd + */ +function formatMethodParametersTable(tableMd) { + const leadingNewlines = (tableMd.match(/^\n+/) ?? [''])[0]; + const nonEmpty = tableMd.split('\n').filter(l => l.trim().length); + if (nonEmpty.length < 3) { + return tableMd; + } + const header = nonEmpty[0]; + const sep = nonEmpty[1]; + const dataLines = nonEmpty.slice(2).filter(l => l.trim().startsWith('|')); + if (dataLines.length <= 1) { + return tableMd; + } + + /** @param {string} line */ + const firstName = line => { + const m = line.match(/^\|\s*(?:<\/a>\s*)?`([^`]+)`/); + return m ? m[1] : ''; + }; + /** `parent.child` / `parent?.child` → grouping key `parent` (matches top-level `parent` or `parent?` via fallback below). */ + /** @param {string} raw */ + const parentOfNested = raw => { + const j = raw.indexOf('?.'); + if (j !== -1) { + return raw.slice(0, j); + } + const i = raw.indexOf('.'); + return i === -1 ? '' : raw.slice(0, i); + }; + /** `a.b.c` → `a?.b?.c`; leave `foo?` and names without `.` alone. */ + /** @param {string} raw */ + const nameForDisplay = raw => (!raw.includes('.') || raw.includes('?.') ? raw : raw.split('.').join('?.')); + /** @param {string} line @param {string} name */ + const replaceFirstName = (line, name) => + line.replace(/^(\|\s*(?:<\/a>\s*)?)`[^`]+`/, `$1\`${name}\``); + + const topLevelOrder = []; + const seenTop = new Set(); + /** @type {Map} */ + const childrenOf = new Map(); + + for (const line of dataLines) { + const raw = firstName(line); + if (!raw) { + continue; + } + if (!raw.includes('.')) { + if (!seenTop.has(raw)) { + seenTop.add(raw); + topLevelOrder.push(raw); + } + continue; + } + const p = parentOfNested(raw); + if (!p) { + continue; + } + let bucket = childrenOf.get(p); + if (!bucket) { + bucket = []; + childrenOf.set(p, bucket); + } + bucket.push(line); + } + + for (const lines of childrenOf.values()) { + lines.sort((a, b) => firstName(a).localeCompare(firstName(b))); + } + + /** @param {string} top */ + const rowsForParent = top => + childrenOf.get(top) ?? (top.endsWith('?') ? childrenOf.get(top.slice(0, -1)) : undefined); + + const body = []; + const emitted = new Set(); + + for (const top of topLevelOrder) { + const topLine = dataLines.find(l => firstName(l) === top); + if (topLine) { + const r = firstName(topLine); + body.push(replaceFirstName(topLine, nameForDisplay(r))); + emitted.add(topLine); + } + const kids = rowsForParent(top); + if (kids) { + for (const line of kids) { + body.push(replaceFirstName(line, nameForDisplay(firstName(line)))); + emitted.add(line); + } + } + } + + for (const line of dataLines) { + if (!emitted.has(line)) { + const r = firstName(line); + body.push(r ? replaceFirstName(line, nameForDisplay(r)) : line); + } + } + + return `${leadingNewlines}${[header, sep, ...body].join('\n')}\n`; +} + +/** + * @param {import('typedoc').Application} app + * @param {import('typedoc').ProjectReflection} project + * @param {string} pageUrl e.g. `shared/clerk/index.mdx` + * @param {import('typedoc').DeclarationReflection} interfaceDecl + */ +function createThemeContextForReferencePage(app, project, pageUrl, interfaceDecl) { + const page = new MarkdownPageEvent(interfaceDecl); + page.url = pageUrl; + page.filename = path.join(app.options.getValue('out') ?? '', pageUrl); + page.pageKind = PageKind.Reflection; + page.project = project; + const theme = /** @type {InstanceType | undefined} */ (app.renderer.theme); + if (!theme || typeof theme.getRenderContext !== 'function') { + throw new Error('[extract-methods] Renderer theme is not ready; call prepareMarkdownRenderer(app) after convert'); + } + return /** @type {import('typedoc-plugin-markdown').MarkdownThemeContext} */ (theme.getRenderContext(page)); +} + +/** + * TypeDoc `code` display parts often already include backticks (same as {@link Comment.combineDisplayParts}). + * Wrapping again would produce `` `Client` `` in MDX. + * + * @param {string} text + */ +function codeDisplayPartToMarkdown(text) { + const trimmed = text.trim(); + if (trimmed.length >= 2 && trimmed.startsWith('`') && trimmed.endsWith('`')) { + return trimmed; + } + return `\`${text}\``; +} + +/** + * @param {import('typedoc').CommentDisplayPart[] | undefined} parts + */ +function displayPartsToString(parts) { + if (!parts?.length) { + return ''; + } + return parts + .map(p => { + if (p.kind === 'text') { + return p.text; + } + if (p.kind === 'code') { + return codeDisplayPartToMarkdown(p.text); + } + if (p.kind === 'inline-tag') { + return p.text; + } + if (p.kind === 'relative-link') { + return p.text; + } + return ''; + }) + .join(''); +} + +/** + * @param {import('typedoc').ProjectReflection} project + * @param {string} name + * @param {string} [sourcePathHint] e.g. `types/clerk` + */ +function findInterfaceOrClass(project, name, sourcePathHint) { + /** @type {import('typedoc').DeclarationReflection[]} */ + const candidates = []; + for (const r of Object.values(project.reflections)) { + if (r.name !== name) { + continue; + } + if (!r.kindOf(ReflectionKind.Interface) && !r.kindOf(ReflectionKind.Class)) { + continue; + } + candidates.push(/** @type {import('typedoc').DeclarationReflection} */ (r)); + } + if (candidates.length === 0) { + return undefined; + } + if (candidates.length === 1) { + return candidates[0]; + } + if (sourcePathHint) { + const hit = candidates.find(c => c.sources?.some(s => s.fileName.replace(/\\/g, '/').includes(sourcePathHint))); + if (hit) { + return hit; + } + } + return candidates[0]; +} + +/** + * Walk instantiated generic / alias chains (e.g. `CheckAuthorization` → `CheckAuthorizationFn` → `(…) => boolean`) until we find a {@link ReflectionType} call signature. Uses reflection IDs to avoid infinite loops. + * + * @param {import('typedoc').Type | undefined} t + * @param {Set} visitedReflectionIds + * @returns {import('typedoc').SignatureReflection | undefined} + */ +function getCallSignatureFromType(t, visitedReflectionIds) { + if (!t || typeof t !== 'object') { + return undefined; + } + const tag = /** @type {{ type?: string }} */ (t).type; + if (tag === 'optional' && 'elementType' in t) { + return getCallSignatureFromType( + /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType, + visitedReflectionIds, + ); + } + if (t instanceof ReflectionType) { + if (t.declaration?.signatures?.length) { + return t.declaration.signatures[0]; + } + return undefined; + } + if (t instanceof ReferenceType) { + const target = t.reflection; + if ( + target && + 'signatures' in target && + /** @type {{ signatures?: import('typedoc').SignatureReflection[] }} */ (target).signatures?.length + ) { + return /** @type {import('typedoc').DeclarationReflection} */ (target).signatures[0]; + } + if (!target || !('kind' in target)) { + return undefined; + } + const decl = /** @type {import('typedoc').DeclarationReflection} */ (target); + const id = decl.id; + if (id != null) { + if (visitedReflectionIds.has(id)) { + return undefined; + } + visitedReflectionIds.add(id); + } + try { + if (decl.kind === ReflectionKind.TypeAlias && decl.type) { + return getCallSignatureFromType(decl.type, visitedReflectionIds); + } + } finally { + if (id != null) { + visitedReflectionIds.delete(id); + } + } + return undefined; + } + if (t instanceof UnionType) { + for (const arm of t.types) { + const sig = getCallSignatureFromType(arm, visitedReflectionIds); + if (sig) { + return sig; + } + } + return undefined; + } + if (t instanceof IntersectionType) { + for (const arm of t.types) { + const sig = getCallSignatureFromType(arm, visitedReflectionIds); + if (sig) { + return sig; + } + } + } + return undefined; +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + * @returns {import('typedoc').SignatureReflection | undefined} + */ +function getPrimaryCallSignature(decl) { + if (decl.signatures?.length) { + return decl.signatures[0]; + } + const t = decl.type; + if (t && 'declaration' in t && t.declaration?.signatures?.length) { + return t.declaration.signatures[0]; + } + // 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; +} + +/** + * @param {import('typedoc').Type | undefined} t + */ +function unwrapOptionalType(t) { + if (!t || typeof t !== 'object') { + return t; + } + if (/** @type {{ type?: string }} */ (t).type === 'optional' && 'elementType' in t) { + return /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + } + return t; +} + +/** + * For `prop: OuterAlias` where `type OuterAlias = SomeFn`, maps generic parameter names on `SomeFn` to the instantiated type arguments (e.g. `Params` → `CheckAuthorizationParams`). + * + * @param {import('typedoc').DeclarationReflection} propertyDecl + * @returns {Map | undefined} + */ +function getGenericInstantiationMapFromCallableProperty(propertyDecl) { + const t = unwrapOptionalType(propertyDecl.type); + if (!(t instanceof ReferenceType) || !t.reflection) { + return undefined; + } + const alias = /** @type {import('typedoc').DeclarationReflection} */ (t.reflection); + if (!alias.kindOf(ReflectionKind.TypeAlias) || !alias.type) { + return undefined; + } + const inner = unwrapOptionalType(alias.type); + if (!(inner instanceof ReferenceType) || !inner.typeArguments?.length || !inner.reflection) { + return undefined; + } + const generic = /** @type {import('typedoc').DeclarationReflection} */ (inner.reflection); + const tpls = generic.typeParameters; + if (!tpls?.length) { + return undefined; + } + /** @type {Map} */ + const map = new Map(); + for (let i = 0; i < inner.typeArguments.length; i++) { + const tp = tpls[i]; + const arg = inner.typeArguments[i]; + if (tp?.name && arg) { + map.set(tp.name, arg); + } + } + return map.size ? map : undefined; +} + +/** + * Replace references to generic type parameters with instantiated types from {@link getGenericInstantiationMapFromCallableProperty}. + * + * @param {import('typedoc').Type | undefined} t + * @param {Map | undefined} map + * @returns {import('typedoc').Type | undefined} + */ +function substituteGenericParamRefsInType(t, map) { + if (!t || !map?.size) { + return t; + } + if (/** @type {{ type?: string }} */ (t).type === 'optional' && 'elementType' in t) { + const el = /** @type {{ elementType: import('typedoc').Type }} */ (t).elementType; + const next = substituteGenericParamRefsInType(el, map); + if (next && next !== el) { + return new OptionalType(/** @type {import('typedoc').SomeType} */ (/** @type {unknown} */ (next))); + } + return t; + } + if (t instanceof ReferenceType && map.has(t.name)) { + return map.get(t.name) ?? t; + } + return t; +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {Map | undefined} instantiationMap + */ +function signatureWithInstantiation(sig, instantiationMap) { + if (!instantiationMap?.size) { + return sig; + } + const parameters = (sig.parameters ?? []).map(p => { + const newType = substituteGenericParamRefsInType(p.type, instantiationMap); + if (newType === p.type) { + return p; + } + return Object.assign(Object.create(Object.getPrototypeOf(p)), p, { type: newType }); + }); + const newReturn = substituteGenericParamRefsInType(sig.type, instantiationMap) ?? sig.type; + const out = Object.assign(Object.create(Object.getPrototypeOf(sig)), sig, { + parameters, + type: newReturn, + typeParameters: undefined, + }); + if (sig.project) { + out.project = sig.project; + } + return out; +} + +/** + * Must stay aligned with allowlisted `propertiesTable` filtering in `custom-theme.mjs` (`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').SomeType | undefined} t + * @returns {import('typedoc').SomeType | undefined} + */ +function unwrapOptionalLayersSomeType(t) { + let cur = /** @type {import('typedoc').SomeType | undefined} */ (t); + while ( + cur && + typeof cur === 'object' && + /** @type {{ type?: string }} */ (cur).type === 'optional' && + 'elementType' in cur + ) { + cur = /** @type {import('typedoc').SomeType} */ (/** @type {import('typedoc').OptionalType} */ (cur).elementType); + } + return cur; +} + +/** + * Object-literal (or single object arm of `T | null`) property rows for a properties table. + * + * @param {import('typedoc').SomeType | undefined} valueType + * @returns {import('typedoc').DeclarationReflection[] | undefined} + */ +function resolveObjectShapeMembersForPropertyTable(valueType) { + let t = unwrapOptionalLayersSomeType(valueType); + if (t instanceof UnionType) { + const objectArms = t.types.filter(u => u instanceof ReflectionType && (u.declaration?.children?.length ?? 0) > 0); + if (objectArms.length !== 1) { + return undefined; + } + t = /** @type {import('typedoc').ReflectionType} */ (objectArms[0]); + } + if (!(t instanceof ReflectionType)) { + return undefined; + } + const kids = t.declaration?.children ?? []; + return kids.filter( + c => c.kind === ReflectionKind.Property || c.kind === ReflectionKind.Variable || c.kind === ReflectionKind.Accessor, + ); +} + +/** + * @param {string} parentName + * @param {import('typedoc').DeclarationReflection} nestedDecl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + */ +function buildPropertyTableDocMdx(parentName, nestedDecl, ctx) { + const qualifiedName = `${parentName}.${nestedDecl.name}`; + const title = `### \`${qualifiedName}\``; + const description = commentSummaryAndBody(nestedDecl.comment); + const 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 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; +} + +/** + * @param {string} pageUrl e.g. `shared/clerk/clerk.mdx` + */ +function extractPropertiesAndTrimSourcePage(pageUrl) { + const sourcePath = path.join(__dirname, 'temp-docs', pageUrl); + if (!fs.existsSync(sourcePath)) { + console.warn(`[extract-methods] Expected TypeDoc output missing: ${sourcePath}`); + return; + } + const raw = fs.readFileSync(sourcePath, 'utf-8'); + const body = extractPropertiesSectionBody(raw); + const pageDir = path.dirname(pageUrl); + const objectDir = path.join(__dirname, 'temp-docs', pageDir); + fs.mkdirSync(objectDir, { recursive: true }); + + if (body) { + const propertiesDoc = `${body.trimEnd()}\n`; + const propertiesPath = path.join(objectDir, 'properties.mdx'); + fs.writeFileSync( + propertiesPath, + applyCatchAllMdReplacements(applyRelativeLinkReplacements(propertiesDoc)), + 'utf-8', + ); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), propertiesPath)}`); + } + + const stripped = stripReferenceObjectPropertiesSection(raw); + if (stripped !== raw) { + fs.writeFileSync(sourcePath, stripped, 'utf-8'); + console.log(`[extract-methods] Stripped Properties from ${path.relative(path.join(__dirname, '..'), sourcePath)}`); + } +} + +/** + * @param {string} name + */ +function toKebabCase(name) { + return name + .replace(/([a-z\d])([A-Z])/g, '$1-$2') + .replace(/[\s_]+/g, '-') + .toLowerCase(); +} + +/** + * Plain TypeScript-like type text for ```typescript``` fences (no markdown / backticks from {@link MarkdownThemeContext.partials.someType}). + * + * @param {import('typedoc').Type | undefined} t + */ +function typeStringForTypeScriptFence(t) { + if (!t) { + return 'unknown'; + } + return removeLineBreaks(t.toString()); +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {string} memberName + * @param {Map | undefined} instantiationMap + */ +function formatTypeScriptSignature(sig, memberName, 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'; + return `function ${memberName}${typeParamStr}(${params.join(', ')}): ${ret}`; +} + +/** + * `@returns - foo` is often stored with a leading dash, which renders as a bullet. Normalize to prose for "Returns …" lines. + * @param {string} body + */ +function normalizeReturnsBody(body) { + return body.replace(/^\s*[-*]\s+/, '').trim(); +} + +/** + * Lowercase the first character so the line reads "Returns an …" not "Returns An …". + * @param {string} body + */ +function lowercaseFirstCharacter(body) { + if (!body) { + return body; + } + return body.charAt(0).toLowerCase() + body.slice(1); +} + +/** + * @param {import('typedoc').CommentTag} tag + */ +function formatReturnsLineFromTag(tag) { + const raw = Comment.combineDisplayParts(tag.content).trim(); + if (!raw) { + return ''; + } + const body = lowercaseFirstCharacter(normalizeReturnsBody(raw)); + return `Returns ${body}`; +} + +/** + * @param {import('typedoc').Comment | undefined} comment + */ +/** + * `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').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'); +} + +/** + * When `@returns` exists only on the call signature (not on the declaration), append it to the prose. + * @param {import('typedoc').Comment | undefined} declComment + * @param {import('typedoc').Comment | undefined} sigComment + */ +function appendSignatureOnlyReturns(declComment, sigComment) { + if (declComment?.getTag('@returns')?.content?.length) { + return ''; + } + const tag = sigComment?.getTag('@returns'); + if (!tag?.content?.length) { + return ''; + } + return formatReturnsLineFromTag(tag); +} + +/** + * @param {import('typedoc').DeclarationReflection} prop + */ +function propertyReflectionTypeIsNever(prop) { + let ty = prop.type; + while (ty?.type === 'optional') { + ty = /** @type {import('typedoc').OptionalType} */ (ty).elementType; + } + return ty?.type === 'intrinsic' && ty.name === 'never'; +} + +/** + * Union discriminators often use `otherProp?: never`. Prefer the branch with a documentable type. + * + * @param {import('typedoc').DeclarationReflection} existing + * @param {import('typedoc').DeclarationReflection} candidate + */ +function pickBetterUnionPropertyCandidate(existing, candidate) { + const existingNever = propertyReflectionTypeIsNever(existing); + const candidateNever = propertyReflectionTypeIsNever(candidate); + if (existingNever && !candidateNever) { + return candidate; + } + if (!existingNever && candidateNever) { + return existing; + } + const existingDoc = existing.comment?.summary?.length ?? 0; + const candidateDoc = candidate.comment?.summary?.length ?? 0; + return candidateDoc > existingDoc ? candidate : existing; +} + +/** + * Object / type-literal declaration for a parameter type (reference, inlined reflection, intersection). + * TypeDoc applies `@param parent.prop` descriptions onto property reflections under this declaration. + * + * @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 === 'reflection') { + const d = t.declaration; + if (d.children?.length) { + return d; + } + } + if (t.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + const typeArgs = ref.typeArguments ?? []; + + let decl = + ref.reflection && 'kind' in ref.reflection + ? /** @type {import('typedoc').DeclarationReflection} */ (ref.reflection) + : undefined; + if (!decl && project && ref.name) { + decl = lookupInterfaceOrTypeAliasByName(project, ref.name); + } + if (decl) { + /** + * Generic aliases like `ClerkPaginationParams<{ status?: … }>` are a reference with `typeArguments`. + * TypeDoc often puts pagination fields only on the target alias `children` and omits `decl.type`, so returning `decl` early drops the type argument object. Merge base + each type argument's properties. + */ + if (typeArgs.length > 0) { + /** @type {Map} */ + const byName = new Map(); + if (decl.type) { + const fromType = resolveDeclarationWithObjectMembers(decl.type, project); + if (fromType?.children?.length) { + for (const c of fromType.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } + } + } + if (byName.size === 0 && decl.children?.length) { + for (const c of decl.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } + } + for (const ta of typeArgs) { + const fromArg = resolveDeclarationWithObjectMembers(ta, project); + if (fromArg?.children?.length) { + for (const c of fromArg.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } + } + } + if (byName.size > 0) { + return /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)), + kind: ReflectionKind.TypeLiteral, + name: '__referenceMerged', + }) + ); + } + } + + if (decl.children?.length) { + return decl; + } + if (decl.type) { + return resolveDeclarationWithObjectMembers(decl.type, project); + } + } + } + if (t.type === 'intersection') { + const inter = /** @type {import('typedoc').IntersectionType} */ (t); + /** @type {Map} */ + const byName = new Map(); + for (const inner of inter.types) { + const res = resolveDeclarationWithObjectMembers(inner, project); + if (res?.children?.length) { + for (const c of res.children) { + if (c.kindOf(ReflectionKind.Property)) { + byName.set(c.name, c); + } + } + } + } + if (byName.size === 0) { + return undefined; + } + // Synthetic holder so nominal param sections list every `&` arm (e.g. `RedirectOptions`). + return /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)), + kind: ReflectionKind.TypeLiteral, + name: '__intersectionMerged', + }) + ); + } + if (t.type === 'union') { + const u = /** @type {import('typedoc').UnionType} */ (t); + /** @type {Map} */ + const byName = new Map(); + for (const inner of u.types) { + const res = resolveDeclarationWithObjectMembers(inner, project); + if (!res?.children?.length) { + continue; + } + for (const c of res.children) { + if (!c.kindOf(ReflectionKind.Property)) { + continue; + } + if (propertyReflectionTypeIsNever(c)) { + continue; + } + const existing = byName.get(c.name); + if (!existing) { + byName.set(c.name, c); + } else { + byName.set(c.name, pickBetterUnionPropertyCandidate(existing, c)); + } + } + } + if (byName.size === 0) { + return undefined; + } + return /** @type {import('typedoc').DeclarationReflection} */ ( + /** @type {unknown} */ ({ + children: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)), + kind: ReflectionKind.TypeLiteral, + name: '__unionMerged', + }) + ); + } + if (t.type === 'optional') { + return resolveDeclarationWithObjectMembers(/** @type {import('typedoc').OptionalType} */ (t).elementType, project); + } + return undefined; +} + +/** + * @param {string} baseName + * @param {string[]} pathSegments + */ +function formatNestedParamNameColumn(baseName, pathSegments) { + const pathChain = pathSegments.join('?.'); + return `\`${baseName}?.${pathChain}\``; +} + +/** + * This function unwraps a TypeDoc parameter type if it is an optional type. If the provided type is of type "optional", it returns the underlying element type (the real type being wrapped). If it is not optional or is undefined, it returns the type as-is. + * + * @param {import('typedoc').SomeType | undefined} t + * @returns {import('typedoc').SomeType | undefined} + */ +function unwrapOptionalParamType(t) { + if (t?.type === 'optional') { + return /** @type {import('typedoc').OptionalType} */ (t).elementType; + } + return t; +} + +/** + * When TypeDoc renders a parameter type as a markdown link to another generated `.mdx` file, that type has a dedicated page — omit nested `param?.prop` rows so readers follow the type link instead. + * `@inline` aliases are expanded by the theme and do not link to a standalone page. + * + * @param {import('typedoc').SomeType | undefined} t + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + */ +function parameterTypeLinksToStandaloneMdxPage(t, ctx) { + const bare = unwrapOptionalParamType(t); + if (!bare) { + return false; + } + if (bare.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (bare); + if (ref.reflection?.comment?.hasModifier('@inline')) { + return false; + } + } + const md = removeLineBreaksForTableCell(ctx.partials.someType(bare) ?? '') ?? ''; + return /\.mdx(?:#[^)]*)?\)/.test(md); +} + +/** + * Rows for object properties on a nominal param type (e.g. `HandleOAuthCallbackParams`), including from `@param parent.prop` on the method. + * Lists every property on the resolved shape; uses the property comment when present, otherwise `—` (intersection aliases often omit comments on some arms in the model). + * + * @param {import('typedoc').ParameterReflection} param + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + */ +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 holder = resolveDeclarationWithObjectMembers(param.type, project); + if (!holder?.children?.length) { + return []; + } + const props = holder.children.filter(c => c.kindOf(ReflectionKind.Property)); + props.sort((a, b) => a.name.localeCompare(b.name)); + /** @type {string[]} */ + const rows = []; + for (const child of props) { + const summary = child.comment?.summary; + const typeCell = child.type ? removeLineBreaksForTableCell(ctx.partials.someType(child.type)) : '`unknown`'; + const nestedNameCol = formatNestedParamNameColumn(param.name, [child.name]); + const nestedDesc = summary?.length ? displayPartsToString(summary).trim() || '—' : '—'; + 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 that name and the declaration holding object properties. + * + * @param {import('typedoc').SomeType | undefined} t + * @param {import('typedoc').ProjectReflection} project + * @returns {{ sectionTitle: string, holder: import('typedoc').DeclarationReflection, typeDecl: import('typedoc').DeclarationReflection } | undefined} + */ +function resolveNominalObjectTypeForSingleParam(t, project) { + if (!t) { + return undefined; + } + if (t.type === 'optional') { + return resolveNominalObjectTypeForSingleParam( + /** @type {import('typedoc').OptionalType} */ (t).elementType, + project, + ); + } + if (t.type === 'reference') { + const ref = /** @type {import('typedoc').ReferenceType} */ (t); + let typeDecl = + ref.reflection && 'kind' in ref.reflection + ? /** @type {import('typedoc').DeclarationReflection} */ (ref.reflection) + : lookupInterfaceOrTypeAliasByName(project, ref.name); + if (!typeDecl) { + return undefined; + } + if (typeDecl.kindOf(ReflectionKind.Interface)) { + if (!typeDecl.children?.length) { + return undefined; + } + return { sectionTitle: typeDecl.name, holder: typeDecl, typeDecl }; + } + if (typeDecl.kindOf(ReflectionKind.TypeAlias)) { + // 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 holder = fromResolvedType?.children?.length + ? fromResolvedType + : typeDecl.children?.length + ? typeDecl + : undefined; + if (!holder?.children?.length) { + return undefined; + } + return { sectionTitle: typeDecl.name, holder, 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); +} + +/** + * Single parameter that is a named object type (interface / type alias): one section titled after the type, table lists every property (not the outer `params` row). Uses the same `propertiesTable` partial as TypeDoc. + * + * @param {import('typedoc').SignatureReflection} sig + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @returns {string | undefined} + */ +function trySingleNominalParameterTypeSection(sig, ctx) { + 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.holder.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(4, nominal.sectionTitle), '', tableMd, ''].join('\n'); +} + +/** + * @param {import('typedoc').SignatureReflection} sig + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {Map | undefined} instantiationMap + */ +function parametersMarkdownTable(sig, ctx, instantiationMap) { + const sigForDisplay = signatureWithInstantiation(sig, instantiationMap); + const params = sigForDisplay.parameters ?? []; + if (params.length === 0) { + return ''; + } + + const singleNominal = trySingleNominalParameterTypeSection(sigForDisplay, ctx); + 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 = appendMarkdownTableRows(tableMd, nested); + } + + tableMd = formatMethodParametersTable(tableMd); + + return [markdownHeading(4, ReflectionKind.pluralString(ReflectionKind.Parameter)), '', tableMd, ''].join('\n'); +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {{ qualifiedName?: string }} [options] Nested namespace methods use `parent.child` for headings / signatures. + */ +function buildMethodMdx(decl, ctx, options = {}) { + const name = options.qualifiedName ?? decl.name; + const sig = getPrimaryCallSignature(decl); + if (!sig) { + return ''; + } + const title = `### \`${name}()\``; + /** Prefer the declaration comment (property-style methods document `addListener` on the property, not the signature). */ + const comment = decl.comment ?? sig.comment; + let description = commentSummaryAndBody(comment); + const sigReturns = appendSignatureOnlyReturns(decl.comment, sig.comment); + if (sigReturns) { + description = [description, sigReturns].filter(Boolean).join('\n\n'); + } + const instantiationMap = getGenericInstantiationMapFromCallableProperty(decl); + const ts = ['```typescript', formatTypeScriptSignature(sig, name, instantiationMap), '```'].join('\n'); + const skipParametersSection = + Boolean(decl.comment?.hasModifier('@skipParametersSection')) || + Boolean(sig.comment?.hasModifier('@skipParametersSection')); + const paramsMd = skipParametersSection ? '' : parametersMarkdownTable(sig, ctx, instantiationMap); + + // Same post-process as `custom-plugin.mjs` `MarkdownPageEvent.END`: relative `.mdx` links, then catch-alls. + // Skip the ```typescript``` fence so signatures stay plain code. + const head = applyCatchAllMdReplacements(applyRelativeLinkReplacements([title, '', description].join('\n'))); + const paramsProcessed = paramsMd ? applyCatchAllMdReplacements(applyRelativeLinkReplacements(paramsMd)) : ''; + const chunks = [head, 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')); +} + +/** + * Writes `methods/-.mdx` for each direct member of an `@extractMethods` object-like type: + * callables via {@link buildMethodMdx}, non-callables with a resolvable object shape via + * {@link buildPropertyTableDocMdx}. + * + * Supports inline object literals and named references (`interface` / object-like `type` aliases) by resolving + * the holder with {@link resolveDeclarationWithObjectMembers}. + * + * @param {import('typedoc').DeclarationReflection} parentDecl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {string} outDir + * @returns {number} Number of files written + */ +function processExtractMethodsNamespace(parentDecl, ctx, outDir) { + const project = ctx.page?.project; + const holder = resolveDeclarationWithObjectMembers(parentDecl.type, project); + const members = holder?.children ?? []; + if (members.length === 0) { + console.warn( + `[extract-methods] @extractMethods on "${parentDecl.name}" requires an object-like type with members; skipping nested extraction`, + ); + return 0; + } + const parentName = parentDecl.name; + let count = 0; + /** @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 = `${toKebabCase(parentName)}-${toKebabCase(nd.name)}`; + const filePath = path.join(outDir, `${fileSlug}.mdx`); + if (shouldExtractCallableMember(nd, ctx)) { + const mdx = buildMethodMdx(nd, ctx, { qualifiedName: `${parentName}.${nd.name}` }); + if (!mdx) { + continue; + } + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + continue; + } + nonCallableMembers.push(nd); + const propTableMdx = buildPropertyTableDocMdx(parentName, nd, ctx); + if (!propTableMdx) { + continue; + } + fs.writeFileSync(filePath, propTableMdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } + if (nonCallableMembers.length) { + const namespaceMdx = buildExtractMethodsNamespacePropertyTableMdx(parentDecl, nonCallableMembers, ctx); + if (namespaceMdx) { + const namespacePath = path.join(outDir, `${toKebabCase(parentName)}.mdx`); + fs.writeFileSync(namespacePath, namespaceMdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), namespacePath)}`); + count++; + } + } + return count; +} + +/** + * @param {import('typedoc').DeclarationReflection} decl + * @param {import('typedoc-plugin-markdown').MarkdownThemeContext} ctx + * @param {string} outDir + */ +function extractCallableMembersFromDeclaration(decl, ctx, outDir) { + let count = 0; + if (!decl.children) { + return 0; + } + for (const child of decl.children) { + if (child.name.startsWith('__')) { + continue; + } + const childDecl = /** @type {import('typedoc').DeclarationReflection} */ (child); + + if (hasExtractMethodsModifier(childDecl)) { + count += processExtractMethodsNamespace(childDecl, ctx, outDir); + continue; + } + + if (shouldExtractCallableMember(childDecl, ctx)) { + const mdx = buildMethodMdx(childDecl, ctx); + if (mdx) { + const fileName = `${toKebabCase(child.name)}.mdx`; + const filePath = path.join(outDir, fileName); + fs.writeFileSync(filePath, mdx, 'utf-8'); + console.log(`[extract-methods] Wrote ${path.relative(path.join(__dirname, '..'), filePath)}`); + count++; + } + } + } + return count; +} + +/** + * @param {string} pageUrl + * @param {import('typedoc').ProjectReflection} project + * @param {import('typedoc').Application} app + */ +function extractMethodsForPage(pageUrl, project, app) { + const entry = REFERENCE_OBJECT_CONFIG[/** @type {keyof typeof REFERENCE_OBJECT_CONFIG} */ (pageUrl)]; + if (!entry) { + console.warn(`[extract-methods] No symbol mapping for ${pageUrl}, skipping`); + return 0; + } + + const { symbol, declarationHint } = entry; + const extraMethodInterfaces = 'extraMethodInterfaces' in entry ? entry.extraMethodInterfaces : undefined; + const decl = findInterfaceOrClass(project, symbol, declarationHint); + if (!decl?.children) { + console.warn(`[extract-methods] Could not find interface/class "${symbol}"`); + return 0; + } + + extractPropertiesAndTrimSourcePage(pageUrl); + + const ctx = createThemeContextForReferencePage(app, project, pageUrl, decl); + + const pageDir = path.dirname(pageUrl); + const objectDir = path.join(__dirname, 'temp-docs', pageDir); + const outDir = path.join(objectDir, 'methods'); + fs.mkdirSync(outDir, { recursive: true }); + + let count = extractCallableMembersFromDeclaration(decl, ctx, outDir); + + if (Array.isArray(extraMethodInterfaces)) { + for (const extra of extraMethodInterfaces) { + const extraDecl = findInterfaceOrClass(project, extra.symbol, extra.declarationHint); + if (!extraDecl?.children) { + console.warn(`[extract-methods] extraMethodInterfaces: could not find "${extra.symbol}" for ${pageUrl}`); + continue; + } + count += extractCallableMembersFromDeclaration(extraDecl, ctx, outDir); + } + } + + return count; +} + +async function main() { + const app = await Application.bootstrapWithPlugins({ + ...typedocConfig, + // Avoid writing markdown twice; we only need reflections. + out: path.join(__dirname, 'temp-docs-unused'), + }); + + const project = await app.convert(); + if (!project) { + console.error('[extract-methods] TypeDoc conversion failed'); + process.exit(1); + } + + prepareMarkdownRenderer(app, project); + + let total = 0; + for (const pageUrl of REFERENCE_OBJECTS_LIST) { + total += extractMethodsForPage(pageUrl, project, app); + } + console.log(`[extract-methods] Wrote ${total} method files total`); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/.typedoc/prepare-markdown-renderer.mjs b/.typedoc/prepare-markdown-renderer.mjs new file mode 100644 index 00000000000..bbf373c63f7 --- /dev/null +++ b/.typedoc/prepare-markdown-renderer.mjs @@ -0,0 +1,118 @@ +// @ts-check +/** + * Mirrors `prepareRouter` + `prepareTheme` from `typedoc-plugin-markdown` `render()` so code outside the + * markdown render pass can build a `MarkdownThemeContext` (same `partials` as generated pages). + * + * Only `member`, `module`, and plugin-registered routers (e.g. `clerk-router`) are supported — matching this repo's + * TypeDoc config. + * + * @see https://github.com/typedoc2md/typedoc-plugin-markdown/blob/main/packages/typedoc-plugin-markdown/src/renderer/render.ts + */ +import { MarkdownTheme, MemberRouter, ModuleRouter } from 'typedoc-plugin-markdown'; + +/** + * @param {import('typedoc').Renderer} renderer + * @returns {string} + */ +function getRouterName(renderer) { + const routerOption = renderer.application.options.getValue('router'); + if (!renderer.application.options.isSet('router')) { + if (renderer.application.options.isSet('outputFileStrategy')) { + const outputFileStrategy = renderer.application.options.getValue('outputFileStrategy'); + return outputFileStrategy === 'modules' ? 'module' : 'member'; + } + return 'member'; + } + return routerOption; +} + +/** + * TypeDoc types `Renderer['routers']` as private; at runtime plugins register routers on this map (e.g. `clerk-router`). + * + * @param {import('typedoc').Renderer} renderer + * @param {string} routerName + * @returns {typeof MemberRouter | typeof ModuleRouter | (new (application: import('typedoc').Application) => import('typedoc').Router) | undefined} + */ +function getRouterConstructor(renderer, routerName) { + if (routerName === 'member') { + return MemberRouter; + } + if (routerName === 'module') { + return ModuleRouter; + } + const routers = + /** @type {{ routers: Map import('typedoc').Router> }} */ ( + /** @type {unknown} */ (renderer) + ).routers; + return routers.get(routerName); +} + +/** + * Same situation as {@link getRouterConstructor}: `themes` is public at runtime but typed private. + * + * @param {import('typedoc').Renderer} renderer + * @returns {Map import('typedoc').Theme>} + */ +function getThemeRegistry(renderer) { + return /** @type {{ themes: Map import('typedoc').Theme> }} */ ( + /** @type {unknown} */ (renderer) + ).themes; +} + +/** + * @param {import('typedoc').Renderer} renderer + */ +function prepareRouter(renderer) { + const routerName = getRouterName(renderer); + const RouterCtor = getRouterConstructor(renderer, routerName); + if (!RouterCtor) { + throw new Error( + `[prepare-markdown-renderer] Router "${routerName}" is not registered (expected member, module, or a custom router from a plugin)`, + ); + } + renderer.router = new RouterCtor(renderer.application); +} + +/** + * @param {import('typedoc').Renderer} renderer + */ +function getThemeName(renderer) { + const themeOption = renderer.application.options.getValue('theme'); + return themeOption === 'default' ? 'markdown' : themeOption; +} + +/** + * @param {import('typedoc').Renderer} renderer + */ +function prepareTheme(renderer) { + const themes = getThemeRegistry(renderer); + const themeName = getThemeName(renderer); + const ThemeCtor = themes.get(themeName); + if (!ThemeCtor) { + throw new Error(`[prepare-markdown-renderer] Theme "${themeName}" is not registered`); + } + const theme = new ThemeCtor(renderer); + if (!(theme instanceof MarkdownTheme)) { + renderer.application.logger.warn( + `[prepare-markdown-renderer] Theme "${themeName}" is not MarkdownTheme; falling back to built-in markdown theme`, + ); + renderer.theme = new /** @type {typeof MarkdownTheme} */ (themes.get('markdown'))(renderer); + return; + } + renderer.theme = theme; +} + +/** + * @param {import('typedoc').Application} app + * @param {import('typedoc').ProjectReflection} project + */ +export function prepareMarkdownRenderer(app, project) { + prepareRouter(app.renderer); + prepareTheme(app.renderer); + // Required so `referenceType` / links can resolve (`getFullUrl`); same as `render()` before each page. + const router = app.renderer.router; + if (!router) { + throw new Error('[prepare-markdown-renderer] Router was not set after prepareRouter'); + } + router.buildPages(project); +} diff --git a/.typedoc/reference-objects.mjs b/.typedoc/reference-objects.mjs new file mode 100644 index 00000000000..ecb196dac00 --- /dev/null +++ b/.typedoc/reference-objects.mjs @@ -0,0 +1,69 @@ +// @ts-check +/** + * Shared between the markdown theme and extract-methods.mjs. + * `page.url` values are relative to TypeDoc `out` (e.g. `.typedoc/temp-docs`). + */ + +/** + * Reference object page: MDX path → TypeDoc symbol + optional source hint. + * + * Keys **must** match `custom-router.mjs` (`ClerkRouter`): for each symbol, `shared//.mdx` + * (e.g. `SessionResource` → `shared/session-resource/session-resource.mdx`). If the path drifts, TypeDoc writes one folder while `extract-methods.mjs` strips/writes under another. + * + * `declarationHint` is a substring of `packages/shared/src/**` file paths (used by `findInterfaceOrClass()` in `extract-methods.mjs` when multiple reflections share the same interface/class name). + * + * `extract-methods.mjs` reads each file, writes `properties.mdx` with the same Properties table as TypeDoc (no `## Properties` heading), strips Properties from `.mdx`, and writes methods under `methods/`. + * + * Optional **`extraMethodInterfaces`**: extra `interface` / `class` declarations whose callable members (and `@extractMethods` namespaces) are emitted into the same `methods/` folder. Use when the documented resource type and the API surface live on different types (e.g. `APIKeyResource` vs `APIKeysNamespace` on `Clerk.apiKeys`). + */ +export const REFERENCE_OBJECT_CONFIG = { + 'shared/clerk/clerk.mdx': { + symbol: 'Clerk', + declarationHint: 'types/clerk', + }, + 'shared/client-resource/client-resource.mdx': { + symbol: 'ClientResource', + declarationHint: 'types/client', + }, + 'shared/session-resource/session-resource.mdx': { + symbol: 'SessionResource', + declarationHint: 'types/session', + }, + 'shared/user-resource/user-resource.mdx': { + symbol: 'UserResource', + declarationHint: 'types/user', + }, + 'shared/sign-in-future-resource/sign-in-future-resource.mdx': { + symbol: 'SignInFutureResource', + declarationHint: 'types/signInFuture', + }, + 'shared/sign-up-future-resource/sign-up-future-resource.mdx': { + symbol: 'SignUpFutureResource', + declarationHint: 'types/signUpFuture', + }, + 'shared/organization-resource/organization-resource.mdx': { + symbol: 'OrganizationResource', + declarationHint: 'types/organization', + }, + 'shared/api-key-resource/api-key-resource.mdx': { + symbol: 'APIKeyResource', + declarationHint: 'types/apiKeys', + extraMethodInterfaces: [{ symbol: 'APIKeysNamespace', declarationHint: 'types/apiKeys' }], + }, + 'shared/billing-namespace/billing-namespace.mdx': { + symbol: 'BillingNamespace', + declarationHint: 'types/billing', + extraMethodInterfaces: [{ symbol: 'BillingNamespace', declarationHint: 'types/billing' }], + }, +}; + +/** Stable iteration order matches key order in {@link REFERENCE_OBJECT_CONFIG}. */ +export const REFERENCE_OBJECTS_LIST = Object.keys(REFERENCE_OBJECT_CONFIG); + +/** + * Primary interface/class documented on each reference object page (used to resolve TypeDoc reflections). + * Derived from {@link REFERENCE_OBJECT_CONFIG}; kept for callers that only need `pageUrl → symbol`. + */ +export const REFERENCE_OBJECT_PAGE_SYMBOLS = Object.fromEntries( + Object.entries(REFERENCE_OBJECT_CONFIG).map(([url, { symbol }]) => [url, symbol]), +); diff --git a/package.json b/package.json index 424681d40ee..9bcb3913bab 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "test:typedoc": "pnpm typedoc:generate && cd ./.typedoc && vitest run", "turbo:clean": "turbo daemon clean", "typedoc:generate": "pnpm build && pnpm typedoc:generate:skip-build", - "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && node .typedoc/extract-returns-and-params.mjs && rimraf .typedoc/docs && cpy '.typedoc/temp-docs/**' '.typedoc/docs' && rimraf .typedoc/temp-docs", + "typedoc:generate:skip-build": "typedoc --tsconfig tsconfig.typedoc.json && node .typedoc/extract-returns-and-params.mjs && node .typedoc/extract-methods.mjs && rimraf .typedoc/docs && cpy '.typedoc/temp-docs/**' '.typedoc/docs' && rimraf .typedoc/temp-docs", "version-packages": "changeset version && pnpm install --lockfile-only --engine-strict=false", "version-packages:canary": "./scripts/canary.mjs", "version-packages:canary-core3": "./scripts/canary-core3.mjs", diff --git a/packages/backend/src/api/endpoints/ActorTokenApi.ts b/packages/backend/src/api/endpoints/ActorTokenApi.ts index c3dca6a9706..2c4e3e114ba 100644 --- a/packages/backend/src/api/endpoints/ActorTokenApi.ts +++ b/packages/backend/src/api/endpoints/ActorTokenApi.ts @@ -28,7 +28,7 @@ type ActorTokenCreateParams = { */ actor: ActorTokenActorCreateParams; /** - * Optional parameter to specify the life duration of the actor token in seconds. + * The lifetime of the actor token in seconds. * * @remarks * By default, the duration is 1 hour. diff --git a/packages/backend/src/api/endpoints/SessionApi.ts b/packages/backend/src/api/endpoints/SessionApi.ts index a4fc70ddf55..170408ea5ac 100644 --- a/packages/backend/src/api/endpoints/SessionApi.ts +++ b/packages/backend/src/api/endpoints/SessionApi.ts @@ -72,11 +72,11 @@ export class SessionAPI extends AbstractAPI { } /** - * Retrieves a session token or generates a JWT using a specified template. + * Gets a session token or generates a JWT using a specified template. * * @param sessionId - The ID of the session for which to generate the token - * @param template - Optional name of the JWT template configured in the Clerk Dashboard. - * @param expiresInSeconds - Optional expiration time for the token in seconds. + * @param template - The name of the JWT template configured in the Clerk Dashboard. + * @param expiresInSeconds - The expiration time for the token in seconds. * If not provided, uses the default expiration. * * @returns A promise that resolves to the generated token diff --git a/packages/backend/src/api/resources/APIKey.ts b/packages/backend/src/api/resources/APIKey.ts index 6e3c3fbf044..5add117abc6 100644 --- a/packages/backend/src/api/resources/APIKey.ts +++ b/packages/backend/src/api/resources/APIKey.ts @@ -50,7 +50,7 @@ export class APIKey { */ readonly createdBy: string | null, /** - * An optional description for the API key. + * A description for the API key. */ readonly description: string | null, /** diff --git a/packages/backend/src/tokens/authObjects.ts b/packages/backend/src/tokens/authObjects.ts index 205d7d1fd3a..8353fa1286d 100644 --- a/packages/backend/src/tokens/authObjects.ts +++ b/packages/backend/src/tokens/authObjects.ts @@ -390,7 +390,7 @@ export const makeAuthObjectSerializable = >(ob * * @param sessionId - The ID of the session * @param template - The JWT template name to use for token generation - * @param expiresInSeconds - Optional expiration time in seconds for the token + * @param expiresInSeconds - The expiration time in seconds for the token * @returns A promise that resolves to the token string */ type TokenFetcher = (sessionId: string, template?: string, expiresInSeconds?: number) => Promise; @@ -406,8 +406,7 @@ type CreateGetToken = (params: { sessionId: string; sessionToken: string; fetche /** * Creates a token retrieval function for authenticated sessions. * - * This factory function returns a getToken function that can either return the raw session token - * or generate a JWT using a specified template with optional custom expiration. + * This factory function returns a getToken function that can either return the raw session token or generate a JWT using a specified template with custom expiration. * * @param params - Configuration object * @param params.sessionId - The session ID for token generation diff --git a/packages/backend/src/tokens/authenticateContext.ts b/packages/backend/src/tokens/authenticateContext.ts index 794c9268874..e8d1d682330 100644 --- a/packages/backend/src/tokens/authenticateContext.ts +++ b/packages/backend/src/tokens/authenticateContext.ts @@ -58,7 +58,7 @@ class AuthenticateContext implements AuthenticateContext { private originalFrontendApi: string = ''; /** - * Retrieves the session token from either the cookie or the header. + * Gets the session token from either the cookie or the header. * * @returns {string | undefined} The session token if available, otherwise undefined. */ diff --git a/packages/clerk-js/src/core/modules/apiKeys/index.ts b/packages/clerk-js/src/core/modules/apiKeys/index.ts index 77949b70ae0..58e589c1a2d 100644 --- a/packages/clerk-js/src/core/modules/apiKeys/index.ts +++ b/packages/clerk-js/src/core/modules/apiKeys/index.ts @@ -40,7 +40,7 @@ export class APIKeys implements APIKeysNamespace { } /** - * Retrieves a paginated list of API keys. + * Gets a paginated list of API keys. * * The subject (owner) is resolved in the following order: * 1. Explicit `subject` param (user or organization ID) diff --git a/packages/clerk-js/src/core/modules/debug/index.ts b/packages/clerk-js/src/core/modules/debug/index.ts index cb7ec30695e..30e37e51a13 100644 --- a/packages/clerk-js/src/core/modules/debug/index.ts +++ b/packages/clerk-js/src/core/modules/debug/index.ts @@ -22,11 +22,11 @@ function validateLoggerOptions(options: T): vo * Options for configuring the debug logger. */ export interface LoggerOptions { - /** Optional URL to which telemetry logs will be sent. */ + /** The URL to which telemetry logs will be sent. */ endpoint?: string; /** Minimum log level to capture. */ logLevel?: DebugLogLevel; - /** Optional collector instance for custom telemetry handling. */ + /** A collector instance for custom telemetry handling. */ telemetryCollector?: TelemetryCollector; } @@ -42,11 +42,11 @@ export interface ConsoleLoggerOptions { * Configuration options for a telemetry-based debug logger. */ export interface TelemetryLoggerOptions { - /** Optional URL to which telemetry logs will be sent. */ + /** The URL to which telemetry logs will be sent. */ endpoint?: string; /** Minimum log level to capture. */ logLevel?: DebugLogLevel; - /** Optional collector instance for custom telemetry handling. */ + /** A collector instance for custom telemetry handling. */ telemetryCollector?: TelemetryCollector; } diff --git a/packages/clerk-js/src/core/resources/Client.ts b/packages/clerk-js/src/core/resources/Client.ts index 986252b20dd..6b690c7261c 100644 --- a/packages/clerk-js/src/core/resources/Client.ts +++ b/packages/clerk-js/src/core/resources/Client.ts @@ -33,7 +33,6 @@ export class Client extends BaseResource implements ClientResource { lastActiveSessionId: string | null = null; captchaBypass = false; cookieExpiresAt: Date | null = null; - /** Last authentication strategy used by this client; `null` when unknown/disabled. */ lastAuthenticationStrategy: LastAuthenticationStrategy | null = null; createdAt: Date | null = null; updatedAt: Date | null = null; diff --git a/packages/expo/src/google-one-tap/types.ts b/packages/expo/src/google-one-tap/types.ts index fdc8e95df74..7133fc1be46 100644 --- a/packages/expo/src/google-one-tap/types.ts +++ b/packages/expo/src/google-one-tap/types.ts @@ -18,7 +18,7 @@ export type ConfigureParams = { iosClientId?: string; /** - * Optional hosted domain to restrict sign-in to a specific domain. + * The hosted domain to restrict sign-in to a specific domain. */ hostedDomain?: string; diff --git a/packages/express/src/getAuth.ts b/packages/express/src/getAuth.ts index 0119cbeec7f..31b12cdff04 100644 --- a/packages/express/src/getAuth.ts +++ b/packages/express/src/getAuth.ts @@ -8,7 +8,7 @@ import { requestHasAuthObject } from './utils'; /** * Retrieves the Clerk AuthObject using the current request object. * - * @param {GetAuthOptions} options - Optional configuration for retriving auth object. + * @param {GetAuthOptions} options - Optional configuration for retrieving auth object. * @returns {AuthObject} Object with information about the request state and claims. * @throws {Error} `clerkMiddleware` or `requireAuth` is required to be set in the middleware chain before this util is used. */ diff --git a/packages/nextjs/src/app-router/server/keyless-provider.tsx b/packages/nextjs/src/app-router/server/keyless-provider.tsx index 0eb7a87405c..fe3c1b9777b 100644 --- a/packages/nextjs/src/app-router/server/keyless-provider.tsx +++ b/packages/nextjs/src/app-router/server/keyless-provider.tsx @@ -43,8 +43,9 @@ export const KeylessProvider = async (props: KeylessProviderProps) => { .then(mod => mod.keyless().getOrCreateKeys()) .catch(() => null); - const { clerkDevelopmentCache, createConfirmationMessage, createKeylessModeMessage } = - await import('../../server/keyless-log-cache.js'); + const { clerkDevelopmentCache, createConfirmationMessage, createKeylessModeMessage } = await import( + '../../server/keyless-log-cache.js' + ); if (!newOrReadKeys) { // When case keyless should run, but keys are not available, then fallback to throwing for missing keys diff --git a/packages/nextjs/src/server/createGetAuth.ts b/packages/nextjs/src/server/createGetAuth.ts index 0b29d105b27..dfd60fa861b 100644 --- a/packages/nextjs/src/server/createGetAuth.ts +++ b/packages/nextjs/src/server/createGetAuth.ts @@ -94,7 +94,7 @@ export const createSyncGetAuth = ({ * > If you are using App Router, use the [`auth()` helper](https://clerk.com/docs/reference/nextjs/app-router/auth) instead. * * @param req - The Next.js request object. - * @param [options] - An optional object that can be used to configure the behavior of the `getAuth()` function. + * @param [options] - An object that can be used to configure the behavior of the `getAuth()` function. * @param [options.secretKey] - A string that represents the Secret Key used to sign the session token. If not provided, the Secret Key is retrieved from the environment variable `CLERK_SECRET_KEY`. * @returns The `Auth` object. See the [Auth reference](https://clerk.com/docs/reference/backend/types/auth-object) for more information. * diff --git a/packages/react/src/components/uiComponents.tsx b/packages/react/src/components/uiComponents.tsx index ba7c941618b..7be70a3bc6c 100644 --- a/packages/react/src/components/uiComponents.tsx +++ b/packages/react/src/components/uiComponents.tsx @@ -55,7 +55,7 @@ import { withClerk } from './withClerk'; type FallbackProp = { /** - * An optional element to render while the component is mounting. + * The element to render while the component is mounting. */ fallback?: ReactNode; }; diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 9c38a97c989..bf773d029c9 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -53,11 +53,11 @@ export type ClerkProviderProps = Omit< */ __internal_bypassMissingPublishableKey?: boolean; /** - * Optional object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * An object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ appearance?: ExtractAppearanceType; /** - * Optional object to use the bundled Clerk UI instead of loading from CDN. + * An object to use the bundled Clerk UI instead of loading from CDN. * Import `ui` from `@clerk/ui` and pass it here to bundle the UI with your application. * When omitted, UI is loaded from Clerk's CDN. * Note: When `ui` is used, appearance is automatically typed based on the specific UI version. diff --git a/packages/shared/src/eventBus.ts b/packages/shared/src/eventBus.ts index ad6766b4d7c..d89fd2e4bb3 100644 --- a/packages/shared/src/eventBus.ts +++ b/packages/shared/src/eventBus.ts @@ -47,7 +47,7 @@ type EventBus> = { * Unsubscribe from an event * * @param event - The event name to unsubscribe from - * @param handler - Optional specific handler to remove. If omitted, all handlers for the event are removed + * @param handler - A specific handler to remove. If omitted, all handlers for the event are removed * @returns void */ off: (event: Event, handler?: EventHandler) => void; @@ -56,7 +56,7 @@ type EventBus> = { * Unsubscribe from a pre-dispatch event * * @param event - The event name to unsubscribe from - * @param handler - Optional specific handler to remove. If omitted, all pre-dispatch handlers for the event are removed + * @param handler - A specific handler to remove. If omitted, all pre-dispatch handlers for the event are removed * @returns void */ prioritizedOff: (event: Event, handler?: EventHandler) => void; diff --git a/packages/shared/src/react/billing/payment-element.tsx b/packages/shared/src/react/billing/payment-element.tsx index f293399fac2..35c0ac3fd3c 100644 --- a/packages/shared/src/react/billing/payment-element.tsx +++ b/packages/shared/src/react/billing/payment-element.tsx @@ -98,11 +98,11 @@ type internalStripeAppearance = { */ export type PaymentElementProviderProps = { /** - * An optional checkout resource object. When provided, the payment element is scoped to the specific checkout session. + * A checkout resource object. When provided, the payment element is scoped to the specific checkout session. */ checkout?: CheckoutFlowResource | BillingCheckoutResource | ReturnType['checkout']; /** - * An optional object to customize the appearance of the Stripe Payment Element. This allows you to match the form's styling to your application's theme. + * An object to customize the appearance of the Stripe Payment Element. This allows you to match the form's styling to your application's theme. */ stripeAppearance?: internalStripeAppearance; /** @@ -112,7 +112,7 @@ export type PaymentElementProviderProps = { */ for?: ForPayerType; /** - * An optional description to display to the user within the payment element UI. + * A description to display to the user within the payment element UI. */ paymentDescription?: string; }; diff --git a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx index 8b057f0309a..d41bdc57d78 100644 --- a/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx +++ b/packages/shared/src/react/hooks/createBillingPaginatedHook.tsx @@ -26,26 +26,27 @@ type BillingHookConfig { +export interface HookParams + extends PaginatedHookConfig< + PagesOrInfiniteOptions & { + /** + * If `true`, a request will be triggered when the hook is mounted. + * + * @default 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. + * + * @default undefined + * + * @hidden + * + * @experimental + */ + __experimental_mode?: 'cache'; + } + > { /** * Specifies whether to fetch for the current user or Organization. * diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts index 09679504ecf..dd4ea909ace 100644 --- a/packages/shared/src/types/apiKeys.ts +++ b/packages/shared/src/types/apiKeys.ts @@ -2,28 +2,80 @@ import type { CreateAPIKeyParams, GetAPIKeysParams, RevokeAPIKeyParams } from '. import type { ClerkPaginatedResponse } from './pagination'; import type { ClerkResource } from './resource'; +/** + * The `APIKeys` object provides methods for managing API keys that allow your application's users to grant third-party services programmatic access to your application's API endpoints on their behalf. API keys are long-lived, opaque tokens that can be instantly revoked. + */ export interface APIKeyResource extends ClerkResource { + /** + * A unique identifier for the API key. + */ id: string; + /** + * The type of the API key. + */ type: string; + /** + * The name of the API key. + */ name: string; + /** + * The user or organization ID that the API key is associated with. + */ subject: string; + /** + * An array of scopes that define what the API key can access. + */ scopes: string[]; + /** + * Custom claims associated with the API key, or `null` if none. + */ claims: Record | null; + /** + * Indicates whether the API key has been revoked. + */ revoked: boolean; + /** + * The reason the API key was revoked, or `null` if not revoked. + */ revocationReason: string | null; + /** + * Indicates whether the API key has expired. + */ expired: boolean; + /** + * The expiration date and time for the API key, or `null` if the key never expires. + */ expiration: Date | null; + /** + * The ID of the user that created the API key. + */ createdBy: string | null; + /** + * 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.** + */ secret?: string; + /** + * The date and time when the API key was last used to authenticate a request, or `null` if it has never been used. + */ lastUsedAt: Date | null; + /** + * The date and time when the API key was created. + */ createdAt: Date; + /** + * The date and time when the API key was last updated. + */ updatedAt: Date; } +/** @document */ export interface APIKeysNamespace { /** - * Retrieves a paginated list of API keys for the current user or organization. + * Gets a paginated list of API keys for the current user or organization. */ getAll(params?: GetAPIKeysParams): Promise>; /** diff --git a/packages/shared/src/types/attributes.ts b/packages/shared/src/types/attributes.ts index 7cc23cbcc46..0b2ca92c6fc 100644 --- a/packages/shared/src/types/attributes.ts +++ b/packages/shared/src/types/attributes.ts @@ -1,4 +1,8 @@ +/** @inline */ export type FirstNameAttribute = 'first_name'; +/** @inline */ export type LastNameAttribute = 'last_name'; +/** @inline */ export type PasswordAttribute = 'password'; +/** @inline */ export type LegalAcceptedAttribute = 'legal_accepted'; diff --git a/packages/shared/src/types/billing.ts b/packages/shared/src/types/billing.ts index 786887fd2b6..f0446bf7f9a 100644 --- a/packages/shared/src/types/billing.ts +++ b/packages/shared/src/types/billing.ts @@ -6,7 +6,10 @@ import type { ClerkPaginatedResponse, ClerkPaginationParams } from './pagination import type { ClerkResource } from './resource'; import type { ForceNull, RemoveFunctions, Simplify } from './utils'; -type WithOptionalOrgType = T & { +/** + * Intersects `T` with an optional organization scope (`orgId`) for billing and related requests. + */ +export type WithOptionalOrgType = T & { /** * The Organization ID to perform the request on. */ @@ -18,56 +21,64 @@ type WithOptionalOrgType = T & { */ export interface BillingNamespace { /** - * Returns a list of payment attempts for the current user or supplied organization. + * Gets a list of payment attempts for the current user or supplied organization. + * @returns A [`ClerkPaginatedResponse`](/docs/reference/types/clerk-paginated-response) of [`BillingPaymentResource`](/docs/reference/types/billing-payment-resource) objects. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPaymentAttempts: (params: GetPaymentAttemptsParams) => Promise>; /** - * Returns details of a specific payment attempt for the current user or supplied Organization. + * Gets details of a specific payment attempt for the current user or supplied Organization. + * @returns A [`BillingPaymentResource`](https://clerk.com/docs/reference/types/billing-payment-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPaymentAttempt: (params: GetPaymentAttemptParams) => Promise; /** - * Returns a list of all publically visible Billing Plans. + * Gets a list of all publically visible Billing Plans. + * @returns A [`ClerkPaginatedResponse`](/docs/reference/types/clerk-paginated-response) of [`BillingPlanResource`](/docs/reference/types/billing-plan-resource) objects. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPlans: (params?: GetPlansParams) => Promise>; /** - * Returns a Billing Plan by ID. + * Gets a given Billing Plan. + * @returns A [`BillingPlanResource`](/docs/reference/types/billing-plan-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPlan: (params: GetPlanParams) => Promise; /** - * Returns the main Billing Subscription for the current user or supplied Organization. + * Gets the main Billing Subscription for the current user or supplied Organization. + * @returns A [`BillingSubscriptionResource`](/docs/reference/types/billing-subscription-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getSubscription: (params: GetSubscriptionParams) => Promise; /** - * Returns a list of billing statements for the current user or supplied Organization. + * Gets a list of billing statements for the current user or supplied Organization. + * @returns A [`ClerkPaginatedResponse`](/docs/reference/types/clerk-paginated-response) of [`BillingStatementResource`](/docs/reference/types/billing-statement-resource) objects. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getStatements: (params: GetStatementsParams) => Promise>; /** - * Returns a billing statement by ID. + * Gets a given Billing Statement. + * @returns A [`BillingStatementResource`](/docs/reference/types/billing-statement-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getStatement: (params: GetStatementParams) => Promise; /** - * Creates a new billing checkout for the current user or supplied Organization. + * Creates a new Billing checkout for the current user or supplied Organization. + * @returns A [`BillingCheckoutResource`](/docs/reference/types/billing-checkout-resource) object. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ @@ -107,14 +118,20 @@ export type BillingSubscriptionPlanPeriod = 'month' | 'annual'; */ export interface BillingPayerMethods { /** + * Initializes a payment method. + * @returns An [`BillingInitializedPaymentMethodResource`](https://clerk.com/docs/reference/types/billing-initialized-payment-method-resource) object. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ initializePaymentMethod: (params: InitializePaymentMethodParams) => Promise; /** + * Adds a payment method. + * @returns An [`BillingPaymentMethodResource`](https://clerk.com/docs/reference/types/billing-payment-method-resource) object. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ addPaymentMethod: (params: AddPaymentMethodParams) => Promise; /** + * Gets a list of payment methods that have been stored. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`BillingPaymentMethodResource`](https://clerk.com/docs/reference/types/billing-payment-method-resource) objects. * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ getPaymentMethods: ( @@ -127,7 +144,7 @@ export interface BillingPayerMethods { */ export type GetPlanParams = { /** - * The ID of the Billing Plan to fetch. + * The ID of the Billing Plan to get. */ id: string; }; @@ -557,14 +574,19 @@ export type GetPaymentAttemptsParams = WithOptionalOrgType; +export type GetPaymentAttemptParams = { + /** + * The unique identifier for the payment attempt to get. + */ + id: string; +} & WithOptionalOrgType; /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ export type GetStatementParams = { /** - * The ID of the statement to fetch. + * The ID of the statement to get. */ id: string; } & WithOptionalOrgType; @@ -630,6 +652,9 @@ export interface BillingStatementGroup { * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ export type GetSubscriptionParams = { + /** + * The unique identifier for the Organization to get the subscription for. + */ orgId?: string; }; diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 88613446bcb..7af56f46670 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -135,7 +135,16 @@ export type SDKMetadata = { environment?: string; }; +/** + * A callback function that is called when Clerk resources change. + * @inline + */ export type ListenerCallback = (emission: Resources) => void; +/** + * Optional configuration for the `addListener()` method. + * @param skipInitialEmit - If `true`, the callback will not be called immediately after registration. Defaults to `false`. + * @inline + */ export type ListenerOptions = { skipInitialEmit?: boolean }; export type UnsubscribeCallback = () => void; @@ -175,16 +184,21 @@ export type SetActiveNavigate = (params: { decorateUrl: DecorateUrl; }) => void | Promise; +/** + * A callback that runs after sign out completes. + * @inline */ export type SignOutCallback = () => void | Promise; +/** + * Configuration options. + */ export type SignOutOptions = { /** - * Specify a specific session to sign out. Useful for - * multi-session applications. + * Specify a specific session to sign out. Useful for multi-session applications. */ sessionId?: string; /** - * Specify a redirect URL to navigate to after sign out is complete. + * Specify a redirect URL to navigate to after sign-out is complete. */ redirectUrl?: string; }; @@ -203,40 +217,66 @@ type EventHandler = (payload: ClerkEventPayload[E]) => voi export type ClerkEventPayload = { status: ClerkStatus; }; -type OnEventListener = (event: E, handler: EventHandler, opt?: { notify: boolean }) => void; -type OffEventListener = (event: E, handler: EventHandler) => void; + +/** + * Registers an event listener for a specific Clerk event. + * + * @param event - The event name to subscribe to. + * @param handler - The callback function to execute when the event is dispatched. + * @param opt - Optional configuration. + * @param opt.notify - If true and the event was previously dispatched, handler will be called immediately with the latest payload. + */ +export type OnEventListener = ( + event: E, + handler: EventHandler, + opt?: { notify: boolean }, +) => void; + +/** + * Unregisters an event listener for a specific Clerk event. + * + * @param event - The event name to unsubscribe from. + * @param handler - The callback function to remove. + */ +export type OffEventListener = (event: E, handler: EventHandler) => void; /** * @inline + * @property {ClerkStatus} degraded - Set when Clerk is partially operational. + * @property {ClerkStatus} error - Set when hotloading `clerk-js` or `Clerk.load()` failed. + * @property {ClerkStatus} loading - Set during initialization. + * @property {ClerkStatus} ready - Set when Clerk is fully operational. */ export type ClerkStatus = 'degraded' | 'error' | 'loading' | 'ready'; /** - * Main Clerk SDK object. + * The `Clerk` class serves as the central interface for working with Clerk's authentication and user management functionality in your application. As a top-level class in the Clerk SDK, it provides access to key methods and properties for managing users, sessions, API keys, billing, organizations, and more. */ export interface Clerk { /** - * Clerk SDK version number. + * The Clerk SDK version number. */ version: string | undefined; /** * If present, contains information about the SDK that the host application is using. - * For example, if Clerk is loaded through `@clerk/nextjs`, this would be `{ name: '@clerk/nextjs', version: '1.0.0' }` + * For example, if Clerk is loaded through `@clerk/nextjs`, this would be `{ name: '@clerk/nextjs', version: '1.0.0' }`. You don't need to set this value yourself unless you're [developing an SDK](https://clerk.com/docs/guides/development/sdk-development/overview). */ sdkMetadata: SDKMetadata | undefined; /** - * If true the bootstrapping of Clerk.load() has completed successfully. + * Indicates if the `Clerk` object is ready for use. Set to `false` when the `status` is `"loading"`. Set to `true` when the `status` is `"ready"` or `"degraded"`. */ loaded: boolean; /** - * Describes the state the clerk singleton operates in: - * - `"error"`: Clerk failed to initialize. - * - `"loading"`: Clerk is still attempting to load. - * - `"ready"`: Clerk singleton is fully operational. - * - `"degraded"`: Clerk singleton is partially operational. + * The status of the `Clerk` instance. Possible values are: + *
    + *
  • `"error"`: Set when hotloading `clerk-js` or `Clerk.load()` failed.
  • + *
  • `"loading"`: Set during initialization.
  • + *
  • `"ready"`: Set when Clerk is fully operational.
  • + *
  • `"degraded"`: Set when Clerk is partially operational.
  • + *
*/ status: ClerkStatus; @@ -247,39 +287,42 @@ export interface Clerk { frontendApi: string; - /** Clerk Publishable Key string. */ + /** Your Clerk [Publishable Key](!publishable-key). */ publishableKey: string; - /** Clerk Proxy url 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`). */ proxyUrl: string | undefined; - /** Clerk Satellite Frontend API string. */ + /** The current Clerk app's domain. Prefixed with `clerk.` on production if not already prefixed. Returns `""` when ran on the server. */ domain: string; - /** Clerk Flag for satellite apps. */ + /** Indicates if the instance is a satellite app. */ isSatellite: boolean; - /** Clerk Instance type is defined from the Publishable key */ + /** Indicates if the Clerk instance is running in a production or development environment. */ instanceType: InstanceType | undefined; - /** Clerk flag for loading Clerk in a standard browser setup */ + /** + * Indicates if the instance is being loaded in a standard browser environment. Set to `false` on native platforms where cookies cannot be set. When `undefined`, Clerk assumes a standard browser. + * @inline + */ isStandardBrowser: boolean | undefined; /** - * Indicates whether the current user has a valid signed-in client session + * Indicates whether the current user has a valid signed-in client session. */ isSignedIn: boolean; - /** Client handling most Clerk operations. */ + /** The `Client` object for the current window. */ client: ClientResource | undefined; - /** Current Session. */ + /** The currently active `Session`, which is guaranteed to be one of the sessions in `Client.sessions`. If there is no active session, this field will be `null`. If the session is loading, this field will be `undefined`. */ session: SignedInSessionResource | null | undefined; - /** Active Organization */ + /** A shortcut to the last active `Session.user.organizationMemberships` which holds an instance of a `Organization` object. If the session is `null` or `undefined`, the user field will match. */ organization: OrganizationResource | null | undefined; - /** Current User. */ + /** A shortcut to `Session.user` which holds the currently active `User` object. If the session is `null` or `undefined`, the user field will match. */ user: UserResource | null | undefined; /** @@ -293,25 +336,30 @@ export interface Clerk { * Entrypoint for Clerk's Signal API containing resource signals along with accessible versions of `computed()` and * `effect()` that can be used to subscribe to changes from Signals. * + * @internal * @experimental This experimental API is subject to change. */ __internal_state: State; /** + * The `Billing` object used for managing billing. + * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ billing: BillingNamespace; + /** + * [Telemetry](https://clerk.com/docs/guides/how-clerk-works/security/clerk-telemetry) configuration. + */ telemetry: TelemetryCollector | undefined; + /** + * @internal + */ __internal_country?: string | null; /** - * Signs out the current user on single-session instances, or all users on multi-session instances - * - * @param signOutCallback - Optional A callback that runs after sign out completes. - * @param options - Optional Configuration options, see {@link SignOutOptions} - * @returns A promise that resolves when the sign out process completes. + * Signs out the current user on single-session instances, or all users on multi-session instances. */ signOut: SignOut; @@ -331,11 +379,13 @@ export interface Clerk { * Opens the Clerk Checkout component in a drawer. * * @param props - Optional checkout configuration parameters. + * @internal */ __internal_openCheckout: (props?: __internal_CheckoutProps) => void; /** * Closes the Clerk Checkout drawer. + * @internal */ __internal_closeCheckout: () => void; @@ -343,11 +393,13 @@ export interface Clerk { * Opens the Clerk PlanDetails drawer component in a drawer. * * @param props - `plan` or `planId` parameters are required. + * @internal */ __internal_openPlanDetails: (props: __internal_PlanDetailsProps) => void; /** * Closes the Clerk PlanDetails drawer. + * @internal */ __internal_closePlanDetails: () => void; @@ -355,11 +407,13 @@ export interface Clerk { * Opens the Clerk SubscriptionDetails drawer component in a drawer. * * @param props - Optional configuration parameters. + * @internal */ __internal_openSubscriptionDetails: (props?: __internal_SubscriptionDetailsProps) => void; /** * Closes the Clerk SubscriptionDetails drawer. + * @internal */ __internal_closeSubscriptionDetails: () => void; @@ -367,16 +421,19 @@ export interface Clerk { * Opens the Clerk UserVerification component in a modal. * * @param props - Optional user verification configuration parameters. + * @internal */ __internal_openReverification: (props?: __internal_UserVerificationModalProps) => void; /** * Closes the Clerk user verification modal. + * @internal */ __internal_closeReverification: () => void; /** * Attempts to enable a environment setting from a development instance, prompting if disabled. + * @internal */ __internal_attemptToEnableEnvironmentSetting: ( options: __internal_AttemptToEnableEnvironmentSettingParams, @@ -384,11 +441,13 @@ export interface Clerk { /** * Opens the Clerk Enable Organizations prompt for development instance + * @internal */ __internal_openEnableOrganizationsPrompt: (props: __internal_EnableOrganizationsPromptProps) => void; /** * Closes the Clerk Enable Organizations modal. + * @internal */ __internal_closeEnableOrganizationsPrompt: () => void; @@ -686,6 +745,7 @@ export interface Clerk { * * @param targetNode - Target node to mount the OAuth consent component. * @param oauthConsentProps - OAuth consent configuration parameters. + * @internal */ __internal_mountOAuthConsent: (targetNode: HTMLDivElement, oauthConsentProps?: __internal_OAuthConsentProps) => void; @@ -693,6 +753,7 @@ export interface Clerk { * Unmounts a OAuth consent component from the target element. * * @param targetNode - Target node to unmount the OAuth consent component from. + * @internal */ __internal_unmountOAuthConsent: (targetNode: HTMLDivElement) => void; @@ -767,27 +828,29 @@ export interface Clerk { __internal_loadStripeJs: () => Promise; /** - * Register a listener that triggers a callback each time important Clerk resources are changed. - * Allows to hook up at different steps in the sign up, sign in processes. + * Register a listener that triggers a callback whenever a change in the [`Client`](https://clerk.com/docs/reference/objects/client), [`Session`](https://clerk.com/docs/reference/objects/session), [`User`](https://clerk.com/docs/reference/objects/user), or [`Organization`](https://clerk.com/docs/reference/objects/organization) resources occurs. This method is primarily used to build frontend SDKs like [`@clerk/react`](https://www.npmjs.com/package/@clerk/react). + * + * Allows hooking up at different steps in the sign up, sign in processes. * * Some important checkpoints: - * When there is an active session, user === session.user. - * When there is no active session, user and session will both be null. - * When a session is loading, user and session will be undefined. + * - When there is an active session, `user === session.user`. + * - When there is no active session, user and session will both be `null`. + * - When a session is loading, user and session will be `undefined`. * - * @param callback - Callback function receiving the most updated Clerk resources after a change. - * @param options.skipInitialEmit - If true, the callback will not be called immediately after registration. - * @returns - Unsubscribe callback + * @param callback - The function to call when Clerk resources change. + * @param options - Optional configuration. + * @param options.skipInitialEmit - If `true`, the callback will not be called immediately after registration. Defaults to `false`. + * @returns - An `UnsubscribeCallback` function that can be used to remove the listener from the `Clerk` object. */ addListener: (callback: ListenerCallback, options?: ListenerOptions) => UnsubscribeCallback; /** * Registers an event handler for a specific Clerk event. * - * @param event - The event name to subscribe to - * @param handler - The callback function to execute when the event is dispatched - * @param opt - Optional configuration object - * @param opt.notify - If true and the event was previously dispatched, handler will be called immediately with the latest payload + * @param event - The event name to subscribe to. + * @param handler - The callback function to execute when the event is triggered. + * @param opt - An object to control the behavior of the event handler. If true, and the event was previously dispatched, handler will be called immediately with the latest payload. + * @param opt.notify - If `true` and the event was previously dispatched, the handler will be called immediately with the latest payload. */ on: OnEventListener; @@ -795,7 +858,7 @@ export interface Clerk { * Removes an event handler for a specific Clerk event. * * @param event - The event name to unsubscribe from - * @param handler - The callback function to remove + * @param handler - The callback function to remove. */ off: OffEventListener; @@ -808,7 +871,7 @@ export interface Clerk { __internal_addNavigationListener: (callback: () => void) => UnsubscribeCallback; /** - * Set the active session and Organization explicitly. + * A method used to set the current session and/or Organization for the client. Accepts a [`SetActiveParams`](https://clerk.com/docs/reference/types/set-active-params) object. * * If the session param is `null`, the active session is deleted. * In a similar fashion, if the organization param is `null`, the current organization is removed as active. @@ -816,148 +879,170 @@ export interface Clerk { setActive: SetActive; /** - * Function used to commit a navigation after certain steps in the Clerk processes. + * Helper method which will use the custom push navigation function of your application to navigate to the provided URL or relative path. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ navigate: CustomNavigation; /** - * Decorates the provided url with the auth token for development instances. + * Decorates the provided URL with the auth token for development instances. * - * @param to + * @param to - The route to create a URL towards. */ buildUrlWithAuth(to: string): string; /** - * Returns the configured url where `` is mounted or a custom sign-in page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/sign-in) is mounted or a custom sign-in page is rendered. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options used to control the redirect in the constructed URL. */ buildSignInUrl(opts?: RedirectOptions): string; /** - * Returns the configured url where `` is mounted or a custom sign-up page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/sign-up) is mounted or a custom sign-up page is rendered. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options used to control the redirect in the constructed URL. */ buildSignUpUrl(opts?: RedirectOptions): string; /** - * Returns the url where `` is mounted or a custom user-profile page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/user/user-profile) is mounted or a custom user-profile page is rendered. */ buildUserProfileUrl(): string; /** - * Returns the configured url where `` is mounted or a custom create-organization page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/organization/create-organization) is mounted or a custom create-organization page is rendered. */ buildCreateOrganizationUrl(): string; /** - * Returns the configured url where `` is mounted or a custom organization-profile page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/organization/organization-profile) is mounted or a custom organization-profile page is rendered. */ buildOrganizationProfileUrl(): string; /** - * Returns the configured url where tasks are mounted. + * Returns the configured URL where [session tasks](https://clerk.com/docs/guides/configure/session-tasks) are mounted. */ buildTasksUrl(): string; /** - * Returns the configured afterSignInUrl of the instance. + * Returns the configured `afterSignInUrl` of the instance. + * @param params - Optional query parameters to append to the URL. */ buildAfterSignInUrl({ params }?: { params?: URLSearchParams }): string; /** - * Returns the configured afterSignInUrl of the instance. + * Returns the configured `afterSignInUrl` of the instance. + * @param params - Optional query parameters to append to the URL. */ buildAfterSignUpUrl({ params }?: { params?: URLSearchParams }): string; /** - * Returns the configured afterSignOutUrl of the instance. + * Returns the configured `afterSignOutUrl` of the instance. */ buildAfterSignOutUrl(): string; /** - * Returns the configured newSubscriptionRedirectUrl of the instance. + * Returns the configured `newSubscriptionRedirectUrl` of the instance. */ buildNewSubscriptionRedirectUrl(): string; /** - * Returns the configured afterMultiSessionSingleSignOutUrl of the instance. + * Returns the configured `afterMultiSessionSingleSignOutUrl` of the instance. */ buildAfterMultiSessionSingleSignOutUrl(): string; /** - * Returns the configured url where `` is mounted or a custom waitlist page is rendered. + * Returns the configured URL where [``](https://clerk.com/docs/reference/components/authentication/waitlist) is mounted or a custom waitlist page is rendered. + * + * @param opts - Options to control the waitlist URL. + * @param opts.initialValues - Initial values to prefill the waitlist form. */ buildWaitlistUrl(opts?: { initialValues?: Record }): string; /** * - * Redirects to the provided url after decorating it with the auth token for development instances. + * Redirects to the provided URL after appending authentication credentials. For development instances, this method decorates the URL with an auth token to maintain authentication state. For production instances, the standard session cookie is used. * - * @param to + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. + * + * @param to - The URL to redirect to. */ redirectWithAuth(to: string): Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the sign-in URL, as configured in your application's instance settings. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options to control the redirect. */ redirectToSignIn(opts?: SignInRedirectOptions): Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the sign-up URL, as configured in your application's instance settings. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options to control the redirect. */ redirectToSignUp(opts?: SignUpRedirectOptions): Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/user/user-profile) is mounted. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ redirectToUserProfile: () => Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/organization/organization-profile) is mounted. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ redirectToOrganizationProfile: () => Promise; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/organization/create-organization) is mounted. This method uses the [`navigate()`](https://clerk.com/docs/reference/objects/clerk#navigate) method under the hood. + * + * Returns a promise that can be `await`ed in order to listen for the navigation to finish. The inner value should not be relied on, as it can change based on the framework it's used within. */ redirectToCreateOrganization: () => Promise; /** - * Redirects to the configured afterSignIn URL. + * Redirects to the configured `afterSignIn` URL. */ redirectToAfterSignIn: () => void; /** - * Redirects to the configured afterSignUp URL. + * Redirects to the configured `afterSignUp` URL. */ redirectToAfterSignUp: () => void; /** - * Redirects to the configured afterSignOut URL. + * Redirects to the configured `afterSignOut` URL. */ redirectToAfterSignOut: () => void; /** - * Redirects to the configured URL where `` is mounted. + * Redirects to the configured URL where [``](https://clerk.com/docs/reference/components/authentication/waitlist) is mounted. */ redirectToWaitlist: () => void; /** - * Redirects to the configured URL where tasks are mounted. + * Redirects to the configured URL where [session tasks](https://clerk.com/docs/reference/objects/session) are mounted. * - * @param opts - A {@link RedirectOptions} object + * @param opts - Options to control the redirect (e.g. redirect URL after tasks are complete). */ redirectToTasks(opts?: TasksRedirectOptions): Promise; /** - * Completes a Google One Tap redirection flow started by - * {@link Clerk.authenticateWithGoogleOneTap} + * Completes a Google One Tap redirection flow started by [`authenticateWithGoogleOneTap()`](https://clerk.com/docs/reference/objects/clerk#authenticate-with-google-one-tap). This method should be called after the user is redirected back from visiting the Google One Tap prompt. + * + * @param signInOrUp - The resource returned from the initial `authenticateWithGoogleOneTap()` call (before redirect). + * @param params - Additional props that define where the user will be redirected to at the end of a successful Google One Tap flow. + * @param customNavigate - A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. */ handleGoogleOneTapCallback: ( signInOrUp: SignInResource | SignUpResource, @@ -966,8 +1051,10 @@ export interface Clerk { ) => Promise; /** - * Completes an OAuth or SAML redirection flow started by - * {@link Clerk.client.signIn.authenticateWithRedirect} or {@link Clerk.client.signUp.authenticateWithRedirect} + * Completes a custom OAuth or SAML redirect flow that was started by calling [`SignIn.authenticateWithRedirect(params)`](https://clerk.com/docs/reference/objects/sign-in) or [`SignUp.authenticateWithRedirect(params)`](https://clerk.com/docs/reference/objects/sign-up). + * + * @param params - Additional props that define where the user will be redirected to at the end of a successful OAuth or SAML flow. + * @param customNavigate - A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. */ handleRedirectCallback: ( params: HandleOAuthCallbackParams | HandleSamlCallbackParams, @@ -975,7 +1062,9 @@ export interface Clerk { ) => Promise; /** - * Completes a Email Link flow started by {@link Clerk.client.signIn.createEmailLinkFlow} or {@link Clerk.client.signUp.createEmailLinkFlow} + * Completes an email link verification flow started by `Clerk.client.signIn.createEmailLinkFlow` or `Clerk.client.signUp.createEmailLinkFlow`, by processing the verification results from the redirect URL query parameters. This method should be called after the user is redirected back from visiting the verification link in their email. + * @param params - Allows you to define the URLs where the user should be redirected to on successful verification or pending/completed sign-up or sign-in attempts. If the email link is successfully verified on another device, there's a callback function parameter that allows custom code execution. + * @param customNavigate - A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. */ handleEmailLinkVerification: ( params: HandleEmailLinkVerificationParams, @@ -983,32 +1072,32 @@ export interface Clerk { ) => Promise; /** - * Authenticates user using their Metamask browser extension + * Starts a sign-in flow that uses MetaMask to authenticate the user using their Metamask wallet address. */ authenticateWithMetamask: (params?: AuthenticateWithMetamaskParams) => Promise; /** - * Authenticates user using their Coinbase Smart Wallet and browser extension + * Starts a sign-in flow that uses Coinbase Smart Wallet to authenticate the user using their Coinbase wallet address. */ authenticateWithCoinbaseWallet: (params?: AuthenticateWithCoinbaseWalletParams) => Promise; /** - * Authenticates user using their OKX Wallet browser extension + * Starts a sign-in flow that uses OKX Wallet to authenticate the user using their OKX wallet address. */ authenticateWithOKXWallet: (params?: AuthenticateWithOKXWalletParams) => Promise; /** - * Authenticates user using Base Account SDK + * Starts a sign-in flow that uses Base to authenticate the user using their Web3 wallet address. */ authenticateWithBase: (params?: AuthenticateWithBaseParams) => Promise; /** - * Authenticates user using their Solana supported Web3 wallet browser extension + * Starts a sign-in flow that uses Solana to authenticate the user using their Solana wallet address. */ authenticateWithSolana: (params: AuthenticateWithSolanaParams) => Promise; /** - * Authenticates user using their Web3 Wallet browser extension + * Starts a sign-in flow that uses a Web3 Wallet browser extension to authenticate the user using their Ethereum wallet address. */ authenticateWithWeb3: (params: ClerkAuthenticateWithWeb3Params) => Promise; @@ -1020,20 +1109,28 @@ export interface Clerk { ) => Promise; /** - * Creates an Organization, adding the current user as admin. + * Creates an Organization programmatically, adding the current user as admin. Returns an [`Organization`](https://clerk.com/docs/reference/objects/organization) object. + * + * > [!NOTE] + * > For React-based apps, consider using the [``](https://clerk.com/docs/reference/components/organization/create-organization) component. */ createOrganization: (params: CreateOrganizationParams) => Promise; /** - * Retrieves a single Organization by ID. + * Gets a single [Organization](https://clerk.com/docs/reference/objects/organization) by ID. + * + * @param organizationId - The ID of the Organization to get. */ getOrganization: (organizationId: string) => Promise; /** - * Handles a 401 response from Frontend API by refreshing the client and session object accordingly + * Handles a 401 response from the Frontend API by refreshing the [`Client`](https://clerk.com/docs/reference/objects/client) and [`Session`](https://clerk.com/docs/reference/objects/session) object accordingly. */ handleUnauthenticated: () => Promise; + /** + * Create a new waitlist entry programmatically. Requires that you set your app's sign-up mode to [**Waitlist**](https://clerk.com/docs/guides/secure/restricting-access#waitlist) in the Clerk Dashboard. + */ joinWaitlist: (params: JoinWaitlistParams) => Promise; /** @@ -1056,11 +1153,13 @@ export interface Clerk { /** * Internal flag indicating whether a `setActive` call is in progress. Used to prevent navigations from being * initiated outside of the Clerk class. + * + * @internal */ __internal_setActiveInProgress: boolean; /** - * API Keys Object + * The `APIKeys` object used for managing API keys. */ apiKeys: APIKeysNamespace; @@ -1078,44 +1177,42 @@ export interface Clerk { __experimental_checkout: __experimental_CheckoutFunction; } +/** @document */ export type HandleOAuthCallbackParams = TransferableOption & SignInForceRedirectUrl & SignInFallbackRedirectUrl & SignUpForceRedirectUrl & SignUpFallbackRedirectUrl & { /** - * Full URL or path where the SignIn component is mounted. + * The full URL or path where the [``](https://clerk.com/docs/reference/components/authentication/sign-in) component is mounted. */ signInUrl?: string; /** - * Full URL or path where the SignUp component is mounted. + * The full URL or path where the [``](https://clerk.com/docs/reference/components/authentication/sign-up) component is mounted. */ signUpUrl?: string; /** - * Full URL or path to navigate to during sign in, - * if identifier verification is required. + * The full URL or path to navigate to during sign in, if [first factor verification](!first-factor-verification) is required. */ firstFactorUrl?: string; /** - * Full URL or path to navigate to during sign in, - * if 2FA is enabled. + * The full URL or path to navigate to during sign in, if [multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) is enabled. */ secondFactorUrl?: string; /** - * Full URL or path to navigate to during sign in, - * if the user is required to reset their password. + * The full URL or path to navigate to during sign in, if the user is required to reset their password. */ resetPasswordUrl?: string; /** - * Full URL or path to navigate to after an incomplete sign up. + * The full URL or path to navigate to if the sign up requires additional information. */ continueSignUpUrl?: string | null; /** - * Full URL or path to navigate to after requesting email verification. + * The full URL or path to navigate to after requesting email verification. */ verifyEmailAddressUrl?: string | null; /** - * Full URL or path to navigate to after requesting phone verification. + * The full URL or path to navigate to after requesting phone verification. */ verifyPhoneNumberUrl?: string | null; /** @@ -1123,13 +1220,21 @@ export type HandleOAuthCallbackParams = TransferableOption & */ reloadResource?: 'signIn' | 'signUp'; /** - * Additional arbitrary metadata to be stored alongside the User object when a sign-up transfer occurs. + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. */ unsafeMetadata?: SignUpUnsafeMetadata; }; export type HandleSamlCallbackParams = HandleOAuthCallbackParams; +/** + * A function used to navigate to a given URL after certain steps in the Clerk processes. + * + * @param to - The URL or relative path to navigate to. + * @param options - Optional configuration. + * @param options.replace? - If `true`, replace the current history entry instead of pushing a new one. + * @param options.metadata? - Optional router metadata. + */ export type CustomNavigation = (to: string, options?: NavigateOptions) => Promise | void; export type ClerkThemeOptions = DeepSnakeToCamel>; @@ -1156,6 +1261,7 @@ type ClerkOptionsNavigation = routerDebug?: boolean; }; +/** @document */ type ClerkUnsafeOptions = { /** * Disables the `Clerk has been loaded with development keys` console warning that is logged when Clerk is @@ -1178,6 +1284,7 @@ type ClerkUnsafeOptions = { unsafe_disableDevelopmentModeConsoleWarning?: boolean; }; +/** @document */ export type ClerkOptions = ClerkOptionsNavigation & SignInForceRedirectUrl & SignInFallbackRedirectUrl & @@ -1188,30 +1295,38 @@ export type ClerkOptions = ClerkOptionsNavigation & AfterMultiSessionSingleSignOutUrl & ClerkUnsafeOptions & { /** - * Clerk UI module. Pass the `ui` export from `@clerk/ui` to bundle the UI - * with your application instead of loading it from the CDN. + * **Only required if you're bundling Clerk's UI (`@clerk/ui`) instead of loading it from the Clerk CDN**. Provide the UI module to embed Clerk's prebuilt authentication components directly in your application. */ - ui?: { ClerkUI?: ClerkUIConstructor | Promise }; + ui?: { + /** + * Pass the `ui` export from `@clerk/ui`. + */ + ClerkUI?: ClerkUIConstructor | Promise; + }; /** - * Optional object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * An object to style your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. See the [Appearance](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ // TODO @nikos appearance?: any; /** - * Optional object to localize your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * An object to localize your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ localization?: LocalizationResource; + /** + * Indicates whether Clerk should poll against Clerk's backend every 5 minutes. + * @default true + */ polling?: boolean; /** * By default, the last signed-in session is used during client initialization. This option allows you to override that behavior, e.g. by selecting a specific session. */ selectInitialSession?: (client: ClientResource) => SignedInSessionResource | null; /** - * By default, ClerkJS is loaded with the assumption that cookies can be set (browser setup). On native platforms this value must be set to `false`. + * Indicates whether ClerkJS is loaded with the assumption that cookies can be set (browser setup). On native platforms this value must be set to `false`. */ standardBrowser?: boolean; /** - * Optional support email for display in authentication screens. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. + * The support email address for display in authentication screens. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ supportEmail?: string; /** @@ -1223,19 +1338,19 @@ export type ClerkOptions = ClerkOptionsNavigation & */ signInUrl?: string; /** - * This URL will be used for any redirects that might happen and needs to point to your primary application on the client-side. This option is optional for production instances but **must be set for a satellite application in a development instance**. It's recommended to use [the environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. + * This URL will be used for any redirects that might happen and needs to point to your primary application on the client-side. This option is optional for production instances. **It is required to be set for a satellite application in a development instance**. It's recommended to use [the environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. */ signUpUrl?: string; /** - * An optional array of domains to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. + * An array of domains to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. */ allowedRedirectOrigins?: Array; /** - * An optional array of protocols to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. + * An array of protocols to validate user-provided redirect URLs against. If no match is made, the redirect is considered unsafe and the default redirect will be used with a warning logged in the console. */ allowedRedirectProtocols?: Array; /** - * This option defines that the application is a satellite application. + * Indicates whether the application is a satellite application. */ isSatellite?: boolean | ((url: URL) => boolean); /** @@ -1258,9 +1373,12 @@ export type ClerkOptions = ClerkOptionsNavigation & telemetry?: | false | { + /** + * If `true`, telemetry will not be collected. + */ disabled?: boolean; /** - * Telemetry events are only logged to the console and not sent to Clerk + * If `true`, telemetry events are only logged to the console and not sent to Clerk */ debug?: boolean; /** @@ -1337,11 +1455,21 @@ export type ClerkOptions = ClerkOptionsNavigation & taskUrls?: Partial>; }; +/** @inline */ export interface NavigateOptions { + /** + * If `true`, replace the current history entry instead of pushing a new one. + */ replace?: boolean; + /** + * Router metadata. + */ metadata?: RouterMetadata; } +/** + * @inline + */ export interface Resources { client: ClientResource; session?: SignedInSessionResource | null; @@ -1387,7 +1515,7 @@ type RouterFn = ( */ to: string, /** - * Optional metadata + * Metadata */ metadata?: { /** @@ -1457,13 +1585,9 @@ export type SetActiveParams = { redirectUrl?: string; /** - * 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. + * A custom navigation function to be called just before the session and/or Organization is set. When provided, it takes precedence over the `redirectUrl` parameter for navigation. * - * The callback receives a `decorateUrl` function that should be used to wrap destination URLs. - * This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL - * (starting with `https://`) that requires `window.location.href` instead of client-side navigation. + * The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. See the [section on using the `navigate()` parameter](https://clerk.com/docs/reference/objects/clerk#using-the-navigate-parameter) for more details. * * @example * ```typescript @@ -1498,6 +1622,7 @@ export type RoutingOptions = | { path: string | undefined; routing?: Extract } | { path?: never; routing?: Extract }; +/** @document */ export type SignInProps = RoutingOptions & { /** * Full URL or path to navigate to after successful sign in. @@ -1525,9 +1650,7 @@ export type SignInProps = RoutingOptions & { */ signUpUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /** @@ -1564,10 +1687,12 @@ export type SignInProps = RoutingOptions & { SignUpFallbackRedirectUrl & AfterSignOutUrl; +/** + * @interface + */ export interface TransferableOption { /** - * Indicates whether or not sign in attempts are transferable to the sign up flow. - * When set to false, prevents opaque sign ups when a user attempts to sign in via OAuth with an email that doesn't exist. + * Indicates whether or not sign-in attempts are transferable to the sign-up flow. Defaults to `true`. When set to `false`, prevents [opaque sign-ups](!opaque-sign-up) when a user attempts to sign in via OAuth with an email that doesn't exist. * * @default true */ @@ -1609,9 +1734,7 @@ export type __internal_UserVerificationProps = RoutingOptions & { level?: SessionVerificationLevel; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; }; @@ -1649,6 +1772,7 @@ export type __internal_AttemptToEnableEnvironmentSettingResult = { type GoogleOneTapRedirectUrlProps = SignInForceRedirectUrl & SignUpForceRedirectUrl; +/** @document */ export type GoogleOneTapProps = GoogleOneTapRedirectUrlProps & { /** * Whether to cancel the Google One Tap request if a user clicks outside the prompt. @@ -1671,9 +1795,13 @@ export type GoogleOneTapProps = GoogleOneTapRedirectUrlProps & { * @default true */ fedCmSupport?: boolean; + /** + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. + */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type SignUpProps = RoutingOptions & { /** * Full URL or path to navigate to after successful sign up. @@ -1696,9 +1824,7 @@ export type SignUpProps = RoutingOptions & { */ signInUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; @@ -1740,11 +1866,10 @@ export type SignUpModalProps = WithoutRouting & { getContainer?: () => HTMLElement | null; }; +/** @document */ export type UserProfileProps = RoutingOptions & { /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /* @@ -1789,6 +1914,7 @@ export type UserProfileModalProps = WithoutRouting & { getContainer?: () => HTMLElement | null; }; +/** @document */ export type OrganizationProfileProps = RoutingOptions & { /** * Full URL or path to navigate to after the user leaves the currently Active Organization. @@ -1797,9 +1923,7 @@ export type OrganizationProfileProps = RoutingOptions & { */ afterLeaveOrganizationUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /* @@ -1839,6 +1963,7 @@ export type OrganizationProfileModalProps = WithoutRouting HTMLElement | null; }; +/** @document */ export type CreateOrganizationProps = RoutingOptions & { /** * Full URL or path to navigate to after creating a new Organization. @@ -1856,13 +1981,12 @@ export type CreateOrganizationProps = RoutingOptions & { */ skipInvitationScreen?: boolean; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type CreateOrganizationModalProps = WithoutRouting & { /** * Function that returns the container element where portals should be rendered. @@ -1872,7 +1996,10 @@ export type CreateOrganizationModalProps = WithoutRouting HTMLElement | null; }; +/** @inline */ type UserProfileMode = 'modal' | 'navigation'; + +/** @document */ type UserButtonProfileMode = | { userProfileUrl?: never; @@ -1914,9 +2041,7 @@ export type UserButtonProps = UserButtonProfileMode & { */ afterSwitchSessionUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; @@ -1933,6 +2058,9 @@ export type UserButtonProps = UserButtonProfileMode & { }; export type UserAvatarProps = { + /** + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. + */ appearance?: ClerkAppearanceTheme; rounded?: boolean; }; @@ -1951,6 +2079,7 @@ type CreateOrganizationMode = | { createOrganizationUrl: string; createOrganizationMode?: 'navigation' } | { createOrganizationUrl?: never; createOrganizationMode?: 'modal' }; +/** @document */ export type OrganizationSwitcherProps = CreateOrganizationMode & OrganizationProfileMode & { /** @@ -2015,9 +2144,7 @@ export type OrganizationSwitcherProps = CreateOrganizationMode & */ skipInvitationScreen?: boolean; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider(if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /* @@ -2027,6 +2154,7 @@ export type OrganizationSwitcherProps = CreateOrganizationMode & organizationProfileProps?: Pick; }; +/** @document */ export type OrganizationListProps = { /** * Full URL or path to navigate to after creating a new Organization. @@ -2046,9 +2174,7 @@ export type OrganizationListProps = { | ((organization: OrganizationResource) => string) | LooseExtractedParams>; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /** @@ -2076,15 +2202,14 @@ export type OrganizationListProps = { afterSelectPersonalUrl?: ((user: UserResource) => string) | LooseExtractedParams>; }; +/** @document */ export type WaitlistProps = { /** * Full URL or path to navigate to after join waitlist. */ afterJoinWaitlistUrl?: string; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvided (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /** @@ -2093,6 +2218,7 @@ export type WaitlistProps = { signInUrl?: string; }; +/** @document */ export type WaitlistModalProps = WaitlistProps & { /** * Function that returns the container element where portals should be rendered. @@ -2102,6 +2228,7 @@ export type WaitlistModalProps = WaitlistProps & { getContainer?: () => HTMLElement | null; }; +/** @document */ type PricingTableDefaultProps = { /** * The position of the CTA button. @@ -2123,6 +2250,7 @@ type PricingTableDefaultProps = { newSubscriptionRedirectUrl?: string; }; +/** @document */ type PricingTableBaseProps = { /** * The subscriber type to display plans for. @@ -2132,9 +2260,7 @@ type PricingTableBaseProps = { */ for?: ForPayerType; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /* @@ -2146,8 +2272,10 @@ type PricingTableBaseProps = { type PortalRoot = HTMLElement | null | undefined; +/** @document */ export type PricingTableProps = PricingTableBaseProps & PricingTableDefaultProps; +/** @document */ export type APIKeysProps = { /** * The number of API keys to show per page. @@ -2156,9 +2284,7 @@ export type APIKeysProps = { */ perPage?: number; /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /** @@ -2174,27 +2300,52 @@ export type APIKeysProps = { */ export type __experimental_ConfigureSSOProps = { /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvider (if one is provided) + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type GetAPIKeysParams = ClerkPaginationParams<{ + /** + * The user or organization ID to query API keys by. If not provided, defaults to the [Active Organization](!active-organization), then the current User. + */ subject?: string; + /** + * A search query to filter API keys by name. + */ query?: string; }>; +/** @document */ export type CreateAPIKeyParams = { + /** + * The name of the API key. + */ name: string; + /** + * The user or organization ID to associate the API key with. If not provided, defaults to the [Active Organization](!active-organization), then the current User. + */ subject?: string; + /** + * The number of seconds until the API key expires. Set to `null` or omit to create a key that never expires. + */ secondsUntilExpiration?: number; + /** + * The description of the API key. + */ description?: string; }; +/** @document */ export type RevokeAPIKeyParams = { + /** + * The ID of the API key to revoke. + */ apiKeyID: string; + /** + * The reason for revoking the API key. + */ revocationReason?: string; }; @@ -2202,6 +2353,9 @@ export type RevokeAPIKeyParams = { * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ export type __internal_CheckoutProps = { + /** + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. + */ appearance?: ClerkAppearanceTheme; planId?: string; planPeriod?: BillingSubscriptionPlanPeriod; @@ -2324,7 +2478,7 @@ export type __experimental_SubscriptionDetailsButtonProps = { export type OAuthConsentProps = { /** - * Customize the appearance of the component. + * 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](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. */ appearance?: ClerkAppearanceTheme; /** @@ -2388,20 +2542,19 @@ export type OAuthConsentProps = { /** @deprecated Use OAuthConsentProps instead. */ export type __internal_OAuthConsentProps = OAuthConsentProps; +/** @inline */ + export interface HandleEmailLinkVerificationParams { /** - * Full URL or path to navigate to after successful magic link verification - * on completed sign up or sign in on the same device. + * The full URL to navigate to after successful email link verification on completed sign-up or sign-in on the same device. */ redirectUrlComplete?: string; /** - * Full URL or path to navigate to after successful magic link verification - * on the same device, but not completed sign in or sign up. + * The full URL to navigate to after successful email link verification on the same device, but without completing sign-in or sign-up. */ redirectUrl?: string; /** - * Callback function to be executed after successful magic link - * verification on another device. + * Callback function to be executed after successful email link verification on another device. */ onVerifiedOnOtherDevice?: () => void; } @@ -2444,37 +2597,63 @@ export type SignUpButtonProps = (SignUpButtonPropsModal | ButtonPropsRedirect) & | 'oauthFlow' >; +/** @document */ export type TaskChooseOrganizationProps = { /** * Full URL or path to navigate to after successfully resolving all tasks */ redirectUrlComplete: string; + /** + * Customization options to fully match the Clerk components to your own brand. These options serve as overrides and will be merged with the global `appearance` configuration (if one is provided). See the [Appearance](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. + */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type TaskResetPasswordProps = { /** * Full URL or path to navigate to after successfully resolving all tasks */ redirectUrlComplete: string; + /** + * Customization options to fully match the Clerk components to your own brand. These options serve as overrides and will be merged with the global `appearance` configuration (if one is provided). See the [Appearance](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. + */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type TaskSetupMFAProps = { /** * Full URL or path to navigate to after successfully resolving all tasks */ redirectUrlComplete: string; + /** + * Customization options to fully match the Clerk components to your own brand. These options serve as overrides and will be merged with the global `appearance` configuration (if one is provided). See the [Appearance](https://clerk.com/docs/guides/customizing-clerk/appearance-prop/overview) docs for more information. + */ appearance?: ClerkAppearanceTheme; }; +/** @document */ export type CreateOrganizationInvitationParams = { + /** + * The email address of the user to invite. + */ emailAddress: string; + /** + * The role of the user to invite. + */ role: OrganizationCustomRoleKey; }; +/** @document */ export type CreateBulkOrganizationInvitationParams = { + /** + * The email addresses of the users to invite. + */ emailAddresses: string[]; + /** + * The role of the users to invite. + */ role: OrganizationCustomRoleKey; }; @@ -2492,60 +2671,175 @@ export interface CreateOrganizationParams { slug?: string; } +/** @document */ export interface ClerkAuthenticateWithWeb3Params { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * The strategy to use for the sign-in flow. + */ strategy: Web3Strategy; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; + /** + * The URL to navigate to if [second factor](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) is required. + */ secondFactorUrl?: string; + /** + * The name of the wallet to use for authentication. + */ walletName?: string; } +/** @document */ export interface AuthenticateWithMetamaskParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithCoinbaseWalletParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithOKXWalletParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithGoogleOneTapParams { + /** + * The Google credential token from the Google Identity Services response. + */ token: string; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithBaseParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; } +/** @document */ export interface AuthenticateWithSolanaParams { + /** + * A function that overrides Clerk's default navigation behavior, allowing custom handling of navigation during sign-up and sign-in flows. + */ customNavigate?: (to: string) => Promise; + /** + * The full URL or path to navigate to after a successful sign-in or sign-up. + */ redirectUrl?: string; + /** + * The URL to navigate to if the sign-up process is missing user information. + */ signUpContinueUrl?: string; + /** + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created `User` object. + */ unsafeMetadata?: SignUpUnsafeMetadata; + /** + * A boolean indicating whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + */ legalAccepted?: boolean; + /** + * The name of the Solana wallet to use for authentication. + */ walletName: string; } @@ -2557,7 +2851,15 @@ export interface BrowserClerkConstructor { new (publishableKey: string, options?: DomainOrProxyUrl): BrowserClerk; } +/** + * Browser `Clerk` instance after `@clerk/clerk-js` loads. Extends [`Clerk`](https://clerk.com/docs/reference/objects/clerk) with `load()` and related browser-only APIs. + */ export interface HeadlessBrowserClerk extends Clerk { + /** + * Initializes the `Clerk` object and loads all necessary environment configuration and instance settings from the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }}. + * + * When using the JavaScript SDK, you must call the `load()` method before using the `Clerk` object in your code. Refer to the [quickstart guide](https://clerk.com/docs/js-frontend/getting-started/quickstart) for more information. + */ load: (opts?: Without) => Promise; updateClient: (client: ClientResource) => void; } diff --git a/packages/shared/src/types/client.ts b/packages/shared/src/types/client.ts index 1c89ed554aa..ff7d1456abc 100644 --- a/packages/shared/src/types/client.ts +++ b/packages/shared/src/types/client.ts @@ -5,27 +5,102 @@ import type { SignInResource } from './signIn'; import type { SignUpResource } from './signUp'; import type { ClientJSONSnapshot } from './snapshots'; +/** + * The `Client` object keeps track of the authenticated sessions in the current device. The device can be a browser, a native application, or any other medium that is usually the requesting part in a request/response architecture. + * + * The `Client` object also holds information about any sign-in or sign-up attempts that might be in progress, tracking the sign-in or sign-up progress. + */ export interface ClientResource extends ClerkResource { + /** + * A list of sessions that have been created on this client. + */ sessions: SessionResource[]; + /** + * A list of sessions on this client where the user has completed the full sign-in flow. Sessions can be in one of the following states: + *
    + *
  • `"active"`: The user has completed the full sign-in flow and all pending tasks.
  • + *
  • `"pending"`: The user has completed the sign-in flow but still needs to complete one or more required steps (**pending tasks**).
  • + *
+ */ signedInSessions: SignedInSessionResource[]; + /** + * The current sign up attempt, or `null` if there is none. + */ signUp: SignUpResource; + /** + * The current sign in attempt, or `null` if there is none. + */ signIn: SignInResource; + /** + * Returns `true` if this client hasn't been saved (created) yet in the Frontend API. Returns `false` otherwise. + */ isNew: () => boolean; + /** + * Creates a new client for the current instance along with its cookie. + */ create: () => Promise; + /** + * Deletes the client. All sessions will be reset. + */ destroy: () => Promise; + /** + * Removes all sessions created on the client. + */ removeSessions: () => Promise; + /** + * Clears any locally cached session data for the current client. + */ clearCache: () => void; + /** + * Resets the current sign-in attempt. Clears the in-progress sign-in state on the client. + */ resetSignIn: () => void; + /** + * Resets the current sign-up attempt. Clears the in-progress sign-up state on the client. + */ resetSignUp: () => void; + /** + * Returns `true` if the client cookie is due to expire in 8 days or less. Returns `false` otherwise. + */ isEligibleForTouch: () => boolean; + /** + * Builds a URL that refreshes the current client's authentication state and then redirects the user to the specified URL. + * + * @param params - The URL to redirect the user to. + */ buildTouchUrl: (params: { redirectUrl: URL }) => string; + /** + * The ID of the last active [`Session`](https://clerk.com/docs/reference/objects/session) on this client. + */ lastActiveSessionId: string | null; - /** Last authentication strategy used by this client; `null` when unknown or feature disabled. */ + /** + * The last authentication strategy used by this client; `null` when unknown or feature disabled. + */ lastAuthenticationStrategy: LastAuthenticationStrategy | null; + /** + * Indicates whether CAPTCHA checks are skipped for this client. + */ captchaBypass: boolean; + /** + * The date and time when the client's authentication cookie will expire. + */ cookieExpiresAt: Date | null; + /** + * The date and time when the client was created. + */ createdAt: Date | null; + /** + * The date and time when the client was last updated. + */ updatedAt: Date | null; + /** + * Sends a CAPTCHA token to the client. + * @internal + */ __internal_sendCaptchaToken: (params: unknown) => Promise; + /** + * Converts the client to a snapshot. + * @internal + */ __internal_toSnapshot: () => ClientJSONSnapshot; } diff --git a/packages/shared/src/types/factors.ts b/packages/shared/src/types/factors.ts index 04aa639e87b..7896b708380 100644 --- a/packages/shared/src/types/factors.ts +++ b/packages/shared/src/types/factors.ts @@ -15,169 +15,392 @@ import type { Web3Strategy, } from './strategies'; +/** @document */ export type EmailCodeFactor = { + /** + * The strategy type. + */ strategy: EmailCodeStrategy; + /** + * The ID of the email address used for the email code factor. + */ emailAddressId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type EmailLinkFactor = { + /** + * The strategy type. + */ strategy: EmailLinkStrategy; + /** + * The ID of the email address used for the email link factor. + */ emailAddressId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type PhoneCodeFactor = { + /** + * The strategy type. + */ strategy: PhoneCodeStrategy; + /** + * The ID of the phone number used for the phone code factor. + */ phoneNumberId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the phone number is set as the primary phone number, as multiple can be added to a user's profile. + */ primary?: boolean; + /** + * Indicates whether the phone number is set as the default identifier. + */ default?: boolean; + /** + * The channel used for the phone code factor. + */ channel?: PhoneCodeChannel; }; +/** @document */ export type Web3SignatureFactor = { + /** + * The strategy type. + */ strategy: Web3Strategy; + /** + * The ID of the Web3 Wallet. + */ web3WalletId: string; + /** + * Indicates whether the Web3 Wallet is set as the primary Web3 Wallet, as multiple can be added to a user's profile. + */ primary?: boolean; + /** + * The name of the Web3 Wallet. + */ walletName?: string; }; +/** @inline */ export type PasswordFactor = { strategy: PasswordStrategy; }; +/** @inline */ export type PasskeyFactor = { strategy: PasskeyStrategy; }; +/** @inline */ export type OauthFactor = { strategy: OAuthStrategy; }; +/** @document */ export type EnterpriseSSOFactor = { + /** + * The strategy type. + */ strategy: EnterpriseSSOStrategy; /** + * The ID of the enterprise connection. * @experimental */ enterpriseConnectionId?: string; /** + * The name of the enterprise connection. * @experimental */ enterpriseConnectionName?: string; }; +/** @inline */ export type TOTPFactor = { strategy: TOTPStrategy; }; +/** @inline */ export type BackupCodeFactor = { strategy: BackupCodeStrategy; }; +/** @document */ export type ResetPasswordPhoneCodeFactor = { + /** + * The strategy type. + */ strategy: ResetPasswordPhoneCodeStrategy; + /** + * The ID of the phone number used for the reset password phone code factor. + */ phoneNumberId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the phone number is set as the primary phone number, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type ResetPasswordEmailCodeFactor = { + /** + * The strategy type. + */ strategy: ResetPasswordEmailCodeStrategy; + /** + * The ID of the email address used for the reset password email code factor. + */ emailAddressId: string; + /** + * The identifier provided by the user, but masked for security reasons. + */ safeIdentifier: string; + /** + * Indicates whether the email address is set as the primary email address, as multiple can be added to a user's profile. + */ primary?: boolean; }; +/** @document */ export type ResetPasswordCodeFactor = ResetPasswordEmailCodeFactor | ResetPasswordPhoneCodeFactor; +/** @document */ export type ResetPasswordPhoneCodeFactorConfig = Omit; +/** @document */ export type ResetPasswordEmailCodeFactorConfig = Omit; +/** @document */ export type EmailCodeConfig = Omit; +/** @document */ export type EmailLinkConfig = Omit & { + /** + * The URL to redirect to after the email link is clicked. + */ redirectUrl: string; }; +/** @document */ export type PhoneCodeConfig = Omit; +/** @document */ export type Web3SignatureConfig = Web3SignatureFactor; +/** @inline */ export type PassKeyConfig = PasskeyFactor; + +/** @document */ export type OAuthConfig = OauthFactor & { + /** + * The URL to redirect to after the OAuth flow is completed. + */ redirectUrl: string; + /** + * + */ actionCompleteRedirectUrl: string; + /** + * The OIDC prompt parameter to use for the OAuth flow. + */ oidcPrompt?: string; + /** + * The OIDC login hint parameter to use for the OAuth flow. + */ oidcLoginHint?: string; }; +/** @document */ export type EnterpriseSSOConfig = EnterpriseSSOFactor & { + /** + * The URL to redirect to after the OAuth flow is completed. + */ redirectUrl: string; + /** + * + */ actionCompleteRedirectUrl: string; + /** + * The OIDC prompt parameter to use for the OAuth flow. + */ oidcPrompt?: string; /** + * The ID of the email address used for the enterprise SSO factor. * @experimental */ emailAddressId?: string; /** + * The ID of the enterprise connection used for the enterprise SSO factor. * @experimental */ enterpriseConnectionId?: string; }; +/** @document */ export type PhoneCodeSecondFactorConfig = { + /** + * The strategy type. + */ strategy: PhoneCodeStrategy; + /** + * The ID of the phone number used for the phone code second factor. + */ phoneNumberId?: string; }; +/** @document */ export type EmailCodeSecondFactorConfig = { + /** + * The strategy type. + */ strategy: EmailCodeStrategy; + /** + * The ID of the email address used for the email code second factor. + */ emailAddressId?: string; }; +/** + * @document + */ export type EmailCodeAttempt = { + /** + * The strategy type. + */ strategy: EmailCodeStrategy; + /** + * The one-time code sent to the user's email. + */ code: string; }; +/** + * @document + */ export type PhoneCodeAttempt = { + /** + * The strategy type. + */ strategy: PhoneCodeStrategy; + /** + * The one-time code sent via SMS. + */ code: string; }; +/** + * @document + */ export type PasswordAttempt = { + /** + * The strategy type. + */ strategy: PasswordStrategy; + /** + * The user's password. + */ password: string; }; +/** + * @document + */ export type PasskeyAttempt = { + /** + * The strategy type. + */ strategy: PasskeyStrategy; + /** + * The Web Authentication assertion returned by the browser. + */ publicKeyCredential: PublicKeyCredentialWithAuthenticatorAssertionResponse; }; +/** @document */ export type Web3Attempt = { + /** + * The strategy type. + */ strategy: Web3Strategy; + /** + * The signature of the Web3 transaction. + */ signature: string; }; +/** @document */ export type TOTPAttempt = { + /** + * The strategy type. + */ strategy: TOTPStrategy; + /** + * The code generated by the authenticator app. + */ code: string; }; +/** @document */ export type BackupCodeAttempt = { + /** + * The strategy type. + */ strategy: BackupCodeStrategy; + /** + * The backup code. + */ code: string; }; +/** @document */ export type ResetPasswordPhoneCodeAttempt = { + /** + * The strategy type. + */ strategy: ResetPasswordPhoneCodeStrategy; + /** + * The one-time code sent via SMS. + */ code: string; + /** + * The password provided by the user. + */ password?: string; }; +/** @document */ export type ResetPasswordEmailCodeAttempt = { + /** + * The strategy type. + */ strategy: ResetPasswordEmailCodeStrategy; + /** + * The one-time code sent to the user's email. + */ code: string; + /** + * The password provided by the user. + */ password?: string; }; diff --git a/packages/shared/src/types/hooks.ts b/packages/shared/src/types/hooks.ts index 3de88e8ef40..3c0bae1542c 100644 --- a/packages/shared/src/types/hooks.ts +++ b/packages/shared/src/types/hooks.ts @@ -123,7 +123,7 @@ export type UseAuthReturn = export type UseSignInReturn = | { /** - * A boolean that indicates whether Clerk has completed initialization. Initially `false`, becomes `true` once Clerk loads. + * A boolean that 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)). */ isLoaded: false; /** @@ -147,7 +147,7 @@ export type UseSignInReturn = export type UseSignUpReturn = | { /** - * A boolean that indicates whether Clerk has completed initialization. Initially `false`, becomes `true` once Clerk loads. + * A boolean that 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)). */ isLoaded: false; /** @@ -200,7 +200,7 @@ export type UseSessionReturn = export type UseSessionListReturn = | { /** - * A boolean that indicates whether Clerk has completed initialization. Initially `false`, becomes `true` once Clerk loads. + * A boolean that 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)). */ isLoaded: false; /** diff --git a/packages/shared/src/types/identifiers.ts b/packages/shared/src/types/identifiers.ts index 0b1802f7130..9e967d7d43d 100644 --- a/packages/shared/src/types/identifiers.ts +++ b/packages/shared/src/types/identifiers.ts @@ -1,5 +1,10 @@ +/** @inline */ export type UsernameIdentifier = 'username'; +/** @inline */ export type EmailAddressIdentifier = 'email_address'; +/** @inline */ export type PhoneNumberIdentifier = 'phone_number'; +/** @inline */ export type Web3WalletIdentifier = 'web3_wallet'; +/** @inline */ export type EmailAddressOrPhoneNumberIdentifier = 'email_address_or_phone_number'; diff --git a/packages/shared/src/types/image.ts b/packages/shared/src/types/image.ts index 51b3c77fb61..b0ddd6e94be 100644 --- a/packages/shared/src/types/image.ts +++ b/packages/shared/src/types/image.ts @@ -1,7 +1,17 @@ import type { ClerkResource } from './resource'; +/** Represents information about an image. */ export interface ImageResource extends ClerkResource { + /** + * The unique identifier of the image. + */ id?: string; + /** + * The name of the image. + */ name: string | null; + /** + * The public URL of the image. + */ publicUrl: string | null; } diff --git a/packages/shared/src/types/instance.ts b/packages/shared/src/types/instance.ts index 617799635de..921926dd885 100644 --- a/packages/shared/src/types/instance.ts +++ b/packages/shared/src/types/instance.ts @@ -1 +1,4 @@ +/** + * @inline + */ export type InstanceType = 'production' | 'development'; diff --git a/packages/shared/src/types/multiDomain.ts b/packages/shared/src/types/multiDomain.ts index 3661e7132bf..de7bf6fe432 100644 --- a/packages/shared/src/types/multiDomain.ts +++ b/packages/shared/src/types/multiDomain.ts @@ -60,13 +60,16 @@ export type MultiDomainAndOrProxyPrimitives = domain?: never; }; +/** + * Only one of the two properties are allowed to be set at a time. + */ export type DomainOrProxyUrl = { /** - * **Required for applications that run behind a reverse proxy**. The URL that Clerk will proxy requests to. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`). + * **Required for applications that run behind a reverse proxy**. The URL that Clerk will proxy requests to. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`), or a function that will be called with a `URL` made from `window.location.href`. */ proxyUrl?: string | ((url: URL) => string); /** - * **Required if your application is a satellite application**. Sets the domain of the satellite application. + * **Required if your application is a satellite application**. Sets the domain of the satellite application. Can be either a relative path (`/__clerk`) or a full URL (`https:///__clerk`), or a function that will be called with a `URL` made from `window.location.href`. */ domain?: string | ((url: URL) => string); }; diff --git a/packages/shared/src/types/oauth.ts b/packages/shared/src/types/oauth.ts index cf20b674495..83cd8c30e41 100644 --- a/packages/shared/src/types/oauth.ts +++ b/packages/shared/src/types/oauth.ts @@ -9,37 +9,68 @@ export interface OAuthProviderData { docsUrl: string; } +/** @inline */ export type FacebookOauthProvider = 'facebook'; +/** @inline */ export type GoogleOauthProvider = 'google'; +/** @inline */ export type HubspotOauthProvider = 'hubspot'; +/** @inline */ export type GithubOauthProvider = 'github'; +/** @inline */ export type TiktokOauthProvider = 'tiktok'; +/** @inline */ export type GitlabOauthProvider = 'gitlab'; +/** @inline */ export type DiscordOauthProvider = 'discord'; +/** @inline */ export type TwitterOauthProvider = 'twitter'; +/** @inline */ export type TwitchOauthProvider = 'twitch'; +/** @inline */ export type LinkedinOauthProvider = 'linkedin'; +/** @inline */ export type LinkedinOIDCOauthProvider = 'linkedin_oidc'; +/** @inline */ export type DropboxOauthProvider = 'dropbox'; +/** @inline */ export type AtlassianOauthProvider = 'atlassian'; +/** @inline */ export type BitbucketOauthProvider = 'bitbucket'; +/** @inline */ export type MicrosoftOauthProvider = 'microsoft'; +/** @inline */ export type NotionOauthProvider = 'notion'; +/** @inline */ export type AppleOauthProvider = 'apple'; +/** @inline */ export type LineOauthProvider = 'line'; +/** @inline */ export type InstagramOauthProvider = 'instagram'; +/** @inline */ export type CoinbaseOauthProvider = 'coinbase'; +/** @inline */ export type SpotifyOauthProvider = 'spotify'; +/** @inline */ export type XeroOauthProvider = 'xero'; +/** @inline */ export type BoxOauthProvider = 'box'; +/** @inline */ export type SlackOauthProvider = 'slack'; +/** @inline */ export type LinearOauthProvider = 'linear'; +/** @inline */ export type XOauthProvider = 'x'; +/** @inline */ export type EnstallOauthProvider = 'enstall'; +/** @inline */ export type HuggingfaceOAuthProvider = 'huggingface'; +/** @inline */ export type VercelOauthProvider = 'vercel'; +/** @inline */ export type CustomOauthProvider = `custom_${string}`; +/** Represents the available OAuth providers. */ export type OAuthProvider = | FacebookOauthProvider | GoogleOauthProvider diff --git a/packages/shared/src/types/organization.ts b/packages/shared/src/types/organization.ts index 98ced75e217..00107ff1cd3 100644 --- a/packages/shared/src/types/organization.ts +++ b/packages/shared/src/types/organization.ts @@ -36,90 +36,260 @@ declare global { * @interface */ export interface OrganizationResource extends ClerkResource, BillingPayerMethods { + /** + * The unique identifier for the Organization. + */ id: string; + /** + * The name of the Organization. + */ name: string; + /** + * The URL-friendly identifier of the Organization. If supplied, it must be unique for the instance. + */ slug: string | null; + /** + * Holds the Organization's logo. Compatible with Clerk's [Image Optimization](https://clerk.com/docs/guides/development/image-optimization). + */ imageUrl: string; + /** + * Whether the Organization has an image. + */ hasImage: boolean; + /** + * The number of members in the Organization. + */ membersCount: number; + /** + * The number of pending invitations in the Organization. + */ pendingInvitationsCount: number; + /** + * Metadata that can be read from both the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }} and [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. Once the user accepts the invitation and signs up, these metadata will end up in the user's public metadata. + */ publicMetadata: OrganizationPublicMetadata; + /** + * Whether the Organization allows admins to delete users. + */ adminDeleteEnabled: boolean; + /** + * The maximum number of memberships allowed in the Organization. + */ maxAllowedMemberships: number; + /** + * The date when the Organization was first created. + */ createdAt: Date; + /** + * The date when the Organization was last updated. + */ updatedAt: Date; + /** + * Updates the current Organization. + */ update: (params: UpdateOrganizationParams) => Promise; + /** + * Gets the list of Organization Memberships. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) objects. + */ getMemberships: GetMemberships; + + /** + * Gets the list of invitations. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationInvitationResource`](https://clerk.com/docs/reference/types/organization-invitation) objects. + */ getInvitations: (params?: GetInvitationsParams) => Promise>; + /** + * Gets the list of [Roles](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) available. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`RoleResource`](https://clerk.com/docs/reference/types/role) objects and a `has_role_set_migration` status. + * + * When `has_role_set_migration` is `true`, updating Organization membership Roles is not allowed. Learn how to [build a custom flow for managing member Roles in an Organization](/docs/guides/development/custom-flows/organizations/manage-roles). + */ getRoles: (params?: GetRolesParams) => Promise; + /** + * Gets the list of domains. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) objects. + * + * > [!WARNING] + * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + */ getDomains: (params?: GetDomainsParams) => Promise>; + /** + * Gets the list of membership requests. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipRequestResource`](https://clerk.com/docs/reference/types/organization-membership-request) objects. + * + * > [!WARNING] + * > You must have [**Verified domains** and **Automatic suggestion**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + */ getMembershipRequests: ( params?: GetMembershipRequestParams, ) => Promise>; + /** + * Adds a user as a member to an organization. A user can only be added to an organization if they are not already a member of it and if they already exist in the same instance as the organization. Only administrators can add members to an organization. + * @returns An [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) object. + */ addMember: (params: AddMemberParams) => Promise; + /** + * Creates and sends an invitation to the given email address. + * @returns An [`OrganizationInvitationResource`](https://clerk.com/docs/reference/types/organization-invitation) object. + */ inviteMember: (params: InviteMemberParams) => Promise; + /** + * Creates and sends invitations to the given email addresses. + * @returns An array of [`OrganizationInvitationResource`](https://clerk.com/docs/reference/types/organization-invitation) objects. + */ inviteMembers: (params: InviteMembersParams) => Promise; + /** + * Updates a given member. + * @returns An [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) object. + */ updateMember: (params: UpdateMembershipParams) => Promise; + /** + * Removes a member. + * @returns An [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) object. + * @param userId - The unique identifier of the user to remove. + */ removeMember: (userId: string) => Promise; + /** + * Creates a new domain. + * @returns An [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) object. + * + * > [!WARNING] + * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + * @param domainName - The name of the domain to create. + */ createDomain: (domainName: string) => Promise; + /** + * Gets a domain for an Organization based on the given domain ID. + * @returns An [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain) object. + * + * > [!WARNING] + * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + * @param domainId - The unique identifier of the domain to get. + */ getDomain: ({ domainId }: { domainId: string }) => Promise; + /** + * Deletes the Organization. Only administrators can delete an Organization. + * + * Deleting an Organization will also delete all memberships and invitations. **This is not reversible.** + */ destroy: () => Promise; setLogo: (params: SetOrganizationLogoParams) => Promise; __internal_toSnapshot: () => OrganizationJSONSnapshot; } +/** @document */ export type GetRolesParams = ClerkPaginationParams; export interface GetRolesResponse extends ClerkPaginatedResponse { has_role_set_migration?: boolean; } +/** @document */ export type GetMembersParams = ClerkPaginationParams<{ + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) to filter the users by. + */ role?: OrganizationCustomRoleKey[]; + /** + * The query to filter the users by. + */ query?: string; }>; +/** @document */ export type GetDomainsParams = ClerkPaginationParams<{ + /** + * The [enrollment mode](https://clerk.com/docs/guides/organizations/add-members/verified-domains#enable-verified-domains) will decide how new users join an organization. + */ enrollmentMode?: OrganizationEnrollmentMode; }>; +/** @document */ export type GetInvitationsParams = ClerkPaginationParams<{ + /** + * The status of the invitations to get. + */ status?: OrganizationInvitationStatus[]; }>; +/** @document */ export type GetMembershipRequestParams = ClerkPaginationParams<{ + /** + * The status of the membership requests to get. + */ status?: OrganizationInvitationStatus; }>; +/** @document */ export interface AddMemberParams { + /** + * The unique identifier of the user to add as a member. + */ userId: string; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the user. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface InviteMemberParams { + /** + * The email address of the user to invite. + */ emailAddress: string; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the user. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface InviteMembersParams { + /** + * The email addresses of the users to invite. + */ emailAddresses: string[]; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the users. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface UpdateMembershipParams { + /** + * The unique identifier of the user to update. + */ userId: string; + /** + * The [Role](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions) that will be assigned to the user. + */ role: OrganizationCustomRoleKey; } +/** @document */ export interface UpdateOrganizationParams { + /** + * The name of the Organization. + */ name: string; + /** + * The URL-friendly identifier of the Organization. If supplied, it must be unique for the instance. + */ slug?: string; } +/** @document */ export interface SetOrganizationLogoParams { + /** + * The file to set as the Organization's logo. + */ file: Blob | File | string | null; } +/** @document */ export type GetMemberships = ( params?: GetMembersParams, ) => Promise>; diff --git a/packages/shared/src/types/organizationDomain.ts b/packages/shared/src/types/organizationDomain.ts index 86b845a1aa7..c64f4e196ba 100644 --- a/packages/shared/src/types/organizationDomain.ts +++ b/packages/shared/src/types/organizationDomain.ts @@ -7,14 +7,10 @@ export interface OrganizationDomainVerification { expiresAt: Date; } -/** - * @inline - */ +/** @inline */ export type OrganizationDomainVerificationStatus = 'unverified' | 'verified'; -/** - * @inline - */ +/** @inline */ export type OrganizationEnrollmentMode = 'manual_invitation' | 'automatic_invitation' | 'automatic_suggestion'; /** diff --git a/packages/shared/src/types/organizationInvitation.ts b/packages/shared/src/types/organizationInvitation.ts index 61581a28e37..0027efc929b 100644 --- a/packages/shared/src/types/organizationInvitation.ts +++ b/packages/shared/src/types/organizationInvitation.ts @@ -34,7 +34,5 @@ export interface OrganizationInvitationResource extends ClerkResource { revoke: () => Promise; } -/** - * @inline - */ +/** @inline */ export type OrganizationInvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired'; diff --git a/packages/shared/src/types/phoneCodeChannel.ts b/packages/shared/src/types/phoneCodeChannel.ts index 516e9e57324..e75163d79c6 100644 --- a/packages/shared/src/types/phoneCodeChannel.ts +++ b/packages/shared/src/types/phoneCodeChannel.ts @@ -3,8 +3,13 @@ export interface PhoneCodeChannelData { name: string; } +/** @inline */ export type PhoneCodeSMSChannel = 'sms'; +/** @inline */ export type PhoneCodeWhatsAppChannel = 'whatsapp'; +/** @inline */ export type PhoneCodeChannel = PhoneCodeSMSChannel | PhoneCodeWhatsAppChannel; + +/** @inline */ export type PhoneCodeProvider = PhoneCodeChannel; diff --git a/packages/shared/src/types/redirects.ts b/packages/shared/src/types/redirects.ts index bafcdf1db63..8788da83bb0 100644 --- a/packages/shared/src/types/redirects.ts +++ b/packages/shared/src/types/redirects.ts @@ -1,12 +1,14 @@ import type { EnterpriseSSOStrategy, OAuthStrategy } from './strategies'; +/** @document */ export type AfterSignOutUrl = { /** - * Full URL or path to navigate to after successful sign out. + * The full URL or path to navigate to after successful sign out. */ afterSignOutUrl?: string | null; }; +/** @document */ export type AfterMultiSessionSingleSignOutUrl = { /** * The full URL or path to navigate to after signing out the current user is complete. @@ -17,7 +19,6 @@ export type AfterMultiSessionSingleSignOutUrl = { /** * Redirect URLs for different actions. - * Mainly used to be used to type internal Clerk functions. */ export type RedirectOptions = SignInForceRedirectUrl & SignInFallbackRedirectUrl & @@ -25,6 +26,7 @@ export type RedirectOptions = SignInForceRedirectUrl & SignUpFallbackRedirectUrl & RedirectUrlProp; +/** @document */ export type AuthenticateWithRedirectParams = { /** * The full URL or path to the route that will complete the OAuth or SAML flow. @@ -82,6 +84,7 @@ export type AuthenticateWithRedirectParams = { export type AuthenticateWithPopupParams = AuthenticateWithRedirectParams & { popup: Window | null }; +/** @document */ export type RedirectUrlProp = { /** * Full URL or path to navigate to after a successful action. @@ -89,13 +92,15 @@ export type RedirectUrlProp = { redirectUrl?: string | null; }; +/** @document */ export type SignUpForceRedirectUrl = { /** - * This URL will always be redirected to after the user signs up. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. + * If provided, this URL will always be redirected to after the user signs up. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. */ signUpForceRedirectUrl?: string | null; }; +/** @document */ export type SignUpFallbackRedirectUrl = { /** * The fallback URL to redirect to after the user signs up, if there's no `redirect_url` in the path already. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. @@ -105,6 +110,7 @@ export type SignUpFallbackRedirectUrl = { signUpFallbackRedirectUrl?: string | null; }; +/** @document */ export type SignInFallbackRedirectUrl = { /** * The fallback URL to redirect to after the user signs in, if there's no `redirect_url` in the path already. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. @@ -114,16 +120,18 @@ export type SignInFallbackRedirectUrl = { signInFallbackRedirectUrl?: string | null; }; +/** @document */ export type SignInForceRedirectUrl = { /** - * This URL will always be redirected to after the user signs in. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. + * If provided, this URL will always be redirected to after the user signs in. It's recommended to use the [environment variable](https://clerk.com/docs/guides/development/clerk-environment-variables#sign-in-and-sign-up-redirects) instead. */ signInForceRedirectUrl?: string | null; }; +/** @document */ export type NewSubscriptionRedirectUrl = { /** - * The URL to navigate to after the user completes the checkout and clicks the "Continue" button. + * The full URL or path to navigate to after the user completes the checkout and clicks the "Continue" button. */ newSubscriptionRedirectUrl?: string | null; }; diff --git a/packages/shared/src/types/resource.ts b/packages/shared/src/types/resource.ts index 140af2e7fe2..fa948bec0c9 100644 --- a/packages/shared/src/types/resource.ts +++ b/packages/shared/src/types/resource.ts @@ -1,4 +1,8 @@ +/** @document */ export type ClerkResourceReloadParams = { + /** + * A nonce to use for rotating the user's token. Used in native application OAuth flows to allow the native client to update its JWT once despite changes in its rotating token. + */ rotatingTokenNonce?: string; }; @@ -10,12 +14,9 @@ export interface ClerkResource { * The unique identifier of the resource. */ readonly id?: string | undefined; - /** - * The root path of the resource. - */ pathRoot: string; /** - * Reload the resource and return the resource itself. + * Reloads the resource, which is useful when you want to access the latest user data after performing a mutation. To make the updated data immediately available, this method forces a session token refresh instead of waiting for the automatic refresh cycle that could temporarily retain stale information. Learn more about [forcing a token refresh](https://clerk.com/docs/guides/sessions/force-token-refresh). */ reload(p?: ClerkResourceReloadParams): Promise; } diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index a512fc2baf9..430a798c3eb 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -100,6 +100,7 @@ export type CheckAuthorizationParamsWithCustomPermissions = WithReverification< | { role?: never; permission?: never; feature?: never; plan?: never } >; +/** @document */ export type CheckAuthorization = CheckAuthorizationFn; type CheckAuthorizationParams = WithReverification< @@ -217,49 +218,133 @@ export interface SessionResource extends ClerkResource { * The current state of the session. */ status: SessionStatus; + /** + * The date and time when the session will expire. + */ expireAt: Date; + /** + * The date and time when the session was abandoned by the user. + */ abandonAt: Date; /** * An array where each item represents the number of minutes since the last verification of a first or second factor: `[firstFactorAge, secondFactorAge]`. */ factorVerificationAge: [firstFactorAge: number, secondFactorAge: number] | null; + /** + * The token that was last used to authenticate the session. + */ lastActiveToken: TokenResource | null; + /** + * The ID of the last [Active Organization](!active-organization). + */ lastActiveOrganizationId: string | null; + /** + * The date and time when the session was last active on the [`Client`](https://clerk.com/docs/reference/objects/client). + */ lastActiveAt: Date; + /** + * The JWT actor for the session. Holds identifier for the user that is impersonating the current user. Read more about [impersonation](https://clerk.com/docs/guides/users/impersonation). + */ actor: ActClaim | null; + /** + * When the session's actor claim has `type: 'agent'`, this property exposes information about the agent and [Agent Task](https://clerk.com/docs/reference/objects/agent-task) that was used to create the session. + */ agent: AgentActClaim | null; + /** + * The user's pending [session tasks](https://clerk.com/docs/guides/configure/session-tasks). + */ tasks: Array | null; + /** + * The user's current pending [session task](https://clerk.com/docs/guides/configure/session-tasks). + */ currentTask?: SessionTask; /** - * The user associated with the session. + * The [`User`](https://clerk.com/docs/reference/objects/user) associated with the session. */ user: UserResource | null; + /** + * Publicly available information about the current [`User`](https://clerk.com/docs/reference/objects/user). + */ publicUserData: PublicUserData; + /** * Marks the session as ended. The session will no longer be active for this `Client` and its status will become **ended**. */ end: () => Promise; + /** + * Invalidates the current session by marking it as removed. Once removed, the session will be deactivated for the current Client instance and its `status` will be set to `removed`. This operation cannot be undone. + */ remove: () => Promise; + /** + * Updates the session's last active timestamp to the current time. This method should be called periodically to indicate ongoing user activity and prevent the session from becoming stale. The updated timestamp is used for session management and analytics purposes. + */ touch: (params?: SessionTouchParams) => Promise; + /** + * Gets the current user's [session token](https://clerk.com/docs/guides/sessions/session-tokens) or a [custom JWT template](https://clerk.com/docs/guides/sessions/jwt-templates). + * + * This method uses a cache so a network request will only be made if the token in memory has expired. The TTL for a Clerk token is one minute. It retries on transient failures (e.g. network errors); when the browser is offline and retries are exhausted, it throws `ClerkOfflineError`. + * + * Tokens can only be generated if the user is signed in. + */ getToken: GetToken; + /** + * Checks if the user is [authorized for the specified Role, Permission, Feature, or Plan](https://clerk.com/docs/guides/secure/authorization-checks) or requires the user to [reverify their credentials](https://clerk.com/docs/guides/secure/reverification) if their last verification is older than allowed. + * @skipParametersSection + */ checkAuthorization: CheckAuthorization; + /** + * Clears the cache for the current session. This is useful if the session has been updated and the cache is no longer valid. + */ clearCache: () => void; + /** + * The date and time when the session was first created. + */ createdAt: Date; + /** + * The date and time when the session was last updated. + */ updatedAt: Date; - + /** + * Initiates the reverification flow. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ startVerification: (params: SessionVerifyCreateParams) => Promise; + /** + * Initiates the [first factor verification](!first-factor-verification) process. This is a required step to complete a reverification flow when using a preparable factor. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection + */ prepareFirstFactorVerification: ( factor: SessionVerifyPrepareFirstFactorParams, ) => Promise; + /** + * Attempts to complete the [first factor verification](!first-factor-verification) process. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection + */ attemptFirstFactorVerification: ( attemptFactor: SessionVerifyAttemptFirstFactorParams, ) => Promise; + /** + * Initiates the [second factor verification](!second-factor-verification) process. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection + */ prepareSecondFactorVerification: ( params: SessionVerifyPrepareSecondFactorParams, ) => Promise; + /** + * Attempts to complete the [second factor verification](!second-factor-verification) process. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + * @skipParametersSection + */ attemptSecondFactorVerification: ( params: SessionVerifyAttemptSecondFactorParams, ) => Promise; + /** + * Initiates a verification flow using passkeys. + * @returns A [`SessionVerification`](https://clerk.com/docs/reference/types/session-verification) instance with its status and supported factors. + */ verifyWithPasskey: () => Promise; __internal_toSnapshot: () => SessionJSONSnapshot; __internal_touch: (params?: SessionTouchParams) => Promise; @@ -312,30 +397,89 @@ export interface SessionActivity { isMobile?: boolean; } +/** + * The current state of the session. + */ export type SessionStatus = + /** + * The session was abandoned client-side. + */ | 'abandoned' + /** + * The session is valid and all activity is allowed. + */ | 'active' + /** + * The user signed out of the session, but the [`Session`](https://clerk.com/docs/reference/objects/session) remains in the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'ended' + /** + * The period of allowed activity for this session has passed. + */ | 'expired' + /** + * The user signed out of the session and the [`Session`](https://clerk.com/docs/reference/objects/session) was removed from the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'removed' + /** + * The session has been replaced by another one, but the previous [`Session`](https://clerk.com/docs/reference/objects/session) remains in the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'replaced' + /** + * The application ended the session and the [`Session`](https://clerk.com/docs/reference/objects/session) was removed from the [`Client`](https://clerk.com/docs/reference/objects/client). + */ | 'revoked' + /** + * The user has signed in but hasn't completed [session tasks](https://clerk.com/docs/guides/configure/session-tasks). + */ | 'pending'; +/** @inline */ export type SessionTouchIntent = 'focus' | 'select_session' | 'select_org'; +/** @document */ export type SessionTouchParams = { + /** + * The intent of the touch operation. + */ intent?: SessionTouchIntent; }; +/** + * Information about the user that's publicly available. + */ export interface PublicUserData { + /** + * The user's first name. + */ firstName: string | null; + /** + * The user's last name. + */ lastName: string | null; + /** + * Holds the default avatar or user's uploaded profile image. Compatible with Clerk's [Image Optimization](https://clerk.com/docs/guides/development/image-optimization). + */ imageUrl: string; + /** + * Indicates whether the user has a profile picture. + */ hasImage: boolean; + /** + * The user's identifier, such as their email address or phone number. Can be used for user identification. + */ identifier: string; + /** + * The user's unique identifier. + */ userId?: string; + /** + * The user's username. + */ username?: string; + /** + * Indicates whether the user is banned. + */ banned?: boolean; } @@ -349,9 +493,19 @@ export interface SessionTask { key: 'choose-organization' | 'reset-password' | 'setup-mfa'; } +/** @document */ export type GetTokenOptions = { + /** + * The Organization associated with the generated session token. _Does not modify the session's currently [Active Organization](!active-organization)._ + */ organizationId?: string; + /** + * Whether to skip the cache lookup and force a call to the server instead, even within the TTL. Useful if the token claims are time-sensitive or depend on data that can be updated (e.g. user fields). Defaults to `false`. + */ skipCache?: boolean; + /** + * The name of the JWT template from the [Clerk Dashboard](https://dashboard.clerk.com/~/jwt-templates) to generate a new token from. E.g. 'firebase', 'grafbase', or your custom template's name. + */ template?: string; }; /** @@ -359,6 +513,7 @@ export type GetTokenOptions = { */ export type GetToken = (options?: GetTokenOptions) => Promise; +/** @document */ export type SessionVerifyCreateParams = { level: SessionVerificationLevel; }; @@ -371,6 +526,7 @@ export type SessionVerifyPrepareFirstFactorParams = * @experimental */ | Omit; + export type SessionVerifyAttemptFirstFactorParams = | EmailCodeAttempt | PhoneCodeAttempt diff --git a/packages/shared/src/types/signInFuture.ts b/packages/shared/src/types/signInFuture.ts index b320d3afcf7..06d9fb380c2 100644 --- a/packages/shared/src/types/signInFuture.ts +++ b/packages/shared/src/types/signInFuture.ts @@ -6,20 +6,18 @@ import type { OAuthStrategy, PasskeyStrategy, TicketStrategy, Web3Strategy } fro import type { VerificationResource } from './verification'; import type { Web3Provider } from './web3'; +/** @document */ export interface SignInFutureCreateParams { /** - * The authentication identifier for the sign-in. This can be the value of the user's email address, phone number, - * username, or Web3 wallet address. + * The authentication identifier for the sign-in. This can be the value of the user's email address, phone number, username, or Web3 wallet address. */ identifier?: string; /** - * The user's password. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. + * The user's password. Only supported if [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. */ password?: string; /** - * The first factor verification strategy to use in the sign-in flow. Depends on the `identifier` value. Each - * authentication identifier supports different verification strategies. + * The first factor verification strategy to use in the sign-in flow. Depends on the `identifier` value. Each authentication identifier supports different verification strategies. */ strategy?: OAuthStrategy | 'enterprise_sso' | PasskeyStrategy | TicketStrategy; /** @@ -27,28 +25,24 @@ export interface SignInFutureCreateParams { */ redirectUrl?: string; /** - * The URL that the user will be redirected to, after successful authorization from the OAuth provider and - * Clerk sign-in. + * The URL that the user will be redirected to, after successful authorization from the OAuth provider and Clerk sign-in. */ actionCompleteRedirectUrl?: string; /** - * When set to `true`, the `SignIn` will attempt to retrieve information from the active `SignUp` instance and use it - * to complete the sign-in process. This is useful when you want to seamlessly transition a user from a sign-up - * attempt to a sign-in attempt. + * When set to `true`, the `SignIn` will attempt to retrieve information from the active `SignUp` instance and use it to complete the sign-in process. This is useful when you want to seamlessly transition a user from a sign-up attempt to a sign-in attempt. */ transfer?: boolean; /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. **Required** if `strategy` is set to `'ticket'`. + * **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket?: string; /** - * When set to `true`, if a user does not exist, the sign-up will prepare a transfer to sign up a new - * account. If bot sign-up protection is enabled, captcha will also be required on sign in. + * When set to `true`, if a user does not exist, the sign-up will prepare a transfer to sign up a new account. If bot sign-up protection is enabled, captcha will also be required on sign in. */ signUpIfMissing?: boolean; } +/** Parameters for submitting a password to sign-in. */ export type SignInFuturePasswordParams = { /** * The user's password. Only supported if @@ -58,8 +52,7 @@ export type SignInFuturePasswordParams = { } & ( | { /** - * The authentication identifier for the sign-in. This can be the value of the user's email address, phone number, - * username, or Web3 wallet address. + * The authentication identifier for the sign-in (email address, phone number, username, or Web3 wallet address). Provide exactly one of `identifier`, `emailAddress`, or `phoneNumber`. Omit all when a sign-in already exists to use the current identifier. */ identifier: string; emailAddress?: never; @@ -67,8 +60,7 @@ export type SignInFuturePasswordParams = { } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled. Provide exactly one of `identifier`, `emailAddress`, or `phoneNumber`. */ emailAddress: string; identifier?: never; @@ -76,8 +68,7 @@ export type SignInFuturePasswordParams = { } | { /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Provide exactly one of `identifier`, `emailAddress`, or `phoneNumber`. */ phoneNumber: string; identifier?: never; @@ -90,6 +81,7 @@ export type SignInFuturePasswordParams = { } ); +/** Parameters for sending a sign-in email verification code. */ export type SignInFutureEmailCodeSendParams = | { /** @@ -107,6 +99,7 @@ export type SignInFutureEmailCodeSendParams = emailAddress?: never; }; +/** Parameters for sending a sign-in email link. */ export type SignInFutureEmailLinkSendParams = { /** * The full URL that the user will be redirected to when they visit the email link. @@ -115,21 +108,21 @@ export type SignInFutureEmailLinkSendParams = { } & ( | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. + * 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. + * The ID for the user's email address that will receive an email with the email link. Provide either `emailAddress` or `emailAddressId`, not both. Omit both when a sign-in already exists. */ emailAddressId?: string; emailAddress?: never; } ); +/** @document */ export interface SignInFutureEmailCodeVerifyParams { /** * The one-time code that was sent to the user. @@ -137,6 +130,7 @@ export interface SignInFutureEmailCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureResetPasswordSubmitParams { /** * The new password for the user. @@ -148,6 +142,7 @@ export interface SignInFutureResetPasswordSubmitParams { signOutOfOtherSessions?: boolean; } +/** @document */ export interface SignInFutureResetPasswordPhoneCodeSendParams { /** * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if @@ -156,6 +151,7 @@ export interface SignInFutureResetPasswordPhoneCodeSendParams { phoneNumber?: string; } +/** @document */ export type SignInFuturePhoneCodeSendParams = { /** * The mechanism to use to send the code to the provided phone number. Defaults to `'sms'`. @@ -164,21 +160,21 @@ export type SignInFuturePhoneCodeSendParams = { } & ( | { /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. Provide either `phoneNumber` or `phoneNumberId`, not both. Omit both when a sign-in already exists. */ phoneNumber?: string; phoneNumberId?: never; } | { /** - * The ID for the user's phone number that will receive a message with the one-time authentication code. + * The ID for the user's phone number that will receive a message with the one-time authentication code. Provide either `phoneNumber` or `phoneNumberId`, not both. Omit both when a sign-in already exists. */ phoneNumberId: string; phoneNumber?: never; } ); +/** @document */ export interface SignInFuturePhoneCodeVerifyParams { /** * The one-time code that was sent to the user. @@ -186,6 +182,7 @@ export interface SignInFuturePhoneCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureResetPasswordPhoneCodeVerifyParams { /** * The one-time code that was sent to the user. @@ -193,6 +190,7 @@ export interface SignInFutureResetPasswordPhoneCodeVerifyParams { code: string; } +/** @document */ export interface SignInFutureSSOParams { /** * The strategy to use for authentication. @@ -203,12 +201,12 @@ export interface SignInFutureSSOParams { */ redirectUrl: string; /** + * The URL to redirect to if a session was not created, and needs additional information. * TODO @revamp-hooks: This should be handled by FAPI instead. */ redirectCallbackUrl: string; /** - * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an - * OAuth provider. + * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an OAuth provider. * * @example * ```ts @@ -221,12 +219,11 @@ export interface SignInFutureSSOParams { */ popup?: Window; /** - * Optional for `oauth_` or `enterprise_sso` strategies. The value to pass to the - * [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) - * in the generated OAuth redirect URL. + * The value to pass to the [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) in the generated OAuth redirect URL. */ oidcPrompt?: string; /** + * The identifier of the enterprise connection to target when using the `enterprise_sso` strategy. * @experimental */ enterpriseConnectionId?: string; @@ -236,20 +233,23 @@ export interface SignInFutureSSOParams { identifier?: string; } +/** @document */ export interface SignInFutureMFAPhoneCodeVerifyParams { /** - * The one-time code that was sent to the user as part of the `signIn.mfa.sendPhoneCode()` method. + * The one-time code that was sent to the user. */ code: string; } +/** @document */ export interface SignInFutureMFAEmailCodeVerifyParams { /** - * The one-time code that was sent to the user as part of the `signIn.mfa.sendEmailCode()` method. + * The one-time code that was sent to the user. */ code: string; } +/** @document */ export interface SignInFutureTOTPVerifyParams { /** * The TOTP generated by the user's authenticator app. @@ -257,21 +257,23 @@ export interface SignInFutureTOTPVerifyParams { code: string; } +/** @document */ export interface SignInFutureBackupCodeVerifyParams { /** - * The backup code that was provided to the user when they set up two-step authentication. + * The backup code that was provided to the user when they set up backup codes. */ code: string; } +/** @document */ export interface SignInFutureTicketParams { /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. + * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket: string; } +/** @document */ export interface SignInFutureWeb3Params { /** * The verification strategy to validate the user's sign-in request. @@ -282,29 +284,34 @@ export interface SignInFutureWeb3Params { */ provider: Web3Provider; /** - * The name of the wallet to use for Solana sign-ins. Required when `provider` is set to `'solana'`. + * **Required** when `provider` is set to `'solana'`. The name of the wallet to use for Solana sign-ins. */ walletName?: string; } +/** @document */ export interface SignInFuturePasskeyParams { /** * The flow to use for the passkey sign-in. * - * - `'autofill'`: The client prompts your users to select a passkey before they interact with your app. - * - `'discoverable'`: The client requires the user to interact with the client. + *
    + *
  • `'autofill'`: The client prompts your users to select a passkey before they interact with your app.
  • + *
  • `'discoverable'`: The client requires the user to interact with the client.
  • + *
*/ flow?: 'autofill' | 'discoverable'; } +/** @document */ export interface SignInFutureFinalizeParams { + /** + * A custom navigation function to be called just before the session and/or Organization is set. When provided, it takes precedence over the `redirectUrl` parameter for navigation. The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. See the [section on using the `navigate()` parameter](https://clerk.com/docs/reference/objects/clerk#using-the-navigate-parameter) for more details. + */ navigate?: SetActiveNavigate; } /** - * The `SignInFuture` class holds the state of the current sign-in and provides helper methods to navigate and complete - * the sign-in process. It is used to manage the sign-in lifecycle, including the first and second factor verification, - * and the creation of a new session. + * The `SignInFuture` class holds the state of the current sign-in and provides helper methods to navigate and complete the sign-in process. It is used to manage the sign-in lifecycle, including the first and second factor verification, and the creation of a new session. */ export interface SignInFutureResource { /** @@ -313,58 +320,71 @@ export interface SignInFutureResource { readonly id?: string; /** - * Array of the first factors that are supported in the current sign-in. Each factor contains information about the - * verification strategy that can be used. + * Array of the first factors that are supported in the current sign-in. Each factor contains information about the verification strategy that can be used. */ readonly supportedFirstFactors: SignInFirstFactor[]; /** - * Array of the second factors that are supported in the current sign-in. Each factor contains information about the - * verification strategy that can be used. This property is populated only when the first factor is verified. + * Array of the second factors that are supported in the current sign-in. Each factor contains information about the verification strategy that can be used. This property is populated only when the first factor is verified. */ readonly supportedSecondFactors: SignInSecondFactor[]; /** * The current status of the sign-in. + *
    + *
  • `'complete'` - The sign-in process has been completed successfully.
  • + *
  • `'needs_client_trust'` - The user is signing in from a new device and must complete a [second factor verification](!second-factor-verification) to establish [Client Trust](https://clerk.com/docs/guides/secure/client-trust). See the [Client Trust custom flow guide](https://clerk.com/docs/guides/development/custom-flows/authentication/client-trust) for more information.
  • + *
  • `'needs_identifier'` - The user's identifier (e.g., email address, phone number, username) hasn't been provided.
  • + *
  • `'needs_first_factor'` - One of the following [first factor verification](!first-factor-verification) strategies is missing: `'email_link'`, `'email_code'`, `passkey`, `password`, `'phone_code'`, `'web3_base_signature'`, `'web3_metamask_signature'`, `'web3_coinbase_wallet_signature'`, `'web3_okx_wallet_signature'`, `'web3_solana_signature'`, [`OAuthStrategy`](https://clerk.com/docs/reference/types/sso#o-auth-strategy), or `'enterprise_sso'`.
  • + *
  • `'needs_second_factor'` - One of the following [second factor verification](!second-factor-verification) strategies is missing: `'phone_code'`, `'totp'`, `'backup_code'`, `'email_code'`, or `'email_link'`.
  • + *
  • `'needs_new_password'` - The user needs to set a new password. See the [dedicated custom flow](https://clerk.com/docs/guides/development/custom-flows/account-updates/forgot-password) guide for more information.
  • + *
*/ readonly status: SignInStatus; /** - * 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; - readonly existingSession?: { sessionId: string }; + /** + * 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. + */ + sessionId: string; + }; /** - * The state of the verification process for the selected first factor. Initially, this property contains an empty - * verification object, since there is no first factor selected. + * The state of the verification process for the selected first factor. Initially, this property contains an empty verification object, since there is no first factor selected. */ readonly firstFactorVerification: VerificationResource; /** - * The state of the verification process for the selected second factor. Initially, this property contains an empty - * verification object, since there is no second factor selected. + * The state of the verification process for the selected second factor. Initially, this property contains an empty verification object, since there is no second factor selected. */ readonly secondFactorVerification: VerificationResource; /** - * The authentication identifier value for the current sign-in. `null` if the `strategy` is `'oauth_'` - * or `'enterprise_sso'`. + * The authentication identifier value for the current sign-in. `null` if the `strategy` is `'oauth_'` or `'enterprise_sso'`. */ readonly identifier: string | null; /** - * The identifier of the session that was created upon completion of the current sign-in. The value of this property - * is `null` if the sign-in status is not `'complete'`. + * The ID of the session that was created upon completion of the current sign-in. The value of this property is `null` if the sign-in status is not `'complete'`. */ readonly createdSessionId: string | null; /** - * An object containing information about the user of the current sign-in. This property is populated only once an - * identifier is given to the `SignIn` object through `signIn.create()` or another method that populates the - * `identifier` property. + * An object containing information about the user of the current sign-in. This property is populated only once an identifier is given to the `SignIn` object through `signIn.create()` or another method that populates the `identifier` property. + *
    + *
  • `'firstName'`: The user's first name.
  • + *
  • `'lastName'`: The user's last name.
  • + *
  • `'imageUrl'`: The user's profile image URL.
  • + *
  • `'hasImage'`: Whether the user has a profile image.
  • + *
*/ readonly userData: UserData; @@ -376,202 +396,179 @@ export interface SignInFutureResource { readonly canBeDiscarded: boolean; /** - * Creates a new `SignIn` instance initialized with the provided parameters. The instance maintains the sign-in - * lifecycle state through its `status` property, which updates as the authentication flow progresses. + * Creates a new `SignIn` instance initialized with the provided parameters. The instance maintains the sign-in lifecycle state through its `status` property, which updates as the authentication flow progresses. Once the sign-in process is complete, call the `signIn.finalize()` method to set the newly created session as the active session. * - * What you must pass to `params` depends on which [sign-in options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) - * you have enabled in your app's settings in the Clerk Dashboard. + * What you must pass to `params` depends on which [sign-in options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) you have enabled in your app's settings in the Clerk Dashboard. * - * You can complete the sign-in process in one step if you supply the required fields to `create()`. Otherwise, - * Clerk's sign-in process provides great flexibility and allows users to easily create multi-step sign-in flows. + * You can complete the sign-in process in one step if you supply the required fields to `create()`. Otherwise, Clerk's sign-in process provides great flexibility and allows users to easily create multi-step sign-in flows. * - * > [!WARNING] - * > Once the sign-in process is complete, call the `signIn.finalize()` method to set the newly created session as - * > the active session. + * > [!IMPORTANT] + * > The `signIn.create()` method is intended for advanced use cases. For most use cases, prefer the use of the factor-specific methods such as `signIn.password()`, `signIn.emailCode.sendCode()`, etc. */ create: (params: SignInFutureCreateParams) => Promise<{ error: ClerkError | null }>; /** - * Used to submit a password to sign-in. + * Submits a password to sign-in. */ password: (params: SignInFuturePasswordParams) => Promise<{ error: ClerkError | null }>; - /** - * - */ + /** @extractMethods */ emailCode: { /** - * Used to send an email code to sign-in + * Sends an email code to sign-in. */ sendCode: (params?: SignInFutureEmailCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via email to sign-in + * Verifies a code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#email-code-send-code) method. */ verifyCode: (params: SignInFutureEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; - /** - * - */ + /** @extractMethods */ emailLink: { /** - * Used to send an email link to sign-in + * Sends an email link to sign in with. */ sendLink: (params: SignInFutureEmailLinkSendParams) => Promise<{ error: ClerkError | null }>; /** - * Will wait for verification to complete or expire + * Waits for email link verification to complete or expire. */ waitForVerification: () => Promise<{ error: ClerkError | null }>; /** - * The verification status + * 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 created session ID + * 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: { /** - * Used to send a phone code to sign-in + * Sends a phone code to sign in with. */ sendCode: (params?: SignInFuturePhoneCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via phone to sign-in + * Verifies a code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#phone-code-send-code) method. */ verifyCode: (params: SignInFuturePhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; - /** - * - */ + /** @extractMethods */ resetPasswordEmailCode: { /** - * Used to send a password reset code to the first email address on the account + * Sends a password reset code to the first email address on the account. */ sendCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify a password reset code sent via email. Will cause `signIn.status` to become `'needs_new_password'`. + * Verifies a password reset code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-email-code-send-code) method. Will cause `signIn.status` to become `'needs_new_password'`. This is when you will call the [`resetPasswordEmailCode.submitPassword()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-email-code-submit-password) method to complete the password reset flow. */ verifyCode: (params: SignInFutureEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to submit a new password, and move the `signIn.status` to `'complete'`. + * Submits a new password and moves the sign-in status to `'complete'`. */ submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: ClerkError | null }>; }; - /** - * - */ + /** @extractMethods */ resetPasswordPhoneCode: { /** - * Used to send a password reset code to the first phone number on the account + * Sends a password reset code to the first phone number on the account. */ sendCode: (params?: SignInFutureResetPasswordPhoneCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a password reset code sent via phone. Will cause `signIn.status` to become `'needs_new_password'`. + * Verifies a password reset code sent with the [`sendCode()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-phone-code-send-code) method. Will cause `signIn.status` to become `'needs_new_password'`. This is when you will call the [`resetPasswordPhoneCode.submitPassword()`](https://clerk.com/docs/reference/objects/sign-in-future#reset-password-phone-code-submit-password) method to complete the password reset flow. */ verifyCode: (params: SignInFutureResetPasswordPhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to submit a new password, and move the `signIn.status` to `'complete'`. + * Submits a new password and moves the sign-in status to `'complete'`. */ submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: ClerkError | null }>; }; /** - * Used to perform OAuth authentication. + * Performs an SSO-based sign-in (Social/OAuth or Enterprise). */ sso: (params: SignInFutureSSOParams) => Promise<{ error: ClerkError | null }>; - /** - * - */ + /** @extractMethods */ mfa: { /** - * Used to send a phone code as a second factor to sign-in + * Sends a phone code to sign in with as a second factor. */ sendPhoneCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify a phone code sent as a second factor to sign-in + * Verifies a phone code sent with the [`sendPhoneCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-phone-code) method. */ verifyPhoneCode: (params: SignInFutureMFAPhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to send an email code as a second factor to sign-in + * Sends an email code to sign in with as a second factor. */ sendEmailCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify an email code sent as a second factor to sign-in + * Verifies an email code sent with the [`sendEmailCode()`](https://clerk.com/docs/reference/objects/sign-in-future#mfa-send-email-code) method. */ verifyEmailCode: (params: SignInFutureMFAEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a TOTP code as a second factor to sign-in + * Verifies an authenticator app (TOTP) code to sign in with as a second factor. */ verifyTOTP: (params: SignInFutureTOTPVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a backup code as a second factor to sign-in + * Verifies a backup code to sign in with as a second factor. */ verifyBackupCode: (params: SignInFutureBackupCodeVerifyParams) => Promise<{ error: ClerkError | null }>; }; /** - * Used to perform a ticket-based sign-in. + * Performs a ticket-based sign-in. */ ticket: (params?: SignInFutureTicketParams) => Promise<{ error: ClerkError | null }>; /** - * Used to perform a Web3-based sign-in. + * Performs a Web3-based sign-in. */ web3: (params: SignInFutureWeb3Params) => Promise<{ error: ClerkError | null }>; /** - * Initiates a passkey-based authentication flow, enabling users to authenticate using a previously - * registered passkey. When called without parameters, this method requires a prior call to - * `SignIn.create({ strategy: 'passkey' })` to initialize the sign-in context. This pattern is particularly useful in - * scenarios where the authentication strategy needs to be determined dynamically at runtime. + * Initiates a passkey-based authentication flow, enabling users to authenticate using a previously registered passkey. When called without parameters, this method requires a prior call to `SignIn.create({ strategy: 'passkey' })` to initialize the sign-in context. This pattern is particularly useful in scenarios where the authentication strategy needs to be determined dynamically at runtime. */ passkey: (params?: SignInFuturePasskeyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to convert a sign-in with `status === 'complete'` into an active session. Will cause anything observing the - * session state (such as the `useUser()` hook) to update automatically. + * Converts a sign-in with `status === 'complete'` into an active session. Will cause anything observing the session state (such as the [`useUser()`](https://clerk.com/docs/reference/hooks/use-user) hook) to update automatically. */ finalize: (params?: SignInFutureFinalizeParams) => Promise<{ error: ClerkError | null }>; /** - * Resets the current sign-in attempt by clearing all local state back to null. - * This is useful when you want to allow users to go back to the beginning of - * the sign-in flow (e.g., to change their identifier during verification). + * Resets the current sign-in attempt by clearing all local state back to null. This is useful when you want to allow users to go back to the beginning of the sign-in flow (e.g., to change their identifier during verification). * - * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change - * to `'fetching'` and does not make any API calls - it only clears local state. + * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change to `'fetching'` and does not make any API calls - it only clears local state. */ reset: () => Promise<{ error: ClerkError | null }>; } diff --git a/packages/shared/src/types/signUpCommon.ts b/packages/shared/src/types/signUpCommon.ts index 41c15035b46..db5ca815f03 100644 --- a/packages/shared/src/types/signUpCommon.ts +++ b/packages/shared/src/types/signUpCommon.ts @@ -22,8 +22,10 @@ import type { import type { SnakeToCamel } from './utils'; import type { VerificationResource } from './verification'; +/** @inline */ export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned'; +/** @inline */ export type SignUpField = SignUpAttributeField | SignUpIdentificationField; export type PrepareVerificationParams = @@ -64,9 +66,11 @@ export type AttemptVerificationParams = signature: string; }; +/** @inline */ export type SignUpAttributeField = FirstNameAttribute | LastNameAttribute | PasswordAttribute | LegalAcceptedAttribute; // TODO: SignUpVerifiableField or SignUpIdentifier? +/** @inline */ export type SignUpVerifiableField = | UsernameIdentifier | EmailAddressIdentifier @@ -75,6 +79,7 @@ export type SignUpVerifiableField = | Web3WalletIdentifier; // TODO: Does it make sense that the identification *field* holds a *strategy*? +/** @inline */ export type SignUpIdentificationField = SignUpVerifiableField | OAuthStrategy | EnterpriseSSOStrategy; // TODO: Replace with discriminated union type diff --git a/packages/shared/src/types/signUpFuture.ts b/packages/shared/src/types/signUpFuture.ts index 1daf1239ece..91dbf8a6511 100644 --- a/packages/shared/src/types/signUpFuture.ts +++ b/packages/shared/src/types/signUpFuture.ts @@ -13,42 +13,42 @@ import type { } from './strategies'; import type { VerificationResource } from './verification'; +/** @document */ export interface SignUpFutureAdditionalParams { /** - * The user's first name. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The user's first name. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ firstName?: string; /** - * The user's last name. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The user's last name. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ lastName?: string; /** - * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be - * automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use - * it to implement custom fields that can be collected during sign-up and will automatically be attached to the - * created User object. + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created User object. */ unsafeMetadata?: SignUpUnsafeMetadata; /** - * A boolean indicating whether the user has agreed to the - * [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + * Indicates whether the user has agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. */ legalAccepted?: boolean; /** - * The locale to assign to the user in [BCP 47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) - * format (e.g., "en-US", "fr-FR"). If omitted, defaults to the browser's locale. + * The locale to assign to the user in [BCP 47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) format (e.g., "en-US", "fr-FR"). If omitted, defaults to the browser's locale. */ locale?: string; } +/** @document */ export interface SignUpFutureCreateParams extends SignUpFutureAdditionalParams { /** - * The first factor verification strategy to use in the sign-in flow. Depends on the `identifier` value. Each - * authentication identifier supports different verification strategies. + * The strategy to use for the sign-up. The following strategies are supported: + *
    + *
  • `'oauth_'`: The user will be authenticated with their [social connection account](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview). See a list of [supported values for ``](https://clerk.com/docs/reference/types/sso).
  • + *
  • `'enterprise_sso'`: The user will be authenticated either through SAML or OIDC depending on the configuration of their [enterprise SSO account](https://clerk.com/docs/guides/configure/auth-strategies/enterprise-connections/overview).
  • + *
  • `'ticket'`: The user will be authenticated via the ticket _or token_ generated from the Backend API.
  • + *
  • `'google_one_tap'`: The user will be authenticated with the Google One Tap UI. It's recommended to use [`authenticateWithGoogleOneTap()`](https://clerk.com/docs/reference/components/authentication/google-one-tap#authenticate-with-google-one-tap) instead, as it will also set the user's current session as active for you.
  • + *
  • `'oauth_token_apple'`: The user will be authenticated using a native [Sign in with Apple](https://clerk.com/docs/guides/configure/auth-strategies/sign-in-with-apple) identity token.
  • + *
  • `'phone_code'`: The user will receive a one-time code via SMS to verify their phone number.
  • + *
*/ strategy?: | OAuthStrategy @@ -58,66 +58,52 @@ export interface SignUpFutureCreateParams extends SignUpFutureAdditionalParams { | AppleIdTokenStrategy | PhoneCodeStrategy; /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; /** - * The user's password. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. + * The user's password. [Password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) must be enabled in the instance settings. */ password?: string; /** - * When set to `true`, the `SignUp` will attempt to retrieve information from the active `SignIn` instance and use it - * to complete the sign-up process. This is useful when you want to seamlessly transition a user from a sign-in - * attempt to a sign-up attempt. + * When set to `true`, the `SignUp` will attempt to retrieve information from the active `SignIn` instance and use it to complete the sign-up process. This is useful when you want to seamlessly transition a user from a sign-in attempt to a sign-up attempt. */ transfer?: boolean; /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. **Required** if `strategy` is set to `'ticket'`. + * **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket?: string; /** - * The Web3 wallet address, made up of 0x + 40 hexadecimal characters. **Required** if - * [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) - * is enabled. + * The Web3 wallet address, made up of 0x + 40 hexadecimal characters. Only supported if [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) is enabled in the instance settings. */ web3Wallet?: string; } +/** @document */ export interface SignUpFutureUpdateParams extends SignUpFutureAdditionalParams { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } +/** @document */ export interface SignUpFutureEmailCodeVerifyParams { /** * The code that was sent to the user. @@ -125,6 +111,7 @@ export interface SignUpFutureEmailCodeVerifyParams { code: string; } +/** @document */ export interface SignUpFutureEmailLinkSendParams { /** * The full URL that the user will be redirected to when they visit the email link. @@ -132,91 +119,72 @@ export interface SignUpFutureEmailLinkSendParams { verificationUrl: string; } +/** @document */ export type SignUpFuturePasswordParams = SignUpFutureAdditionalParams & { /** - * The user's password. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled. + * The user's password. [Password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) must be enabled in the instance settings. */ password: string; } & ( | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. Keep in mind that the email address requires an extra verification process. */ emailAddress: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. Keep in mind that the phone number requires an extra verification process. */ phoneNumber: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username: string; } | { /** - * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) - * is enabled. Keep in mind that the email address requires an extra verification process. + * The user's email address. Only supported if [Email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. Keep in mind that the email address requires an extra verification process. */ emailAddress?: string; /** - * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled. - * Keep in mind that the phone number requires an extra verification process. + * The user's phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164). Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. Keep in mind that the phone number requires an extra verification process. */ phoneNumber?: string; /** - * The user's username. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The user's username. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ username?: string; } ); +/** @document */ export interface SignUpFuturePhoneCodeSendParams { /** * The mechanism to use to send the code to the provided phone number. Defaults to `'sms'`. @@ -224,6 +192,7 @@ export interface SignUpFuturePhoneCodeSendParams { channel?: PhoneCodeChannel; } +/** @document */ export interface SignUpFuturePhoneCodeVerifyParams { /** * The code that was sent to the user. @@ -231,23 +200,23 @@ export interface SignUpFuturePhoneCodeVerifyParams { code: string; } +/** @document */ export interface SignUpFutureSSOParams extends SignUpFutureAdditionalParams { /** * The strategy to use for authentication. */ - strategy: string; + strategy: OAuthStrategy | EnterpriseSSOStrategy; /** - * The URL or path to navigate to after the OAuth or SAML flow completes. Can be provided as a relative URL (such as - * `/dashboard`), in which case it will be prefixed with the base URL of the current page. + * The URL or path to navigate to after the OAuth or SAML flow completes. Can be provided as a relative URL (such as `/dashboard`), in which case it will be prefixed with the base URL of the current page. */ redirectUrl: string; /** + * The URL or path to navigate to if a session was not created, and needs additional information. * TODO @revamp-hooks: This should be handled by FAPI instead. */ redirectCallbackUrl: string; /** - * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an - * OAuth provider. + * If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an OAuth provider. * * @example * ```ts @@ -260,29 +229,29 @@ export interface SignUpFutureSSOParams extends SignUpFutureAdditionalParams { */ popup?: Window; /** - * Optional for `oauth_` or `enterprise_sso` strategies. The value to pass to the - * [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) - * in the generated OAuth redirect URL. + * The value to pass to the [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) in the generated OAuth redirect URL. */ oidcPrompt?: string; /** + * The identifier of the enterprise connection to target when using the `enterprise_sso` strategy. * @experimental */ enterpriseConnectionId?: string; /** - * Email address to use for targeting an enterprise connection at sign-up. + * The email address to use for targeting an enterprise connection at sign-up. */ emailAddress?: string; } +/** @document */ export interface SignUpFutureTicketParams extends SignUpFutureAdditionalParams { /** - * The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) - * generated from the Backend API. **Required** if `strategy` is set to `'ticket'`. + * **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](https://clerk.com/docs/guides/development/custom-flows/authentication/application-invitations) generated from the Backend API. */ ticket: string; } +/** @document */ export interface SignUpFutureWeb3Params extends SignUpFutureAdditionalParams { /** * The verification strategy to validate the user's sign-up request. @@ -290,31 +259,35 @@ export interface SignUpFutureWeb3Params extends SignUpFutureAdditionalParams { strategy: Web3Strategy; } +/** @document */ export interface SignUpFutureFinalizeParams { + /** + * A custom navigation function to be called just before the session and/or Organization is set. When provided, it takes precedence over the `redirectUrl` parameter for navigation. The callback receives a `decorateUrl` function that should be used to wrap destination URLs. This enables Safari ITP cookie refresh when needed. The decorated URL may be an external URL (starting with `https://`) that requires `window.location.href` instead of client-side navigation. See the [section on using the `navigate()` parameter](https://clerk.com/docs/reference/objects/clerk#using-the-navigate-parameter) for more details. + */ navigate?: SetActiveNavigate; } /** - * An object that contains information about all available verification strategies. + * Contains information about the available verification strategies for a sign-up attempt. */ export interface SignUpFutureVerifications { /** - * An object holding information about the email address verification. + * Holds information about the email address verification. */ readonly emailAddress: SignUpVerificationResource; /** - * An object holding information about the phone number verification. + * Holds information about the phone number verification. */ readonly phoneNumber: SignUpVerificationResource; /** - * An object holding information about the Web3 wallet verification. + * Holds information about the Web3 wallet verification. */ readonly web3Wallet: VerificationResource; /** - * An object holding information about the external account verification. + * Holds information about the external account verification. */ readonly externalAccount: VerificationResource; @@ -339,39 +312,38 @@ export interface SignUpFutureVerifications { } | null; /** - * Used to send an email code to verify an email address. + * Sends an email code to verify an email address. */ sendEmailCode: () => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via email. + * Verifies a code sent via [`verifications.sendEmailCode()`](https://clerk.com/docs/reference/objects/sign-up-future#verifications-send-email-code). */ verifyEmailCode: (params: SignUpFutureEmailCodeVerifyParams) => Promise<{ error: ClerkError | null }>; /** - * Used to send an email link to verify an email address. + * Sends an email link to verify an email address. */ sendEmailLink: (params: SignUpFutureEmailLinkSendParams) => Promise<{ error: ClerkError | null }>; /** - * Will wait for email link verification to complete or expire. + * Will wait for email link verification to complete or expire after calling [`verifications.sendEmailLink()`](https://clerk.com/docs/reference/objects/sign-up-future#verifications-send-email-link). */ waitForEmailLinkVerification: () => Promise<{ error: ClerkError | null }>; /** - * Used to send a phone code to verify a phone number. + * Sends a phone code to verify a phone number. */ sendPhoneCode: (params?: SignUpFuturePhoneCodeSendParams) => Promise<{ error: ClerkError | null }>; /** - * Used to verify a code sent via phone. + * Verifies a code sent via [`verifications.sendPhoneCode()`](https://clerk.com/docs/reference/objects/sign-up-future#verifications-send-phone-code). */ verifyPhoneCode: (params: SignUpFuturePhoneCodeVerifyParams) => Promise<{ error: ClerkError | null }>; } /** - * The `SignUpFuture` class holds the state of the current sign-up attempt and provides methods to drive custom sign-up - * flows, including email/phone verification, password, SSO, ticket-based, and Web3-based account creation. + * The `SignUpFuture` class holds the state of the current sign-up attempt and provides methods to drive custom sign-up flows, including email/phone verification, password, SSO, ticket-based, and Web3-based account creation. */ export interface SignUpFutureResource { /** @@ -385,92 +357,77 @@ export interface SignUpFutureResource { readonly status: SignUpStatus; /** - * An array of all the required fields that need to be supplied and verified in order for this sign-up to be marked - * as complete and converted into a user. + * An array of all the required fields that need to be supplied and verified in order for this sign-up to be marked as complete and converted into a user. */ readonly requiredFields: SignUpField[]; /** - * An array of all the fields that can be supplied to the sign-up, but their absence does not prevent the sign-up - * from being marked as complete. + * An array of all the fields that can be supplied to the sign-up, but their absence does not prevent the sign-up from being marked as complete. */ readonly optionalFields: SignUpField[]; /** - * An array of all the fields whose values are not supplied yet but they are mandatory in order for a sign-up to be - * marked as complete. + * An array of all the fields whose values are not supplied yet but they are mandatory in order for a sign-up to be marked as complete. */ readonly missingFields: SignUpField[]; /** - * An array of all the fields whose values have been supplied, but they need additional verification in order for - * them to be accepted. Examples of such fields are `email_address` and `phone_number`. + * An array of all the fields whose values have been supplied, but they need additional verification in order for them to be accepted. Examples of such fields are `email_address` and `phone_number`. */ readonly unverifiedFields: SignUpIdentificationField[]; /** - * Indicates that there is a matching user for provided identifier, and that the sign-up can be transferred to - * a sign-in. + * Indicates that there is a matching user for provided identifier, and that the sign-up can be transferred to a sign-in. */ readonly isTransferable: boolean; - readonly existingSession?: { sessionId: string }; + /** + * Indicates that the sign-up was not able to create a new session because the identifier already exists in an existing session. + */ + readonly existingSession?: { + /** + * The ID of the existing session. + */ + sessionId: string; + }; /** - * The `username` supplied to the current sign-up. Only supported if - * [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in - * the instance settings. + * The `username` supplied to the current sign-up. Only supported if [username](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#username) is enabled in the instance settings. */ readonly username: string | null; /** - * The `firstName` supplied to the current sign-up. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The `firstName` supplied to the current sign-up. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ readonly firstName: string | null; /** - * The `lastName` supplied to the current sign-up. Only supported if - * [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) - * is enabled in the instance settings. + * The `lastName` supplied to the current sign-up. Only supported if [First and last name](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#user-model) is enabled in the instance settings. */ readonly lastName: string | null; /** - * The `emailAddress` supplied to the current sign-up. Only supported if - * [email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled - * in the instance settings. + * The `emailAddress` supplied to the current sign-up. Only supported if [email address](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) is enabled in the instance settings. */ readonly emailAddress: string | null; /** - * The `phoneNumber` supplied to the current sign-up in E.164 format. Only supported if - * [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled - * in the instance settings. + * The `phoneNumber` supplied to the current sign-up in E.164 format. Only supported if [phone number](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) is enabled in the instance settings. */ readonly phoneNumber: string | null; /** - * The Web3 wallet address supplied to the current sign-up, made up of 0x + 40 hexadecimal characters. Only supported - * if - * [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) - * is enabled in the instance settings. + * The Web3 wallet address supplied to the current sign-up, made up of 0x + 40 hexadecimal characters. Only supported if [Web3 authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#web3-authentication) is enabled in the instance settings. */ readonly web3Wallet: string | null; /** - * The value of this attribute is true if a password was supplied to the current sign-up. Only supported if - * [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled in - * the instance settings. + * The value of this attribute is true if a password was supplied to the current sign-up. Only supported if [password](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#password) is enabled in the instance settings. */ readonly hasPassword: boolean; /** - * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be - * automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use - * it to implement custom fields that can be collected during sign-up and will automatically be attached to the - * created User object. + * Metadata that can be read and set from the frontend. Once the sign-up is complete, the value of this field will be automatically copied to the newly created user's unsafe metadata. One common use case for this attribute is to use it to implement custom fields that can be collected during sign-up and will automatically be attached to the created User object. */ readonly unsafeMetadata: SignUpUnsafeMetadata; @@ -490,13 +447,12 @@ export interface SignUpFutureResource { readonly abandonAt: number | null; /** - * The epoch numerical time when the user agreed to the - * [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. + * The epoch numerical time when the user agreed to the [legal compliance](https://clerk.com/docs/guides/secure/legal-compliance) documents. */ readonly legalAcceptedAt: number | null; /** - * The locale of the user in BCP 47 format. + * The locale of the user in [BCP 47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) format (e.g., "en-US", "fr-FR"), or `null` if not set. */ readonly locale: string | null; @@ -508,66 +464,57 @@ export interface SignUpFutureResource { readonly canBeDiscarded: boolean; /** - * Creates a new `SignUp` instance initialized with the provided parameters. The instance maintains the sign-up - * lifecycle state through its `status` property, which updates as the authentication flow progresses. Will also - * deactivate any existing sign-up process the client may already have in progress. + * Creates a new `SignUp` instance initialized with the provided parameters. The instance maintains the sign-up lifecycle state through its `status` property, which updates as the authentication flow progresses. Will also deactivate any existing sign-up process the client may already have in progress. Once the sign-up process is complete, call the [`signUp.finalize()`](https://clerk.com/docs/reference/objects/sign-up-future#finalize) method to set the newly created session as the active session. * - * What you must pass to `params` depends on which - * [sign-up options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) you have - * enabled in your app's settings in the Clerk Dashboard. + * What you must pass to `params` depends on which [sign-up options](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options) you have enabled in your app's settings in the Clerk Dashboard. * - * You can complete the sign-up process in one step if you supply the required fields to `create()`. Otherwise, - * Clerk's sign-up process provides great flexibility and allows users to easily create multi-step sign-up flows. + * You can complete the sign-up process in one step if you supply the required fields to `create()`. Otherwise, Clerk's sign-up process provides great flexibility and allows users to easily create multi-step sign-up flows. * - * > [!WARNING] - * > Once the sign-up process is complete, call the `signUp.finalize()` method to set the newly created session as - * > the active session. + * > [!IMPORTANT] + * > The `signUp.create()` method is intended for advanced use cases. For most use cases, prefer the use of the factor-specific methods such as `signUp.password()`, `signUp.sso()`, etc. */ create: (params: SignUpFutureCreateParams) => Promise<{ error: ClerkError | null }>; /** - * Updates the current `SignUp`. + * Updates the current `SignUpFutureResource` instance with the provided parameters. */ update: (params: SignUpFutureUpdateParams) => Promise<{ error: ClerkError | null }>; /** * An object that contains information about all available verification strategies. + * @extractMethods */ verifications: SignUpFutureVerifications; /** - * Used to sign up using an email address and password. + * Performs a password-based sign-up. */ password: (params: SignUpFuturePasswordParams) => Promise<{ error: ClerkError | null }>; /** - * Used to create an account using an OAuth connection. + * Performs an SSO-based sign-up ([Social/OAuth](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview) or [Enterprise](https://clerk.com/docs/guides/configure/auth-strategies/enterprise-connections/overview)). */ sso: (params: SignUpFutureSSOParams) => Promise<{ error: ClerkError | null }>; /** - * Used to perform a ticket-based sign-up. + * Performs a ticket-based sign-up. */ ticket: (params?: SignUpFutureTicketParams) => Promise<{ error: ClerkError | null }>; /** - * Used to perform a Web3-based sign-up. + * Performs a Web3-based sign-up. */ web3: (params: SignUpFutureWeb3Params) => Promise<{ error: ClerkError | null }>; /** - * Used to convert a sign-up with `status === 'complete'` into an active session. Will cause anything observing the - * session state (such as the `useUser()` hook) to update automatically. + * Converts a sign-up with `status === 'complete'` into an active session. Will cause anything observing the session state (such as the `useUser()` hook) to update automatically. */ finalize: (params?: SignUpFutureFinalizeParams) => Promise<{ error: ClerkError | null }>; /** - * Resets the current sign-up attempt by clearing all local state back to null. - * This is useful when you want to allow users to go back to the beginning of - * the sign-up flow (e.g., to change their email address during verification). + * Resets the current sign-up attempt by clearing all local state back to null. This is useful when you want to allow users to go back to the beginning of the sign-up flow (e.g., to change their email address during verification). * - * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change - * to `'fetching'` and does not make any API calls - it only clears local state. + * Unlike other methods, `reset()` does not trigger the `fetchStatus` to change to `'fetching'` and does not make any API calls - it only clears local state. */ reset: () => Promise<{ error: ClerkError | null }>; } diff --git a/packages/shared/src/types/strategies.ts b/packages/shared/src/types/strategies.ts index eb6dde71ed5..2055c1a5056 100644 --- a/packages/shared/src/types/strategies.ts +++ b/packages/shared/src/types/strategies.ts @@ -1,20 +1,37 @@ import type { OAuthProvider } from './oauth'; import type { Web3Provider } from './web3'; +/** @inline */ export type GoogleOneTapStrategy = 'google_one_tap'; +/** @inline */ export type AppleIdTokenStrategy = 'oauth_token_apple'; +/** @inline */ export type PasskeyStrategy = 'passkey'; +/** @inline */ export type PasswordStrategy = 'password'; +/** @inline */ export type PhoneCodeStrategy = 'phone_code'; +/** @inline */ export type EmailCodeStrategy = 'email_code'; +/** @inline */ export type EmailLinkStrategy = 'email_link'; +/** @inline */ export type TicketStrategy = 'ticket'; +/** @inline */ export type TOTPStrategy = 'totp'; +/** @inline */ export type BackupCodeStrategy = 'backup_code'; +/** @inline */ export type ResetPasswordPhoneCodeStrategy = 'reset_password_phone_code'; +/** @inline */ export type ResetPasswordEmailCodeStrategy = 'reset_password_email_code'; +/** @inline */ export type CustomOAuthStrategy = `oauth_custom_${string}`; +/** @inline */ export type EnterpriseSSOStrategy = 'enterprise_sso'; +/** OAuth-related authentication strategies (`oauth_` and custom OAuth). */ export type OAuthStrategy = `oauth_${OAuthProvider}` | CustomOAuthStrategy; + +/** @inline */ export type Web3Strategy = `web3_${Web3Provider}_signature`; diff --git a/packages/shared/src/types/telemetry.ts b/packages/shared/src/types/telemetry.ts index bc518207818..b7009bffb6b 100644 --- a/packages/shared/src/types/telemetry.ts +++ b/packages/shared/src/types/telemetry.ts @@ -57,9 +57,24 @@ export interface TelemetryLogEntry { readonly userId?: string; } +/** + * @inline + */ export interface TelemetryCollector { + /** + * If `true`, telemetry events are only logged to the console and not sent to Clerk. + */ isEnabled: boolean; + /** + * If `true`, telemetry events are only logged to the console and not sent to Clerk. + */ isDebug: boolean; + /** + * Records a telemetry event. + */ record(event: TelemetryEventRaw): void; + /** + * Records a telemetry log entry. + */ recordLog(entry: TelemetryLogEntry): void; } diff --git a/packages/shared/src/types/user.ts b/packages/shared/src/types/user.ts index 43e5aa0a492..203dca90aab 100644 --- a/packages/shared/src/types/user.ts +++ b/packages/shared/src/types/user.ts @@ -63,70 +63,244 @@ declare global { } /** - * The `User` object holds all of the information for a single user of your application and provides a set of methods to manage their account. + * The `User` object holds all of the information for a single user of your application and provides a set of methods to manage their account. Each `User` has at least one authentication identifier, which might be their email address, phone number, or a username. * - * A user can be contacted at their primary email address or primary phone number. They can have more than one registered email address, but only one of them will be their primary email address. This goes for phone numbers as well; a user can have more than one, but only one phone number will be their primary. At the same time, a user can also have one or more external accounts by connecting to [social providers](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview) such as Google, Apple, Facebook, and many more. + * A user can be contacted at their primary email address or primary phone number. They can have more than one registered email address or phone number, but only one of them will be their primary email address (`User.primaryEmailAddress`) or primary phone number (`User.primaryPhoneNumber`). At the same time, a user can also have one or more external accounts by connecting to [social providers](https://clerk.com/docs/guides/configure/auth-strategies/social-connections/overview) such as Google, Apple, Facebook, and many more (`User.externalAccounts`). * - * Finally, a `User` object holds profile data like the user's name, profile picture, and a set of [metadata](/docs/guides/users/extending) that can be used internally to store arbitrary information. The metadata are split into `publicMetadata` and `privateMetadata`. Both types are set from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but public metadata can also be accessed from the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }}. - * - * The ClerkJS SDK provides some helper [methods](#methods) on the `User` object to help retrieve and update user information and authentication status. + * Finally, a `User` object holds profile data like the user's name, profile picture, and a set of [metadata](https://clerk.com/docs/guides/users/extending) that can be used internally to store arbitrary information. The metadata are split into `publicMetadata` and `privateMetadata`. Both types are set from the [Backend API](https://clerk.com/docs/reference/backend-api){{ target: '_blank' }}, but public metadata can also be accessed from the [Frontend API](https://clerk.com/docs/reference/frontend-api){{ target: '_blank' }}. + */ export interface UserResource extends ClerkResource, BillingPayerMethods { + /** + * The unique identifier of the user. + */ id: string; + /** + * The user's ID as used in your external systems. Must be unique across your instance. + */ externalId: string | null; + /** + * The ID of the user's primary email address. + */ primaryEmailAddressId: string | null; + /** + * The user's primary email address. + */ primaryEmailAddress: EmailAddressResource | null; + /** + * The ID of the user's primary phone number. + */ primaryPhoneNumberId: string | null; + /** + * The user's primary phone number. + */ primaryPhoneNumber: PhoneNumberResource | null; + /** + * The ID of the user's primary Web3 wallet. + */ primaryWeb3WalletId: string | null; + /** + * The user's primary Web3 wallet. + */ primaryWeb3Wallet: Web3WalletResource | null; + /** + * The user's username. + */ username: string | null; + /** + * The user's full name. + */ fullName: string | null; + /** + * The user's first name. + */ firstName: string | null; + /** + * The user's last name. + */ lastName: string | null; + /** + * Holds the default avatar or user's uploaded profile image. Compatible with Clerk's [Image Optimization](https://clerk.com/docs/guides/development/image-optimization). + */ imageUrl: string; + /** + * Indicates whether the user has uploaded an image or one was copied from OAuth. Returns `false` if Clerk is displaying an avatar for the user. + */ hasImage: boolean; + /** + * An array of all the `EmailAddress` objects associated with the user. Includes the primary. + */ emailAddresses: EmailAddressResource[]; + /** + * An array of all the `PhoneNumber` objects associated with the user. Includes the primary. + */ phoneNumbers: PhoneNumberResource[]; + /** + * An array of all the `Web3Wallet` objects associated with the user. Includes the primary. + */ web3Wallets: Web3WalletResource[]; + /** + * An array of all the `ExternalAccount` objects associated with the user via OAuth. + */ externalAccounts: ExternalAccountResource[]; + /** + * An array of all the `EnterpriseAccount` objects associated with the user via enterprise SSO. + */ enterpriseAccounts: EnterpriseAccountResource[]; + /** + * An array of all the `Passkey` objects associated with the user. + */ passkeys: PasskeyResource[]; + /** + * An array of all the `OrganizationMembership` objects associated with the user. + */ organizationMemberships: OrganizationMembershipResource[]; + /** + * Indicates whether the user has a password on their account. + */ passwordEnabled: boolean; + /** + * Indicates whether the user has enabled TOTP. + */ totpEnabled: boolean; + /** + * Indicates whether the user has enabled backup codes. + */ backupCodeEnabled: boolean; + /** + * Indicates whether the user has enabled two-factor authentication. + */ twoFactorEnabled: boolean; + /** + * Metadata that can be read from the Frontend API and Backend API and can be set only from the Backend API. + */ publicMetadata: UserPublicMetadata; + /** + * Metadata that can be read and set from the Frontend API. It's considered unsafe because it can be modified from the frontend. + * + * There is also an `unsafeMetadata` attribute in the [`SignUp`](https://clerk.com/docs/reference/objects/sign-up-future) object. The value of that field will be automatically copied to the user's unsafe metadata once the sign up is complete. + */ unsafeMetadata: UserUnsafeMetadata; + /** + * The date and time when the user last signed in. + */ lastSignInAt: Date | null; + /** + * The date and time when the user accepted the legal compliance documents. + */ legalAcceptedAt: Date | null; + /** + * Indicates whether the user can create organizations. + */ createOrganizationEnabled: boolean; + /** + * The maximum number of organizations the user can create. + */ createOrganizationsLimit: number | null; + /** + * Indicates whether the user can delete their own account. + */ deleteSelfEnabled: boolean; + /** + * The date and time when the user was last updated. + */ updatedAt: Date | null; + /** + * The date and time when the user was created. + */ createdAt: Date | null; + /** + * Updates the user's attributes. Use this method to save information you collected about the user. + * + * The appropriate settings must be enabled in the Clerk Dashboard for the user to be able to update their attributes. For example, if you want to use the `update({ firstName })` method, you must enable the **First and last name** setting. It can be found on the [**User & authentication**](https://dashboard.clerk.com/~/user-authentication/user-and-authentication?user_auth_tab=user-profile) page in the Clerk Dashboard. + * @skipParametersSection + */ update: (params: UpdateUserParams) => Promise; + /** + * Deletes the current user. + */ delete: () => Promise; + /** + * Updates the user's password. + */ updatePassword: (params: UpdateUserPasswordParams) => Promise; + /** + * Removes the user's password. + */ removePassword: (params: RemoveUserPasswordParams) => Promise; + /** + * Adds an email address for the user. A new [`EmailAddress`](https://clerk.com/docs/reference/types/email-address) will be created and associated with the user. + * + * > [!WARNING] + * > [**Email** must be enabled](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) in your app's settings in the Clerk Dashboard. + */ createEmailAddress: (params: CreateEmailAddressParams) => Promise; + /** + * Creates a passkey for the signed-in user. For an example, see the [custom flow guide](https://clerk.com/docs/guides/development/custom-flows/authentication/passkeys#create-user-passkeys). + * @returns A [`PasskeyResource`](https://clerk.com/docs/reference/types/passkey-resource) object. + * + * > [!NOTE] + * > When creating a passkey for a user in a multi-domain Clerk app, `createPasskey()` must be called from the primary domain. + */ createPasskey: () => Promise; + /** + * Adds a phone number for the user. A new [`PhoneNumber`](https://clerk.com/docs/reference/types/phone-number) will be created and associated with the user. + * + * > [!WARNING] + * > [**Phone** must be enabled](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#phone) in your app's settings in the Clerk Dashboard. + */ createPhoneNumber: (params: CreatePhoneNumberParams) => Promise; + /** + * Adds a Web3 wallet for the user. A new [`Web3WalletResource`](https://clerk.com/docs/reference/types/web3-wallet) will be created and associated with the user. + */ createWeb3Wallet: (params: CreateWeb3WalletParams) => Promise; + /** + * A check whether or not the given resource is the primary identifier for the user. + * @param ident - The resource checked against the user to see if it is the primary identifier. + */ isPrimaryIdentification: (ident: EmailAddressResource | PhoneNumberResource | Web3WalletResource) => boolean; + /** + * Gets all **active** sessions for this user. This method uses a cache so a network request will only be triggered only once. + * @returns An array of [`SessionWithActivities`](https://clerk.com/docs/reference/types/session-with-activities) objects. + */ getSessions: () => Promise; + /** + * Adds the user's profile image or replaces it if one already exists. This method will upload an image and associate it with the user. + */ setProfileImage: (params: SetProfileImageParams) => Promise; + /** + * Adds an external account for the user. A new [`ExternalAccount`](https://clerk.com/docs/reference/types/external-account) will be created and associated with the user. This method is useful if you want to allow an already signed-in user to connect their account with an external provider, such as Facebook, GitHub, etc., so that they can sign in with that provider in the future. + * + * > [!WARNING] + * > The social provider that you want to connect to [must be enabled](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#sso-connections) in your app's settings in the Clerk Dashboard. + */ createExternalAccount: (params: CreateExternalAccountParams) => Promise; getOrganizationMemberships: GetOrganizationMemberships; + /** + * Gets a list of Organization invitations for the user. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`UserOrganizationInvitation`](https://clerk.com/docs/reference/types/user-organization-invitation) objects. + */ getOrganizationInvitations: ( params?: GetUserOrganizationInvitationsParams, ) => Promise>; + /** + * Gets a list of Organization suggestions for the user. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationSuggestionResource`](https://clerk.com/docs/reference/types/organization-suggestion) objects. + */ getOrganizationSuggestions: ( params?: GetUserOrganizationSuggestionsParams, ) => Promise>; + /** + * Gets organization creation defaults for the current user. + * @returns A [`OrganizationCreationDefaultsResource`](https://clerk.com/docs/reference/types/organization-creation-defaults) object. + */ getOrganizationCreationDefaults: () => Promise; + /** + * Leaves an organization that the user is a member of. + * @param organizationId - The ID of the organization to leave. + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource) object. + */ leaveOrganization: (organizationId: string) => Promise; getEnterpriseConnections: (params?: GetEnterpriseConnectionsParams) => Promise; createEnterpriseConnection: (params: CreateMeEnterpriseConnectionParams) => Promise; @@ -142,9 +316,34 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { enterpriseConnectionId: string, params?: GetEnterpriseConnectionTestRunsParams, ) => Promise>; + /** + * Generates a TOTP secret for a user that can be used to register the application on the user's authenticator app of choice. If this method is called again (while still unverified), it replaces the previously generated secret. + * @returns A [`TOTPResource`](https://clerk.com/docs/reference/types/totp-resource) object. + * + * > [!WARNING] + * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. + */ createTOTP: () => Promise; + /** + * Verifies a TOTP secret after a user has created it. The user must provide a code from their authenticator app that has been generated using the previously created secret. This way, correct set up and ownership of the authenticator app can be validated. + * @returns A [`TOTPResource`](https://clerk.com/docs/reference/types/totp-resource) object. + * + * > [!WARNING] + * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. + */ verifyTOTP: (params: VerifyTOTPParams) => Promise; + /** + * Disables TOTP by deleting the user's TOTP secret. + * @returns A [`DeletedObjectResource`](https://clerk.com/docs/reference/types/deleted-object-resource) object. + * + * > [!WARNING] + * > The **Authenticator application** multi-factor strategy must be enabled in your app's settings in the Clerk Dashboard. See the [Multi-factor authentication](https://clerk.com/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) section to learn more. + */ disableTOTP: () => Promise; + /** + * Generates a fresh new set of backup codes for the user. Every time the method is called, it will replace the previously generated backup codes. + * @returns A [`BackupCodeResource`](https://clerk.com/docs/reference/types/backup-code-resource) object. + */ createBackupCode: () => Promise; get verifiedExternalAccounts(): ExternalAccountResource[]; @@ -160,19 +359,69 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { __internal_toSnapshot: () => UserJSONSnapshot; } -export type CreateEmailAddressParams = { email: string }; -export type CreatePhoneNumberParams = { phoneNumber: string }; -export type CreateWeb3WalletParams = { web3Wallet: string }; -export type SetProfileImageParams = { file: Blob | File | string | null }; +/** @document */ +export type CreateEmailAddressParams = { + /** + * The email address to add to the user. + */ + email: string; +}; +/** @document */ +export type CreatePhoneNumberParams = { + /** + * The phone number to add to the user. + */ + phoneNumber: string; +}; +/** @document */ +export type CreateWeb3WalletParams = { + /** + * The Web3 wallet address, made up of either 0x + 40 hexadecimal characters or a `base58` encoded `ed25519` public key (for Solana wallets). + */ + web3Wallet: string; +}; +/** @document */ +export type SetProfileImageParams = { + /** + * The file to set as the user's profile image, or `null` to remove the current image. + */ + file: Blob | File | string | null; +}; +/** @document */ export type CreateExternalAccountParams = { + /** + * The strategy corresponding to the OAuth provider. For example: `'oauth_google'`. + */ strategy?: OAuthStrategy; + /** + * The ID of the enterprise connection to connect to the user. + */ enterpriseConnectionId?: string; + /** + * The full URL or path that the OAuth provider should redirect to, on successful authorization on their part. Typically, this will be a simple `/sso-callback` route that calls [`Clerk.handleRedirectCallback`](https://clerk.com/docs/reference/objects/clerk#handle-redirect-callback) or mounts the [``](https://clerk.com/docs/reference/components/control/authenticate-with-redirect-callback) component. See the [custom flow](https://clerk.com/docs/guides/development/custom-flows/authentication/oauth-connections) for implementation details. + + */ redirectUrl?: string; + /** + * Additional scopes for your user to be prompted to approve. + */ additionalScopes?: OAuthScope[]; + /** + * The value to pass to the [OIDC `prompt` parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) in the generated OAuth redirect URL. + */ oidcPrompt?: string; + /** + * The value to pass to the [OIDC `login_hint` parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=login_hint,in%20\(if%20necessary\).) in the generated OAuth redirect URL. + */ oidcLoginHint?: string; }; -export type VerifyTOTPParams = { code: string }; +/** @document */ +export type VerifyTOTPParams = { + /** + * The code to verify. + */ + code: string; +}; type UpdateUserJSON = Pick< UserJSON, @@ -187,24 +436,52 @@ type UpdateUserJSON = Pick< export type UpdateUserParams = Partial>; +/** @document */ export type UpdateUserPasswordParams = { + /** + * The user's new password. + */ newPassword: string; + /** + * The user's current password. + */ currentPassword?: string; + /** + * Whether to sign out the user from all their active sessions once their password is updated. + */ signOutOfOtherSessions?: boolean; }; -export type RemoveUserPasswordParams = Pick; +/** @document */ +export type RemoveUserPasswordParams = { + /** + * The user's current password. + */ + currentPassword?: string; +}; +/** @document */ export type GetUserOrganizationInvitationsParams = ClerkPaginationParams<{ + /** + * The status an invitation can have. + */ status?: OrganizationInvitationStatus; }>; +/** @document */ export type GetUserOrganizationSuggestionsParams = ClerkPaginationParams<{ + /** + * The status a suggestion can have. + */ status?: OrganizationSuggestionStatus | OrganizationSuggestionStatus[]; }>; export type GetUserOrganizationMembershipParams = ClerkPaginationParams; +/** + * Gets a list of Organization memberships for the user. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`OrganizationMembershipResource`](https://clerk.com/docs/reference/types/organization-membership) objects. + */ export type GetOrganizationMemberships = ( params?: GetUserOrganizationMembershipParams, ) => Promise>; diff --git a/packages/shared/src/types/waitlist.ts b/packages/shared/src/types/waitlist.ts index 52f16a427b6..74540af1692 100644 --- a/packages/shared/src/types/waitlist.ts +++ b/packages/shared/src/types/waitlist.ts @@ -23,6 +23,10 @@ export interface WaitlistResource extends ClerkResource { join: (params: JoinWaitlistParams) => Promise<{ error: ClerkError | null }>; } +/** @document */ export type JoinWaitlistParams = { + /** + * The email address of the user to add to the waitlist. + */ emailAddress: string; }; diff --git a/packages/shared/src/types/web3.ts b/packages/shared/src/types/web3.ts index 450c8533946..0b38b6f52cb 100644 --- a/packages/shared/src/types/web3.ts +++ b/packages/shared/src/types/web3.ts @@ -6,14 +6,21 @@ export interface Web3ProviderData { name: string; } +/** @inline */ export type MetamaskWeb3Provider = 'metamask'; +/** @inline */ export type CoinbaseWalletWeb3Provider = 'coinbase_wallet'; +/** @inline */ export type OKXWalletWeb3Provider = 'okx_wallet'; +/** @inline */ export type BaseWeb3Provider = 'base'; +/** @inline */ export type SolanaWeb3Provider = 'solana'; +/** @inline */ export type Web3Provider = EthereumWeb3Provider | SolanaWeb3Provider; +/** @inline */ export type EthereumWeb3Provider = | MetamaskWeb3Provider | BaseWeb3Provider diff --git a/packages/ui/src/internal/appearance.ts b/packages/ui/src/internal/appearance.ts index 5cc35d313be..9cf5d845817 100644 --- a/packages/ui/src/internal/appearance.ts +++ b/packages/ui/src/internal/appearance.ts @@ -859,7 +859,7 @@ export type BaseTheme = (BaseThemeTaggedType | 'clerk' | 'simple') & { cssLayerN export type Theme = { /** * A theme used as the base theme for the components. - * For further customisation, you can use the {@link Theme.options}, {@link Theme.variables} and {@link Theme.elements} props. + * For further customization, you can use the {@link Theme.options}, {@link Theme.variables} and {@link Theme.elements} props. * * Supports both object-based themes and string-based themes: * diff --git a/packages/ui/src/styledSystem/StyleCacheProvider.tsx b/packages/ui/src/styledSystem/StyleCacheProvider.tsx index 55ee736f5f4..0863f32172b 100644 --- a/packages/ui/src/styledSystem/StyleCacheProvider.tsx +++ b/packages/ui/src/styledSystem/StyleCacheProvider.tsx @@ -7,9 +7,9 @@ import React, { useMemo } from 'react'; const el = document.querySelector('style#cl-style-insertion-point'); type StyleCacheProviderProps = React.PropsWithChildren<{ - /** Optional nonce value for CSP (Content Security Policy) */ + /** The nonce value for CSP (Content Security Policy). */ nonce?: string; - /** Optional CSS layer name to wrap styles in */ + /** The CSS layer name to wrap styles in. */ cssLayerName?: string; }>; diff --git a/packages/vue/src/plugin.ts b/packages/vue/src/plugin.ts index 0aba60a8d0c..7c6ff11109e 100644 --- a/packages/vue/src/plugin.ts +++ b/packages/vue/src/plugin.ts @@ -32,7 +32,7 @@ export type PluginOptions = Without< initialState?: InitialState; appearance?: Appearance; /** - * Optional object to use the bundled Clerk UI instead of loading from CDN. + * An object to use the bundled Clerk UI instead of loading from CDN. * Import `ui` from `@clerk/ui` and pass it here to bundle the UI with your application. * When omitted, UI is loaded from Clerk's CDN. */ diff --git a/tsconfig.typedoc.json b/tsconfig.typedoc.json index f2461cbc421..71338f87dc4 100644 --- a/tsconfig.typedoc.json +++ b/tsconfig.typedoc.json @@ -6,5 +6,5 @@ "strict": false, "importHelpers": false }, - "exclude": ["node_modules", "dist", "**/dist/**", "packages/*/dist/**", "**/*.test.ts', '**/*.test.tsx"] + "exclude": ["node_modules", "dist", "**/dist/**", "packages/*/dist/**", "**/*.test.ts", "**/*.test.tsx"] } diff --git a/turbo.json b/turbo.json index 0c18ed94be5..d19e070127a 100644 --- a/turbo.json +++ b/turbo.json @@ -339,7 +339,14 @@ }, "//#typedoc:generate": { "dependsOn": ["@clerk/nextjs#build", "@clerk/react#build", "@clerk/shared#build"], - "inputs": ["tsconfig.typedoc.json", "typedoc.config.mjs"], + "inputs": [ + "tsconfig.typedoc.json", + "typedoc.config.mjs", + ".typedoc/reference-objects.mjs", + ".typedoc/comment-utils.mjs", + ".typedoc/extract-methods.mjs", + ".typedoc/extract-returns-and-params.mjs" + ], "outputs": [".typedoc/**"], "outputLogs": "new-only" }, diff --git a/typedoc.config.mjs b/typedoc.config.mjs index bbd02278b1a..56db774f8a2 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -85,7 +85,13 @@ const config = { theme: 'clerkTheme', router: 'clerk-router', readme: 'none', - notRenderedTags: [...OptionDefaults.notRenderedTags, ...CUSTOM_BLOCK_TAGS], + notRenderedTags: [ + ...OptionDefaults.notRenderedTags, + ...CUSTOM_BLOCK_TAGS, + /** Parsed for router/theme; must not appear as a doc section (otherwise renders as **Inline**). */ + '@inline', + '@inlineType', + ], packageOptions: { includeVersion: false, excludePrivate: true, @@ -96,7 +102,23 @@ const config = { excludeNotDocumented: true, gitRevision: 'main', blockTags: [...OptionDefaults.blockTags, ...CUSTOM_BLOCK_TAGS], - modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental')], + modifierTags: [ + ...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), + /** Suppresses the Parameters table in `.typedoc/extract-methods.mjs` method MDX. */ + '@skipParametersSection', + /** + * On a reference-object property whose value is an inline object type: omit the parent from the main Properties table; + * extract each callable member as `methods/-.mdx` and each non-callable object member as a nested heading + property table (see `.typedoc/extract-methods.mjs`). + */ + '@extractMethods', + /** Type-only / router hints; not user-facing prose (see `notRenderedTags`). */ + '@inline', + '@inlineType', + ], + /** + * Stops TypeDoc from generating standalone pages for inline types. + */ + excludeTags: OptionDefaults.excludeTags.filter(tag => tag !== '@inline' && tag !== '@inlineType'), exclude: ['src/**/*.test.ts', 'src/**/*.test.tsx'], readme: 'none', disableGit: true,