Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
chore(clerk-js): manifest for cross-bundle internal ABI by jacekradko · Pull Request #8567 · clerk/javascript · GitHub
Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(clerk-js): manifest for cross-bundle internal ABI by jacekradko · Pull Request #8567 · clerk/javascript · GitHub
Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(clerk-js): manifest for cross-bundle internal ABI by jacekradko · Pull Request #8567 · clerk/javascript · GitHub
Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' chore(clerk-js): manifest for cross-bundle internal ABI by jacekradko · Pull Request #8567 · clerk/javascript · GitHub
Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(clerk-js): manifest for cross-bundle internal ABI by jacekradko · Pull Request #8567 · clerk/javascript · GitHub
Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(clerk-js): manifest for cross-bundle internal ABI by jacekradko · Pull Request #8567 · clerk/javascript · GitHub
Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); chore(clerk-js): manifest for cross-bundle internal ABI by jacekradko · Pull Request #8567 · clerk/javascript · GitHub
Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/clerk-js-abi-contract-tests.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
269 changes: 269 additions & 0 deletions packages/clerk-js/src/core/__abi__/manifest.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
/**
* Cross-bundle ABI manifest for `Clerk` and `IsomorphicClerk`.
*
* Each entry below is a runtime `__internal_*` member that is read or written
* by an independently-shipped npm package (e.g. `@clerk/shared`, `@clerk/react`,
* `@clerk/nextjs`, `@clerk/expo`, `@clerk/tanstack-react-start`). Because
* `@clerk/clerk-js` auto-updates from the CDN underneath those pinned-on-npm
* consumers, removing or reshaping any member here is a breaking change for
* every still-supported consumer that calls it, even if no monorepo file does.
*
* Scope (intentionally narrow):
* - Members on the live `Clerk` instance (and `IsomorphicClerk` queue)
* - That are reached at runtime from outside `@clerk/clerk-js`
*
* Out of scope (separate manifests, separate tests):
* - Script-loader options (`__internal_clerkJSUrl`, `__internal_clerkJSVersion`,
* `__internal_clerkUIUrl`, `__internal_clerkUIVersion`)
* - Window-level globals (`window.__internal_onBeforeSetActive`,
* `window.__internal_onAfterSetActive`, `window.__internal_ClerkUICtor`)
* - Resource-level internals (`__internal_toSnapshot` on Resource shapes)
*
* Policy: see `docs/CONTRIBUTING.md` ("Cross-bundle ABI").
* Adding an entry is always fine. Removing or reshaping an entry requires a
* functional compat shim until the SDK compatibility matrix (separate work)
* confirms no still-supported consumer remains.
*
* Sibling tests:
* - `clerk.abi-contract.test.ts` asserts each entry exists on a fresh
* `new Clerk(pk)` with the right `kind`, and pins this list via snapshot.
* - `clerk.abi-usage-scan.test.ts` scans consumer packages and asserts every
* `<clerkInstance>.__internal_X` read corresponds to an entry here.
*/

export type AbiMemberKind =
/** ES getter on the class (`get name() { ... }`). Read-only. */
| 'getter'
/** Bound method or arrow function. `typeof inst[name] === 'function'`. */
| 'method'
/** Plain property with a default value baked in. */
| 'property'
/** Property declared as `T | undefined`, intentionally written by SDKs. */
| 'assignable-slot';

export type AbiMember = {
/** Member name on the `Clerk` instance. */
readonly name: string;
/** How the member is declared on `Clerk`; drives contract-test assertion. */
readonly kind: AbiMemberKind;
/**
* Consumer packages that read or write this member at runtime today.
* Names match `packages/<dir>` not npm names.
*/
readonly consumers: readonly string[];
/**
* Published package versions that still consume this member, even if current
* main no longer does. These keep compat shims in the manifest until the
* compatibility matrix proves they are out of the support window.
*/
readonly historicalConsumers?: readonly {
readonly packageName: string;
readonly range: string;
readonly note: string;
}[];
/** Free-form context (e.g. "restored by #8562 as backward-compat shim"). */
readonly note?: string;
};

export const ABI_MEMBERS = [
// ── Core state and options ─────────────────────────────────────────────
{
name: '__internal_state',
kind: 'property',
consumers: ['react', 'shared'],
},
{
name: '__internal_country',
kind: 'property',
consumers: ['ui'],
},
{
name: '__internal_getOption',
kind: 'method',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_lastEmittedResources',
kind: 'property',
consumers: ['react', 'shared', 'ui'],
},
{
name: '__internal_environment',
kind: 'getter',
consumers: ['react', 'shared', 'expo'],
},
{
name: '__internal_setActiveInProgress',
kind: 'property',
consumers: ['ui'],
},

// ── Modal open/close (consumed by @clerk/react through IsomorphicClerk) ─
{
name: '__internal_openCheckout',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeCheckout',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openPlanDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closePlanDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openSubscriptionDetails',
kind: 'method',
consumers: ['astro', 'react', 'ui'],
},
{
name: '__internal_closeSubscriptionDetails',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openReverification',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_closeReverification',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_openEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_closeEnableOrganizationsPrompt',
kind: 'method',
consumers: ['react', 'ui'],
},
{
name: '__internal_mountOAuthConsent',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_unmountOAuthConsent',
kind: 'method',
consumers: ['react'],
},

// ── Modal-adjacent helpers ─────────────────────────────────────────────
{
name: '__internal_attemptToEnableEnvironmentSetting',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_loadStripeJs',
kind: 'method',
consumers: ['react', 'shared'],
},
{
name: '__internal_setEnvironment',
kind: 'method',
consumers: ['react'],
},
{
name: '__internal_updateProps',
kind: 'method',
consumers: ['astro', 'react', 'vue'],
},

// ── Navigation ─────────────────────────────────────────────────────────
{
name: '__internal_addNavigationListener',
kind: 'method',
consumers: ['ui'],
},
{
name: '__internal_navigateWithError',
kind: 'method',
consumers: ['ui'],
},

// ── Cached / initial resources (read AND written by SDKs) ──────────────
{
name: '__internal_getCachedResources',
kind: 'assignable-slot',
consumers: ['expo'],
note: 'expo assigns this so non-standard browsers can fall back to cached initial resources.',
},
{
name: '__internal_reloadInitialResources',
kind: 'method',
consumers: ['expo'],
},

// ── FAPI hook installation (expo) ──────────────────────────────────────
{
name: '__internal_onBeforeRequest',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},
{
name: '__internal_onAfterResponse',
kind: 'method',
consumers: ['chrome-extension', 'expo'],
},

// ── WebAuthn extension slots (expo assigns these at boot) ──────────────
{
name: '__internal_createPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_getPublicCredentials',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnSupported',
kind: 'assignable-slot',
consumers: ['expo', 'ui'],
},
{
name: '__internal_isWebAuthnAutofillSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},
{
name: '__internal_isWebAuthnPlatformAuthenticatorSupported',
kind: 'assignable-slot',
consumers: ['expo'],
},

// ── Backward-compat shims (added in response to incidents) ─────────────
{
name: '__internal_queryClient',
kind: 'getter',
consumers: [],
historicalConsumers: [
{
packageName: '@clerk/shared',
range: '<4.10.0',
note: '@clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient.',
},
],
note:
'Restored in #8562 after #8434 removed it; @clerk/shared < 4.10.0 reads this to bootstrap its TanStack QueryClient. Removable only when the compatibility matrix confirms no in-window shared version still reads it.',
},
] as const satisfies readonly AbiMember[];

/** Index by name for O(1) lookup in tests. */
export const ABI_MEMBERS_BY_NAME: Readonly<Record<string, AbiMember>> = Object.freeze(
Object.fromEntries(ABI_MEMBERS.map(m => [m.name, m])),
);
Loading
Loading