Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .typedoc/custom-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ 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'],
];

/**
Expand Down Expand Up @@ -127,6 +128,10 @@ function getRelativeLinkReplacements() {

function getCatchAllReplacements() {
return [
{
pattern: /(?<![\[\w`])`?APIKeysNamespace`?(?![\]\w`])/g,
replace: '[`APIKeysNamespace`](/docs/reference/objects/api-keys)',
},
{
pattern: /(?<![\[\w`])`Appearance`\\<`Theme`\\>/g,
replace: '[`Appearance<Theme>`](/docs/guides/customizing-clerk/appearance-prop/overview)',
Expand Down
119 changes: 118 additions & 1 deletion .typedoc/custom-theme.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-check
import { ReflectionKind, ReflectionType, UnionType } from 'typedoc';
import { IntersectionType, ReferenceType, ReflectionKind, ReflectionType, UnionType } from 'typedoc';
import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown';

/**
Expand Down Expand Up @@ -48,6 +48,21 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext {

this.partials = {
...superPartials,
/**
* Drop function-valued interface/class properties from the properties table (they remain TypeScript "Property"
* members because they use property syntax). Keeps data fields and object namespaces in the table.
*/
/**
* @param {import('typedoc').DeclarationReflection[]} model
* @param {Parameters<typeof superPartials.propertiesTable>[1]} [options]
*/
propertiesTable: (model, options) => {
if (!Array.isArray(model)) {
return superPartials.propertiesTable(/** @type {any} */ (model), options);
}
const filtered = model.filter(prop => !isCallableInterfaceProperty(prop, this.helpers));
return superPartials.propertiesTable(filtered, 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
Expand Down Expand Up @@ -637,3 +652,105 @@ 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.
*
* @param {import('typedoc').Type | undefined} t
* @param {import('typedoc-plugin-markdown').MarkdownThemeContext['helpers']} helpers
* @param {Set<number>} 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;
}
48 changes: 48 additions & 0 deletions packages/shared/src/types/apiKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,69 @@ import type { ClerkPaginatedResponse } from './pagination';
import type { ClerkResource } from './resource';

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<string, any> | 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;
/**
* An optional 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;
}

Expand Down
Loading
Loading