diff --git a/.changeset/fix-typedoc-inline-object-comment-backfill.md b/.changeset/fix-typedoc-inline-object-comment-backfill.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/fix-typedoc-inline-object-comment-backfill.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.typedoc/custom-plugin.mjs b/.typedoc/custom-plugin.mjs index 53d3b774ce3..ee160a04d23 100644 --- a/.typedoc/custom-plugin.mjs +++ b/.typedoc/custom-plugin.mjs @@ -509,6 +509,119 @@ function collectPropertiesFromType(type, reflectionsByName) { return []; } +/** + * Structural fingerprint for a `Type`. Recurses into composite shapes so two types that only differ in their type arguments (`Foo` vs `Foo`) or in their nested property types (`{ x: string }` vs `{ x: number }`) get distinct fingerprints. Two shapes that produce the same fingerprint are treated as structurally identical and so eligible for cross-pollinating JSDoc comments. + * + * Recursion guard: a single shared `Set` of visited reflection ids threads through every nested call to avoid infinite loops on cyclic types (e.g. a type literal that ultimately references itself). + * + * @param {import('typedoc').SomeType | undefined} type + * @param {Set} [seen] + * @returns {string} + */ +function typeFingerprint(type, seen = new Set()) { + if (!type) return '?'; + const t = + /** @type {{ type?: string; name?: string; value?: unknown; elementType?: import('typedoc').SomeType; types?: import('typedoc').SomeType[]; typeArguments?: import('typedoc').SomeType[]; declaration?: import('typedoc').DeclarationReflection }} */ ( + /** @type {unknown} */ (type) + ); + switch (t.type) { + case 'intrinsic': + return `i:${t.name ?? ''}`; + case 'literal': + return `l:${JSON.stringify(t.value)}`; + case 'reference': { + const args = t.typeArguments?.length ? `<${t.typeArguments.map(a => typeFingerprint(a, seen)).join(',')}>` : ''; + return `r:${t.name ?? ''}${args}`; + } + case 'array': + return `a:${typeFingerprint(t.elementType, seen)}`; + case 'optional': + return `o:${typeFingerprint(t.elementType, seen)}`; + case 'union': + return `u:[${(t.types ?? []) + .map(a => typeFingerprint(a, seen)) + .sort() + .join(',')}]`; + case 'intersection': + return `n:[${(t.types ?? []) + .map(a => typeFingerprint(a, seen)) + .sort() + .join(',')}]`; + case 'reflection': { + const decl = t.declaration; + if (decl?.id != null) { + if (seen.has(decl.id)) return `rf:`; + seen.add(decl.id); + } + const kids = decl?.children?.filter(c => c.kindOf?.(ReflectionKind.Property)) ?? []; + return `rf:[${kids + .map(c => `${c.name}${c.flags?.isOptional ? '?' : ''}:${typeFingerprint(c.type, seen)}`) + .sort() + .join(',')}]`; + } + default: + return t.type ?? '?'; + } +} + +/** + * When TypeScript resolves a type through `Omit<...>` / `Pick<...>` (e.g. `ClerkProviderProps = Omit & { … }`), inline anonymous object literal property types get re-synthesized — and TypeDoc loses the JSDoc on most of their members. Only the first/leading property's comment survives, the rest come through with `comment === undefined`. The same shape elsewhere in the project (e.g. directly under `IsomorphicClerkOptions['telemetry']`) carries all comments correctly. + * + * Match `@kind:typeLiteral` reflections by structural fingerprint (set of `(name, type, optional)` tuples on their property children); within each group, pick the reflection with the most commented children as the source-of-truth and copy missing comments onto its siblings. + * + * @param {import('typedoc').Reflection[]} all + */ +function backfillInlineObjectChildComments(all) { + /** @type {Map} */ + const groups = new Map(); + for (const r of all) { + if (!r.kindOf?.(ReflectionKind.TypeLiteral)) continue; + const decl = /** @type {import('typedoc').DeclarationReflection} */ (r); + const propChildren = decl.children?.filter(c => c.kindOf?.(ReflectionKind.Property)); + if (!propChildren?.length) continue; + const key = propChildren + .map(c => `${c.name}${c.flags?.isOptional ? '?' : ''}:${typeFingerprint(c.type)}`) + .sort() + .join('|'); + if (!groups.has(key)) groups.set(key, []); + /** @type {import('typedoc').DeclarationReflection[]} */ (groups.get(key)).push(decl); + } + + for (const group of groups.values()) { + if (group.length < 2) continue; + /** @type {import('typedoc').DeclarationReflection | null} */ + let best = null; + let bestScore = -1; + for (const refl of group) { + const score = + refl.children?.filter(c => c.kindOf?.(ReflectionKind.Property) && c.comment?.summary?.length).length ?? 0; + if (score > bestScore) { + best = refl; + bestScore = score; + } + } + if (!best || bestScore === 0) continue; + /** @type {Map} */ + const bestByName = new Map(); + for (const c of best.children ?? []) { + if (c.kindOf?.(ReflectionKind.Property)) bestByName.set(c.name, c); + } + for (const refl of group) { + if (refl === best) continue; + for (const child of refl.children ?? []) { + if (!child.kindOf?.(ReflectionKind.Property)) continue; + // Any `.comment` object means TypeDoc found a JSDoc block at the source — including + // intentionally empty comments left over from `@generateWithEmptyComment` after the + // modifier tag is stripped in `EVENT_RESOLVE_END`. Only fill in children that have + // no comment at all. + if (child.comment) continue; + const src = bestByName.get(child.name); + if (src?.comment?.summary?.length) child.comment = src.comment; + } + } + } +} + /** * @param {import('typedoc-plugin-markdown').MarkdownApplication} app */ @@ -565,6 +678,8 @@ export function load(app) { } } } + + backfillInlineObjectChildComments(all); }); app.renderer.on(MarkdownPageEvent.END, output => { diff --git a/packages/shared/src/types/billing.ts b/packages/shared/src/types/billing.ts index 724fa60286f..1db27d37140 100644 --- a/packages/shared/src/types/billing.ts +++ b/packages/shared/src/types/billing.ts @@ -87,6 +87,7 @@ export interface BillingNamespace { /** * Gets the credit balance for the current payer. + * @returns A [`BillingCreditBalanceResource`](https://clerk.com/docs/reference/types/billing-credit-balance-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. */ @@ -94,6 +95,7 @@ export interface BillingNamespace { /** * Gets the credit history for the current payer. + * @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`BillingCreditLedgerResource`](https://clerk.com/docs/reference/types/billing-credit-ledger-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. */ @@ -913,19 +915,19 @@ export interface BillingSubscriptionResource extends ClerkResource { } /** + * The `BillingCreditBalanceResource` type represents the credit balance for a payer. * @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 interface BillingCreditBalanceResource { - /** - * The balance of the credit. - */ + /** The balance of the credit. */ balance: BillingMoneyAmount | null; } +/** + * @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 GetCreditBalanceParams = { - /** - * The ID of the Organization to get the credit balance for. - */ + /** The ID of the Organization to get the credit balance for. */ orgId?: string; }; @@ -940,13 +942,19 @@ export type GetCreditHistoryParams = { }; /** + * The `BillingCreditLedgerResource` type represents a credit ledger entry for the current payer or given Organization. * @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 interface BillingCreditLedgerResource { + /** The ID of the credit ledger entry. */ id: string; + /** The amount of the credit ledger entry. */ amount: BillingMoneyAmount; + /** The type of the source of the credit ledger entry. */ sourceType: string; + /** The ID of the source of the credit ledger entry. */ sourceId: string; + /** The date when the credit ledger entry was created. */ createdAt: Date; } diff --git a/packages/shared/src/types/organizationDomain.ts b/packages/shared/src/types/organizationDomain.ts index eeca290333e..e5a48634084 100644 --- a/packages/shared/src/types/organizationDomain.ts +++ b/packages/shared/src/types/organizationDomain.ts @@ -173,13 +173,13 @@ export interface OrganizationDomainResource extends ClerkResource { totalPendingSuggestions: number; /** * Begins the verification process of a created Organization domain by sending a verification code to the provided email address. - * @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource) object. + * @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain-resource) object. */ prepareAffiliationVerification: (params: PrepareAffiliationVerificationParams) => Promise; /** - * Completes the verification process started by [`prepareAffiliationVerification()`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource#prepare-affiliation-verification), by validating the provided verification code. - * @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource) object. + * Completes the verification process started by [`prepareAffiliationVerification()`](https://clerk.com/docs/reference/types/organization-domain-resource#prepare-affiliation-verification), by validating the provided verification code. + * @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain-resource) object. */ attemptAffiliationVerification: (params: AttemptAffiliationVerificationParams) => Promise; /** @@ -189,7 +189,7 @@ export interface OrganizationDomainResource extends ClerkResource { delete: () => Promise; /** * Updates the enrollment mode of the Verified Domain. - * @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource) object. + * @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain-resource) object. */ updateEnrollmentMode: (params: UpdateEnrollmentModeParams) => Promise; }