From 0887de968e146c37756481a1464489b5cbd9d4df Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Wed, 9 Sep 2026 17:42:46 -0600 Subject: [PATCH 1/6] fix(repo): flatten Pick parameters in TypeDoc output --- .changeset/thick-singers-pick.md | 2 + ...ization-resource-methods-create-domain.mdx | 21 +++++++ .typedoc/__tests__/extract-methods.test.ts | 6 ++ .typedoc/custom-theme.mjs | 62 ++++++++++++++++++- 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 .changeset/thick-singers-pick.md create mode 100644 .typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx diff --git a/.changeset/thick-singers-pick.md b/.changeset/thick-singers-pick.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/thick-singers-pick.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx b/.typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx new file mode 100644 index 00000000000..8271dc358a2 --- /dev/null +++ b/.typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx @@ -0,0 +1,21 @@ +### `createDomain()` + +Creates a new domain. + +Returns an [`OrganizationDomainResource`](/docs/reference/types/organization-domain-resource) object. + +> [!WARNING] +> You must have [**Verified domains**](/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. + +```typescript +function createDomain(domainName: string, params?: Pick): Promise +``` + +#### Parameters + + +| Parameter | Type | Description | +| ------ | ------ | ------ | +| `domainName` | `string` | The name of the domain to create. | +| `params?` | `Pick`\<[`CreateOrganizationDomainParams`](#create-organization-domain-params), `"enrollmentMode"`\> | Optional parameters, including the `enrollmentMode` to assign to the new domain. | +| `params?.enrollmentMode?` | "manual_invitation" \| "automatic_invitation" \| "automatic_suggestion" \| "enterprise_sso" | The enrollment mode that determines how matching users are added to the Organization. Defaults to `manual_invitation`. | diff --git a/.typedoc/__tests__/extract-methods.test.ts b/.typedoc/__tests__/extract-methods.test.ts index d62f715d917..941021557c4 100644 --- a/.typedoc/__tests__/extract-methods.test.ts +++ b/.typedoc/__tests__/extract-methods.test.ts @@ -13,6 +13,7 @@ import { describe, expect, it } from 'vitest'; * - `methods/sign-out.mdx` – simple zero-arg callable * - `methods/handle-redirect-callback.mdx` – multi-param `parametersTable` with nested rows * - `methods/handle-email-link-verification.mdx` – required parent (`params`) flattened to `.` + * - `methods/create-domain.mdx` – `Pick` parameter flattened to only the selected property * - `methods/join-waitlist.mdx` – single nominal-param section (`JoinWaitlistParams`) * - `methods/create.mdx` (api-key) – another single-nominal-param case + warning callout * - `methods/check-authorization.mdx` – generic instantiation (`CheckAuthorization`) @@ -45,6 +46,11 @@ describe('extract-methods snapshots', () => { await expect(content).toMatchFileSnapshot('./__snapshots__/clerk-methods-handle-email-link-verification.mdx'); }); + it('Pick parameter includes only selected properties: organization.createDomain()', async () => { + const content = await readGenerated('shared/organization-resource/methods/create-domain.mdx'); + await expect(content).toMatchFileSnapshot('./__snapshots__/organization-resource-methods-create-domain.mdx'); + }); + it('single nominal-param section: clerk.joinWaitlist()', async () => { const content = await readGenerated('shared/clerk/methods/join-waitlist.mdx'); await expect(content).toMatchFileSnapshot('./__snapshots__/clerk-methods-join-waitlist.mdx'); diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index c2c0d1c79c3..d78e55136b4 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -446,7 +446,40 @@ function hasDefaultValuesForParameters(parameters) { } /** - * Object shape for a parameter: inline `{ … }`, optional-wrapped, or reference to a type alias / interface. + * Collects string literal members from a type used as `Pick`'s key argument. + * + * @param {import('typedoc').Type | undefined} t + * @returns {string[] | undefined} + */ +function getPickPropertyNames(t) { + const unwrapped = unwrapOptional(t); + if (!unwrapped || typeof unwrapped !== 'object') { + return undefined; + } + if (unwrapped.type === 'literal') { + const literal = /** @type {import('typedoc').LiteralType} */ (unwrapped); + if (typeof literal.value === 'string') { + return [literal.value]; + } + return undefined; + } + if (!isUnionTypeDoc(unwrapped)) { + return undefined; + } + const names = []; + const union = /** @type {import('typedoc').UnionType} */ (unwrapped); + for (const type of union.types) { + const nestedNames = getPickPropertyNames(type); + if (!nestedNames) { + return undefined; + } + names.push(...nestedNames); + } + return names; +} + +/** + * Object shape for a parameter: inline `{ … }`, optional-wrapped, reference to a type alias / interface, or `Pick` with literal keys. * * @param {import('typedoc').Type | undefined} t * @returns {import('typedoc').DeclarationReflection | undefined} @@ -470,6 +503,33 @@ function getParameterObjectShapeDeclaration(t) { } if (o.type === 'reference') { const ref = /** @type {import('typedoc').ReferenceType} */ (t); + if (ref.name === 'Pick' && ref.package === 'typescript' && ref.typeArguments?.length === 2) { + const [sourceType, keysType] = ref.typeArguments; + const propertyNames = getPickPropertyNames(keysType); + if (!propertyNames?.length) { + return undefined; + } + const sourceDecl = getParameterObjectShapeDeclaration(sourceType); + const sourceRef = sourceType.type === 'reference' ? sourceType.reflection : undefined; + const sourceWithChildren = + sourceDecl ?? + (sourceRef && 'children' in sourceRef + ? /** @type {import('typedoc').DeclarationReflection} */ (sourceRef) + : undefined); + if (!sourceWithChildren?.children?.length) { + return undefined; + } + const selected = new Set(propertyNames); + const children = sourceWithChildren.children.filter(child => selected.has(child.name)); + if (children.length !== selected.size) { + return undefined; + } + return /** @type {import('typedoc').DeclarationReflection} */ ({ + ...sourceWithChildren, + kind: ReflectionKind.TypeLiteral, + children, + }); + } const sym = ref.reflection; if (!sym) { return undefined; From ceb7a342640bd0fc64b2c5f9fc49a2527501a440 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 10 Sep 2026 10:20:02 -0600 Subject: [PATCH 2/6] fix(repo): unlink flattened Pick source types --- ...ization-resource-methods-create-domain.mdx | 2 +- .typedoc/__tests__/extract-methods.test.ts | 2 +- .typedoc/custom-theme.mjs | 28 ++++++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx b/.typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx index 8271dc358a2..882997f821f 100644 --- a/.typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx +++ b/.typedoc/__tests__/__snapshots__/organization-resource-methods-create-domain.mdx @@ -17,5 +17,5 @@ function createDomain(domainName: string, params?: Pick | Optional parameters, including the `enrollmentMode` to assign to the new domain. | +| `params?` | `Pick`\<`CreateOrganizationDomainParams`, `"enrollmentMode"`\> | Optional parameters, including the `enrollmentMode` to assign to the new domain. | | `params?.enrollmentMode?` | "manual_invitation" \| "automatic_invitation" \| "automatic_suggestion" \| "enterprise_sso" | The enrollment mode that determines how matching users are added to the Organization. Defaults to `manual_invitation`. | diff --git a/.typedoc/__tests__/extract-methods.test.ts b/.typedoc/__tests__/extract-methods.test.ts index 941021557c4..3d0b6fe0882 100644 --- a/.typedoc/__tests__/extract-methods.test.ts +++ b/.typedoc/__tests__/extract-methods.test.ts @@ -46,7 +46,7 @@ describe('extract-methods snapshots', () => { await expect(content).toMatchFileSnapshot('./__snapshots__/clerk-methods-handle-email-link-verification.mdx'); }); - it('Pick parameter includes only selected properties: organization.createDomain()', async () => { + it('Pick parameter includes only selected properties without linking the full type: organization.createDomain()', async () => { const content = await readGenerated('shared/organization-resource/methods/create-domain.mdx'); await expect(content).toMatchFileSnapshot('./__snapshots__/organization-resource-methods-create-domain.mdx'); }); diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index d78e55136b4..3fb5805d403 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -564,6 +564,31 @@ function shouldFlattenInlineObjectParameter(decl) { return Boolean(only?.comment?.hasVisibleComponent()); } +/** + * Whether a parameter is a built-in `Pick` that will be flattened into nested rows. Its source type should not + * link to the full unpicked declaration, which documents properties the parameter does not accept. + * + * @param {import('typedoc').Type | undefined} t + */ +function isFlattenedPickParameter(t) { + const unwrapped = unwrapOptional(t); + if (!unwrapped || unwrapped.type !== 'reference') { + return false; + } + const ref = /** @type {import('typedoc').ReferenceType} */ (unwrapped); + if (ref.name !== 'Pick' || ref.package !== 'typescript' || ref.typeArguments?.length !== 2) { + return false; + } + return shouldFlattenInlineObjectParameter(getParameterObjectShapeDeclaration(t)); +} + +/** + * @param {string} value + */ +function stripMarkdownLinks(value) { + return value.replace(/\[([^\[\]]*)\]\((.*?)\)/gm, '$1'); +} + /** * Same as typedoc-plugin-markdown `member.parametersTable`, with `shouldFlattenInlineObjectParameter` and `getParameterObjectShapeDeclaration`. * @@ -641,12 +666,13 @@ function clerkParametersTable(model) { const optional = isOptional ? '?' : ''; row.push(`${rest}${backTicks(`${parameter.name}${optional}`)}`); if (parameter.type) { - const displayType = + const renderedType = parameter.type instanceof ReflectionType ? this.partials.reflectionType(parameter.type, { forceCollapse: true, }) : this.partials.someType(parameter.type); + const displayType = isFlattenedPickParameter(parameter.type) ? stripMarkdownLinks(renderedType) : renderedType; row.push(removeLineBreaks(displayType)); } if (showDefaults) { From 0bbd87ed81917f865e3247488e07d05b1e034216 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 10 Sep 2026 12:39:12 -0600 Subject: [PATCH 3/6] fix(repo): guard Pick source reflection kinds --- .typedoc/__tests__/custom-theme.test.mts | 31 ++++++++++++++++++++++++ .typedoc/custom-theme.mjs | 29 +++++++++++++++------- 2 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 .typedoc/__tests__/custom-theme.test.mts diff --git a/.typedoc/__tests__/custom-theme.test.mts b/.typedoc/__tests__/custom-theme.test.mts new file mode 100644 index 00000000000..4b945e4bf7f --- /dev/null +++ b/.typedoc/__tests__/custom-theme.test.mts @@ -0,0 +1,31 @@ +import { ReflectionKind, type Type } from 'typedoc'; +import { describe, expect, it } from 'vitest'; + +import { getParameterObjectShapeDeclaration } from '../custom-theme.mjs'; + +describe('getParameterObjectShapeDeclaration', () => { + it('does not resolve Pick sources whose declaration kind is unsupported', () => { + const sourceType = { + type: 'reference', + name: 'ExampleClass', + reflection: { + kind: ReflectionKind.Class, + children: [ + { + name: 'someMethodName', + kind: ReflectionKind.Method, + signatures: [{}], + }, + ], + }, + } as unknown as Type; + const pickType = { + type: 'reference', + name: 'Pick', + package: 'typescript', + typeArguments: [sourceType, { type: 'literal', value: 'someMethodName' }], + } as unknown as Type; + + expect(getParameterObjectShapeDeclaration(pickType)).toBeUndefined(); + }); +}); diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 3fb5805d403..391656329e1 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -478,6 +478,17 @@ function getPickPropertyNames(t) { return names; } +/** + * @param {import('typedoc').Type | undefined} t + * @returns {boolean} + */ +function isPickReferenceType(t) { + if (!isReferenceTypeDoc(t)) { + return false; + } + return t.name === 'Pick' && t.package === 'typescript' && t.typeArguments?.length === 2; +} + /** * Object shape for a parameter: inline `{ … }`, optional-wrapped, reference to a type alias / interface, or `Pick` with literal keys. * @@ -503,8 +514,10 @@ function getParameterObjectShapeDeclaration(t) { } if (o.type === 'reference') { const ref = /** @type {import('typedoc').ReferenceType} */ (t); - if (ref.name === 'Pick' && ref.package === 'typescript' && ref.typeArguments?.length === 2) { - const [sourceType, keysType] = ref.typeArguments; + if (isPickReferenceType(ref)) { + const [sourceType, keysType] = /** @type {[import('typedoc').Type, import('typedoc').Type]} */ ( + ref.typeArguments + ); const propertyNames = getPickPropertyNames(keysType); if (!propertyNames?.length) { return undefined; @@ -513,7 +526,9 @@ function getParameterObjectShapeDeclaration(t) { const sourceRef = sourceType.type === 'reference' ? sourceType.reflection : undefined; const sourceWithChildren = sourceDecl ?? - (sourceRef && 'children' in sourceRef + (sourceRef && + (sourceRef.kind === ReflectionKind.TypeAlias || sourceRef.kind === ReflectionKind.Interface) && + 'children' in sourceRef ? /** @type {import('typedoc').DeclarationReflection} */ (sourceRef) : undefined); if (!sourceWithChildren?.children?.length) { @@ -572,11 +587,7 @@ function shouldFlattenInlineObjectParameter(decl) { */ function isFlattenedPickParameter(t) { const unwrapped = unwrapOptional(t); - if (!unwrapped || unwrapped.type !== 'reference') { - return false; - } - const ref = /** @type {import('typedoc').ReferenceType} */ (unwrapped); - if (ref.name !== 'Pick' || ref.package !== 'typescript' || ref.typeArguments?.length !== 2) { + if (!isPickReferenceType(unwrapped)) { return false; } return shouldFlattenInlineObjectParameter(getParameterObjectShapeDeclaration(t)); @@ -2040,4 +2051,4 @@ function isCallablePropertyValueType(t, helpers, seenReflectionIds) { return false; } -export { isCallableInterfaceProperty }; +export { getParameterObjectShapeDeclaration, isCallableInterfaceProperty }; From c0af671c7b64df12713e593ac7ed5c3ab4f23eec Mon Sep 17 00:00:00 2001 From: Michael Novotny Date: Thu, 10 Sep 2026 15:40:06 -0500 Subject: [PATCH 4/6] test(repo): cover Pick parameter flattening resolvers Export getPickPropertyNames and extend custom-theme.test.mts with the multi-key case and the fail-closed branches (missing keys, non-literal keys, Omit) that the generated-output snapshot does not exercise. Co-Authored-By: Claude Opus 4.8 --- .typedoc/__tests__/custom-theme.test.mts | 82 +++++++++++++++++++++++- .typedoc/custom-theme.mjs | 2 +- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/.typedoc/__tests__/custom-theme.test.mts b/.typedoc/__tests__/custom-theme.test.mts index 4b945e4bf7f..4d286bc462e 100644 --- a/.typedoc/__tests__/custom-theme.test.mts +++ b/.typedoc/__tests__/custom-theme.test.mts @@ -1,9 +1,50 @@ import { ReflectionKind, type Type } from 'typedoc'; import { describe, expect, it } from 'vitest'; -import { getParameterObjectShapeDeclaration } from '../custom-theme.mjs'; +import { getParameterObjectShapeDeclaration, getPickPropertyNames } from '../custom-theme.mjs'; + +const literal = (value: unknown) => ({ type: 'literal', value }) as unknown as Type; +const union = (...types: unknown[]) => ({ type: 'union', types }) as unknown as Type; + +describe('getPickPropertyNames', () => { + it('returns the single key for a string literal', () => { + expect(getPickPropertyNames(literal('enrollmentMode'))).toEqual(['enrollmentMode']); + }); + + it('flattens a union of string literals', () => { + expect(getPickPropertyNames(union(literal('a'), literal('b')))).toEqual(['a', 'b']); + }); + + it('recurses into nested unions', () => { + expect(getPickPropertyNames(union(literal('a'), union(literal('b'), literal('c'))))).toEqual(['a', 'b', 'c']); + }); + + it('bails on a non-string literal key', () => { + expect(getPickPropertyNames(literal(0))).toBeUndefined(); + }); + + it('bails when any union arm is not a string literal', () => { + expect(getPickPropertyNames(union(literal('a'), { type: 'reference', name: 'Foo' }))).toBeUndefined(); + }); +}); describe('getParameterObjectShapeDeclaration', () => { + const sourceInterface = (...names: string[]) => ({ + reflection: { + kind: ReflectionKind.Interface, + name: 'Src', + children: names.map(name => ({ name })), + }, + }); + + const pick = (source: unknown, keys: unknown) => + ({ + type: 'reference', + name: 'Pick', + package: 'typescript', + typeArguments: [{ type: 'reference', ...(source as object) }, keys], + }) as unknown as Type; + it('does not resolve Pick sources whose declaration kind is unsupported', () => { const sourceType = { type: 'reference', @@ -28,4 +69,43 @@ describe('getParameterObjectShapeDeclaration', () => { expect(getParameterObjectShapeDeclaration(pickType)).toBeUndefined(); }); + + it('selects only the picked properties from a multi-key Pick', () => { + const decl = getParameterObjectShapeDeclaration( + pick(sourceInterface('name', 'enrollmentMode', 'other'), union(literal('name'), literal('enrollmentMode'))), + ); + expect(decl?.children?.map(child => child.name)).toEqual(['name', 'enrollmentMode']); + }); + + it('does not mutate the source declaration', () => { + const source = sourceInterface('name', 'enrollmentMode'); + getParameterObjectShapeDeclaration(pick(source, literal('enrollmentMode'))); + expect(source.reflection.children.map(child => child.name)).toEqual(['name', 'enrollmentMode']); + }); + + it('fails closed when a picked key is not a property of the source', () => { + expect(getParameterObjectShapeDeclaration(pick(sourceInterface('name'), literal('missing')))).toBeUndefined(); + }); + + it('fails closed for non-literal keys', () => { + expect( + getParameterObjectShapeDeclaration( + pick(sourceInterface('name', 'enrollmentMode'), { type: 'reference', name: 'keyof Src' }), + ), + ).toBeUndefined(); + }); + + it('does not treat Omit as a flattenable builtin', () => { + const omit = { + type: 'reference', + name: 'Omit', + package: 'typescript', + reflection: undefined, + typeArguments: [ + { type: 'reference', ...sourceInterface('name', 'enrollmentMode') }, + { type: 'literal', value: 'name' }, + ], + } as unknown as Type; + expect(getParameterObjectShapeDeclaration(omit)).toBeUndefined(); + }); }); diff --git a/.typedoc/custom-theme.mjs b/.typedoc/custom-theme.mjs index 391656329e1..9b10a4651e7 100644 --- a/.typedoc/custom-theme.mjs +++ b/.typedoc/custom-theme.mjs @@ -2051,4 +2051,4 @@ function isCallablePropertyValueType(t, helpers, seenReflectionIds) { return false; } -export { getParameterObjectShapeDeclaration, isCallableInterfaceProperty }; +export { getParameterObjectShapeDeclaration, getPickPropertyNames, isCallableInterfaceProperty }; From 5568fb7fa7d1cde6e2438f543a162b1e26af997b Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 10 Sep 2026 16:42:33 -0600 Subject: [PATCH 5/6] fix(localizations): format Hebrew OAuth consent messages --- packages/localizations/src/he-IL.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 39fa6e8bb4c..02598076485 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -935,23 +935,21 @@ export const heIL: LocalizationResource = { membershipRole__basicMember: 'חבר', membershipRole__guestMember: 'אורח', oauthConsent: { - action__allow: "אישור", - action__deny: "דחייה", - offlineAccessNotice: "תישאר מחובר עד שתתנתק או תבטל את הגישה.", - redirectNotice: "אם תאשר גישה, האפליקציה תעביר אותך אל {{domainAction}}.", + action__allow: 'אישור', + action__deny: 'דחייה', + offlineAccessNotice: 'תישאר מחובר עד שתתנתק או תבטל את הגישה.', + redirectNotice: 'אם תאשר גישה, האפליקציה תעביר אותך אל {{domainAction}}.', redirectUriModal: { - subtitle: - "ודא שאתה סומך על {{applicationName}} ושכתובת URL זו שייכת ל-{{applicationName}}.", - title: "כתובת להפניה", + subtitle: 'ודא שאתה סומך על {{applicationName}} ושכתובת URL זו שייכת ל-{{applicationName}}.', + title: 'כתובת להפניה', }, scopeList: { - title: "פעולה זו תאפשר ל-{{applicationName}} גישה אל:", + title: 'פעולה זו תאפשר ל-{{applicationName}} גישה אל:', privateMetadata: undefined, }, - subtitle: "מבקש גישה ל-{{applicationName}} בשם {{identifier}}", - viewFullUrl: "הצג כתובת מלאה", - warning: - "ודא שאתה סומך על {{applicationName}} ({{domainAction}}). ייתכן שתשתף מידע רגיש עם אתר או אפליקציה זו.", + subtitle: 'מבקש גישה ל-{{applicationName}} בשם {{identifier}}', + viewFullUrl: 'הצג כתובת מלאה', + warning: 'ודא שאתה סומך על {{applicationName}} ({{domainAction}}). ייתכן שתשתף מידע רגיש עם אתר או אפליקציה זו.', }, oauthDeviceVerification: { action__tryAnotherCode: undefined, From fe3a2f38f90052072bce52cf37150476621f42c1 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 10 Sep 2026 16:48:40 -0600 Subject: [PATCH 6/6] fix(localizations): regenerate Hebrew localization --- packages/localizations/src/he-IL.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 02598076485..b87cd52df23 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -944,8 +944,8 @@ export const heIL: LocalizationResource = { title: 'כתובת להפניה', }, scopeList: { - title: 'פעולה זו תאפשר ל-{{applicationName}} גישה אל:', privateMetadata: undefined, + title: 'פעולה זו תאפשר ל-{{applicationName}} גישה אל:', }, subtitle: 'מבקש גישה ל-{{applicationName}} בשם {{identifier}}', viewFullUrl: 'הצג כתובת מלאה',