From 93b1b360bf4e4c123cf3da1043a6bf1a854dbed0 Mon Sep 17 00:00:00 2001 From: Theo Zourzouvillys Date: Fri, 31 Jul 2026 17:13:20 -0800 Subject: [PATCH 1/4] feat(clerk-js,shared,react): supply a Protect assertion from the application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Protect assertion is a short-lived signed token an application mints from its own backend, carrying key/value pairs its Protect rules can read. Until now the only way to deliver one was the __clerk_protect_assertion cookie, which needs the app and Frontend API to be same-site — true with a production CNAME setup, not on development instances. The token can now be handed to Clerk instead, and is attached to sign-in and sign-up requests: Clerk.load({ protectAssertion: token }) Clerk.load({ protectAssertion: () => readToken() }) clerk.setProtectAssertion(token) A function is supported alongside a string because assertions are short-lived by design and a page routinely outlives one. A string captured at load time stops applying at expiry; a function is re-read per request, so a token refreshed in the background takes effect without re-configuring Clerk. It travels as a form param, not a header. A custom header would trigger a CORS preflight — the same thing fapiClient already avoids by tunnelling PATCH/DELETE through a _method query param. The merge has to happen in request(), before the body is stringified: onBeforeRequest runs after stringification, so a callback cannot add a body param. Nothing here can fail a sign-in, which is the invariant the tests are built around. A resolver that throws, rejects, or returns anything other than a non-empty string yields no assertion and a warning, and the request proceeds. A body that is not a plain object is left alone rather than spread — spreading a FormData would discard the caller's payload instead of adding to it. With no assertion configured the request is byte-for-byte what it was before, which is why the params resolve to undefined rather than {}. setProtectAssertion tracks whether it has been called, separately from the value it was given. Without that, clearing with undefined would silently fall back to the option, and a call before load() would be overwritten by it. IsomorphicClerk implements it through the existing premountMethodCalls map, which is keyed by method name — so a second call before load replaces the first, which is the semantics a setter wants. Bundlewatch: clerk.native.js gains ~0.2KB gzipped and crossed its 74KB ceiling; raised to 75KB, matching the headroom the other entries carry. The cookie path is unchanged. Where both are present, the SDK value wins. Requires the matching server change to be deployed first. --- .changeset/protect-assertion-sdk-option.md | 28 +++++ packages/clerk-js/bundlewatch.config.json | 2 +- .../src/core/__tests__/fapiClient.test.ts | 114 ++++++++++++++++++ .../core/__tests__/protectAssertion.test.ts | 86 +++++++++++++ packages/clerk-js/src/core/clerk.ts | 22 ++++ packages/clerk-js/src/core/fapiClient.ts | 47 +++++++- .../clerk-js/src/core/protectAssertion.ts | 65 ++++++++++ packages/react/src/isomorphicClerk.ts | 12 ++ packages/shared/src/types/clerk.ts | 27 +++++ packages/shared/src/types/protectConfig.ts | 24 ++++ 10 files changed, 425 insertions(+), 2 deletions(-) create mode 100644 .changeset/protect-assertion-sdk-option.md create mode 100644 packages/clerk-js/src/core/__tests__/protectAssertion.test.ts create mode 100644 packages/clerk-js/src/core/protectAssertion.ts diff --git a/.changeset/protect-assertion-sdk-option.md b/.changeset/protect-assertion-sdk-option.md new file mode 100644 index 00000000000..cdcfeb0573e --- /dev/null +++ b/.changeset/protect-assertion-sdk-option.md @@ -0,0 +1,28 @@ +--- +'@clerk/clerk-js': minor +'@clerk/shared': minor +'@clerk/react': minor +--- + +Add a way to supply a Clerk Protect assertion from your application, so a token minted by your own backend reaches Protect without your having to set a cookie. + +A Protect assertion is a short-lived, signed token you create with the Clerk Backend API, carrying key/value pairs your Protect rules can read. Until now the only way to deliver one was the `__clerk_protect_assertion` cookie, which requires your app and Frontend API to be on the same site — true with a production CNAME setup, but not on development instances. + +Pass the token to Clerk and it is attached to sign-in and sign-up requests instead: + +```ts +// A token you already have. +Clerk.load({ protectAssertion: token }); + +// Or a function, re-read for each request. +Clerk.load({ protectAssertion: () => sessionStorage.getItem('protect_assertion') ?? undefined }); + +// Or set it later, once your app has fetched one. +clerk.setProtectAssertion(token); +``` + +Prefer the function form when a page can outlive the token. Assertions are short-lived by design, so a string captured at load time stops applying once it expires, whereas a function picks up a refreshed one. + +An assertion is an input to rules you author, never a decision on its own, and it applies only from the context you constrained it to when you minted it. Nothing about it can fail a sign-in: a resolver that throws, rejects, or returns anything other than a non-empty string simply results in no assertion being attached, and the request proceeds. + +The cookie continues to work unchanged. If both are present, the value supplied to the SDK wins. diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 95614c3de45..6a282fb032f 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -4,7 +4,7 @@ { "path": "./dist/clerk.browser.js", "maxSize": "75KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "117KB" }, { "path": "./dist/clerk.no-rhc.js", "maxSize": "316KB" }, - { "path": "./dist/clerk.native.js", "maxSize": "74KB" }, + { "path": "./dist/clerk.native.js", "maxSize": "75KB" }, { "path": "./dist/vendors*.js", "maxSize": "7KB" }, { "path": "./dist/coinbase*.js", "maxSize": "36KB" }, { "path": "./dist/base-account-sdk*.js", "maxSize": "207KB" }, diff --git a/packages/clerk-js/src/core/__tests__/fapiClient.test.ts b/packages/clerk-js/src/core/__tests__/fapiClient.test.ts index 5de3432bdd5..2aff74ea8bc 100644 --- a/packages/clerk-js/src/core/__tests__/fapiClient.test.ts +++ b/packages/clerk-js/src/core/__tests__/fapiClient.test.ts @@ -384,6 +384,120 @@ describe('request', () => { }); }); + describe('Protect params', () => { + // A body param rather than a header, because a custom header would trigger a CORS + // preflight — the same reason `_method` is a query param. These tests pin that the params + // reach the encoded body, and reach nothing else. + const protectParams = { __clerk_protect_assertion: 'token-abc' }; + const clientWithProtect = createFapiClient({ + ...baseFapiClientOptions, + getProtectParams: () => Promise.resolve(protectParams), + }); + + it.each([ + ['/client/sign_ins'], + ['/client/sign_ins/sia_123/attempt_first_factor'], + ['/client/sign_ups'], + ['/client/sign_ups/sua_123/attempt_verification'], + ])('attaches them to POST %s', async path => { + await clientWithProtect.request({ path, method: 'POST', body: { identifier: 'user@example.com' } as any }); + + expect(fetch).toHaveBeenCalledWith( + expect.any(URL), + expect.objectContaining({ + body: 'identifier=user%40example.com&__clerk_protect_assertion=token-abc', + }), + ); + }); + + it('attaches them when the request has no body of its own', async () => { + await clientWithProtect.request({ path: '/client/sign_ins', method: 'POST' }); + + expect(fetch).toHaveBeenCalledWith( + expect.any(URL), + expect.objectContaining({ body: '__clerk_protect_assertion=token-abc' }), + ); + }); + + // The param name survives the body's camelCase→snake_case key encoder untouched — it is + // all lower-case, so there is nothing for that encoder to rewrite. If it ever did not + // survive, the server would see an unknown param and reject the whole request. + it('does not mangle the param name', async () => { + await clientWithProtect.request({ path: '/client/sign_ins', method: 'POST' }); + + const [, init] = (fetch as Mock).mock.calls.at(-1); + expect(init.body).toBe('__clerk_protect_assertion=token-abc'); + }); + + it.each([ + ['a GET', 'GET', '/client/sign_ins'], + ['an unrelated path', 'POST', '/client/sessions'], + ['a path that merely shares a prefix', 'POST', '/client/sign_ins_other'], + ])('does not attach them to %s', async (_label, method, path) => { + await clientWithProtect.request({ path, method: method as any, body: { a: 'b' } as any }); + + const [, init] = (fetch as Mock).mock.calls.at(-1); + expect(init.body ?? '').not.toContain('__clerk_protect_assertion'); + }); + + // Spreading a FormData would discard the caller's payload rather than add to it, so a body + // that is not a plain object is left completely alone. + it('leaves a FormData body untouched', async () => { + const formData = new FormData(); + formData.append('identifier', 'user@example.com'); + + await clientWithProtect.request({ path: '/client/sign_ins', method: 'POST', body: formData }); + + expect(fetch).toHaveBeenCalledWith(expect.any(URL), expect.objectContaining({ body: formData })); + }); + + it('leaves a string body untouched', async () => { + // text/plain so the form-urlencoded encoder stays out of it; the point here is that the + // merge does not touch a body it cannot safely spread. + await clientWithProtect.request({ + path: '/client/sign_ins', + method: 'POST', + body: 'raw string body', + headers: { 'content-type': 'text/plain' }, + }); + + expect(fetch).toHaveBeenCalledWith(expect.any(URL), expect.objectContaining({ body: 'raw string body' })); + }); + + // Protect may influence a sign-in but must never fail one. + it('sends the request unchanged when resolving the params rejects', async () => { + const failing = createFapiClient({ + ...baseFapiClientOptions, + getProtectParams: () => Promise.reject(new Error('boom')), + }); + + await expect( + failing.request({ path: '/client/sign_ins', method: 'POST', body: { identifier: 'a' } as any }), + ).resolves.toBeTruthy(); + + expect(fetch).toHaveBeenCalledWith(expect.any(URL), expect.objectContaining({ body: 'identifier=a' })); + }); + + it('sends the request unchanged when there are no params', async () => { + const none = createFapiClient({ + ...baseFapiClientOptions, + getProtectParams: () => Promise.resolve(undefined), + }); + + await none.request({ path: '/client/sign_ins', method: 'POST', body: { identifier: 'a' } as any }); + + expect(fetch).toHaveBeenCalledWith(expect.any(URL), expect.objectContaining({ body: 'identifier=a' })); + }); + + // Every client built before this existed passes no hook at all; it must behave exactly as + // it did. + it('is inert when no hook is configured', async () => { + await fapiClient.request({ path: '/client/sign_ins', method: 'POST', body: { identifier: 'a' } as any }); + + expect(fetch).toHaveBeenCalledWith(expect.any(URL), expect.objectContaining({ body: 'identifier=a' })); + }); + }); + describe('retry logic', () => { it('does not send retry query parameter on initial request', async () => { await fapiClient.request({ diff --git a/packages/clerk-js/src/core/__tests__/protectAssertion.test.ts b/packages/clerk-js/src/core/__tests__/protectAssertion.test.ts new file mode 100644 index 00000000000..24633257762 --- /dev/null +++ b/packages/clerk-js/src/core/__tests__/protectAssertion.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { PROTECT_ASSERTION_PARAM, protectAssertionParams, resolveProtectAssertion } from '../protectAssertion'; + +describe('resolveProtectAssertion', () => { + it('returns undefined when nothing is configured', async () => { + await expect(resolveProtectAssertion(undefined)).resolves.toBeUndefined(); + }); + + it('returns a configured string as-is', async () => { + await expect(resolveProtectAssertion('token-abc')).resolves.toBe('token-abc'); + }); + + it('calls a sync resolver', async () => { + await expect(resolveProtectAssertion(() => 'token-sync')).resolves.toBe('token-sync'); + }); + + it('awaits an async resolver', async () => { + await expect(resolveProtectAssertion(() => Promise.resolve('token-async'))).resolves.toBe('token-async'); + }); + + // The whole reason a function is supported: the token outlives neither the page nor its own + // expiry, so a value captured once at configuration time would silently stop applying. + it('re-reads the resolver on every call', async () => { + const resolver = vi.fn<() => string>(); + resolver.mockReturnValueOnce('first').mockReturnValueOnce('second'); + + await expect(resolveProtectAssertion(resolver)).resolves.toBe('first'); + await expect(resolveProtectAssertion(resolver)).resolves.toBe('second'); + expect(resolver).toHaveBeenCalledTimes(2); + }); + + it('treats a resolver returning undefined as "no assertion right now"', async () => { + await expect(resolveProtectAssertion(() => undefined)).resolves.toBeUndefined(); + }); + + // An assertion may influence a sign-in but must never prevent one, so every bad input + // degrades to "no assertion" rather than propagating. + it.each([ + [ + 'a throwing resolver', + () => { + throw new Error('boom'); + }, + ], + ['a rejecting resolver', () => Promise.reject(new Error('boom'))], + ])('never rejects for %s', async (_label, resolver) => { + await expect(resolveProtectAssertion(resolver as () => string)).resolves.toBeUndefined(); + }); + + it.each([ + ['an empty string', ''], + ['whitespace only', ' '], + ['a number', 42], + ['null', null], + ['an object', { token: 'x' }], + ])('ignores %s', async (_label, value) => { + await expect(resolveProtectAssertion(() => value as unknown as string)).resolves.toBeUndefined(); + }); +}); + +describe('protectAssertionParams', () => { + it('names the param the server expects', async () => { + await expect(protectAssertionParams('token-abc')).resolves.toEqual({ + [PROTECT_ASSERTION_PARAM]: 'token-abc', + }); + }); + + // The param name is a cross-repo contract with the server, and it is deliberately identical + // to the cookie that can carry the same value. It is also all lower-case + underscores, so + // the body's camelCase→snake_case encoder leaves it alone — pinned here because a rename + // would break silently, as an ignored param rather than an error. + it('uses a param name the body encoder cannot mangle', () => { + expect(PROTECT_ASSERTION_PARAM).toBe('__clerk_protect_assertion'); + expect(PROTECT_ASSERTION_PARAM).toBe(PROTECT_ASSERTION_PARAM.toLowerCase()); + expect(PROTECT_ASSERTION_PARAM).not.toMatch(/[A-Z]/); + }); + + // Returning undefined rather than {} is what keeps a request with no assertion byte-for-byte + // the request that would have been sent before this existed. + it('returns undefined when there is nothing to attach', async () => { + await expect(protectAssertionParams(undefined)).resolves.toBeUndefined(); + await expect(protectAssertionParams(() => undefined)).resolves.toBeUndefined(); + await expect(protectAssertionParams('')).resolves.toBeUndefined(); + }); +}); diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 2ce3f87c2e6..0c74df3fd83 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -104,6 +104,7 @@ import type { OrganizationResource, OrganizationSwitcherProps, PricingTableProps, + ProtectAssertion, PublicKeyCredentialCreationOptionsWithoutExtensions, PublicKeyCredentialRequestOptionsWithoutExtensions, PublicKeyCredentialWithAuthenticatorAssertionResponse, @@ -190,6 +191,7 @@ import { Billing } from './modules/billing'; import { createCheckoutInstance } from './modules/checkout/instance'; import { OAuthApplication } from './modules/oauthApplication'; import { Protect } from './protect'; +import { protectAssertionParams } from './protectAssertion'; import { BaseResource, Client, Environment, Organization, Waitlist } from './resources/internal'; import { State } from './state'; @@ -271,6 +273,11 @@ export class Clerk implements ClerkInterface { #listeners: Array<(emission: Resources) => void> = []; #navigationListeners: Array<() => void> = []; #options: ClerkOptions = {}; + #protectAssertion: ProtectAssertion | undefined; + // Distinguishes "never set via setProtectAssertion" from "explicitly cleared with + // undefined". Without it, clearing would silently fall back to the `protectAssertion` + // option, and a setter call before `load()` would be overwritten by it. + #protectAssertionSet = false; #oauthTransport: OAuthTransport | null = null; #pageLifecycle: ReturnType | null = null; #touchThrottledUntil = 0; @@ -476,6 +483,20 @@ export class Clerk implements ClerkInterface { return this.#options[key]; } + public setProtectAssertion = (assertion?: ProtectAssertion): void => { + this.#protectAssertion = assertion; + this.#protectAssertionSet = true; + }; + + /** + * The assertion in force right now: whatever was last passed to `setProtectAssertion`, + * otherwise the `protectAssertion` option. Read per request, so `load()` may run before or + * after the setter without either clobbering the other. + */ + #currentProtectAssertion(): ProtectAssertion | undefined { + return this.#protectAssertionSet ? this.#protectAssertion : this.#options.protectAssertion; + } + get isSignedIn(): boolean { const hasPendingSession = this?.session?.status === 'pending'; if (hasPendingSession) { @@ -513,6 +534,7 @@ export class Clerk implements ClerkInterface { getSessionId: () => { return this.session?.id; }, + getProtectParams: () => protectAssertionParams(this.#currentProtectAssertion()), proxyUrl: this.proxyUrl, }); this.#publicEventBus.emit(clerkEvents.Status, 'loading'); diff --git a/packages/clerk-js/src/core/fapiClient.ts b/packages/clerk-js/src/core/fapiClient.ts index c0595d20852..dd7c5de557a 100644 --- a/packages/clerk-js/src/core/fapiClient.ts +++ b/packages/clerk-js/src/core/fapiClient.ts @@ -65,15 +65,44 @@ export interface FapiClient { // List of paths that should not receive the session ID parameter in the URL const unauthorizedPathPrefixes = ['/client', '/waitlist']; +// The requests Protect gates. Params are attached to these and nothing else. +const protectPathPrefixes = ['/client/sign_ins', '/client/sign_ups']; + type FapiClientOptions = { frontendApi: string; domain?: string; proxyUrl?: string; instanceType: InstanceType; getSessionId: () => string | undefined; + /** + * Resolves the Protect params to merge into the body of a sign-in or sign-up POST, or + * `undefined` when there are none to add. + */ + getProtectParams?: () => Promise | undefined>; isSatellite?: boolean; }; +function isProtectGatedRequest(method: string, path: string | undefined): boolean { + if (method === 'GET' || !path) { + return false; + } + return protectPathPrefixes.some(prefix => path === prefix || path.startsWith(`${prefix}/`)); +} + +/** Only a plain-object body (or none at all) can take extra params without changing its shape. */ +function isMergeableBody(body: unknown): body is Record | undefined { + if (body === undefined) { + return true; + } + if (typeof body !== 'object' || body === null) { + return false; + } + // Spreading anything else — a Blob, a typed array, a stream — would discard the caller's payload + // rather than add to it. + const prototype = Object.getPrototypeOf(body); + return prototype === Object.prototype || prototype === null; +} + export function createFapiClient(options: FapiClientOptions): FapiClient { const onBeforeRequestCallbacks: Array> = []; const onAfterResponseCallbacks: Array> = []; @@ -195,7 +224,23 @@ export function createFapiClient(options: FapiClientOptions): FapiClient { requestOptions?: FapiRequestOptions, ): Promise> { const requestInit = { ..._requestInit }; - const { method = 'GET', body } = requestInit; + const { method = 'GET' } = requestInit; + let { body } = requestInit; + + // Protect params ride in the form-encoded body of sign-in and sign-up POSTs. They have to + // be merged here, before the body is stringified below — the onBeforeRequest callbacks run + // after stringification, so they cannot add a body param. A body param also keeps the + // request CORS-simple; a custom header would trigger the preflight that breaks cookie + // dropping in Safari, the same reason `_method` is a query param. + if (options.getProtectParams && isProtectGatedRequest(method, requestInit.path) && isMergeableBody(body)) { + // Protect can influence a sign-in but must never fail one, so a rejection here costs the + // params and nothing else. + const protectParams = await options.getProtectParams().catch(() => undefined); + if (protectParams) { + body = { ...((body ?? {}) as Record), ...protectParams } as unknown as BodyInit; + requestInit.body = body; + } + } if (body && typeof body === 'object' && !(body instanceof FormData)) { requestInit.body = filterUndefinedValues(body); diff --git a/packages/clerk-js/src/core/protectAssertion.ts b/packages/clerk-js/src/core/protectAssertion.ts new file mode 100644 index 00000000000..9046810695c --- /dev/null +++ b/packages/clerk-js/src/core/protectAssertion.ts @@ -0,0 +1,65 @@ +import { logger } from '@clerk/shared/logger'; +import type { ProtectAssertion } from '@clerk/shared/types'; + +/** + * The request param carrying a Protect assertion. + * + * Deliberately the same name as the cookie that can carry it instead: it is the same value by + * another road, and one name means one thing to search for when working out why an assertion + * did not apply. + */ +export const PROTECT_ASSERTION_PARAM = '__clerk_protect_assertion'; + +/** + * Resolves the configured assertion for one request. + * + * A function is called per request rather than once at configuration time, so an app that + * refreshes its token while the page is open does not have to re-configure Clerk for the new + * one to take effect. + * + * Nothing here can fail a sign-in. A resolver that throws, rejects, or returns something other + * than a non-empty string yields no assertion and a warning — the request proceeds without it, + * because an assertion may influence a sign-in and must never prevent one. + */ +export async function resolveProtectAssertion(assertion: ProtectAssertion | undefined): Promise { + if (assertion === undefined) { + return undefined; + } + + let value: unknown = assertion; + if (typeof assertion === 'function') { + try { + value = await assertion(); + } catch (error) { + logger.warnOnce(`Clerk: protectAssertion resolver failed, continuing without it: ${error}`); + return undefined; + } + } + + // `undefined` is the documented way to say "no assertion right now", so it is not worth a + // warning; anything else is a mistake the developer wants to hear about. + if (value === undefined) { + return undefined; + } + if (typeof value !== 'string' || value.trim() === '') { + logger.warnOnce('Clerk: protectAssertion must be a non-empty string; ignoring it.'); + return undefined; + } + + return value; +} + +/** + * The Protect params to merge into a sign-in or sign-up request body, or `undefined` when + * there is nothing to add. + * + * Returning `undefined` rather than an empty object matters: the caller only touches the body + * when there is something to put in it, so a request with no assertion is byte-for-byte the + * request that would have been sent before. + */ +export async function protectAssertionParams( + assertion: ProtectAssertion | undefined, +): Promise | undefined> { + const token = await resolveProtectAssertion(assertion); + return token ? { [PROTECT_ASSERTION_PARAM]: token } : undefined; +} diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index e017ab2ddcd..8998b3bd14a 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -45,6 +45,7 @@ import type { OrganizationResource, OrganizationSwitcherProps, PricingTableProps, + ProtectAssertion, RedirectOptions, Resources, SetActiveParams, @@ -389,6 +390,17 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { return false; } + setProtectAssertion = (assertion?: ProtectAssertion): void => { + const callback = () => this.clerkjs?.setProtectAssertion(assertion); + if (this.clerkjs && this.loaded) { + callback(); + } else { + // Keyed by method name, so a second call before load replaces the first — which is the + // semantics a setter wants, and means a value set early is not lost. + this.premountMethodCalls.set('setProtectAssertion', callback); + } + }; + buildSignInUrl = (opts?: RedirectOptions): string | void => { const callback = () => this.clerkjs?.buildSignInUrl(opts) || ''; if (this.clerkjs && this.loaded) { diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index cb3999bcd70..ddd7cdf2156 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -25,6 +25,7 @@ import type { OAuthTransport } from './oauthTransport'; import type { OrganizationResource } from './organization'; import type { OrganizationCustomRoleKey } from './organizationMembership'; import type { ClerkPaginationParams } from './pagination'; +import type { ProtectAssertion } from './protectConfig'; import type { AfterMultiSessionSingleSignOutUrl, AfterSignOutUrl, @@ -293,6 +294,20 @@ export interface Clerk { */ __internal_getOption(key: K): ClerkOptions[K]; + /** + * Sets the Protect assertion attached to subsequent sign-in and sign-up requests, replacing + * any value supplied via the `protectAssertion` option. Pass `undefined` to clear it. + * + * Use this when the token is not available at `Clerk.load()` time — for example when your + * app fetches one from your backend after the page has started. Passing a function instead + * of a string has it re-read for each request, which is what you want if the token is + * refreshed while the page is open. + * + * @param assertion - A token minted by your backend, a function returning one, or + * `undefined`. + */ + setProtectAssertion: (assertion?: ProtectAssertion) => void; + /** * @internal * Primary `window.location.href` navigation chokepoint for `@clerk/clerk-js` and `@clerk/ui`. @@ -1414,6 +1429,18 @@ export type ClerkOptions = ClerkOptionsNavigation & * An object to localize your components. Will only affect [Clerk Components](https://clerk.com/docs/reference/components/overview) and not [Account Portal](https://clerk.com/docs/guides/account-portal/overview) pages. */ localization?: LocalizationResource; + /** + * A Clerk Protect assertion — a short-lived, signed token you mint from your own backend + * with the Clerk Backend API — carrying key/value pairs your Protect rules can read. Clerk + * attaches it to sign-in and sign-up requests. + * + * Pass a string if you already have one, or a function to have it re-read for each request. + * Prefer the function when a page can outlive the token: assertions are short-lived by + * design, and a string captured here stops applying once it expires. + * + * Can also be set later with `Clerk.setProtectAssertion()`. + */ + protectAssertion?: ProtectAssertion; /** * Indicates whether Clerk should poll against Clerk's backend every 5 minutes. * diff --git a/packages/shared/src/types/protectConfig.ts b/packages/shared/src/types/protectConfig.ts index 515546aa64d..7f469757a41 100644 --- a/packages/shared/src/types/protectConfig.ts +++ b/packages/shared/src/types/protectConfig.ts @@ -20,3 +20,27 @@ export interface ProtectConfigResource extends ClerkResource { loaders?: ProtectLoader[]; __internal_toSnapshot: () => ProtectConfigJSONSnapshot; } + +/** + * Returns the Protect assertion to attach to the next sign-in or sign-up request, or + * `undefined` to attach none. + * + * Called per request, so a token refreshed in the background is picked up without + * re-configuring Clerk. It must not throw, and a rejected promise is treated the same as + * `undefined`: an assertion may influence a sign-in, but never prevent one. + */ +export type ProtectAssertionResolver = () => string | undefined | Promise; + +/** + * A Protect assertion: a short-lived, signed token you mint from your own backend with the + * Clerk Backend API, carrying key/value pairs your Protect rules can read. + * + * Pass a `string` if you already have one, or a function to have it re-read for each + * sign-in or sign-up request. Prefer the function when a page can outlive the token — + * assertions are short-lived by design, and a string captured at load time stops applying + * once it expires. + * + * The assertion is an input to rules you author, never a decision on its own, and it only + * applies from the context you constrained it to when you minted it. + */ +export type ProtectAssertion = string | ProtectAssertionResolver; From 06ecbc8751ef8198ce7a69907f42fb1c47ddbb90 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 7 Aug 2026 18:50:42 -0700 Subject: [PATCH 2/4] chore: fix bundle size --- .../console-2026-08-07T19-12-27-957Z.log | 5 ++ .../console-2026-08-07T19-14-00-069Z.log | 5 ++ .../console-2026-08-07T19-14-43-098Z.log | 14 ++++++ .../console-2026-08-07T19-16-19-898Z.log | 49 +++++++++++++++++++ .../console-2026-08-07T19-17-35-278Z.log | 19 +++++++ .../console-2026-08-07T19-20-14-759Z.log | 10 ++++ .../page-2026-08-07T19-12-28-035Z.yml | 8 +++ .../page-2026-08-07T19-12-46-367Z.yml | 49 +++++++++++++++++++ .../page-2026-08-07T19-12-51-542Z.yml | 49 +++++++++++++++++++ .../page-2026-08-07T19-13-04-813Z.yml | 15 ++++++ .../page-2026-08-07T19-14-00-158Z.yml | 8 +++ .../page-2026-08-07T19-14-43-165Z.yml | 8 +++ .../page-2026-08-07T19-14-55-829Z.yml | 47 ++++++++++++++++++ .../page-2026-08-07T19-15-01-070Z.yml | 47 ++++++++++++++++++ .../page-2026-08-07T19-15-07-513Z.yml | 13 +++++ .../page-2026-08-07T19-16-19-974Z.yml | 8 +++ .../page-2026-08-07T19-17-35-333Z.yml | 8 +++ .../page-2026-08-07T19-20-14-815Z.yml | 8 +++ packages/clerk-js/bundlewatch.config.json | 2 +- 19 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 .playwright-mcp/console-2026-08-07T19-12-27-957Z.log create mode 100644 .playwright-mcp/console-2026-08-07T19-14-00-069Z.log create mode 100644 .playwright-mcp/console-2026-08-07T19-14-43-098Z.log create mode 100644 .playwright-mcp/console-2026-08-07T19-16-19-898Z.log create mode 100644 .playwright-mcp/console-2026-08-07T19-17-35-278Z.log create mode 100644 .playwright-mcp/console-2026-08-07T19-20-14-759Z.log create mode 100644 .playwright-mcp/page-2026-08-07T19-12-28-035Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-12-46-367Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-12-51-542Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-13-04-813Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-14-00-158Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-14-43-165Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-14-55-829Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-15-01-070Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-15-07-513Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-16-19-974Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-17-35-333Z.yml create mode 100644 .playwright-mcp/page-2026-08-07T19-20-14-815Z.yml diff --git a/.playwright-mcp/console-2026-08-07T19-12-27-957Z.log b/.playwright-mcp/console-2026-08-07T19-12-27-957Z.log new file mode 100644 index 00000000000..b594516760d --- /dev/null +++ b/.playwright-mcp/console-2026-08-07T19-12-27-957Z.log @@ -0,0 +1,5 @@ +[ 68ms] [ERROR] Failed to load resource: the server responded with a status of 404 () @ http://localhost:3010/favicon.ico:0 +[ 329ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=b138766f:20100 +[ 652ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 +[ 12579ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 36306ms] [WARNING] Clerk: The prop "redirectUrl" is deprecated and should be replaced with the new "fallbackRedirectUrl" or "forceRedirectUrl" props instead. Learn more: https://clerk.com/docs/guides/custom-redirects#redirect-url-props @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 diff --git a/.playwright-mcp/console-2026-08-07T19-14-00-069Z.log b/.playwright-mcp/console-2026-08-07T19-14-00-069Z.log new file mode 100644 index 00000000000..a728128ddce --- /dev/null +++ b/.playwright-mcp/console-2026-08-07T19-14-00-069Z.log @@ -0,0 +1,5 @@ +[ 57ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=b138766f:20100 +[ 174ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 +[ 22587ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 +[ 33148ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=09d9b106:20100 +[ 33672ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 diff --git a/.playwright-mcp/console-2026-08-07T19-14-43-098Z.log b/.playwright-mcp/console-2026-08-07T19-14-43-098Z.log new file mode 100644 index 00000000000..b93095eca02 --- /dev/null +++ b/.playwright-mcp/console-2026-08-07T19-14-43-098Z.log @@ -0,0 +1,14 @@ +[ 55ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=09d9b106:20100 +[ 270ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 +[ 7009ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 35744ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 37310ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 38966ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 40730ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 42596ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 44557ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 46619ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 48784ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 73800ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 +[ 85442ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=268ad286:20100 +[ 85564ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 diff --git a/.playwright-mcp/console-2026-08-07T19-16-19-898Z.log b/.playwright-mcp/console-2026-08-07T19-16-19-898Z.log new file mode 100644 index 00000000000..d8e58669e89 --- /dev/null +++ b/.playwright-mcp/console-2026-08-07T19-16-19-898Z.log @@ -0,0 +1,49 @@ +[ 57ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=268ad286:20100 +[ 272ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 +[ 7741ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 7958ms] [WARNING] Clerk: The prop "redirectUrl" is deprecated and should be replaced with the new "fallbackRedirectUrl" or "forceRedirectUrl" props instead. Learn more: https://clerk.com/docs/guides/custom-redirects#redirect-url-props @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 +[ 58010ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 +[ 65343ms] [ERROR] Failed to load resource: the server responded with a status of 404 () @ http://localhost:3010/login#/?redirect_url=http%3A%2F%2Flocalhost%3A3010%2F:0 +[ 65578ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 +[ 65601ms] Error: Hydration failed because the server rendered text didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: + +- A server/client branch `if (typeof window !== 'undefined')`. +- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called. +- Date formatting in a user's locale which doesn't match the server. +- External changing data without sending a snapshot of it along with the HTML. +- Invalid HTML tag nesting. + +It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + +https://react.dev/link/hydration-mismatch + + ... + + + + + + + + <__experimental_CheckoutProvider value={undefined}> + + + + +

+ ++ /login#/?redirect_url=http%3A%2F%2Flocalhost%3A3010%2F +- /login + ... + + at throwOnHydrationMismatch (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:3918:13) + at prepareToHydrateHostInstance (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:3992:23) + at completeWork (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:8997:17) + at runWithFiberInDEV (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:995:72) + at completeUnitOfWork (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12667:22) + at performUnitOfWork (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12573:29) + at workLoopConcurrentByScheduler (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12555:11) + at renderRootConcurrent (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12537:71) + at performWorkOnRoot (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:11764:152) + at performWorkOnRootViaSchedulerTask (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:13503:9) +[ 65794ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 diff --git a/.playwright-mcp/console-2026-08-07T19-17-35-278Z.log b/.playwright-mcp/console-2026-08-07T19-17-35-278Z.log new file mode 100644 index 00000000000..0d334f0864b --- /dev/null +++ b/.playwright-mcp/console-2026-08-07T19-17-35-278Z.log @@ -0,0 +1,19 @@ +[ 56ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 +[ 384ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 +[ 15133ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 16463ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 17823ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 19227ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 20687ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 22241ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 23898ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 25653ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 27559ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 29620ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 31876ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 149081ms] [ERROR] Failed to load resource: the server responded with a status of 500 (Internal Server Error) @ http://localhost:3010/src/routes/signin.tsx?t=1786130404341:0 +[ 149081ms] [ERROR] Failed to load resource: the server responded with a status of 500 (Internal Server Error) @ http://localhost:3010/src/routes/signin.tsx?t=1786130404341&tsr-split=component:0 +[ 149081ms] [ERROR] [vite] Failed to reload /src/routes/signin.tsx. This could be due to syntax errors or importing non-existent modules. (see errors above) @ http://localhost:3010/@vite/client:808 +[ 149081ms] [ERROR] [vite] Failed to reload /src/routes/signin.tsx?tsr-split=component. This could be due to syntax errors or importing non-existent modules. (see errors above) @ http://localhost:3010/@vite/client:808 +[ 149245ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 +[ 149453ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 diff --git a/.playwright-mcp/console-2026-08-07T19-20-14-759Z.log b/.playwright-mcp/console-2026-08-07T19-20-14-759Z.log new file mode 100644 index 00000000000..e1b47e639a3 --- /dev/null +++ b/.playwright-mcp/console-2026-08-07T19-20-14-759Z.log @@ -0,0 +1,10 @@ +[ 58ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 +[ 367ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 +[ 12956ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 14309ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 15761ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 17369ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 19133ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 21084ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 23240ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 +[ 50363ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 diff --git a/.playwright-mcp/page-2026-08-07T19-12-28-035Z.yml b/.playwright-mcp/page-2026-08-07T19-12-28-035Z.yml new file mode 100644 index 00000000000..92f12e921bf --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-12-28-035Z.yml @@ -0,0 +1,8 @@ +- generic [active] [ref=e1]: + - heading [level=2] [ref=e2]: + - text: "pathname:" + - code [ref=e3]: / + - generic [ref=e4]: + - heading "home" [level=1] [ref=e5] + - link "Sign In" [ref=e6] [cursor=pointer]: + - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-12-46-367Z.yml b/.playwright-mcp/page-2026-08-07T19-12-46-367Z.yml new file mode 100644 index 00000000000..cd682613eec --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-12-46-367Z.yml @@ -0,0 +1,49 @@ +- generic [active] [ref=e1]: + - heading [level=2] [ref=e81]: + - text: "pathname:" + - code [ref=e3]: /signin + - generic [ref=e82]: + - heading "sign in" [level=1] [ref=e83] + - link "go home" [ref=e84] [cursor=pointer]: + - /url: / + - generic [ref=e86]: + - generic [ref=e87]: + - generic [ref=e88]: + - link [ref=e90] [cursor=pointer]: + - /url: http://localhost:3010/ + - img "alphaXiv" [ref=e91] + - generic [ref=e92]: + - heading "Sign in to alphaXiv" [level=1] [ref=e93] + - paragraph [ref=e94]: Welcome back! Please sign in to continue + - generic [ref=e95]: + - button "Sign in with Google Continue with Google" [ref=e98] [cursor=pointer]: + - generic [ref=e99]: + - img "Sign in with Google" [ref=e101] + - generic [ref=e102]: Continue with Google + - paragraph [ref=e105]: or + - generic [ref=e107]: + - generic [ref=e108]: + - generic [ref=e111]: + - generic [ref=e112]: Email address + - textbox "Email address" [ref=e114]: + - /placeholder: Enter your email address + - generic: + - generic: + - generic: + - generic: Password + - generic: + - textbox "Password": + - /placeholder: Enter your password + - button "Show password" + - button "Continue" [ref=e117] [cursor=pointer] + - generic [ref=e121]: + - generic [ref=e122]: + - generic [ref=e123]: Don’t have an account? + - link "Sign up" [ref=e124] [cursor=pointer]: + - /url: http://localhost:3010/signup + - paragraph [ref=e127]: Development mode + - generic: + - contentinfo: + - button "Open TanStack Router Devtools" [ref=e7] [cursor=pointer]: + - generic [ref=e79]: "-" + - generic [ref=e80]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-12-51-542Z.yml b/.playwright-mcp/page-2026-08-07T19-12-51-542Z.yml new file mode 100644 index 00000000000..cd682613eec --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-12-51-542Z.yml @@ -0,0 +1,49 @@ +- generic [active] [ref=e1]: + - heading [level=2] [ref=e81]: + - text: "pathname:" + - code [ref=e3]: /signin + - generic [ref=e82]: + - heading "sign in" [level=1] [ref=e83] + - link "go home" [ref=e84] [cursor=pointer]: + - /url: / + - generic [ref=e86]: + - generic [ref=e87]: + - generic [ref=e88]: + - link [ref=e90] [cursor=pointer]: + - /url: http://localhost:3010/ + - img "alphaXiv" [ref=e91] + - generic [ref=e92]: + - heading "Sign in to alphaXiv" [level=1] [ref=e93] + - paragraph [ref=e94]: Welcome back! Please sign in to continue + - generic [ref=e95]: + - button "Sign in with Google Continue with Google" [ref=e98] [cursor=pointer]: + - generic [ref=e99]: + - img "Sign in with Google" [ref=e101] + - generic [ref=e102]: Continue with Google + - paragraph [ref=e105]: or + - generic [ref=e107]: + - generic [ref=e108]: + - generic [ref=e111]: + - generic [ref=e112]: Email address + - textbox "Email address" [ref=e114]: + - /placeholder: Enter your email address + - generic: + - generic: + - generic: + - generic: Password + - generic: + - textbox "Password": + - /placeholder: Enter your password + - button "Show password" + - button "Continue" [ref=e117] [cursor=pointer] + - generic [ref=e121]: + - generic [ref=e122]: + - generic [ref=e123]: Don’t have an account? + - link "Sign up" [ref=e124] [cursor=pointer]: + - /url: http://localhost:3010/signup + - paragraph [ref=e127]: Development mode + - generic: + - contentinfo: + - button "Open TanStack Router Devtools" [ref=e7] [cursor=pointer]: + - generic [ref=e79]: "-" + - generic [ref=e80]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-13-04-813Z.yml b/.playwright-mcp/page-2026-08-07T19-13-04-813Z.yml new file mode 100644 index 00000000000..3d1356ccbaf --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-13-04-813Z.yml @@ -0,0 +1,15 @@ +- generic [active] [ref=e1]: + - heading [level=2] [ref=e128]: + - text: "pathname:" + - code [ref=e3]: /login?redirect_url=http%3A%2F%2Flocalhost%3A3010%2F + - generic [ref=e129]: + - paragraph [ref=e131]: The page you are looking for does not exist. + - paragraph [ref=e132]: + - button "Go back" [ref=e133] + - link "Start Over" [ref=e134] [cursor=pointer]: + - /url: / + - generic: + - contentinfo: + - button "Open TanStack Router Devtools" [ref=e7] [cursor=pointer]: + - generic [ref=e79]: "-" + - generic [ref=e80]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-14-00-158Z.yml b/.playwright-mcp/page-2026-08-07T19-14-00-158Z.yml new file mode 100644 index 00000000000..c130a2ac69d --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-14-00-158Z.yml @@ -0,0 +1,8 @@ +- generic [active] [ref=f1e1]: + - heading [level=2] [ref=f1e2]: + - text: "pathname:" + - code [ref=f1e3]: / + - generic [ref=f1e4]: + - heading "home" [level=1] [ref=f1e5] + - link "Sign In" [ref=f1e6] [cursor=pointer]: + - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-14-43-165Z.yml b/.playwright-mcp/page-2026-08-07T19-14-43-165Z.yml new file mode 100644 index 00000000000..e332e61a6c0 --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-14-43-165Z.yml @@ -0,0 +1,8 @@ +- generic [active] [ref=f3e1]: + - heading [level=2] [ref=f3e2]: + - text: "pathname:" + - code [ref=f3e3]: / + - generic [ref=f3e4]: + - heading "home" [level=1] [ref=f3e5] + - link "Sign In" [ref=f3e6] [cursor=pointer]: + - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-14-55-829Z.yml b/.playwright-mcp/page-2026-08-07T19-14-55-829Z.yml new file mode 100644 index 00000000000..99a24c4b3d9 --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-14-55-829Z.yml @@ -0,0 +1,47 @@ +- generic [active] [ref=f3e1]: + - heading [level=2] [ref=f3e7]: + - text: "pathname:" + - code [ref=f3e3]: /signin + - generic [ref=f3e8]: + - heading "sign in" [level=1] [ref=f3e9] + - link "go home" [ref=f3e10] [cursor=pointer]: + - /url: / + - generic [ref=f3e12]: + - generic [ref=f3e13]: + - generic [ref=f3e14]: + - link [ref=f3e16] [cursor=pointer]: + - /url: http://localhost:3010/ + - img "alphaXiv" [ref=f3e17] + - generic [ref=f3e18]: + - heading "Sign in to alphaXiv" [level=1] [ref=f3e19] + - paragraph [ref=f3e20]: Welcome back! Please sign in to continue + - generic [ref=f3e21]: + - button "Sign in with Google Continue with Google" [ref=f3e24] [cursor=pointer]: + - generic [ref=f3e25]: + - generic "Sign in with Google" [ref=f3e27] + - generic [ref=f3e28]: Continue with Google + - paragraph [ref=f3e31]: or + - generic [ref=f3e33]: + - generic [ref=f3e34]: + - generic [ref=f3e37]: + - generic [ref=f3e38]: Email address + - textbox "Email address" [ref=f3e40]: + - /placeholder: Enter your email address + - generic: + - generic: Password + - generic: + - textbox: + - /placeholder: Enter your password + - button + - button "Continue" [ref=f3e43] [cursor=pointer] + - generic [ref=f3e47]: + - generic [ref=f3e48]: + - generic [ref=f3e49]: Don’t have an account? + - link "Sign up" [ref=f3e50] [cursor=pointer]: + - /url: http://localhost:3010/signup + - paragraph [ref=f3e53]: Development mode + - generic: + - contentinfo: + - button "Open TanStack Router Devtools" [ref=f3e54] [cursor=pointer]: + - generic [ref=f3e126]: "-" + - generic [ref=f3e127]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-15-01-070Z.yml b/.playwright-mcp/page-2026-08-07T19-15-01-070Z.yml new file mode 100644 index 00000000000..99a24c4b3d9 --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-15-01-070Z.yml @@ -0,0 +1,47 @@ +- generic [active] [ref=f3e1]: + - heading [level=2] [ref=f3e7]: + - text: "pathname:" + - code [ref=f3e3]: /signin + - generic [ref=f3e8]: + - heading "sign in" [level=1] [ref=f3e9] + - link "go home" [ref=f3e10] [cursor=pointer]: + - /url: / + - generic [ref=f3e12]: + - generic [ref=f3e13]: + - generic [ref=f3e14]: + - link [ref=f3e16] [cursor=pointer]: + - /url: http://localhost:3010/ + - img "alphaXiv" [ref=f3e17] + - generic [ref=f3e18]: + - heading "Sign in to alphaXiv" [level=1] [ref=f3e19] + - paragraph [ref=f3e20]: Welcome back! Please sign in to continue + - generic [ref=f3e21]: + - button "Sign in with Google Continue with Google" [ref=f3e24] [cursor=pointer]: + - generic [ref=f3e25]: + - generic "Sign in with Google" [ref=f3e27] + - generic [ref=f3e28]: Continue with Google + - paragraph [ref=f3e31]: or + - generic [ref=f3e33]: + - generic [ref=f3e34]: + - generic [ref=f3e37]: + - generic [ref=f3e38]: Email address + - textbox "Email address" [ref=f3e40]: + - /placeholder: Enter your email address + - generic: + - generic: Password + - generic: + - textbox: + - /placeholder: Enter your password + - button + - button "Continue" [ref=f3e43] [cursor=pointer] + - generic [ref=f3e47]: + - generic [ref=f3e48]: + - generic [ref=f3e49]: Don’t have an account? + - link "Sign up" [ref=f3e50] [cursor=pointer]: + - /url: http://localhost:3010/signup + - paragraph [ref=f3e53]: Development mode + - generic: + - contentinfo: + - button "Open TanStack Router Devtools" [ref=f3e54] [cursor=pointer]: + - generic [ref=f3e126]: "-" + - generic [ref=f3e127]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-15-07-513Z.yml b/.playwright-mcp/page-2026-08-07T19-15-07-513Z.yml new file mode 100644 index 00000000000..fd5061a2675 --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-15-07-513Z.yml @@ -0,0 +1,13 @@ +- generic [active] [ref=f3e1]: + - heading [level=2] [ref=f3e128]: + - text: "pathname:" + - code [ref=f3e3]: / + - generic [ref=f3e129]: + - heading "home" [level=1] [ref=f3e130] + - link "Sign In" [ref=f3e131] [cursor=pointer]: + - /url: /signin + - generic: + - contentinfo: + - button "Open TanStack Router Devtools" [ref=f3e54] [cursor=pointer]: + - generic [ref=f3e126]: "-" + - generic [ref=f3e127]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-16-19-974Z.yml b/.playwright-mcp/page-2026-08-07T19-16-19-974Z.yml new file mode 100644 index 00000000000..ce311311929 --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-16-19-974Z.yml @@ -0,0 +1,8 @@ +- generic [active] [ref=f5e1]: + - heading [level=2] [ref=f5e2]: + - text: "pathname:" + - code [ref=f5e3]: / + - generic [ref=f5e4]: + - heading "home" [level=1] [ref=f5e5] + - link "Sign In" [ref=f5e6] [cursor=pointer]: + - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-17-35-333Z.yml b/.playwright-mcp/page-2026-08-07T19-17-35-333Z.yml new file mode 100644 index 00000000000..860ae047b5b --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-17-35-333Z.yml @@ -0,0 +1,8 @@ +- generic [active] [ref=f7e1]: + - heading [level=2] [ref=f7e2]: + - text: "pathname:" + - code [ref=f7e3]: / + - generic [ref=f7e4]: + - heading "home" [level=1] [ref=f7e5] + - link "Sign In" [ref=f7e6] [cursor=pointer]: + - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-20-14-815Z.yml b/.playwright-mcp/page-2026-08-07T19-20-14-815Z.yml new file mode 100644 index 00000000000..8e0898ace38 --- /dev/null +++ b/.playwright-mcp/page-2026-08-07T19-20-14-815Z.yml @@ -0,0 +1,8 @@ +- generic [active] [ref=f9e1]: + - heading [level=2] [ref=f9e2]: + - text: "pathname:" + - code [ref=f9e3]: / + - generic [ref=f9e4]: + - heading "home" [level=1] [ref=f9e5] + - link "Sign In" [ref=f9e6] [cursor=pointer]: + - /url: /signin \ No newline at end of file diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 9b683cb9388..23511be6fe5 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -2,7 +2,7 @@ "files": [ { "path": "./dist/clerk.js", "maxSize": "549KB" }, { "path": "./dist/clerk.browser.js", "maxSize": "75KB" }, - { "path": "./dist/clerk.legacy.browser.js", "maxSize": "117KB" }, + { "path": "./dist/clerk.legacy.browser.js", "maxSize": "119KB" }, { "path": "./dist/clerk.no-rhc.js", "maxSize": "316KB" }, { "path": "./dist/clerk.native.js", "maxSize": "77KB" }, { "path": "./dist/vendors*.js", "maxSize": "7KB" }, From 03d1175bb3dc9bbff197b35169f3fd3e4c4ef218 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 7 Aug 2026 18:51:15 -0700 Subject: [PATCH 3/4] chore: remove unnecessary files --- .../console-2026-08-07T19-12-27-957Z.log | 5 -- .../console-2026-08-07T19-14-00-069Z.log | 5 -- .../console-2026-08-07T19-14-43-098Z.log | 14 ------ .../console-2026-08-07T19-16-19-898Z.log | 49 ------------------- .../console-2026-08-07T19-17-35-278Z.log | 19 ------- .../console-2026-08-07T19-20-14-759Z.log | 10 ---- .../page-2026-08-07T19-12-28-035Z.yml | 8 --- .../page-2026-08-07T19-12-46-367Z.yml | 49 ------------------- .../page-2026-08-07T19-12-51-542Z.yml | 49 ------------------- .../page-2026-08-07T19-13-04-813Z.yml | 15 ------ .../page-2026-08-07T19-14-00-158Z.yml | 8 --- .../page-2026-08-07T19-14-43-165Z.yml | 8 --- .../page-2026-08-07T19-14-55-829Z.yml | 47 ------------------ .../page-2026-08-07T19-15-01-070Z.yml | 47 ------------------ .../page-2026-08-07T19-15-07-513Z.yml | 13 ----- .../page-2026-08-07T19-16-19-974Z.yml | 8 --- .../page-2026-08-07T19-17-35-333Z.yml | 8 --- .../page-2026-08-07T19-20-14-815Z.yml | 8 --- 18 files changed, 370 deletions(-) delete mode 100644 .playwright-mcp/console-2026-08-07T19-12-27-957Z.log delete mode 100644 .playwright-mcp/console-2026-08-07T19-14-00-069Z.log delete mode 100644 .playwright-mcp/console-2026-08-07T19-14-43-098Z.log delete mode 100644 .playwright-mcp/console-2026-08-07T19-16-19-898Z.log delete mode 100644 .playwright-mcp/console-2026-08-07T19-17-35-278Z.log delete mode 100644 .playwright-mcp/console-2026-08-07T19-20-14-759Z.log delete mode 100644 .playwright-mcp/page-2026-08-07T19-12-28-035Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-12-46-367Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-12-51-542Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-13-04-813Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-14-00-158Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-14-43-165Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-14-55-829Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-15-01-070Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-15-07-513Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-16-19-974Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-17-35-333Z.yml delete mode 100644 .playwright-mcp/page-2026-08-07T19-20-14-815Z.yml diff --git a/.playwright-mcp/console-2026-08-07T19-12-27-957Z.log b/.playwright-mcp/console-2026-08-07T19-12-27-957Z.log deleted file mode 100644 index b594516760d..00000000000 --- a/.playwright-mcp/console-2026-08-07T19-12-27-957Z.log +++ /dev/null @@ -1,5 +0,0 @@ -[ 68ms] [ERROR] Failed to load resource: the server responded with a status of 404 () @ http://localhost:3010/favicon.ico:0 -[ 329ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=b138766f:20100 -[ 652ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 -[ 12579ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 36306ms] [WARNING] Clerk: The prop "redirectUrl" is deprecated and should be replaced with the new "fallbackRedirectUrl" or "forceRedirectUrl" props instead. Learn more: https://clerk.com/docs/guides/custom-redirects#redirect-url-props @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 diff --git a/.playwright-mcp/console-2026-08-07T19-14-00-069Z.log b/.playwright-mcp/console-2026-08-07T19-14-00-069Z.log deleted file mode 100644 index a728128ddce..00000000000 --- a/.playwright-mcp/console-2026-08-07T19-14-00-069Z.log +++ /dev/null @@ -1,5 +0,0 @@ -[ 57ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=b138766f:20100 -[ 174ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 -[ 22587ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 -[ 33148ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=09d9b106:20100 -[ 33672ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 diff --git a/.playwright-mcp/console-2026-08-07T19-14-43-098Z.log b/.playwright-mcp/console-2026-08-07T19-14-43-098Z.log deleted file mode 100644 index b93095eca02..00000000000 --- a/.playwright-mcp/console-2026-08-07T19-14-43-098Z.log +++ /dev/null @@ -1,14 +0,0 @@ -[ 55ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=09d9b106:20100 -[ 270ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 -[ 7009ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 35744ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 37310ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 38966ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 40730ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 42596ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 44557ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 46619ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 48784ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 73800ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 -[ 85442ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=268ad286:20100 -[ 85564ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 diff --git a/.playwright-mcp/console-2026-08-07T19-16-19-898Z.log b/.playwright-mcp/console-2026-08-07T19-16-19-898Z.log deleted file mode 100644 index d8e58669e89..00000000000 --- a/.playwright-mcp/console-2026-08-07T19-16-19-898Z.log +++ /dev/null @@ -1,49 +0,0 @@ -[ 57ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/react-dom_client.js?v=268ad286:20100 -[ 272ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 -[ 7741ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 7958ms] [WARNING] Clerk: The prop "redirectUrl" is deprecated and should be replaced with the new "fallbackRedirectUrl" or "forceRedirectUrl" props instead. Learn more: https://clerk.com/docs/guides/custom-redirects#redirect-url-props @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@5/dist/clerk.browser.js:18 -[ 58010ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 -[ 65343ms] [ERROR] Failed to load resource: the server responded with a status of 404 () @ http://localhost:3010/login#/?redirect_url=http%3A%2F%2Flocalhost%3A3010%2F:0 -[ 65578ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 -[ 65601ms] Error: Hydration failed because the server rendered text didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: - -- A server/client branch `if (typeof window !== 'undefined')`. -- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called. -- Date formatting in a user's locale which doesn't match the server. -- External changing data without sending a snapshot of it along with the HTML. -- Invalid HTML tag nesting. - -It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. - -https://react.dev/link/hydration-mismatch - - ... - - - - - - - - <__experimental_CheckoutProvider value={undefined}> - - - - -

- -+ /login#/?redirect_url=http%3A%2F%2Flocalhost%3A3010%2F -- /login - ... - - at throwOnHydrationMismatch (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:3918:13) - at prepareToHydrateHostInstance (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:3992:23) - at completeWork (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:8997:17) - at runWithFiberInDEV (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:995:72) - at completeUnitOfWork (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12667:22) - at performUnitOfWork (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12573:29) - at workLoopConcurrentByScheduler (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12555:11) - at renderRootConcurrent (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:12537:71) - at performWorkOnRoot (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:11764:152) - at performWorkOnRootViaSchedulerTask (http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:13503:9) -[ 65794ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 diff --git a/.playwright-mcp/console-2026-08-07T19-17-35-278Z.log b/.playwright-mcp/console-2026-08-07T19-17-35-278Z.log deleted file mode 100644 index 0d334f0864b..00000000000 --- a/.playwright-mcp/console-2026-08-07T19-17-35-278Z.log +++ /dev/null @@ -1,19 +0,0 @@ -[ 56ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 -[ 384ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 -[ 15133ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 16463ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 17823ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 19227ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 20687ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 22241ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 23898ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 25653ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 27559ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 29620ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 31876ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 149081ms] [ERROR] Failed to load resource: the server responded with a status of 500 (Internal Server Error) @ http://localhost:3010/src/routes/signin.tsx?t=1786130404341:0 -[ 149081ms] [ERROR] Failed to load resource: the server responded with a status of 500 (Internal Server Error) @ http://localhost:3010/src/routes/signin.tsx?t=1786130404341&tsr-split=component:0 -[ 149081ms] [ERROR] [vite] Failed to reload /src/routes/signin.tsx. This could be due to syntax errors or importing non-existent modules. (see errors above) @ http://localhost:3010/@vite/client:808 -[ 149081ms] [ERROR] [vite] Failed to reload /src/routes/signin.tsx?tsr-split=component. This could be due to syntax errors or importing non-existent modules. (see errors above) @ http://localhost:3010/@vite/client:808 -[ 149245ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 -[ 149453ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 diff --git a/.playwright-mcp/console-2026-08-07T19-20-14-759Z.log b/.playwright-mcp/console-2026-08-07T19-20-14-759Z.log deleted file mode 100644 index e1b47e639a3..00000000000 --- a/.playwright-mcp/console-2026-08-07T19-20-14-759Z.log +++ /dev/null @@ -1,10 +0,0 @@ -[ 58ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3010/node_modules/.vite/deps/chunk-B5N7PWF5.js?v=db6c288b:20100 -[ 367ms] [WARNING] Clerk: Clerk has been loaded with development keys. Development instances have strict usage limits and should not be used when deploying your application to production. Learn more: https://clerk.com/docs/deployments/overview @ https://glorious-pegasus-4.clerk.accounts.dev/npm/@clerk/clerk-js@6/dist/clerk.browser.js:11 -[ 12956ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 14309ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 15761ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 17369ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 19133ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 21084ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 23240ms] [VERBOSE] [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3010/signin:0 -[ 50363ms] [LOG] [vite] server connection lost. Polling for restart... @ http://localhost:3010/@vite/client:864 diff --git a/.playwright-mcp/page-2026-08-07T19-12-28-035Z.yml b/.playwright-mcp/page-2026-08-07T19-12-28-035Z.yml deleted file mode 100644 index 92f12e921bf..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-12-28-035Z.yml +++ /dev/null @@ -1,8 +0,0 @@ -- generic [active] [ref=e1]: - - heading [level=2] [ref=e2]: - - text: "pathname:" - - code [ref=e3]: / - - generic [ref=e4]: - - heading "home" [level=1] [ref=e5] - - link "Sign In" [ref=e6] [cursor=pointer]: - - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-12-46-367Z.yml b/.playwright-mcp/page-2026-08-07T19-12-46-367Z.yml deleted file mode 100644 index cd682613eec..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-12-46-367Z.yml +++ /dev/null @@ -1,49 +0,0 @@ -- generic [active] [ref=e1]: - - heading [level=2] [ref=e81]: - - text: "pathname:" - - code [ref=e3]: /signin - - generic [ref=e82]: - - heading "sign in" [level=1] [ref=e83] - - link "go home" [ref=e84] [cursor=pointer]: - - /url: / - - generic [ref=e86]: - - generic [ref=e87]: - - generic [ref=e88]: - - link [ref=e90] [cursor=pointer]: - - /url: http://localhost:3010/ - - img "alphaXiv" [ref=e91] - - generic [ref=e92]: - - heading "Sign in to alphaXiv" [level=1] [ref=e93] - - paragraph [ref=e94]: Welcome back! Please sign in to continue - - generic [ref=e95]: - - button "Sign in with Google Continue with Google" [ref=e98] [cursor=pointer]: - - generic [ref=e99]: - - img "Sign in with Google" [ref=e101] - - generic [ref=e102]: Continue with Google - - paragraph [ref=e105]: or - - generic [ref=e107]: - - generic [ref=e108]: - - generic [ref=e111]: - - generic [ref=e112]: Email address - - textbox "Email address" [ref=e114]: - - /placeholder: Enter your email address - - generic: - - generic: - - generic: - - generic: Password - - generic: - - textbox "Password": - - /placeholder: Enter your password - - button "Show password" - - button "Continue" [ref=e117] [cursor=pointer] - - generic [ref=e121]: - - generic [ref=e122]: - - generic [ref=e123]: Don’t have an account? - - link "Sign up" [ref=e124] [cursor=pointer]: - - /url: http://localhost:3010/signup - - paragraph [ref=e127]: Development mode - - generic: - - contentinfo: - - button "Open TanStack Router Devtools" [ref=e7] [cursor=pointer]: - - generic [ref=e79]: "-" - - generic [ref=e80]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-12-51-542Z.yml b/.playwright-mcp/page-2026-08-07T19-12-51-542Z.yml deleted file mode 100644 index cd682613eec..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-12-51-542Z.yml +++ /dev/null @@ -1,49 +0,0 @@ -- generic [active] [ref=e1]: - - heading [level=2] [ref=e81]: - - text: "pathname:" - - code [ref=e3]: /signin - - generic [ref=e82]: - - heading "sign in" [level=1] [ref=e83] - - link "go home" [ref=e84] [cursor=pointer]: - - /url: / - - generic [ref=e86]: - - generic [ref=e87]: - - generic [ref=e88]: - - link [ref=e90] [cursor=pointer]: - - /url: http://localhost:3010/ - - img "alphaXiv" [ref=e91] - - generic [ref=e92]: - - heading "Sign in to alphaXiv" [level=1] [ref=e93] - - paragraph [ref=e94]: Welcome back! Please sign in to continue - - generic [ref=e95]: - - button "Sign in with Google Continue with Google" [ref=e98] [cursor=pointer]: - - generic [ref=e99]: - - img "Sign in with Google" [ref=e101] - - generic [ref=e102]: Continue with Google - - paragraph [ref=e105]: or - - generic [ref=e107]: - - generic [ref=e108]: - - generic [ref=e111]: - - generic [ref=e112]: Email address - - textbox "Email address" [ref=e114]: - - /placeholder: Enter your email address - - generic: - - generic: - - generic: - - generic: Password - - generic: - - textbox "Password": - - /placeholder: Enter your password - - button "Show password" - - button "Continue" [ref=e117] [cursor=pointer] - - generic [ref=e121]: - - generic [ref=e122]: - - generic [ref=e123]: Don’t have an account? - - link "Sign up" [ref=e124] [cursor=pointer]: - - /url: http://localhost:3010/signup - - paragraph [ref=e127]: Development mode - - generic: - - contentinfo: - - button "Open TanStack Router Devtools" [ref=e7] [cursor=pointer]: - - generic [ref=e79]: "-" - - generic [ref=e80]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-13-04-813Z.yml b/.playwright-mcp/page-2026-08-07T19-13-04-813Z.yml deleted file mode 100644 index 3d1356ccbaf..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-13-04-813Z.yml +++ /dev/null @@ -1,15 +0,0 @@ -- generic [active] [ref=e1]: - - heading [level=2] [ref=e128]: - - text: "pathname:" - - code [ref=e3]: /login?redirect_url=http%3A%2F%2Flocalhost%3A3010%2F - - generic [ref=e129]: - - paragraph [ref=e131]: The page you are looking for does not exist. - - paragraph [ref=e132]: - - button "Go back" [ref=e133] - - link "Start Over" [ref=e134] [cursor=pointer]: - - /url: / - - generic: - - contentinfo: - - button "Open TanStack Router Devtools" [ref=e7] [cursor=pointer]: - - generic [ref=e79]: "-" - - generic [ref=e80]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-14-00-158Z.yml b/.playwright-mcp/page-2026-08-07T19-14-00-158Z.yml deleted file mode 100644 index c130a2ac69d..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-14-00-158Z.yml +++ /dev/null @@ -1,8 +0,0 @@ -- generic [active] [ref=f1e1]: - - heading [level=2] [ref=f1e2]: - - text: "pathname:" - - code [ref=f1e3]: / - - generic [ref=f1e4]: - - heading "home" [level=1] [ref=f1e5] - - link "Sign In" [ref=f1e6] [cursor=pointer]: - - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-14-43-165Z.yml b/.playwright-mcp/page-2026-08-07T19-14-43-165Z.yml deleted file mode 100644 index e332e61a6c0..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-14-43-165Z.yml +++ /dev/null @@ -1,8 +0,0 @@ -- generic [active] [ref=f3e1]: - - heading [level=2] [ref=f3e2]: - - text: "pathname:" - - code [ref=f3e3]: / - - generic [ref=f3e4]: - - heading "home" [level=1] [ref=f3e5] - - link "Sign In" [ref=f3e6] [cursor=pointer]: - - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-14-55-829Z.yml b/.playwright-mcp/page-2026-08-07T19-14-55-829Z.yml deleted file mode 100644 index 99a24c4b3d9..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-14-55-829Z.yml +++ /dev/null @@ -1,47 +0,0 @@ -- generic [active] [ref=f3e1]: - - heading [level=2] [ref=f3e7]: - - text: "pathname:" - - code [ref=f3e3]: /signin - - generic [ref=f3e8]: - - heading "sign in" [level=1] [ref=f3e9] - - link "go home" [ref=f3e10] [cursor=pointer]: - - /url: / - - generic [ref=f3e12]: - - generic [ref=f3e13]: - - generic [ref=f3e14]: - - link [ref=f3e16] [cursor=pointer]: - - /url: http://localhost:3010/ - - img "alphaXiv" [ref=f3e17] - - generic [ref=f3e18]: - - heading "Sign in to alphaXiv" [level=1] [ref=f3e19] - - paragraph [ref=f3e20]: Welcome back! Please sign in to continue - - generic [ref=f3e21]: - - button "Sign in with Google Continue with Google" [ref=f3e24] [cursor=pointer]: - - generic [ref=f3e25]: - - generic "Sign in with Google" [ref=f3e27] - - generic [ref=f3e28]: Continue with Google - - paragraph [ref=f3e31]: or - - generic [ref=f3e33]: - - generic [ref=f3e34]: - - generic [ref=f3e37]: - - generic [ref=f3e38]: Email address - - textbox "Email address" [ref=f3e40]: - - /placeholder: Enter your email address - - generic: - - generic: Password - - generic: - - textbox: - - /placeholder: Enter your password - - button - - button "Continue" [ref=f3e43] [cursor=pointer] - - generic [ref=f3e47]: - - generic [ref=f3e48]: - - generic [ref=f3e49]: Don’t have an account? - - link "Sign up" [ref=f3e50] [cursor=pointer]: - - /url: http://localhost:3010/signup - - paragraph [ref=f3e53]: Development mode - - generic: - - contentinfo: - - button "Open TanStack Router Devtools" [ref=f3e54] [cursor=pointer]: - - generic [ref=f3e126]: "-" - - generic [ref=f3e127]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-15-01-070Z.yml b/.playwright-mcp/page-2026-08-07T19-15-01-070Z.yml deleted file mode 100644 index 99a24c4b3d9..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-15-01-070Z.yml +++ /dev/null @@ -1,47 +0,0 @@ -- generic [active] [ref=f3e1]: - - heading [level=2] [ref=f3e7]: - - text: "pathname:" - - code [ref=f3e3]: /signin - - generic [ref=f3e8]: - - heading "sign in" [level=1] [ref=f3e9] - - link "go home" [ref=f3e10] [cursor=pointer]: - - /url: / - - generic [ref=f3e12]: - - generic [ref=f3e13]: - - generic [ref=f3e14]: - - link [ref=f3e16] [cursor=pointer]: - - /url: http://localhost:3010/ - - img "alphaXiv" [ref=f3e17] - - generic [ref=f3e18]: - - heading "Sign in to alphaXiv" [level=1] [ref=f3e19] - - paragraph [ref=f3e20]: Welcome back! Please sign in to continue - - generic [ref=f3e21]: - - button "Sign in with Google Continue with Google" [ref=f3e24] [cursor=pointer]: - - generic [ref=f3e25]: - - generic "Sign in with Google" [ref=f3e27] - - generic [ref=f3e28]: Continue with Google - - paragraph [ref=f3e31]: or - - generic [ref=f3e33]: - - generic [ref=f3e34]: - - generic [ref=f3e37]: - - generic [ref=f3e38]: Email address - - textbox "Email address" [ref=f3e40]: - - /placeholder: Enter your email address - - generic: - - generic: Password - - generic: - - textbox: - - /placeholder: Enter your password - - button - - button "Continue" [ref=f3e43] [cursor=pointer] - - generic [ref=f3e47]: - - generic [ref=f3e48]: - - generic [ref=f3e49]: Don’t have an account? - - link "Sign up" [ref=f3e50] [cursor=pointer]: - - /url: http://localhost:3010/signup - - paragraph [ref=f3e53]: Development mode - - generic: - - contentinfo: - - button "Open TanStack Router Devtools" [ref=f3e54] [cursor=pointer]: - - generic [ref=f3e126]: "-" - - generic [ref=f3e127]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-15-07-513Z.yml b/.playwright-mcp/page-2026-08-07T19-15-07-513Z.yml deleted file mode 100644 index fd5061a2675..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-15-07-513Z.yml +++ /dev/null @@ -1,13 +0,0 @@ -- generic [active] [ref=f3e1]: - - heading [level=2] [ref=f3e128]: - - text: "pathname:" - - code [ref=f3e3]: / - - generic [ref=f3e129]: - - heading "home" [level=1] [ref=f3e130] - - link "Sign In" [ref=f3e131] [cursor=pointer]: - - /url: /signin - - generic: - - contentinfo: - - button "Open TanStack Router Devtools" [ref=f3e54] [cursor=pointer]: - - generic [ref=f3e126]: "-" - - generic [ref=f3e127]: TanStack Router \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-16-19-974Z.yml b/.playwright-mcp/page-2026-08-07T19-16-19-974Z.yml deleted file mode 100644 index ce311311929..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-16-19-974Z.yml +++ /dev/null @@ -1,8 +0,0 @@ -- generic [active] [ref=f5e1]: - - heading [level=2] [ref=f5e2]: - - text: "pathname:" - - code [ref=f5e3]: / - - generic [ref=f5e4]: - - heading "home" [level=1] [ref=f5e5] - - link "Sign In" [ref=f5e6] [cursor=pointer]: - - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-17-35-333Z.yml b/.playwright-mcp/page-2026-08-07T19-17-35-333Z.yml deleted file mode 100644 index 860ae047b5b..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-17-35-333Z.yml +++ /dev/null @@ -1,8 +0,0 @@ -- generic [active] [ref=f7e1]: - - heading [level=2] [ref=f7e2]: - - text: "pathname:" - - code [ref=f7e3]: / - - generic [ref=f7e4]: - - heading "home" [level=1] [ref=f7e5] - - link "Sign In" [ref=f7e6] [cursor=pointer]: - - /url: /signin \ No newline at end of file diff --git a/.playwright-mcp/page-2026-08-07T19-20-14-815Z.yml b/.playwright-mcp/page-2026-08-07T19-20-14-815Z.yml deleted file mode 100644 index 8e0898ace38..00000000000 --- a/.playwright-mcp/page-2026-08-07T19-20-14-815Z.yml +++ /dev/null @@ -1,8 +0,0 @@ -- generic [active] [ref=f9e1]: - - heading [level=2] [ref=f9e2]: - - text: "pathname:" - - code [ref=f9e3]: / - - generic [ref=f9e4]: - - heading "home" [level=1] [ref=f9e5] - - link "Sign In" [ref=f9e6] [cursor=pointer]: - - /url: /signin \ No newline at end of file From 1cf31dea271392832dbf857256cd16cd12ffc7e8 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sat, 8 Aug 2026 14:49:58 -0700 Subject: [PATCH 4/4] chore: clean up comments ff agents rule --- .../src/core/__tests__/fapiClient.test.ts | 19 ++++-------- .../core/__tests__/protectAssertion.test.ts | 14 ++------- packages/clerk-js/src/core/clerk.ts | 10 ++----- packages/clerk-js/src/core/fapiClient.ts | 17 +++-------- .../clerk-js/src/core/protectAssertion.ts | 30 ++++--------------- packages/react/src/isomorphicClerk.ts | 2 -- 6 files changed, 20 insertions(+), 72 deletions(-) diff --git a/packages/clerk-js/src/core/__tests__/fapiClient.test.ts b/packages/clerk-js/src/core/__tests__/fapiClient.test.ts index 2aff74ea8bc..865ff7e866d 100644 --- a/packages/clerk-js/src/core/__tests__/fapiClient.test.ts +++ b/packages/clerk-js/src/core/__tests__/fapiClient.test.ts @@ -385,9 +385,6 @@ describe('request', () => { }); describe('Protect params', () => { - // A body param rather than a header, because a custom header would trigger a CORS - // preflight — the same reason `_method` is a query param. These tests pin that the params - // reach the encoded body, and reach nothing else. const protectParams = { __clerk_protect_assertion: 'token-abc' }; const clientWithProtect = createFapiClient({ ...baseFapiClientOptions, @@ -419,13 +416,11 @@ describe('request', () => { ); }); - // The param name survives the body's camelCase→snake_case key encoder untouched — it is - // all lower-case, so there is nothing for that encoder to rewrite. If it ever did not - // survive, the server would see an unknown param and reject the whole request. + // All lower-case, so the camel-to-snake body key encoder has nothing to rewrite. it('does not mangle the param name', async () => { await clientWithProtect.request({ path: '/client/sign_ins', method: 'POST' }); - const [, init] = (fetch as Mock).mock.calls.at(-1); + const [, init] = (fetch as Mock).mock.calls.at(-1)!; expect(init.body).toBe('__clerk_protect_assertion=token-abc'); }); @@ -436,12 +431,11 @@ describe('request', () => { ])('does not attach them to %s', async (_label, method, path) => { await clientWithProtect.request({ path, method: method as any, body: { a: 'b' } as any }); - const [, init] = (fetch as Mock).mock.calls.at(-1); + const [, init] = (fetch as Mock).mock.calls.at(-1)!; expect(init.body ?? '').not.toContain('__clerk_protect_assertion'); }); - // Spreading a FormData would discard the caller's payload rather than add to it, so a body - // that is not a plain object is left completely alone. + // Spreading a FormData would discard the caller's payload, so non-plain bodies are left alone. it('leaves a FormData body untouched', async () => { const formData = new FormData(); formData.append('identifier', 'user@example.com'); @@ -452,8 +446,7 @@ describe('request', () => { }); it('leaves a string body untouched', async () => { - // text/plain so the form-urlencoded encoder stays out of it; the point here is that the - // merge does not touch a body it cannot safely spread. + // text/plain keeps the form-urlencoded encoder out of it. await clientWithProtect.request({ path: '/client/sign_ins', method: 'POST', @@ -489,8 +482,6 @@ describe('request', () => { expect(fetch).toHaveBeenCalledWith(expect.any(URL), expect.objectContaining({ body: 'identifier=a' })); }); - // Every client built before this existed passes no hook at all; it must behave exactly as - // it did. it('is inert when no hook is configured', async () => { await fapiClient.request({ path: '/client/sign_ins', method: 'POST', body: { identifier: 'a' } as any }); diff --git a/packages/clerk-js/src/core/__tests__/protectAssertion.test.ts b/packages/clerk-js/src/core/__tests__/protectAssertion.test.ts index 24633257762..7ab3870b452 100644 --- a/packages/clerk-js/src/core/__tests__/protectAssertion.test.ts +++ b/packages/clerk-js/src/core/__tests__/protectAssertion.test.ts @@ -19,8 +19,6 @@ describe('resolveProtectAssertion', () => { await expect(resolveProtectAssertion(() => Promise.resolve('token-async'))).resolves.toBe('token-async'); }); - // The whole reason a function is supported: the token outlives neither the page nor its own - // expiry, so a value captured once at configuration time would silently stop applying. it('re-reads the resolver on every call', async () => { const resolver = vi.fn<() => string>(); resolver.mockReturnValueOnce('first').mockReturnValueOnce('second'); @@ -34,8 +32,7 @@ describe('resolveProtectAssertion', () => { await expect(resolveProtectAssertion(() => undefined)).resolves.toBeUndefined(); }); - // An assertion may influence a sign-in but must never prevent one, so every bad input - // degrades to "no assertion" rather than propagating. + // Bad inputs degrade to "no assertion" rather than propagating; Protect must never fail a sign-in. it.each([ [ 'a throwing resolver', @@ -45,7 +42,7 @@ describe('resolveProtectAssertion', () => { ], ['a rejecting resolver', () => Promise.reject(new Error('boom'))], ])('never rejects for %s', async (_label, resolver) => { - await expect(resolveProtectAssertion(resolver as () => string)).resolves.toBeUndefined(); + await expect(resolveProtectAssertion(resolver as unknown as () => string)).resolves.toBeUndefined(); }); it.each([ @@ -66,18 +63,13 @@ describe('protectAssertionParams', () => { }); }); - // The param name is a cross-repo contract with the server, and it is deliberately identical - // to the cookie that can carry the same value. It is also all lower-case + underscores, so - // the body's camelCase→snake_case encoder leaves it alone — pinned here because a rename - // would break silently, as an ignored param rather than an error. + // The param name is a cross-repo contract with the server; a rename would break silently. it('uses a param name the body encoder cannot mangle', () => { expect(PROTECT_ASSERTION_PARAM).toBe('__clerk_protect_assertion'); expect(PROTECT_ASSERTION_PARAM).toBe(PROTECT_ASSERTION_PARAM.toLowerCase()); expect(PROTECT_ASSERTION_PARAM).not.toMatch(/[A-Z]/); }); - // Returning undefined rather than {} is what keeps a request with no assertion byte-for-byte - // the request that would have been sent before this existed. it('returns undefined when there is nothing to attach', async () => { await expect(protectAssertionParams(undefined)).resolves.toBeUndefined(); await expect(protectAssertionParams(() => undefined)).resolves.toBeUndefined(); diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index a589628a67e..e41fb892eb9 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -275,9 +275,7 @@ export class Clerk implements ClerkInterface { #navigationListeners: Array<() => void> = []; #options: ClerkOptions = {}; #protectAssertion: ProtectAssertion | undefined; - // Distinguishes "never set via setProtectAssertion" from "explicitly cleared with - // undefined". Without it, clearing would silently fall back to the `protectAssertion` - // option, and a setter call before `load()` would be overwritten by it. + // Distinguishes never-set from cleared-with-undefined, so clearing does not fall back to the option. #protectAssertionSet = false; #oauthTransport: OAuthTransport | null = null; #pageLifecycle: ReturnType | null = null; @@ -489,11 +487,7 @@ export class Clerk implements ClerkInterface { this.#protectAssertionSet = true; }; - /** - * The assertion in force right now: whatever was last passed to `setProtectAssertion`, - * otherwise the `protectAssertion` option. Read per request, so `load()` may run before or - * after the setter without either clobbering the other. - */ + /** The last value passed to `setProtectAssertion` once called, otherwise the `protectAssertion` option. */ #currentProtectAssertion(): ProtectAssertion | undefined { return this.#protectAssertionSet ? this.#protectAssertion : this.#options.protectAssertion; } diff --git a/packages/clerk-js/src/core/fapiClient.ts b/packages/clerk-js/src/core/fapiClient.ts index dd7c5de557a..526eaf4f0ca 100644 --- a/packages/clerk-js/src/core/fapiClient.ts +++ b/packages/clerk-js/src/core/fapiClient.ts @@ -74,10 +74,6 @@ type FapiClientOptions = { proxyUrl?: string; instanceType: InstanceType; getSessionId: () => string | undefined; - /** - * Resolves the Protect params to merge into the body of a sign-in or sign-up POST, or - * `undefined` when there are none to add. - */ getProtectParams?: () => Promise | undefined>; isSatellite?: boolean; }; @@ -97,8 +93,6 @@ function isMergeableBody(body: unknown): body is Record | undef if (typeof body !== 'object' || body === null) { return false; } - // Spreading anything else — a Blob, a typed array, a stream — would discard the caller's payload - // rather than add to it. const prototype = Object.getPrototypeOf(body); return prototype === Object.prototype || prototype === null; } @@ -227,14 +221,11 @@ export function createFapiClient(options: FapiClientOptions): FapiClient { const { method = 'GET' } = requestInit; let { body } = requestInit; - // Protect params ride in the form-encoded body of sign-in and sign-up POSTs. They have to - // be merged here, before the body is stringified below — the onBeforeRequest callbacks run - // after stringification, so they cannot add a body param. A body param also keeps the - // request CORS-simple; a custom header would trigger the preflight that breaks cookie - // dropping in Safari, the same reason `_method` is a query param. + // Protect params must merge before the body is stringified below (onBeforeRequest callbacks run + // after stringification), and ride the body rather than a header to avoid the same + // CORS preflight `_method` avoids. if (options.getProtectParams && isProtectGatedRequest(method, requestInit.path) && isMergeableBody(body)) { - // Protect can influence a sign-in but must never fail one, so a rejection here costs the - // params and nothing else. + // A rejection costs the params, never the request. const protectParams = await options.getProtectParams().catch(() => undefined); if (protectParams) { body = { ...((body ?? {}) as Record), ...protectParams } as unknown as BodyInit; diff --git a/packages/clerk-js/src/core/protectAssertion.ts b/packages/clerk-js/src/core/protectAssertion.ts index 9046810695c..43e02c3693b 100644 --- a/packages/clerk-js/src/core/protectAssertion.ts +++ b/packages/clerk-js/src/core/protectAssertion.ts @@ -1,25 +1,12 @@ import { logger } from '@clerk/shared/logger'; import type { ProtectAssertion } from '@clerk/shared/types'; -/** - * The request param carrying a Protect assertion. - * - * Deliberately the same name as the cookie that can carry it instead: it is the same value by - * another road, and one name means one thing to search for when working out why an assertion - * did not apply. - */ +/** The request param carrying a Protect assertion; deliberately the same name as the cookie that can carry it. */ export const PROTECT_ASSERTION_PARAM = '__clerk_protect_assertion'; /** - * Resolves the configured assertion for one request. - * - * A function is called per request rather than once at configuration time, so an app that - * refreshes its token while the page is open does not have to re-configure Clerk for the new - * one to take effect. - * - * Nothing here can fail a sign-in. A resolver that throws, rejects, or returns something other - * than a non-empty string yields no assertion and a warning — the request proceeds without it, - * because an assertion may influence a sign-in and must never prevent one. + * Resolves the configured assertion for one request. Never rejects: a failing resolver or + * invalid value yields `undefined`, because an assertion may influence a sign-in but must never prevent one. */ export async function resolveProtectAssertion(assertion: ProtectAssertion | undefined): Promise { if (assertion === undefined) { @@ -36,8 +23,7 @@ export async function resolveProtectAssertion(assertion: ProtectAssertion | unde } } - // `undefined` is the documented way to say "no assertion right now", so it is not worth a - // warning; anything else is a mistake the developer wants to hear about. + // `undefined` is the documented "no assertion right now", so it is not worth a warning. if (value === undefined) { return undefined; } @@ -50,12 +36,8 @@ export async function resolveProtectAssertion(assertion: ProtectAssertion | unde } /** - * The Protect params to merge into a sign-in or sign-up request body, or `undefined` when - * there is nothing to add. - * - * Returning `undefined` rather than an empty object matters: the caller only touches the body - * when there is something to put in it, so a request with no assertion is byte-for-byte the - * request that would have been sent before. + * The Protect params to merge into a sign-in or sign-up request body. Returns `undefined` + * rather than `{}` so a request with no assertion is byte-for-byte what it was before. */ export async function protectAssertionParams( assertion: ProtectAssertion | undefined, diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 2d0695115b9..f04a9de9258 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -397,8 +397,6 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { if (this.clerkjs && this.loaded) { callback(); } else { - // Keyed by method name, so a second call before load replaces the first — which is the - // semantics a setter wants, and means a value set early is not lost. this.premountMethodCalls.set('setProtectAssertion', callback); } };