diff --git a/.changeset/hotload-boundary-abi-inventory.md b/.changeset/hotload-boundary-abi-inventory.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/hotload-boundary-abi-inventory.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a80ec1b0b37..a0f8ef42e07 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,6 +17,7 @@ - [ ] `pnpm test` runs as expected. - [ ] `pnpm build` runs as expected. +- [ ] (If applicable) Changes to `packages/clerk-js/src/core/clerk.ts`, `packages/react/src/isomorphicClerk.ts`, or `@clerk/ui` runtime calls have been checked against `docs/hotload-boundary-abi-inventory.md`. - [ ] (If applicable) [JSDoc comments](https://jsdoc.app/about-getting-started.html) have been added or updated for any package exports - [ ] (If applicable) [Documentation](https://github.com/clerk/clerk-docs) has been updated diff --git a/docs/hotload-boundary-abi-inventory.md b/docs/hotload-boundary-abi-inventory.md new file mode 100644 index 00000000000..4af6070d56c --- /dev/null +++ b/docs/hotload-boundary-abi-inventory.md @@ -0,0 +1,82 @@ +# Clerk.js Hotload Boundary ABI Inventory + +This inventory tracks `Clerk` and `IsomorphicClerk` members that are internal to +Clerk but consumed across package boundaries. These names are not public product +API, but non-major `@clerk/clerk-js` and `@clerk/ui` releases can be hotloaded +into applications that still run older installed framework SDKs or +`@clerk/shared`. For that reason, any member listed as `hotload-boundary` must +be treated as a compatibility contract until it is explicitly shimmed, +deprecated, or removed in a major version. + +## Boundary Definitions + +| Boundary | Meaning | +| ------------------- | -------------------------------------------------------------------------------------------------------------- | +| `Local-only` | Used only inside the provider package and not a cross-package compatibility surface. | +| `Released-together` | Used across packages that are expected to move together in the same deployed artifact. | +| `Hotload-boundary` | Read or called by code that can remain installed while `@clerk/clerk-js` or `@clerk/ui` updates independently. | + +## Inventory + +| Member | Provider | Consumers | Boundary | Compatibility requirement | Risk | Notes | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `__internal_queryClient` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts` | Historical `@clerk/shared < 4.10.0` query hooks; current shim test in `packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts` | Hotload-boundary | Keep shim until a major-version cleanup plan removes the historical bridge. | High | This is the TanStack Query incident bridge. Removing it makes older installed query hooks stay on the mock client path and can suppress `/me/organization_memberships` requests. | +| `__internal_state` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` fallback proxy | `packages/react/src/hooks/useClerkSignal.ts`; `packages/shared/src/react/hooks/useCheckout.ts` | Hotload-boundary | Keep current `State` shape, including signal getters and `__internal_effect`; incompatible changes need a shim or major. | Medium | The object wraps `alien-signals` behavior. Current evidence proves a cross-package ABI, not a known mixed-version runtime crash. | +| `__internal_environment` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` | `@clerk/shared` billing and organization hooks; `@clerk/expo` resource cache; `@clerk/ui` keyless/dev prompt code; internal `clerk-js` resources | Hotload-boundary | Keep getter and environment resource shape stable for installed package readers; add targeted shims before reshaping. | High | Many feature gates read nested environment data and can silently disable UI if the shape changes. | +| `__internal_getCachedResources` | `@clerk/clerk-js`: assignable hook in `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`: `packages/expo/src/provider/singleton/createClerkInstance.ts` | Hotload-boundary | Keep assignable async hook shape, or provide an Expo compatibility adapter before changing. | High | Used to bootstrap cached environment/client resources in non-standard browsers and native flows. | +| `__internal_reloadInitialResources` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; excluded from `IsomorphicClerk` type forwarding | `@clerk/expo`: provider and native auth views | Hotload-boundary | Keep method or guard old Expo callers with a compatibility path. | High | Used to recover initial resources after native auth/session changes. | +| `__internal_getOption` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` | `@clerk/shared` billing/payment localization; `@clerk/ui` task routing; `clerk-js` sandbox | Hotload-boundary | Keep generic option getter and option-key semantics; major or shim for removal/rename. | Medium | Missing values often degrade behavior rather than throwing, so failures may be quiet. | +| `__internal_attemptToEnableEnvironmentSetting` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` with premount queue | `@clerk/shared`: `packages/shared/src/react/hooks/useAttemptToEnableOrganizations.ts` | Hotload-boundary | Keep return shape `{ isEnabled: boolean }` and premount behavior. | Medium | Drives development-instance enablement prompts for organizations. | +| `__internal_openEnableOrganizationsPrompt` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` | `@clerk/shared` via `__internal_attemptToEnableEnvironmentSetting`; `@clerk/ui` closes prompt from prompt UI | Hotload-boundary | Keep open/close pair until prompt flow is moved behind a stable command boundary. | Medium | Mostly UI flow compatibility; failures can block dev-instance setup flows. | +| `__internal_closeEnableOrganizationsPrompt` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` | `@clerk/ui`: `packages/ui/src/components/devPrompts/EnableOrganizationsPrompt/index.tsx` | Hotload-boundary | Keep close method or make UI tolerate absence before removal. | Low | Optional chaining exists at some call sites, but removal still changes prompt lifecycle behavior. | +| `__internal_loadStripeJs` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` | `@clerk/shared`: `packages/shared/src/react/billing/useStripeClerkLibs.tsx` | Hotload-boundary | Keep async loader returning a Stripe `loadStripe` function; incompatible changes require a compatibility facade. | Medium | Crosses a third-party module boundary, but consumers use the returned loader rather than sharing a long-lived Clerk-owned Stripe object graph. | +| `__internal_createPublicCredentials` | `@clerk/clerk-js`: assignable hook in `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`: `packages/expo/src/provider/singleton/createClerkInstance.ts`; `clerk-js` passkey resources | Hotload-boundary | Keep assignable passkey hook shape for native providers. | Medium | Native passkey integrations attach these hooks to the Clerk instance. | +| `__internal_getPublicCredentials` | `@clerk/clerk-js`: assignable hook in `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`; `clerk-js` sign-in/session passkey resources | Hotload-boundary | Keep assignable passkey hook shape for native providers. | Medium | Breaking it affects passkey get/auth flows. | +| `__internal_isWebAuthnSupported` | `@clerk/clerk-js`: assignable hook in `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`; `clerk-js` sign-in/session/passkey resources | Hotload-boundary | Keep assignable feature-detection hook or provide fallback. | Low | Falls back to web-window checks in browser flows, but native consumers rely on assignment. | +| `__internal_isWebAuthnAutofillSupported` | `@clerk/clerk-js`: assignable hook in `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`; `clerk-js` sign-in resources | Hotload-boundary | Keep assignable feature-detection hook or provide fallback. | Low | Native and autofill-specific; narrower blast radius than credential hooks. | +| `__internal_isWebAuthnPlatformAuthenticatorSupported` | `@clerk/clerk-js`: assignable hook in `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`; `clerk-js` passkey resources | Hotload-boundary | Keep assignable feature-detection hook or provide fallback. | Low | Native and platform-authenticator-specific. | +| `__internal_mountOAuthConsent` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | `@clerk/react`: `packages/react/src/components/uiComponents.tsx` | Hotload-boundary | Keep mount/unmount component command or provide React compatibility wrapper. | Medium | React may queue the mount before the hotloaded runtime is ready. | +| `__internal_unmountOAuthConsent` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | `@clerk/react`: `packages/react/src/components/uiComponents.tsx` | Hotload-boundary | Keep mount/unmount component command or provide React compatibility wrapper. | Medium | Pair must evolve together to avoid leaked mounted nodes. | +| `__internal_openCheckout` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | `@clerk/react`, `@clerk/vue`, `@clerk/astro`, `@clerk/ui`, `@clerk/shared` billing components | Hotload-boundary | Keep drawer command and props shape; incompatible changes need shim/deprecation. | Medium | Used by multiple framework button wrappers and billing UI. | +| `__internal_closeCheckout` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | Current grep finds provider/type/changelog surface; no non-test cross-package runtime call found. | Local-only | Do not remove without re-running consumer grep; if no runtime consumers remain, document as cleanup candidate. | Low | Listed because it is part of the typed drawer pair, but current runtime use appears provider-side only. | +| `__internal_openPlanDetails` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | `@clerk/react`, `@clerk/vue`, `@clerk/astro`, `@clerk/ui` pricing/plans code | Hotload-boundary | Keep drawer command and plan props shape; incompatible changes need shim/deprecation. | Medium | Used from installed framework wrappers and hotloaded UI. | +| `__internal_closePlanDetails` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | Current grep finds provider/type surface; no non-test cross-package runtime call found. | Local-only | Do not remove without re-running consumer grep; if no runtime consumers remain, document as cleanup candidate. | Low | Listed because it is part of the typed drawer pair, but current runtime use appears provider-side only. | +| `__internal_openSubscriptionDetails` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | `@clerk/react`, `@clerk/vue`, `@clerk/astro`, `@clerk/ui` plans code | Hotload-boundary | Keep drawer command and props shape; incompatible changes need shim/deprecation. | Medium | Used by framework wrappers and billing UI. | +| `__internal_closeSubscriptionDetails` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | Current grep finds provider/type surface; no non-test cross-package runtime call found. | Local-only | Do not remove without re-running consumer grep; if no runtime consumers remain, document as cleanup candidate. | Low | Listed because it is part of the typed drawer pair, but current runtime use appears provider-side only. | +| `__internal_openReverification` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | `@clerk/shared`: `packages/shared/src/react/hooks/useReverification.ts` | Hotload-boundary | Keep modal command and props shape; incompatible changes need shim/deprecation. | Medium | Reverification is a user-facing security flow. | +| `__internal_closeReverification` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` premount queue | Current grep finds provider/type/test surface; no non-test cross-package runtime call found. | Local-only | Do not remove without re-running consumer grep; if no runtime consumers remain, document as cleanup candidate. | Low | Open command is a real boundary; close command appears less exposed today. | +| `__internal_lastEmittedResources` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` | `@clerk/react` auth hooks; `@clerk/shared` base resource hooks; `@clerk/ui` component context selectors | Hotload-boundary | Keep stable resources object fields (`client`, `session`, `user`, `organization`) and emission timing. | High | Resource hooks can silently fall back to stale initial state if this shape disappears. | +| `__internal_onBeforeRequest` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`; `@clerk/chrome-extension` | Hotload-boundary | Keep request callback registration shape, or provide package-specific adapters before changing. | Medium | Native and extension packages mutate requests for platform-specific auth behavior. | +| `__internal_onAfterResponse` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts` | `@clerk/expo`; `@clerk/chrome-extension` | Hotload-boundary | Keep response callback registration shape, or provide package-specific adapters before changing. | Medium | Native and extension packages process responses for platform-specific auth behavior. | +| `__internal_updateProps` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; `@clerk/react`: `packages/react/src/isomorphicClerk.ts` loaded bridge | `@clerk/vue`: `packages/vue/src/utils/updateClerkOptions.ts`; historical codemod references | Hotload-boundary | Keep update method and merged-options behavior; incompatible changes need a framework adapter or major. | Medium | Vue updates hotloaded Clerk runtime options after app-level prop changes. | +| `__internal_setActiveInProgress` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; excluded from `IsomorphicLoadedClerk` but read from loaded Clerk | `@clerk/ui`: sign-in/sign-up/task guards and SSO callback paths | Hotload-boundary | Keep boolean read semantics or make UI tolerate absence before changing. | Medium | Guards prevent UI from navigating away while `setActive` is in flight. | +| `__internal_navigateWithError` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts` | `@clerk/ui`: sign-in error navigation paths | Hotload-boundary | Keep method until UI migrates to a stable error-navigation command. | Medium | Used to persist a FAPI error while navigating to a previous step. | +| `__internal_addNavigationListener` | `@clerk/clerk-js`: `packages/clerk-js/src/core/clerk.ts`; intentionally excluded from `IsomorphicLoadedClerk` type | `@clerk/ui`: `packages/ui/src/router/VirtualRouter.tsx` | Hotload-boundary | Keep listener registration shape for virtual routing. | Medium | Removal can break modal/drawer virtual-router sync with external navigation. | + +## Current Decisions + +- `__internal_queryClient`: keep as a backward-compatibility shim. Removal is a + major-version cleanup item only after the mixed-version query-hook regression + is covered. +- `__internal_state`: treat as hotload-boundary ABI now. Before changing the + `State` object, inventory the signal methods used by `@clerk/react` and + `@clerk/shared` and add focused compatibility coverage. +- UI drawer/modal commands: keep as hotload-boundary commands until framework + button wrappers and `@clerk/ui` move to a documented command boundary. +- Expo/native hooks: keep assignable hook fields until Expo has a versioned + adapter for cached resources, request callbacks, and passkey credential + integration. + +## Review Rule + +When a PR removes, renames, changes the return shape of, or changes the side +effects of any member in this inventory, the PR must answer: + +1. Which installed packages can call the old shape? +2. Can a latest hotloaded `@clerk/clerk-js` or `@clerk/ui` pair with that older + installed code? +3. Is the change covered by a compatibility shim, a mixed-version regression + test, or a major-version migration plan? + +If any answer is unknown, treat the change as blocked until the consumer status +is known.