From 74dca0b13de8a8e67fcf6e85991935dcf7032721 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 15 May 2026 11:17:04 -0700 Subject: [PATCH 1/2] fix(clerk-js): Restore __internal_queryClient for backward compat with older SDKs --- packages/clerk-js/bundlewatch.config.json | 1 + packages/clerk-js/package.json | 1 + packages/clerk-js/rspack.config.js | 6 ++ .../clerk.internal-query-client.test.ts | 75 +++++++++++++++++++ packages/clerk-js/src/core/clerk.ts | 24 ++++++ packages/clerk-js/src/core/query-core.ts | 1 + pnpm-lock.yaml | 3 + 7 files changed, 111 insertions(+) create mode 100644 packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts create mode 100644 packages/clerk-js/src/core/query-core.ts diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 2bffa793235..bcea517d7e1 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -9,6 +9,7 @@ { "path": "./dist/coinbase*.js", "maxSize": "36KB" }, { "path": "./dist/base-account-sdk*.js", "maxSize": "203KB" }, { "path": "./dist/stripe-vendors*.js", "maxSize": "1KB" }, + { "path": "./dist/query-core-vendors*.js", "maxSize": "11KB" }, { "path": "./dist/zxcvbn-ts-core*.js", "maxSize": "12KB" }, { "path": "./dist/zxcvbn-common*.js", "maxSize": "226KB" } ] diff --git a/packages/clerk-js/package.json b/packages/clerk-js/package.json index b07e0b3e2e4..c9e3b1de213 100644 --- a/packages/clerk-js/package.json +++ b/packages/clerk-js/package.json @@ -91,6 +91,7 @@ "@solana/wallet-standard": "catalog:module-manager", "@stripe/stripe-js": "5.6.0", "@swc/helpers": "catalog:repo", + "@tanstack/query-core": "catalog:repo", "@wallet-standard/core": "catalog:module-manager", "@zxcvbn-ts/core": "catalog:module-manager", "@zxcvbn-ts/language-common": "catalog:module-manager", diff --git a/packages/clerk-js/rspack.config.js b/packages/clerk-js/rspack.config.js index ba99183e24a..d0d50fc816e 100644 --- a/packages/clerk-js/rspack.config.js +++ b/packages/clerk-js/rspack.config.js @@ -110,6 +110,12 @@ const common = ({ mode, variant, disableRHC = false }) => { chunks: 'all', enforce: true, }, + queryCoreVendor: { + test: /[\\/]node_modules[\\/](@tanstack\/query-core)[\\/]/, + name: 'query-core-vendors', + chunks: 'all', + enforce: true, + }, defaultVendors: { minChunks: 1, test: module => { diff --git a/packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts b/packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts new file mode 100644 index 00000000000..46789077820 --- /dev/null +++ b/packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts @@ -0,0 +1,75 @@ +import { QueryClient } from '@tanstack/query-core'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { Clerk } from '../clerk'; +import { Client, Environment } from '../resources/internal'; + +vi.mock('../resources/Client'); +vi.mock('../resources/Environment'); + +vi.mock('../auth/devBrowser', () => ({ + createDevBrowser: () => ({ + clear: vi.fn(), + setup: vi.fn(), + getDevBrowser: vi.fn(() => 'deadbeef'), + setDevBrowser: vi.fn(), + removeDevBrowser: vi.fn(), + refreshCookies: vi.fn(), + }), +})); + +Client.getOrCreateInstance = vi.fn().mockImplementation(() => ({ fetch: vi.fn() })); +Environment.getInstance = vi.fn().mockImplementation(() => ({ fetch: vi.fn(() => Promise.resolve({})) })); + +const publishableKey = 'pk_test_Y2xlcmsuYWJjZWYuMTIzNDUuZGV2LmxjbGNsZXJrLmNvbSQ'; + +describe('Clerk __internal_queryClient (backward compat shim)', () => { + let clerk: Clerk; + + beforeEach(() => { + clerk = new Clerk(publishableKey); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('returns undefined before the lazy import resolves', () => { + expect(clerk.__internal_queryClient).toBeUndefined(); + }); + + it('returns a tagged QueryClient after the lazy import resolves', async () => { + // Trigger the getter (fires the lazy import) + clerk.__internal_queryClient; + + // Wait for the dynamic import and QueryClient construction to settle + await vi.dynamicImportSettled(); + + const result = clerk.__internal_queryClient; + expect(result).toBeDefined(); + expect(result!.__tag).toBe('clerk-rq-client'); + expect(result!.client).toBeInstanceOf(QueryClient); + }); + + it('returns the same QueryClient instance on repeated access', async () => { + clerk.__internal_queryClient; + await vi.dynamicImportSettled(); + + const first = clerk.__internal_queryClient; + const second = clerk.__internal_queryClient; + expect(first!.client).toBe(second!.client); + }); + + it('emits queryClientStatus event when the client is ready', async () => { + const listener = vi.fn(); + // @ts-expect-error - queryClientStatus is not typed on clerk.on + clerk.on('queryClientStatus', listener); + + clerk.__internal_queryClient; + await vi.dynamicImportSettled(); + + expect(listener).toHaveBeenCalledWith('ready'); + // @ts-expect-error + clerk.off('queryClientStatus', listener); + }); +}); diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 5334dc1496d..d1350e66c26 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -140,6 +140,7 @@ import type { import type { ClerkUI } from '@clerk/shared/ui'; import { addClerkPrefix, isAbsoluteUrl, stripScheme } from '@clerk/shared/url'; import { allSettled, handleValueOrFn, noop } from '@clerk/shared/utils'; +import type { QueryClient } from '@tanstack/query-core'; import { debugLogger, initDebugLogger } from '@/utils/debug'; import { ModuleManager } from '@/utils/moduleManager'; @@ -252,6 +253,7 @@ export class Clerk implements ClerkInterface { // converted to protected environment to support `updateEnvironment` type assertion protected environment?: EnvironmentResource | null; + #queryClient: QueryClient | undefined; #publishableKey = ''; #domain: DomainOrProxyUrl['domain']; #proxyUrl: DomainOrProxyUrl['proxyUrl']; @@ -271,6 +273,28 @@ export class Clerk implements ClerkInterface { #touchThrottledUntil = 0; #publicEventBus = createClerkEventBus(); + get __internal_queryClient(): { __tag: 'clerk-rq-client'; client: QueryClient } | undefined { + if (!this.#queryClient) { + void import('./query-core') + .then(module => module.QueryClient) + .then(QueryClient => { + if (this.#queryClient) { + return; + } + this.#queryClient = new QueryClient(); + // @ts-expect-error - queryClientStatus is not typed + this.#publicEventBus.emit('queryClientStatus', 'ready'); + }); + } + + return this.#queryClient + ? { + __tag: 'clerk-rq-client', + client: this.#queryClient, + } + : undefined; + } + public __internal_getCachedResources: | (() => Promise<{ client: ClientJSONSnapshot | null; environment: EnvironmentJSONSnapshot | null }>) | undefined; diff --git a/packages/clerk-js/src/core/query-core.ts b/packages/clerk-js/src/core/query-core.ts new file mode 100644 index 00000000000..2f86e70e22f --- /dev/null +++ b/packages/clerk-js/src/core/query-core.ts @@ -0,0 +1 @@ +export { QueryClient } from '@tanstack/query-core'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86a19d3d92e..0f62ecad1c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -444,6 +444,9 @@ importers: '@swc/helpers': specifier: catalog:repo version: 0.5.21 + '@tanstack/query-core': + specifier: catalog:repo + version: 5.100.6 '@wallet-standard/core': specifier: catalog:module-manager version: 1.1.1 From d328a48afec0f2d21dff96d8ef6f9d993847e332 Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 15 May 2026 15:45:40 -0500 Subject: [PATCH 2/2] fix(clerk-js): satisfy lint, bundlewatch, and changeset for query-client shim - Use void to discard getter expressions in tests (no-unused-expressions) - Add patch changeset for @clerk/clerk-js - Bump clerk.js (543->549KB) and clerk.no-rhc.js (311->316KB) for the query-core dynamic-import infrastructure re-added by the shim --- .changeset/restore-internal-query-client.md | 5 +++++ packages/clerk-js/bundlewatch.config.json | 4 ++-- .../src/core/__tests__/clerk.internal-query-client.test.ts | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .changeset/restore-internal-query-client.md diff --git a/.changeset/restore-internal-query-client.md b/.changeset/restore-internal-query-client.md new file mode 100644 index 00000000000..08d5f945de0 --- /dev/null +++ b/.changeset/restore-internal-query-client.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Restore the `clerk.__internal_queryClient` getter as a backward-compatibility shim so apps still on `@clerk/shared < 4.10.0` can hydrate their query client and continue to render paginated hooks (e.g. `useOrganizationList`, `useOrganization`). The getter lazily imports `@tanstack/query-core` only when accessed, so apps on `@clerk/shared >= 4.10.0` (which use the singleton in `@clerk/shared`) pay zero runtime cost. diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index bcea517d7e1..f5d5842b777 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -1,9 +1,9 @@ { "files": [ - { "path": "./dist/clerk.js", "maxSize": "543KB" }, + { "path": "./dist/clerk.js", "maxSize": "549KB" }, { "path": "./dist/clerk.browser.js", "maxSize": "70KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "112KB" }, - { "path": "./dist/clerk.no-rhc.js", "maxSize": "311KB" }, + { "path": "./dist/clerk.no-rhc.js", "maxSize": "316KB" }, { "path": "./dist/clerk.native.js", "maxSize": "70KB" }, { "path": "./dist/vendors*.js", "maxSize": "7KB" }, { "path": "./dist/coinbase*.js", "maxSize": "36KB" }, diff --git a/packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts b/packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts index 46789077820..42886a5964c 100644 --- a/packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts +++ b/packages/clerk-js/src/core/__tests__/clerk.internal-query-client.test.ts @@ -40,7 +40,7 @@ describe('Clerk __internal_queryClient (backward compat shim)', () => { it('returns a tagged QueryClient after the lazy import resolves', async () => { // Trigger the getter (fires the lazy import) - clerk.__internal_queryClient; + void clerk.__internal_queryClient; // Wait for the dynamic import and QueryClient construction to settle await vi.dynamicImportSettled(); @@ -52,7 +52,7 @@ describe('Clerk __internal_queryClient (backward compat shim)', () => { }); it('returns the same QueryClient instance on repeated access', async () => { - clerk.__internal_queryClient; + void clerk.__internal_queryClient; await vi.dynamicImportSettled(); const first = clerk.__internal_queryClient; @@ -65,7 +65,7 @@ describe('Clerk __internal_queryClient (backward compat shim)', () => { // @ts-expect-error - queryClientStatus is not typed on clerk.on clerk.on('queryClientStatus', listener); - clerk.__internal_queryClient; + void clerk.__internal_queryClient; await vi.dynamicImportSettled(); expect(listener).toHaveBeenCalledWith('ready');