From 1c717f5c8f3e79d8dea94b3bf2fd555aac99f6d1 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 25 Sep 2025 16:54:56 -0700 Subject: [PATCH 01/33] chore(tanstack-react-start): Init RC update --- packages/tanstack-react-start/package.json | 8 +- .../src/server/clerkMiddleware.ts | 0 pnpm-lock.yaml | 460 ++++++------------ 3 files changed, 166 insertions(+), 302 deletions(-) create mode 100644 packages/tanstack-react-start/src/server/clerkMiddleware.ts diff --git a/packages/tanstack-react-start/package.json b/packages/tanstack-react-start/package.json index afe922a1d64..9b854867379 100644 --- a/packages/tanstack-react-start/package.json +++ b/packages/tanstack-react-start/package.json @@ -76,13 +76,13 @@ "tslib": "catalog:repo" }, "devDependencies": { - "@tanstack/react-router": "1.131.49", - "@tanstack/react-start": "1.131.49", + "@tanstack/react-router": "1.132.0", + "@tanstack/react-start": "1.132.0", "esbuild-plugin-file-path-extensions": "^2.1.4" }, "peerDependencies": { - "@tanstack/react-router": "^1.131.0 <1.132.0", - "@tanstack/react-start": "^1.131.0 <1.132.0", + "@tanstack/react-router": "^1.132.0", + "@tanstack/react-start": "^1.132.0", "react": "catalog:peer-react", "react-dom": "catalog:peer-react" }, diff --git a/packages/tanstack-react-start/src/server/clerkMiddleware.ts b/packages/tanstack-react-start/src/server/clerkMiddleware.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d040cd6bdb..5bb2737c099 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -994,11 +994,11 @@ importers: version: 2.8.1 devDependencies: '@tanstack/react-router': - specifier: 1.131.49 - version: 1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 1.132.0 + version: 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/react-start': - specifier: 1.131.49 - version: 1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@vitejs/plugin-react@4.5.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17))) + specifier: 1.132.0 + version: 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17))) esbuild-plugin-file-path-extensions: specifier: ^2.1.4 version: 2.1.4 @@ -2755,7 +2755,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {node: '>=0.10.0'} + engines: {'0': node >=0.10.0} '@expo/cli@0.22.26': resolution: {integrity: sha512-I689wc8Fn/AX7aUGiwrh3HnssiORMJtR2fpksX+JIe8Cj/EDleblYMSwRPd0025wrwOV9UN1KM/RuEt/QjCS3Q==} @@ -4708,6 +4708,9 @@ packages: '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@statelyai/inspect@0.4.0': resolution: {integrity: sha512-VxQldRlKYcu6rzLY83RSXVwMYexkH6hNx85B89YWYyXYWtNGaWHFCwV7a/Kz8FFPeUz8EKVAnyMOg2kNpn07wQ==} peerDependencies: @@ -4893,52 +4896,44 @@ packages: '@swc/types@0.1.24': resolution: {integrity: sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==} - '@tanstack/directive-functions-plugin@1.131.2': - resolution: {integrity: sha512-5Pz6aVPS0BW+0bLvMzWsoajfjI6ZeWqkbVBaQfIbSTm4DOBO05JuQ/pb7W7m3GbCb5TK1a/SKDhuTX6Ag5I7UQ==} + '@tanstack/directive-functions-plugin@1.132.0': + resolution: {integrity: sha512-5+K3msIpSYkiDE0PTIAT2HzZRps/M2uQsDEA5HApXxOhIAWykQ/yyO1umgkMwYpgJqnT96AVHb0E559Dfvhj0A==} engines: {node: '>=12'} peerDependencies: - vite: '>=6.0.0' + vite: '>=6.0.0 || >=7.0.0' - '@tanstack/history@1.131.2': - resolution: {integrity: sha512-cs1WKawpXIe+vSTeiZUuSBy8JFjEuDgdMKZFRLKwQysKo8y2q6Q1HvS74Yw+m5IhOW1nTZooa6rlgdfXcgFAaw==} + '@tanstack/history@1.132.0': + resolution: {integrity: sha512-GG2R9I6QSlbNR9fEuX2sQCigY6K28w51h2634TWmkaHXlzQw+rWuIWr4nAGM9doA+kWRi1LFSFMvAiG3cOqjXQ==} engines: {node: '>=12'} - '@tanstack/react-router@1.131.49': - resolution: {integrity: sha512-WHgWJ053W8VU8lUYh8abSHVPeQdpaCpfaUAbV+3uYXbip2G+qlmI/Gsbh/BBV3bYtIi6l3t5dqx3ffCXNTzB5Q==} + '@tanstack/react-router@1.132.0': + resolution: {integrity: sha512-tGNmQrFc4zWQZvjqYnC8ib84H/9QokRl73hr0P2XlxCY2KAgPTk2QjdzW03LqXgQZRXg7++vKznJt4LS9/M3iA==} engines: {node: '>=12'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-client@1.131.49': - resolution: {integrity: sha512-Fv/UsoFZ/F34z1j0A7tpVG4Sl1ATSpP5dYoEjiwyvfGcTrdrEQuFCAnIr+mqHRdeH/LelfIiYYhvkiT+NFrwzw==} - engines: {node: '>=12'} + '@tanstack/react-start-client@1.132.0': + resolution: {integrity: sha512-j76SOnplxlk4OrQR0hZHD/+xceUw75PUUBEyCWTgz9TK92o9WuN+zrSJLV0ZCby3Lgj19wrbbtohNEu7HUVMXQ==} + engines: {node: '>=22.12.0'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-plugin@1.131.49': - resolution: {integrity: sha512-4vs/O90ln7TRVGOCK34rRUCpf1J2wHyfBLCyuSg48+SvnyIln2ggW6xlDrTf3YTut3gR1qON9+21BPqvlEa2LQ==} - engines: {node: '>=12'} - peerDependencies: - '@vitejs/plugin-react': '>=4.3.4' - vite: '>=6.0.0' - - '@tanstack/react-start-server@1.131.49': - resolution: {integrity: sha512-nZNWFjqaNzg+garioVg4ZrihglDlUiDHbr5lRz3VqmSc+uqAsw0Ur51knAB3cLS+dRposkTMLLQfyLMiS5En2w==} - engines: {node: '>=12'} + '@tanstack/react-start-server@1.132.0': + resolution: {integrity: sha512-kuucNaq3ik1EYQgn3eSmSgMCR1DXWPs6zIK7Q2mmGtV65BHdWe1AD96VSYmXt3cDycLtJM5iB9uem5sT1Mqfrg==} + engines: {node: '>=22.12.0'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start@1.131.49': - resolution: {integrity: sha512-huDwMHSVu0su41eJjyws104TI03JhauaQXp/iuF/xW99DvugcfqY+X+NEBJf2BLlceTwZIhLzEx/2y8erCdUnQ==} - engines: {node: '>=12'} + '@tanstack/react-start@1.132.0': + resolution: {integrity: sha512-DqafNGJq2Yerk8KwUyGdvSayfrHsR92M1V89RZFYsVkfCvMBNBrU+Pa37b76jFDY8uYXZDN3ZTX0vPf+mIdhkw==} + engines: {node: '>=22.12.0'} peerDependencies: - '@vitejs/plugin-react': '>=4.3.4' react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - vite: '>=6.0.0' + vite: '>=7.0.0' '@tanstack/react-store@0.7.0': resolution: {integrity: sha512-S/Rq17HaGOk+tQHV/yrePMnG1xbsKZIl/VsNWnNXt4XW+tTY8dTlvpJH2ZQ3GRALsusG5K6Q3unAGJ2pd9W/Ng==} @@ -4946,22 +4941,22 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-core@1.131.48': - resolution: {integrity: sha512-Zw024eJECTLn57bR8oncR4YUTvQ8P41pDnVEXevWPuR6wdKRsIUmhfJowzgf4ppF9Lgl5DUWEhOcj4Awr4tTOQ==} + '@tanstack/router-core@1.132.0': + resolution: {integrity: sha512-Iy33xln3oICuifgs4ltukWQLkd2F8ysUxalBFYZ8rs7N8EUfL2xIKnv4kEyffGDMwAxnNzSq/jS7XKy00KmC8Q==} engines: {node: '>=12'} - '@tanstack/router-generator@1.131.48': - resolution: {integrity: sha512-spLqLJ1pD6y8wTxdKSRUS7fMINbuqsTEk1Ot0wxdrD1lKwJT8F7yKovSfl+h2WcifFN84B1gc5F3ZRmcp7fTeA==} + '@tanstack/router-generator@1.132.0': + resolution: {integrity: sha512-3N1rHNzxLzvFXO9iLguz/J4GTr1I6uCPkr5R13kP15jGCZ/AQIDeZjT8jQPZA9gAUAl9+yOyUIRXeL09a6RVVg==} engines: {node: '>=12'} - '@tanstack/router-plugin@1.131.49': - resolution: {integrity: sha512-npL9wqKGMAqaTPqZyZIp1k+omo5Imwvlg/gqQdnphZYIhCJGoxe8CBGQT2zrVnRtL5D7BD9vvpRh2moEDaJF8A==} + '@tanstack/router-plugin@1.132.0': + resolution: {integrity: sha512-ycAhaxf871tmAfmDOxzXOMu9yXM/4PVDOi2/IrcHdqZx3Lgh0l3tBVVdz5P9LY+jxy2spZW1E8GSKbMCNcGdGA==} engines: {node: '>=12'} peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.131.49 - vite: '>=5.0.0 || >=6.0.0' - vite-plugin-solid: ^2.11.2 + '@tanstack/react-router': ^1.132.0 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' + vite-plugin-solid: ^2.11.8 webpack: '>=5.92.0' peerDependenciesMeta: '@rsbuild/core': @@ -4975,49 +4970,37 @@ packages: webpack: optional: true - '@tanstack/router-utils@1.131.2': - resolution: {integrity: sha512-sr3x0d2sx9YIJoVth0QnfEcAcl+39sQYaNQxThtHmRpyeFYNyM2TTH+Ud3TNEnI3bbzmLYEUD+7YqB987GzhDA==} + '@tanstack/router-utils@1.132.0': + resolution: {integrity: sha512-WDnvAi9kO20joLDzlsTvfgXNv+FgQ4G98xAD8r4jKWoTdTTG05DU2sRYimtbdq4Q7E3uVdvyvPdhRy45wan7bw==} engines: {node: '>=12'} - '@tanstack/server-functions-plugin@1.131.2': - resolution: {integrity: sha512-hWsaSgEZAVyzHg8+IcJWCEtfI9ZSlNELErfLiGHG9XCHEXMegFWsrESsKHlASzJqef9RsuOLDl+1IMPIskwdDw==} + '@tanstack/server-functions-plugin@1.132.0': + resolution: {integrity: sha512-0a5NVOfiWWdkHI+ZO63tdUF4n8Qksts76pxYd+PUf0+fx6ENsOjeAF9NJS8RAYfNnjtB/aiyAbElvOxcL756Hw==} engines: {node: '>=12'} - '@tanstack/start-client-core@1.131.48': - resolution: {integrity: sha512-lPnDGdLdRrKTELRkaxNXUjv5nhbf5zudq0aq9GzrB1ly6byIdu0YH0EmRo8xcb7wBnlVNa+woN2BeDcfF+/Q0w==} - engines: {node: '>=12'} + '@tanstack/start-client-core@1.132.0': + resolution: {integrity: sha512-7hhH6eLC2He2BRRobmnk/MRINueBgMT1LiesA1kuEWSHkklilOe77r53+3VPIuRwd94wYdxq+5R+H31l0HgPcw==} + engines: {node: '>=22.12.0'} - '@tanstack/start-plugin-core@1.131.49': - resolution: {integrity: sha512-Xe5+PSNj+dTGl86mkwb1Dh/d8kr0kjrzBQvVMfb0KxfVW09TtDeLme6L/JrU98+zpvOCnsIYFoCPYF040XRjHw==} - engines: {node: '>=12'} + '@tanstack/start-plugin-core@1.132.0': + resolution: {integrity: sha512-ZnbTDzbTo7aYt7/id1IaPkCcISy3gz/q9C3qbjgb2JYGzZHOwwvA4OzRS7PfcxAVzu/bqE6QHuuG096ai11DOw==} + engines: {node: '>=22.12.0'} peerDependencies: - vite: '>=6.0.0' - - '@tanstack/start-server-core@1.131.48': - resolution: {integrity: sha512-kWvvjRGprW8QWodOjVQchVqOEVjNl6ZFwNMp/UdbVuUNry5lDwEJvS859EI54YQYm1ijtzyLVekbvz6sdNt1LA==} - engines: {node: '>=12'} + vite: '>=7.0.0' - '@tanstack/start-server-functions-client@1.131.48': - resolution: {integrity: sha512-WeR537FatdDX3j3CHNvWWxjkfGBuf9QVCjfq34M6pMDOT7kux+AOAkRDXUeQkfT44UhgYygH5BEPd8W5ecCb7w==} - engines: {node: '>=12'} - - '@tanstack/start-server-functions-fetcher@1.131.48': - resolution: {integrity: sha512-mmguzV+Icd8tEcZ9IazaIzqt2BGKwvsvys8DDpMwzoltSzTCnOKiB6GyeGqIz6qMb+L7Lk7d62MhMo8a1BIiuQ==} - engines: {node: '>=12'} + '@tanstack/start-server-core@1.132.0': + resolution: {integrity: sha512-0WJtlCxh1Eo5k77StWfMDc19TxMH7ojdaiRc22a+c32OezYXRt5GAV7PmzHq8bGRVnGy4pudAMKX2nS6uNRgzQ==} + engines: {node: '>=22.12.0'} - '@tanstack/start-server-functions-server@1.131.2': - resolution: {integrity: sha512-u67d6XspczlC/dYki/Id28oWsTjkZMJhDqO4E23U3rHs8eYgxvMBHKqdeqWgOyC+QWT9k6ze1pJmbv+rmc3wOQ==} - engines: {node: '>=12'} - - '@tanstack/start-storage-context@1.131.48': - resolution: {integrity: sha512-bvI0KejHxzGFWHRH3fZ/jNdGVJuTstlzk/RIC8c0qiBk7Hf7DP72s1rA3hNLmOYlM4qloQK/i+HuFrfu8cHoRA==} - engines: {node: '>=12'} + '@tanstack/start-storage-context@1.132.0': + resolution: {integrity: sha512-RGzyNVqSe3cJ+ajjKOQ62elr1jR8quBX6lniBElDC2P66MMvESSwi5Lr+tvSuq7DsVLRWJKexJsIemJt0MARKA==} + engines: {node: '>=22.12.0'} '@tanstack/store@0.7.0': resolution: {integrity: sha512-CNIhdoUsmD2NolYuaIs8VfWM467RK6oIBAW4nPEKZhg1smZ+/CwtCdpURgp7nxSqOaV9oKkzdWD80+bC66F/Jg==} - '@tanstack/virtual-file-routes@1.131.2': - resolution: {integrity: sha512-VEEOxc4mvyu67O+Bl0APtYjwcNRcL9it9B4HKbNgcBTIOEalhk+ufBl4kiqc8WP1sx1+NAaiS+3CcJBhrqaSRg==} + '@tanstack/virtual-file-routes@1.132.0': + resolution: {integrity: sha512-d3do4ih9IdLPBVY4Gb8x7Ho7z0oFDLpxoao7uNVkfWtYU7nc3B+rnnVejXIgprmI5gt1hNzyNDJFr8G/W926GA==} engines: {node: '>=12'} '@testing-library/dom@10.1.0': @@ -5106,9 +5089,6 @@ packages: '@types/aria-query@5.0.1': resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} - '@types/babel__code-frame@7.0.6': - resolution: {integrity: sha512-Anitqkl3+KrzcW2k77lRlg/GfLZLWXBuNgbEcIOU6M92yw42vsd3xV/Z/yAHEj8m+KUjL6bWOVOFqX8PFPJ4LA==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -8524,6 +8504,9 @@ packages: fetch-retry@4.1.1: resolution: {integrity: sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==} + fetchdts@0.1.7: + resolution: {integrity: sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==} + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -8942,12 +8925,18 @@ packages: resolution: {integrity: sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - h3@1.13.0: - resolution: {integrity: sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg==} - h3@1.15.4: resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + h3@2.0.0-beta.4: + resolution: {integrity: sha512-/JdwHUGuHjbBXAVxQN7T7QeI9cVlhsqMKVNFHebZVs9RoEYH85Ogh9O1DEy/1ZiJkmMwa1gNg6bBcGhc1Itjdg==} + engines: {node: '>=20.11.1'} + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} @@ -11290,9 +11279,6 @@ packages: ofetch@1.4.1: resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} - ohash@1.1.6: - resolution: {integrity: sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==} - ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -12707,6 +12693,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rou3@0.7.5: + resolution: {integrity: sha512-bwUHDHw1HSARty7TWNV71R0NZs5fOt74OM+hcMdJyPfchfRktEmxLoMSNa7PwEp6WqJ0a3feKztsIfTUEYhskw==} + router@2.1.0: resolution: {integrity: sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==} engines: {node: '>= 18'} @@ -13152,6 +13141,11 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + srvx@0.8.7: + resolution: {integrity: sha512-g3+15LlwVOGL2QpoTPZlvRjg+9a5Tx/69CatXjFP6txvhIaW2FmGyzJfb8yft5wyfGddvJmP/Yx+e/uNDMRSLQ==} + engines: {node: '>=20.16.0'} + hasBin: true + sshpk@1.18.0: resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} engines: {node: '>=0.10.0'} @@ -14035,9 +14029,6 @@ packages: resolution: {integrity: sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==} engines: {node: '>=20.18.1'} - unenv@1.10.0: - resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} - unenv@2.0.0-rc.21: resolution: {integrity: sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==} @@ -19560,6 +19551,8 @@ snapshots: '@stablelib/base64@1.0.1': {} + '@standard-schema/spec@1.0.0': {} + '@statelyai/inspect@0.4.0(ws@8.18.3)(xstate@5.20.2)': dependencies: fast-safe-stringify: 2.1.1 @@ -19739,138 +19732,73 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@tanstack/directive-functions-plugin@1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))': + '@tanstack/directive-functions-plugin@1.132.0(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.4 '@babel/traverse': 7.28.4 '@babel/types': 7.28.4 - '@tanstack/router-utils': 1.131.2 + '@tanstack/router-utils': 1.132.0 babel-dead-code-elimination: 1.0.10 tiny-invariant: 1.3.3 vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@tanstack/history@1.131.2': {} + '@tanstack/history@1.132.0': {} - '@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-router@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/history': 1.131.2 + '@tanstack/history': 1.132.0 '@tanstack/react-store': 0.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/router-core': 1.131.48 + '@tanstack/router-core': 1.132.0 isbot: 5.1.23 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-client@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-start-client@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/react-router': 1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/router-core': 1.131.48 - '@tanstack/start-client-core': 1.131.48 + '@tanstack/react-router': 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/router-core': 1.132.0 + '@tanstack/start-client-core': 1.132.0 cookie-es: 1.2.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-plugin@1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@vitejs/plugin-react@4.5.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)))': + '@tanstack/react-start-server@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/start-plugin-core': 1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17))) - '@vitejs/plugin-react': 4.5.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) - pathe: 2.0.3 - vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1) - zod: 3.24.2 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - - '@rsbuild/core' - - '@tanstack/react-router' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - better-sqlite3 - - drizzle-orm - - encoding - - idb-keyval - - mysql2 - - rolldown - - sqlite3 - - supports-color - - uploadthing - - vite-plugin-solid - - webpack - - xml2js - - '@tanstack/react-start-server@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@tanstack/history': 1.131.2 - '@tanstack/react-router': 1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/router-core': 1.131.48 - '@tanstack/start-client-core': 1.131.48 - '@tanstack/start-server-core': 1.131.48 - h3: 1.13.0 - isbot: 5.1.23 + '@tanstack/history': 1.132.0 + '@tanstack/react-router': 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/router-core': 1.132.0 + '@tanstack/start-client-core': 1.132.0 + '@tanstack/start-server-core': 1.132.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - crossws - '@tanstack/react-start@1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@vitejs/plugin-react@4.5.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)))': + '@tanstack/react-start@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)))': dependencies: - '@tanstack/react-start-client': 1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/react-start-plugin': 1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@vitejs/plugin-react@4.5.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17))) - '@tanstack/react-start-server': 1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/start-server-functions-client': 1.131.48(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) - '@tanstack/start-server-functions-server': 1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) - '@vitejs/plugin-react': 4.5.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) + '@tanstack/react-router': 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-start-client': 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-start-server': 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/router-utils': 1.132.0 + '@tanstack/start-client-core': 1.132.0 + '@tanstack/start-plugin-core': 1.132.0(@tanstack/react-router@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17))) + pathe: 2.0.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1) transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - '@rsbuild/core' - - '@tanstack/react-router' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - better-sqlite3 - - drizzle-orm - - encoding - - idb-keyval - - mysql2 - - rolldown - - sqlite3 + - crossws - supports-color - - uploadthing - vite-plugin-solid - webpack - - xml2js '@tanstack/react-store@0.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -19879,9 +19807,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.5.0(react@18.3.1) - '@tanstack/router-core@1.131.48': + '@tanstack/router-core@1.132.0': dependencies: - '@tanstack/history': 1.131.2 + '@tanstack/history': 1.132.0 '@tanstack/store': 0.7.0 cookie-es: 1.2.2 seroval: 1.3.2 @@ -19889,11 +19817,11 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-generator@1.131.48': + '@tanstack/router-generator@1.132.0': dependencies: - '@tanstack/router-core': 1.131.48 - '@tanstack/router-utils': 1.131.2 - '@tanstack/virtual-file-routes': 1.131.2 + '@tanstack/router-core': 1.132.0 + '@tanstack/router-utils': 1.132.0 + '@tanstack/virtual-file-routes': 1.132.0 prettier: 3.5.3 recast: 0.23.11 source-map: 0.7.6 @@ -19902,7 +19830,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)))': + '@tanstack/router-plugin@1.132.0(@tanstack/react-router@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)))': dependencies: '@babel/core': 7.28.4 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) @@ -19910,22 +19838,22 @@ snapshots: '@babel/template': 7.27.2 '@babel/traverse': 7.28.4 '@babel/types': 7.28.4 - '@tanstack/router-core': 1.131.48 - '@tanstack/router-generator': 1.131.48 - '@tanstack/router-utils': 1.131.2 - '@tanstack/virtual-file-routes': 1.131.2 + '@tanstack/router-core': 1.132.0 + '@tanstack/router-generator': 1.132.0 + '@tanstack/router-utils': 1.132.0 + '@tanstack/virtual-file-routes': 1.132.0 babel-dead-code-elimination: 1.0.10 chokidar: 3.6.0 unplugin: 2.3.10 zod: 3.24.2 optionalDependencies: - '@tanstack/react-router': 1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-router': 1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1) webpack: 5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)) transitivePeerDependencies: - supports-color - '@tanstack/router-utils@1.131.2': + '@tanstack/router-utils@1.132.0': dependencies: '@babel/core': 7.28.4 '@babel/generator': 7.28.3 @@ -19933,10 +19861,12 @@ snapshots: '@babel/preset-typescript': 7.27.1(@babel/core@7.28.4) ansis: 4.1.0 diff: 8.0.2 + fast-glob: 3.3.3 + pathe: 2.0.3 transitivePeerDependencies: - supports-color - '@tanstack/server-functions-plugin@1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))': + '@tanstack/server-functions-plugin@1.132.0(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.4 @@ -19945,117 +19875,73 @@ snapshots: '@babel/template': 7.27.2 '@babel/traverse': 7.28.4 '@babel/types': 7.28.4 - '@tanstack/directive-functions-plugin': 1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) + '@tanstack/directive-functions-plugin': 1.132.0(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) babel-dead-code-elimination: 1.0.10 tiny-invariant: 1.3.3 transitivePeerDependencies: - supports-color - vite - '@tanstack/start-client-core@1.131.48': + '@tanstack/start-client-core@1.132.0': dependencies: - '@tanstack/router-core': 1.131.48 - '@tanstack/start-storage-context': 1.131.48 + '@tanstack/router-core': 1.132.0 + '@tanstack/start-storage-context': 1.132.0 cookie-es: 1.2.2 + seroval: 1.3.2 tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/start-plugin-core@1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)))': + '@tanstack/start-plugin-core@1.132.0(@tanstack/react-router@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17)))': dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.28.4 '@babel/types': 7.28.4 - '@tanstack/router-core': 1.131.48 - '@tanstack/router-generator': 1.131.48 - '@tanstack/router-plugin': 1.131.49(@tanstack/react-router@1.131.49(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17))) - '@tanstack/router-utils': 1.131.2 - '@tanstack/server-functions-plugin': 1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) - '@tanstack/start-server-core': 1.131.48 - '@types/babel__code-frame': 7.0.6 - '@types/babel__core': 7.20.5 + '@tanstack/router-core': 1.132.0 + '@tanstack/router-generator': 1.132.0 + '@tanstack/router-plugin': 1.132.0(@tanstack/react-router@1.132.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.94.0(@swc/core@1.11.29(@swc/helpers@0.5.17))) + '@tanstack/router-utils': 1.132.0 + '@tanstack/server-functions-plugin': 1.132.0(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) + '@tanstack/start-server-core': 1.132.0 babel-dead-code-elimination: 1.0.10 cheerio: 1.1.0 - h3: 1.13.0 - nitropack: 2.12.6(@netlify/blobs@9.1.2)(idb-keyval@6.2.1) + exsolve: 1.0.7 pathe: 2.0.3 + srvx: 0.8.7 ufo: 1.6.1 vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1) vitefu: 1.1.1(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) xmlbuilder2: 3.1.1 zod: 3.24.2 transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - '@rsbuild/core' - '@tanstack/react-router' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - better-sqlite3 - - drizzle-orm - - encoding - - idb-keyval - - mysql2 - - rolldown - - sqlite3 + - crossws - supports-color - - uploadthing - vite-plugin-solid - webpack - - xml2js - - '@tanstack/start-server-core@1.131.48': - dependencies: - '@tanstack/history': 1.131.2 - '@tanstack/router-core': 1.131.48 - '@tanstack/start-client-core': 1.131.48 - '@tanstack/start-storage-context': 1.131.48 - h3: 1.13.0 - isbot: 5.1.23 - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - unctx: 2.4.1 - - '@tanstack/start-server-functions-client@1.131.48(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))': - dependencies: - '@tanstack/server-functions-plugin': 1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) - '@tanstack/start-server-functions-fetcher': 1.131.48 - transitivePeerDependencies: - - supports-color - - vite - - '@tanstack/start-server-functions-fetcher@1.131.48': - dependencies: - '@tanstack/router-core': 1.131.48 - '@tanstack/start-client-core': 1.131.48 - '@tanstack/start-server-functions-server@1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))': + '@tanstack/start-server-core@1.132.0': dependencies: - '@tanstack/server-functions-plugin': 1.131.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)) + '@standard-schema/spec': 1.0.0 + '@tanstack/history': 1.132.0 + '@tanstack/router-core': 1.132.0 + '@tanstack/start-client-core': 1.132.0 + '@tanstack/start-storage-context': 1.132.0 + cookie-es: 2.0.0 + fetchdts: 0.1.7 + h3: 2.0.0-beta.4 + seroval: 1.3.2 tiny-invariant: 1.3.3 transitivePeerDependencies: - - supports-color - - vite + - crossws - '@tanstack/start-storage-context@1.131.48': + '@tanstack/start-storage-context@1.132.0': dependencies: - '@tanstack/router-core': 1.131.48 + '@tanstack/router-core': 1.132.0 '@tanstack/store@0.7.0': {} - '@tanstack/virtual-file-routes@1.131.2': {} + '@tanstack/virtual-file-routes@1.132.0': {} '@testing-library/dom@10.1.0': dependencies: @@ -20141,8 +20027,6 @@ snapshots: '@types/aria-query@5.0.1': {} - '@types/babel__code-frame@7.0.6': {} - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.4 @@ -20799,18 +20683,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.5.2(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))': - dependencies: - '@babel/core': 7.28.4 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) - '@rolldown/pluginutils': 1.0.0-beta.11 - '@types/babel__core': 7.20.5 - react-refresh: 0.17.0 - vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1) - transitivePeerDependencies: - - supports-color - '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.27.0)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1))(vue@3.5.21(typescript@5.8.3))': dependencies: '@babel/core': 7.28.4 @@ -24516,6 +24388,8 @@ snapshots: fetch-retry@4.1.1: {} + fetchdts@0.1.7: {} + fflate@0.8.2: {} figures@3.2.0: @@ -25007,19 +24881,6 @@ snapshots: dependencies: duplexer: 0.1.2 - h3@1.13.0: - dependencies: - cookie-es: 1.2.2 - crossws: 0.3.5 - defu: 6.1.4 - destr: 2.0.5 - iron-webcrypto: 1.2.1 - ohash: 1.1.6 - radix3: 1.1.2 - ufo: 1.6.1 - uncrypto: 0.1.3 - unenv: 1.10.0 - h3@1.15.4: dependencies: cookie-es: 1.2.2 @@ -25032,6 +24893,13 @@ snapshots: ufo: 1.6.1 uncrypto: 0.1.3 + h3@2.0.0-beta.4: + dependencies: + cookie-es: 2.0.0 + fetchdts: 0.1.7 + rou3: 0.7.5 + srvx: 0.8.7 + handle-thing@2.0.1: {} handlebars@4.7.8: @@ -28198,8 +28066,6 @@ snapshots: node-fetch-native: 1.6.7 ufo: 1.6.1 - ohash@1.1.6: {} - ohash@2.0.11: {} on-change@5.0.1: {} @@ -29727,6 +29593,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.50.1 fsevents: 2.3.3 + rou3@0.7.5: {} + router@2.1.0: dependencies: is-promise: 4.0.0 @@ -30290,6 +30158,10 @@ snapshots: sprintf-js@1.0.3: {} + srvx@0.8.7: + dependencies: + cookie-es: 2.0.0 + sshpk@1.18.0: dependencies: asn1: 0.2.6 @@ -31167,14 +31039,6 @@ snapshots: undici@7.10.0: {} - unenv@1.10.0: - dependencies: - consola: 3.4.2 - defu: 6.1.4 - mime: 3.0.0 - node-fetch-native: 1.6.7 - pathe: 1.1.2 - unenv@2.0.0-rc.21: dependencies: defu: 6.1.4 From 443e7ce019970ab44055cb94028acd5c2e8d0184 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 18:59:40 -0700 Subject: [PATCH 02/33] chore: introduce middleware --- .../src/server/clerkMiddleware.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/tanstack-react-start/src/server/clerkMiddleware.ts b/packages/tanstack-react-start/src/server/clerkMiddleware.ts index e69de29bb2d..9daec9e0736 100644 --- a/packages/tanstack-react-start/src/server/clerkMiddleware.ts +++ b/packages/tanstack-react-start/src/server/clerkMiddleware.ts @@ -0,0 +1,54 @@ +import type { RequestState } from '@clerk/backend/internal'; +import { AuthStatus, constants } from '@clerk/backend/internal'; +import { handleNetlifyCacheInDevInstance } from '@clerk/shared/netlifyCacheHandler'; +import type { PendingSessionOptions } from '@clerk/types'; +import type { AnyRequestMiddleware } from '@tanstack/react-start'; +import { createMiddleware, json } from '@tanstack/react-start'; + +import { clerkClient } from './clerkClient'; +import { loadOptions } from './loadOptions'; +import type { ClerkMiddlewareOptions } from './types'; +import { getResponseClerkState } from './utils'; + +export const clerkMiddleware = (options?: ClerkMiddlewareOptions): AnyRequestMiddleware => { + return createMiddleware().server(async args => { + const loadedOptions = loadOptions(args.request, options); + const requestState = await clerkClient().authenticateRequest(args.request, { + ...loadedOptions, + acceptsToken: 'any', + }); + + const locationHeader = requestState.headers.get(constants.Headers.Location); + if (locationHeader) { + handleNetlifyCacheInDevInstance({ + locationHeader, + requestStateHeaders: requestState.headers, + publishableKey: requestState.publishableKey, + }); + // Trigger a handshake redirect + // eslint-disable-next-line @typescript-eslint/only-throw-error + throw json(null, { status: 307, headers: requestState.headers }); + } + + if (requestState.status === AuthStatus.Handshake) { + throw new Error('Clerk: handshake status without redirect'); + } + + const clerkInitialState = getResponseClerkState(requestState as RequestState, loadedOptions); + + const result = await args.next({ + context: { + clerkInitialState, + auth: (opts?: PendingSessionOptions) => requestState.toAuth(opts), + }, + }); + + if (requestState.headers) { + requestState.headers.forEach((value, key) => { + result.response.headers.append(key, value); + }); + } + + return result; + }); +}; From c0342c5590c0755b7b09fbc0a68dfc951f4bfcf0 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:00:17 -0700 Subject: [PATCH 03/33] chore: Use global context for auth fn --- packages/tanstack-react-start/src/server/getAuth.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/tanstack-react-start/src/server/getAuth.ts b/packages/tanstack-react-start/src/server/getAuth.ts index be95e3c9435..d33d07243b2 100644 --- a/packages/tanstack-react-start/src/server/getAuth.ts +++ b/packages/tanstack-react-start/src/server/getAuth.ts @@ -1,10 +1,10 @@ import type { AuthenticateRequestOptions, GetAuthFn } from '@clerk/backend/internal'; import { getAuthObjectForAcceptedToken } from '@clerk/backend/internal'; import type { PendingSessionOptions } from '@clerk/types'; -import { getContext } from '@tanstack/react-start/server'; +import { getGlobalStartContext } from '@tanstack/react-start'; import { errorThrower } from '../utils'; -import { clerkHandlerNotConfigured, noFetchFnCtxPassedInGetAuth } from '../utils/errors'; +import { clerkMiddlewareNotConfigured, noFetchFnCtxPassedInGetAuth } from '../utils/errors'; type GetAuthOptions = PendingSessionOptions & Pick; @@ -13,13 +13,14 @@ export const getAuth: GetAuthFn = (async (request: Request, opts? return errorThrower.throw(noFetchFnCtxPassedInGetAuth); } - const authObjectFn = getContext('auth'); + // @ts-expect-error: Untyped internal Clerk start context + const authObjectFn = getGlobalStartContext().auth; if (!authObjectFn) { - return errorThrower.throw(clerkHandlerNotConfigured); + return errorThrower.throw(clerkMiddlewareNotConfigured); } - // We're keeping it a promise for now to minimize breaking changes + // We're keeping it a promise for now for future changes const authObject = await Promise.resolve(authObjectFn({ treatPendingAsSignedOut: opts?.treatPendingAsSignedOut })); return getAuthObjectForAcceptedToken({ authObject, acceptsToken: opts?.acceptsToken }); From 3f9b230f2ecdf65f4da89d5895521dbd2b8c19cd Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:00:32 -0700 Subject: [PATCH 04/33] chore: export new middleware --- packages/tanstack-react-start/src/server/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/tanstack-react-start/src/server/index.ts b/packages/tanstack-react-start/src/server/index.ts index 4a56c1e5c15..9fc8b681d2d 100644 --- a/packages/tanstack-react-start/src/server/index.ts +++ b/packages/tanstack-react-start/src/server/index.ts @@ -1,7 +1,6 @@ -export * from './middlewareHandler'; - export * from './getAuth'; export { clerkClient } from './clerkClient'; +export { clerkMiddleware } from './clerkMiddleware'; /** * Re-export resource types from @clerk/backend From 793acc5432fd0476d3cfbc158034becf6636c166 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:00:48 -0700 Subject: [PATCH 05/33] chore: remove context --- packages/tanstack-react-start/src/utils/env.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/tanstack-react-start/src/utils/env.ts b/packages/tanstack-react-start/src/utils/env.ts index d7161c3f6fb..51a00406b2d 100644 --- a/packages/tanstack-react-start/src/utils/env.ts +++ b/packages/tanstack-react-start/src/utils/env.ts @@ -1,10 +1,9 @@ import { getEnvVariable } from '@clerk/shared/getEnvVariable'; import { isTruthy } from '@clerk/shared/underscore'; -import type { H3EventContext } from '@tanstack/react-start/server'; -export const getPublicEnvVariables = (context?: H3EventContext) => { +export const getPublicEnvVariables = () => { const getValue = (name: string): string => { - return getEnvVariable(`VITE_${name}`, context) || getEnvVariable(name, context); + return getEnvVariable(`VITE_${name}`) || getEnvVariable(name); }; return { From 4f4ee08c9f0bf56b096bb1d3f695722b3ad7ec90 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:01:12 -0700 Subject: [PATCH 06/33] chore: add missing middleware error message --- packages/tanstack-react-start/src/utils/errors.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/tanstack-react-start/src/utils/errors.ts b/packages/tanstack-react-start/src/utils/errors.ts index 30f2da1f814..7731579f598 100644 --- a/packages/tanstack-react-start/src/utils/errors.ts +++ b/packages/tanstack-react-start/src/utils/errors.ts @@ -18,10 +18,7 @@ export const noFetchFnCtxPassedInGetAuth = createErrorMessage(` }); `); -export const clerkHandlerNotConfigured = createErrorMessage(` -It looks like you're trying to use Clerk without configuring the Clerk handler. +export const clerkMiddlewareNotConfigured = createErrorMessage(` +It looks like you're trying to use Clerk without configuring the middleware. -To fix this, make sure you have the \`createClerkHandler()\` configured in you custom server handler file (example: src/server.ts). - -For more info, check out the docs: https://clerk.com/docs/references/tanstack-react-start/create-clerk-handler, - `); +To fix this, make sure you have the \`clerkMiddleware()\` configured in your \`createStart()\` function in your \`src/start.ts()\` file.`); From 14a31e324d519ac80390351fc31650548c7bbbcf Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:02:34 -0700 Subject: [PATCH 07/33] chore: export middleware type --- packages/tanstack-react-start/src/server/types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/tanstack-react-start/src/server/types.ts b/packages/tanstack-react-start/src/server/types.ts index b289b865e9a..4a3d43c2e35 100644 --- a/packages/tanstack-react-start/src/server/types.ts +++ b/packages/tanstack-react-start/src/server/types.ts @@ -8,7 +8,7 @@ import type { SignUpForceRedirectUrl, } from '@clerk/types'; -export type LoaderOptions = { +export type ClerkMiddlewareOptions = { publishableKey?: string; jwtKey?: string; secretKey?: string; @@ -23,6 +23,8 @@ export type LoaderOptions = { SignUpFallbackRedirectUrl & LegacyRedirectProps; +export type LoaderOptions = ClerkMiddlewareOptions; + export type AdditionalStateOptions = SignInFallbackRedirectUrl & SignUpFallbackRedirectUrl & SignInForceRedirectUrl & From 5c426833f95e96b1951b83bb561134b286f456a5 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:02:59 -0700 Subject: [PATCH 08/33] chore: Update ClerkProvider to use new context state --- .../src/client/ClerkProvider.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/tanstack-react-start/src/client/ClerkProvider.tsx b/packages/tanstack-react-start/src/client/ClerkProvider.tsx index 7e420ff81af..e93fc7366c5 100644 --- a/packages/tanstack-react-start/src/client/ClerkProvider.tsx +++ b/packages/tanstack-react-start/src/client/ClerkProvider.tsx @@ -1,5 +1,6 @@ import { ClerkProvider as ReactClerkProvider } from '@clerk/clerk-react'; -import { ScriptOnce, useRouteContext } from '@tanstack/react-router'; +import { ScriptOnce } from '@tanstack/react-router'; +import { getGlobalStartContext } from '@tanstack/react-start'; import { useEffect } from 'react'; import { isClient } from '../utils'; @@ -19,15 +20,14 @@ const awaitableNavigateRef: { current: ReturnType | export function ClerkProvider({ children, ...providerProps }: TanstackStartClerkProviderProps): JSX.Element { const awaitableNavigate = useAwaitableNavigate(); - const routerContext = useRouteContext({ - strict: false, - }); + // @ts-expect-error: Untyped internal Clerk initial state + const clerkInitialState = getGlobalStartContext()?.clerkInitialState ?? {}; useEffect(() => { awaitableNavigateRef.current = awaitableNavigate; }, [awaitableNavigate]); - const clerkInitState = isClient() ? (window as any).__clerk_init_state : routerContext?.clerkInitialState; + const clerkInitState = isClient() ? (window as any).__clerk_init_state : clerkInitialState; const { clerkSsrState, ...restInitState } = pickFromClerkInitState(clerkInitState?.__internal_clerk_state); @@ -38,7 +38,7 @@ export function ClerkProvider({ children, ...providerProps }: TanstackStartClerk return ( <> - {`window.__clerk_init_state = ${JSON.stringify(routerContext?.clerkInitialState)};`} + {`window.__clerk_init_state = ${JSON.stringify(clerkInitialState)};`} Date: Thu, 25 Sep 2025 19:03:37 -0700 Subject: [PATCH 09/33] chore: remove unused returned properties --- packages/tanstack-react-start/src/server/utils/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/tanstack-react-start/src/server/utils/index.ts b/packages/tanstack-react-start/src/server/utils/index.ts index f7657e12eb5..e5c5d3f6786 100644 --- a/packages/tanstack-react-start/src/server/utils/index.ts +++ b/packages/tanstack-react-start/src/server/utils/index.ts @@ -48,10 +48,7 @@ export function getResponseClerkState(requestState: RequestState, additionalStat additionalStateOptions.signUpFallbackRedirectUrl || getEnvVariable('CLERK_SIGN_UP_FALLBACK_REDIRECT_URL') || '', }); - return { - clerkInitialState, - headers: requestState.headers, - }; + return clerkInitialState; } /** From 02a32698c6f40e3092e80740ab4d63f4bccccf3a Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:22:35 -0700 Subject: [PATCH 10/33] chore: add placeholder changeset --- .changeset/spotty-cooks-march.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-cooks-march.md diff --git a/.changeset/spotty-cooks-march.md b/.changeset/spotty-cooks-march.md new file mode 100644 index 00000000000..994d8fe3caf --- /dev/null +++ b/.changeset/spotty-cooks-march.md @@ -0,0 +1,5 @@ +--- +"@clerk/tanstack-react-start": minor +--- + +placeholder From c93808904bd335ca13511e1e3bb08fdaadba9244 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:24:09 -0700 Subject: [PATCH 11/33] chore: remove unused function --- .../src/server/authenticateRequest.ts | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 packages/tanstack-react-start/src/server/authenticateRequest.ts diff --git a/packages/tanstack-react-start/src/server/authenticateRequest.ts b/packages/tanstack-react-start/src/server/authenticateRequest.ts deleted file mode 100644 index 8cbdbb994a3..00000000000 --- a/packages/tanstack-react-start/src/server/authenticateRequest.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createClerkClient } from '@clerk/backend'; -import type { AuthenticateRequestOptions, RequestState } from '@clerk/backend/internal'; - -import { patchRequest } from './utils'; - -export async function authenticateRequest(request: Request, opts: AuthenticateRequestOptions): Promise { - const { audience, authorizedParties } = opts; - - const { apiUrl, secretKey, jwtKey, proxyUrl, isSatellite, domain, publishableKey, acceptsToken, machineSecretKey } = - opts; - const { signInUrl, signUpUrl, afterSignInUrl, afterSignUpUrl } = opts; - - const requestState = await createClerkClient({ - apiUrl, - secretKey, - machineSecretKey, - jwtKey, - proxyUrl, - isSatellite, - domain, - publishableKey, - userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}`, - }).authenticateRequest(patchRequest(request), { - audience, - authorizedParties, - signInUrl, - signUpUrl, - afterSignInUrl, - afterSignUpUrl, - acceptsToken, - }); - - return requestState; -} From 41fe920add80336b9808f195f682c467fa44a1a1 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 25 Sep 2025 19:24:20 -0700 Subject: [PATCH 12/33] chore: remove unused function --- .../src/server/middlewareHandler.ts | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 packages/tanstack-react-start/src/server/middlewareHandler.ts diff --git a/packages/tanstack-react-start/src/server/middlewareHandler.ts b/packages/tanstack-react-start/src/server/middlewareHandler.ts deleted file mode 100644 index 42adec5b26d..00000000000 --- a/packages/tanstack-react-start/src/server/middlewareHandler.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { AuthStatus, constants } from '@clerk/backend/internal'; -import { handleNetlifyCacheInDevInstance } from '@clerk/shared/netlifyCacheHandler'; -import type { PendingSessionOptions } from '@clerk/types'; -import type { AnyRouter } from '@tanstack/react-router'; -import { - type CustomizeStartHandler, - getEvent, - getWebRequest, - type HandlerCallback, - type RequestHandler, -} from '@tanstack/react-start/server'; - -import { errorThrower } from '../utils'; -import { authenticateRequest } from './authenticateRequest'; -import { loadOptions } from './loadOptions'; -import type { LoaderOptions } from './types'; -import { getResponseClerkState } from './utils'; - -export function createClerkHandler( - eventHandler: CustomizeStartHandler, - clerkOptions: LoaderOptions = {}, -) { - return async (cb: HandlerCallback): Promise => { - const request = getWebRequest(); - const event = getEvent(); - const loadedOptions = loadOptions(request, clerkOptions); - - const requestState = await authenticateRequest(request, { - ...loadedOptions, - acceptsToken: 'any', - }); - - // Set auth object here so it is available immediately in server functions via getAuth() - event.context.auth = (options?: PendingSessionOptions) => requestState.toAuth(options); - - return eventHandler(async ({ request, router, responseHeaders }) => { - const locationHeader = requestState.headers.get(constants.Headers.Location); - if (locationHeader) { - handleNetlifyCacheInDevInstance({ - locationHeader, - requestStateHeaders: requestState.headers, - publishableKey: requestState.publishableKey, - }); - - return new Response(null, { - status: 307, - headers: requestState.headers, - }); - } - - if (requestState.status === AuthStatus.Handshake) { - // eslint-disable-next-line @typescript-eslint/only-throw-error - throw errorThrower.throw('Clerk: unexpected handshake without redirect'); - } - - const { clerkInitialState, headers } = getResponseClerkState(requestState, loadedOptions); - - // Merging the TanStack router context with the Clerk context and loading the router - router.update({ - context: { ...router.options.context, clerkInitialState }, - }); - - headers.forEach((value, key) => { - responseHeaders.set(key, value); - }); - - await router.load(); - - return cb({ request, router, responseHeaders }); - }); - }; -} From 951cc06cbd424ffe6790cdc1b62d87fa289903d4 Mon Sep 17 00:00:00 2001 From: Nikos Douvlis Date: Fri, 26 Sep 2025 14:13:19 +0300 Subject: [PATCH 13/33] feat(repo): Enable sessions staging e2e runs (#6855) --- .github/workflows/ci.yml | 3 +++ integration/tests/handshake/handshake.test.ts | 21 ++++++++++++--- .../root-subdomain-prod-instances.test.ts | 27 +++++++++++++------ integration/tests/sessions/utils.ts | 8 +++++- package.json | 6 +++-- turbo.json | 18 +++++++++++++ 6 files changed, 68 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a86875008ee..dc466ab6703 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -285,6 +285,9 @@ jobs: 'elements', 'localhost', 'sessions', + 'sessions:staging', + 'handshake', + 'handshake:staging', 'astro', 'expo-web', 'tanstack-react-start', diff --git a/integration/tests/handshake/handshake.test.ts b/integration/tests/handshake/handshake.test.ts index 87717403f30..d8a3b9fd72c 100644 --- a/integration/tests/handshake/handshake.test.ts +++ b/integration/tests/handshake/handshake.test.ts @@ -3,20 +3,33 @@ import type { Server, ServerOptions } from 'node:https'; import { expect, test } from '@playwright/test'; import { constants } from '../../constants'; +import type { Application } from '../../models/application'; import { fs } from '../../scripts'; import { createProxyServer } from '../../scripts/proxyServer'; import type { FakeUserWithEmail } from '../../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; +import { prepareApplication } from '../sessions/utils'; -testAgainstRunningApps({ withPattern: ['next.appRouter.sessionsProd1'] })('handshake flow @handshake', ({ app }) => { +test.describe('handshake flow @handshake', () => { test.describe.configure({ mode: 'serial' }); test.describe('with Production instance', () => { // TODO: change host name (see integration/README.md#production-hosts) - const host = 'multiple-apps-e2e.clerk.app:8443'; + // eslint-disable-next-line turbo/no-undeclared-env-vars + const host = `${process.env.E2E_SESSIONS_APP_1_HOST}:8443`; + // eslint-disable-next-line turbo/no-undeclared-env-vars + const APP_1_ENV_KEY = process.env.E2E_APP_1_ENV_KEY; let fakeUser: FakeUserWithEmail; let server: Server; + let app: Application; + let serverUrl: string; + + test.beforeAll(async () => { + const res = await prepareApplication(APP_1_ENV_KEY); + app = res.app; + serverUrl = res.serverUrl; + }); test.afterAll(async () => { await fakeUser.deleteIfExists(); @@ -34,7 +47,7 @@ testAgainstRunningApps({ withPattern: ['next.appRouter.sessionsProd1'] })('hands server = createProxyServer({ ssl, targets: { - [host]: app.serverUrl, + [host]: serverUrl, }, }); diff --git a/integration/tests/sessions/root-subdomain-prod-instances.test.ts b/integration/tests/sessions/root-subdomain-prod-instances.test.ts index 82c673625c7..19a957a0334 100644 --- a/integration/tests/sessions/root-subdomain-prod-instances.test.ts +++ b/integration/tests/sessions/root-subdomain-prod-instances.test.ts @@ -10,6 +10,13 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils } from '../../testUtils'; import { prepareApplication } from './utils'; +// eslint-disable-next-line turbo/no-undeclared-env-vars +const APP_1_ENV_KEY = process.env.E2E_SESSIONS_APP_1_ENV_KEY; +// eslint-disable-next-line turbo/no-undeclared-env-vars +const APP_1_HOST = process.env.E2E_SESSIONS_APP_1_HOST; +// eslint-disable-next-line turbo/no-undeclared-env-vars +const APP_2_ENV_KEY = process.env.E2E_SESSIONS_APP_2_ENV_KEY; + /** * These two suites need to run in serial mode because they are both using a local proxy server * that listens to port 443. We can't run them in parallel because they would conflict with each other, unless @@ -40,7 +47,7 @@ test.describe('root and subdomain production apps @sessions', () => { * 5. The second app is going to be served on sub-1.multiple-apps-e2e.clerk.app */ test.describe('multiple apps same domain for the same production instances', () => { - const hosts = ['multiple-apps-e2e.clerk.app:8443', 'sub-1.multiple-apps-e2e.clerk.app:8443']; + const hosts = [`${APP_1_HOST}:8443`, `sub-1.${APP_1_HOST}:8443`]; let fakeUser: FakeUser; let server: Server; @@ -49,9 +56,9 @@ test.describe('root and subdomain production apps @sessions', () => { test.beforeAll(async () => { apps = await Promise.all([ // first app - prepareApplication('sessions-prod-1'), + prepareApplication(APP_1_ENV_KEY), // second app using the same instance keys - prepareApplication('sessions-prod-1'), + prepareApplication(APP_1_ENV_KEY), ]); // TODO: Move this into createProxyServer @@ -70,7 +77,11 @@ test.describe('root and subdomain production apps @sessions', () => { const u = createTestUtils({ app: apps[0].app }); fakeUser = u.services.users.createFakeUser(); - await u.services.users.createBapiUser(fakeUser); + try { + await u.services.users.createBapiUser(fakeUser); + } catch (error) { + console.error(error); + } }); test.afterAll(async () => { @@ -180,13 +191,13 @@ test.describe('root and subdomain production apps @sessions', () => { * 5. The second app is going to be served on sub-1.multiple-apps-e2e.clerk.app */ test.describe('multiple apps same domain for different production instances', () => { - const hosts = ['multiple-apps-e2e.clerk.app:8443', 'sub-2.multiple-apps-e2e.clerk.app:8443']; + const hosts = [`${APP_1_HOST}:8443`, `sub-2.${APP_1_HOST}:8443`]; let fakeUsers: FakeUser[]; let server: Server; let apps: Array<{ app: Application; serverUrl: string }>; test.beforeAll(async () => { - apps = await Promise.all([prepareApplication('sessions-prod-1'), prepareApplication('sessions-prod-2')]); + apps = await Promise.all([prepareApplication(APP_1_ENV_KEY), prepareApplication(APP_2_ENV_KEY)]); // TODO: Move this into createProxyServer const ssl: Pick = { @@ -312,13 +323,13 @@ test.describe('root and subdomain production apps @sessions', () => { * */ test.describe('multiple apps different same-level subdomains for different production instances', () => { - const hosts = ['sub-1.multiple-apps-e2e.clerk.app:8443', 'sub-2.multiple-apps-e2e.clerk.app:8443']; + const hosts = [`sub-1.${APP_1_HOST}:8443`, `sub-2.${APP_1_HOST}:8443`]; let fakeUsers: FakeUser[]; let server: Server; let apps: Array<{ app: Application; serverUrl: string }>; test.beforeAll(async () => { - apps = await Promise.all([prepareApplication('sessions-prod-1'), prepareApplication('sessions-prod-2')]); + apps = await Promise.all([prepareApplication(APP_1_ENV_KEY), prepareApplication(APP_2_ENV_KEY)]); // TODO: Move this into createProxyServer const ssl: Pick = { diff --git a/integration/tests/sessions/utils.ts b/integration/tests/sessions/utils.ts index 9ac8eb805ee..882bcf4080b 100644 --- a/integration/tests/sessions/utils.ts +++ b/integration/tests/sessions/utils.ts @@ -1,10 +1,16 @@ import { appConfigs } from '../../presets'; export const getEnvForMultiAppInstance = (envKey: string) => { - return appConfigs.envs.base + const res = appConfigs.envs.base .clone() .setEnvVariable('private', 'CLERK_SECRET_KEY', appConfigs.secrets.instanceKeys.get(envKey).sk) .setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', appConfigs.secrets.instanceKeys.get(envKey).pk); + + if (envKey.includes('clerkstage')) { + res.setEnvVariable('private', 'CLERK_API_URL', 'https://api.clerkstage.dev'); + } + + return res; }; export const prepareApplication = async (envKey: string, port?: number) => { diff --git a/package.json b/package.json index 29d75bf844a..e824b83e4e4 100644 --- a/package.json +++ b/package.json @@ -41,14 +41,16 @@ "test:integration:expo-web": "E2E_APP_ID=expo.expo-web pnpm test:integration:base --grep @expo-web", "test:integration:express": "E2E_APP_ID=express.* pnpm test:integration:base --grep @express", "test:integration:generic": "E2E_APP_ID=react.vite.*,next.appRouter.withEmailCodes* pnpm test:integration:base --grep @generic", - "test:integration:handshake": "DISABLE_WEB_SECURITY=true E2E_APP_ID=next.appRouter.sessionsProd1 pnpm test:integration:base --grep @handshake", + "test:integration:handshake": "DISABLE_WEB_SECURITY=true E2E_APP_1_ENV_KEY=sessions-prod-1 E2E_SESSIONS_APP_1_HOST=multiple-apps-e2e.clerk.app pnpm test:integration:base --grep @handshake", + "test:integration:handshake:staging": "DISABLE_WEB_SECURITY=true E2E_APP_1_ENV_KEY=clerkstage-sessions-prod-1 E2E_SESSIONS_APP_1_HOST=clerkstage-sessions-prod-1-e2e.clerk.app pnpm test:integration:base --grep @handshake", "test:integration:localhost": "pnpm test:integration:base --grep @localhost", "test:integration:machine": "E2E_APP_ID=withMachine.* pnpm test:integration:base --grep @machine", "test:integration:nextjs": "E2E_APP_ID=next.appRouter.* pnpm test:integration:base --grep @nextjs", "test:integration:nuxt": "E2E_APP_ID=nuxt.node npm run test:integration:base -- --grep @nuxt", "test:integration:quickstart": "E2E_APP_ID=quickstart.* pnpm test:integration:base --grep @quickstart", "test:integration:react-router": "E2E_APP_ID=react-router.* npm run test:integration:base -- --grep @react-router", - "test:integration:sessions": "DISABLE_WEB_SECURITY=true pnpm test:integration:base --grep @sessions", + "test:integration:sessions": "DISABLE_WEB_SECURITY=true E2E_SESSIONS_APP_1_ENV_KEY=sessions-prod-1 E2E_SESSIONS_APP_2_ENV_KEY=sessions-prod-2 E2E_SESSIONS_APP_1_HOST=multiple-apps-e2e.clerk.app pnpm test:integration:base --grep @sessions", + "test:integration:sessions:staging": "DISABLE_WEB_SECURITY=true E2E_SESSIONS_APP_1_ENV_KEY=clerkstage-sessions-prod-1 E2E_SESSIONS_APP_2_ENV_KEY=clerkstage-sessions-prod-2 E2E_SESSIONS_APP_1_HOST=clerkstage-sessions-prod-1-e2e.clerk.app pnpm test:integration:base --grep @sessions", "test:integration:tanstack-react-router": "E2E_APP_ID=tanstack.react-router pnpm test:integration:base --grep @tanstack-react-router", "test:integration:tanstack-react-start": "E2E_APP_ID=tanstack.react-start pnpm test:integration:base --grep @tanstack-react-start", "test:integration:vue": "E2E_APP_ID=vue.vite pnpm test:integration:base --grep @vue", diff --git a/turbo.json b/turbo.json index 2a410faebd0..ea0fd5f3d4f 100644 --- a/turbo.json +++ b/turbo.json @@ -258,6 +258,24 @@ "inputs": ["integration/**"], "outputLogs": "new-only" }, + "//#test:integration:sessions:staging": { + "dependsOn": ["@clerk/testing#build", "@clerk/clerk-js#build", "@clerk/backend#build", "@clerk/nextjs#build"], + "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], + "inputs": ["integration/**"], + "outputLogs": "new-only" + }, + "//#test:integration:handshake": { + "dependsOn": ["@clerk/testing#build", "@clerk/clerk-js#build", "@clerk/backend#build", "@clerk/nextjs#build"], + "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], + "inputs": ["integration/**"], + "outputLogs": "new-only" + }, + "//#test:integration:handshake:staging": { + "dependsOn": ["@clerk/testing#build", "@clerk/clerk-js#build", "@clerk/backend#build", "@clerk/nextjs#build"], + "env": ["CLEANUP", "DEBUG", "DISABLE_WEB_SECURITY", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "NODE_EXTRA_CA_CERTS"], + "inputs": ["integration/**"], + "outputLogs": "new-only" + }, "//#test:integration:elements": { "dependsOn": [ "@clerk/testing#build", From 1b772e6600973ea8cb6ee1a5c7a30d348aa248b0 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Fri, 26 Sep 2025 15:15:41 -0500 Subject: [PATCH 14/33] feat(shared): Improve error handling for clerk-js loading (#6856) --- .changeset/tangy-bees-follow.md | 5 +++++ .../src/__tests__/loadClerkJsScript.test.ts | 9 +++++---- packages/shared/src/errors/runtimeError.ts | 8 +++++++- packages/shared/src/loadClerkJsScript.ts | 15 ++++++++++----- packages/shared/src/loadScript.ts | 6 +++--- 5 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 .changeset/tangy-bees-follow.md diff --git a/.changeset/tangy-bees-follow.md b/.changeset/tangy-bees-follow.md new file mode 100644 index 00000000000..2f0b945d942 --- /dev/null +++ b/.changeset/tangy-bees-follow.md @@ -0,0 +1,5 @@ +--- +'@clerk/shared': minor +--- + +Improve error handling when loading clerk-js. diff --git a/packages/shared/src/__tests__/loadClerkJsScript.test.ts b/packages/shared/src/__tests__/loadClerkJsScript.test.ts index bc8b58824ae..d135708634d 100644 --- a/packages/shared/src/__tests__/loadClerkJsScript.test.ts +++ b/packages/shared/src/__tests__/loadClerkJsScript.test.ts @@ -1,3 +1,4 @@ +import { ClerkRuntimeError } from '../error'; import { buildClerkJsScriptAttributes, clerkJsScriptUrl, @@ -86,8 +87,8 @@ describe('loadClerkJsScript(options)', () => { rejectedWith = error; } - expect(rejectedWith).toBeInstanceOf(Error); - expect(rejectedWith.message).toBe('Clerk: Failed to load Clerk'); + expect(rejectedWith).toBeInstanceOf(ClerkRuntimeError); + expect(rejectedWith.message).toContain('Clerk: Failed to load Clerk'); expect((window as any).Clerk).toBeUndefined(); }); @@ -137,8 +138,8 @@ describe('loadClerkJsScript(options)', () => { await loadPromise; fail('Should have thrown error'); } catch (error) { - expect(error).toBeInstanceOf(Error); - expect((error as Error).message).toBe('Clerk: Failed to load Clerk'); + expect(error).toBeInstanceOf(ClerkRuntimeError); + expect((error as Error).message).toContain('Clerk: Failed to load Clerk'); // The malformed Clerk object should still be there since it was set expect((window as any).Clerk).toEqual({ status: 'ready' }); } diff --git a/packages/shared/src/errors/runtimeError.ts b/packages/shared/src/errors/runtimeError.ts index 643f3f63df9..3c341c78edf 100644 --- a/packages/shared/src/errors/runtimeError.ts +++ b/packages/shared/src/errors/runtimeError.ts @@ -18,7 +18,12 @@ export class ClerkRuntimeError extends Error { */ code: string; - constructor(message: string, { code }: { code: string }) { + /** + * The original error that was caught to throw an instance of ClerkRuntimeError. + */ + cause?: Error; + + constructor(message: string, { code, cause }: { code: string; cause?: Error }) { const prefix = '🔒 Clerk:'; const regex = new RegExp(prefix.replace(' ', '\\s*'), 'i'); const sanitized = message.replace(regex, ''); @@ -27,6 +32,7 @@ export class ClerkRuntimeError extends Error { Object.setPrototypeOf(this, ClerkRuntimeError.prototype); + this.cause = cause; this.code = code; this.message = _message; this.clerkRuntimeError = true; diff --git a/packages/shared/src/loadClerkJsScript.ts b/packages/shared/src/loadClerkJsScript.ts index 68f81af1778..1cb78a1e68c 100644 --- a/packages/shared/src/loadClerkJsScript.ts +++ b/packages/shared/src/loadClerkJsScript.ts @@ -1,13 +1,15 @@ import type { ClerkOptions, SDKMetadata, Without } from '@clerk/types'; -import { buildErrorThrower } from './error'; +import { buildErrorThrower, ClerkRuntimeError } from './error'; import { createDevOrStagingUrlCache, parsePublishableKey } from './keys'; import { loadScript } from './loadScript'; import { isValidProxyUrl, proxyUrlToAbsoluteURL } from './proxy'; import { addClerkPrefix } from './url'; import { versionSelector } from './versionSelector'; -const FAILED_TO_LOAD_ERROR = 'Clerk: Failed to load Clerk'; +const ERROR_CODE = 'failed_to_load_clerk_js'; +const ERROR_CODE_TIMEOUT = 'failed_to_load_clerk_js_timeout'; +const FAILED_TO_LOAD_ERROR = 'Failed to load Clerk'; const { isDevOrStagingUrl } = createDevOrStagingUrlCache(); @@ -96,7 +98,7 @@ function waitForClerkWithTimeout(timeoutMs: number): Promise { - throw new Error(FAILED_TO_LOAD_ERROR); + }).catch(error => { + throw new ClerkRuntimeError(FAILED_TO_LOAD_ERROR + (error.message ? `, ${error.message}` : ''), { + code: ERROR_CODE, + cause: error, + }); }); return loadPromise; diff --git a/packages/shared/src/loadScript.ts b/packages/shared/src/loadScript.ts index ae5ea5859b0..e81f8730ef2 100644 --- a/packages/shared/src/loadScript.ts +++ b/packages/shared/src/loadScript.ts @@ -21,7 +21,7 @@ export async function loadScript(src = '', opts: LoadScriptOptions): Promise { + script.addEventListener('error', event => { script.remove(); - reject(); + reject(event.error ?? new Error(`failed to load script: ${src}`)); }); script.src = src; From d55720c9f75f67b1bf786953386661a98e4cbbd8 Mon Sep 17 00:00:00 2001 From: Heat Hamilton <55773810+heatlikeheatwave@users.noreply.github.com> Date: Fri, 26 Sep 2025 18:12:21 -0400 Subject: [PATCH 15/33] feat(shared): Capture auth component mounted for all SDK types (#6858) Co-authored-by: Dylan Staley <88163+dstaley@users.noreply.github.com> --- .changeset/pretty-rings-compare.md | 5 +++++ .../src/telemetry/events/component-mounted.ts | 20 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 .changeset/pretty-rings-compare.md diff --git a/.changeset/pretty-rings-compare.md b/.changeset/pretty-rings-compare.md new file mode 100644 index 00000000000..a46279fd7b8 --- /dev/null +++ b/.changeset/pretty-rings-compare.md @@ -0,0 +1,5 @@ +--- +"@clerk/shared": patch +--- + +Increase sampling for high-signal auth components on mount. diff --git a/packages/shared/src/telemetry/events/component-mounted.ts b/packages/shared/src/telemetry/events/component-mounted.ts index 3b63d75e8b8..940f8a1a943 100644 --- a/packages/shared/src/telemetry/events/component-mounted.ts +++ b/packages/shared/src/telemetry/events/component-mounted.ts @@ -4,6 +4,19 @@ const EVENT_COMPONENT_MOUNTED = 'COMPONENT_MOUNTED'; const EVENT_COMPONENT_OPENED = 'COMPONENT_OPENED'; const EVENT_SAMPLING_RATE = 0.1; +/** Increase sampling for high-signal auth components on mount. */ +const AUTH_COMPONENTS = new Set(['SignIn', 'SignUp']); + +/** + * Returns the per-event sampling rate for component-mounted telemetry events. + * Uses a higher rate for SignIn/SignUp to improve signal quality. + * + * @internal + */ +function getComponentMountedSamplingRate(component: string): number { + return AUTH_COMPONENTS.has(component) ? 1 : EVENT_SAMPLING_RATE; +} + type ComponentMountedBase = { component: string; }; @@ -18,6 +31,8 @@ type EventPrebuiltComponent = ComponentMountedBase & { type EventComponentMounted = ComponentMountedBase & TelemetryEventRaw['payload']; /** + * Factory for prebuilt component telemetry events. + * * @internal */ function createPrebuiltComponentEvent(event: typeof EVENT_COMPONENT_MOUNTED | typeof EVENT_COMPONENT_OPENED) { @@ -28,7 +43,8 @@ function createPrebuiltComponentEvent(event: typeof EVENT_COMPONENT_MOUNTED | ty ): TelemetryEventRaw { return { event, - eventSamplingRate: EVENT_SAMPLING_RATE, + eventSamplingRate: + event === EVENT_COMPONENT_MOUNTED ? getComponentMountedSamplingRate(component) : EVENT_SAMPLING_RATE, payload: { component, appearanceProp: Boolean(props?.appearance), @@ -91,7 +107,7 @@ export function eventComponentMounted( ): TelemetryEventRaw { return { event: EVENT_COMPONENT_MOUNTED, - eventSamplingRate: EVENT_SAMPLING_RATE, + eventSamplingRate: getComponentMountedSamplingRate(component), payload: { component, ...props, From bf496bc5204436e5d31606531610b11684b77b75 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Fri, 26 Sep 2025 15:18:22 -0700 Subject: [PATCH 16/33] feat(react-router): Introduce middleware and context (#6660) --- .changeset/quiet-bats-protect.md | 54 ++++ integration/presets/utils.ts | 4 +- .../react-router-library/package.json | 5 +- .../templates/react-router-node/app/root.tsx | 4 +- .../app/routes/protected.tsx | 7 +- .../templates/react-router-node/package.json | 21 +- .../react-router-node/react-router.config.ts | 4 + integration/tests/react-router/basic.test.ts | 2 +- .../tests/react-router/pre-middleware.test.ts | 169 ++++++++++++ package.json | 2 +- packages/react-router/package.json | 9 +- .../__snapshots__/exports.test.ts.snap | 13 +- .../src/__tests__/exports.test.ts | 17 +- packages/react-router/src/api/index.ts | 14 + .../server/__tests__/clerkMiddleware.test.ts | 151 +++++++++++ .../src/server/__tests__/getAuth.test.ts | 71 +++++ .../server/__tests__/rootAuthLoader.test.ts | 251 ++++++++++++++++++ .../react-router/src/server/clerkClient.ts | 21 ++ .../src/server/clerkMiddleware.ts | 77 ++++++ packages/react-router/src/server/getAuth.ts | 46 ++++ packages/react-router/src/server/index.ts | 5 + .../legacyAuthenticateRequest.ts} | 8 +- .../src/{ssr => server}/loadOptions.ts | 9 +- .../react-router/src/server/rootAuthLoader.ts | 172 ++++++++++++ .../react-router/src/{ssr => server}/types.ts | 23 +- .../react-router/src/{ssr => server}/utils.ts | 19 +- packages/react-router/src/ssr/getAuth.ts | 38 --- packages/react-router/src/ssr/index.ts | 17 +- .../react-router/src/ssr/rootAuthLoader.ts | 98 ------- packages/react-router/src/utils/errors.ts | 35 +++ 30 files changed, 1182 insertions(+), 184 deletions(-) create mode 100644 .changeset/quiet-bats-protect.md create mode 100644 integration/tests/react-router/pre-middleware.test.ts create mode 100644 packages/react-router/src/server/__tests__/clerkMiddleware.test.ts create mode 100644 packages/react-router/src/server/__tests__/getAuth.test.ts create mode 100644 packages/react-router/src/server/__tests__/rootAuthLoader.test.ts create mode 100644 packages/react-router/src/server/clerkClient.ts create mode 100644 packages/react-router/src/server/clerkMiddleware.ts create mode 100644 packages/react-router/src/server/getAuth.ts create mode 100644 packages/react-router/src/server/index.ts rename packages/react-router/src/{ssr/authenticateRequest.ts => server/legacyAuthenticateRequest.ts} (86%) rename packages/react-router/src/{ssr => server}/loadOptions.ts (90%) create mode 100644 packages/react-router/src/server/rootAuthLoader.ts rename packages/react-router/src/{ssr => server}/types.ts (96%) rename packages/react-router/src/{ssr => server}/utils.ts (89%) delete mode 100644 packages/react-router/src/ssr/getAuth.ts delete mode 100644 packages/react-router/src/ssr/rootAuthLoader.ts diff --git a/.changeset/quiet-bats-protect.md b/.changeset/quiet-bats-protect.md new file mode 100644 index 00000000000..73f732b19fe --- /dev/null +++ b/.changeset/quiet-bats-protect.md @@ -0,0 +1,54 @@ +--- +'@clerk/react-router': major +--- + +Introduce [React Router middleware](https://reactrouter.com/how-to/middleware) support with `clerkMiddleware()` for improved performance and streaming capabilities. + +Usage of `rootAuthLoader` without the `clerkMiddleware()` installed is now deprecated and will be removed in the next major version. + +**Before (Deprecated - will be removed):** + +```tsx +import { rootAuthLoader } from '@clerk/react-router/ssr.server' + +export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args) +``` + +**After (Recommended):** + +1. Enable the `v8_middleware` future flag: + +```ts +// react-router.config.ts +export default { + future: { + v8_middleware: true, + }, +} satisfies Config; +``` + +2. Use the middleware in your app: + +```tsx +import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server' + +export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()] + +export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args) +``` + +**Streaming Support (with middleware):** + +```tsx +export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()] + +export const loader = (args: Route.LoaderArgs) => { + const nonCriticalData = new Promise((res) => + setTimeout(() => res('non-critical'), 5000), + ) + + return rootAuthLoader(args, () => ({ + nonCriticalData + })) +} +``` \ No newline at end of file diff --git a/integration/presets/utils.ts b/integration/presets/utils.ts index 2d5d7a87414..f7831c39663 100644 --- a/integration/presets/utils.ts +++ b/integration/presets/utils.ts @@ -2,7 +2,9 @@ import path from 'node:path'; export function linkPackage(pkg: string) { // eslint-disable-next-line turbo/no-undeclared-env-vars - if (process.env.CI === 'true') return '*'; + if (process.env.CI === 'true') { + return '*'; + } return `link:${path.resolve(process.cwd(), `packages/${pkg}`)}`; } diff --git a/integration/templates/react-router-library/package.json b/integration/templates/react-router-library/package.json index 28391f861f1..4febd9a0dee 100644 --- a/integration/templates/react-router-library/package.json +++ b/integration/templates/react-router-library/package.json @@ -9,15 +9,14 @@ "preview": "vite preview --port $PORT" }, "dependencies": { - "@clerk/react-router": "^0.1.2", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router": "^7.1.2" + "react-router": "^7.9.1" }, "devDependencies": { "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-react": "^5.0.3", "globals": "^15.12.0", "typescript": "~5.7.3", "vite": "^6.0.1" diff --git a/integration/templates/react-router-node/app/root.tsx b/integration/templates/react-router-node/app/root.tsx index 0bae3ebbc62..e24f3b1a918 100644 --- a/integration/templates/react-router-node/app/root.tsx +++ b/integration/templates/react-router-node/app/root.tsx @@ -1,9 +1,11 @@ import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'; import { rootAuthLoader } from '@clerk/react-router/ssr.server'; import { ClerkProvider } from '@clerk/react-router'; - import type { Route } from './+types/root'; +// TODO: Uncomment when published +// export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]; + export async function loader(args: Route.LoaderArgs) { return rootAuthLoader(args); } diff --git a/integration/templates/react-router-node/app/routes/protected.tsx b/integration/templates/react-router-node/app/routes/protected.tsx index 2fdc2718e1c..362fcac4fa4 100644 --- a/integration/templates/react-router-node/app/routes/protected.tsx +++ b/integration/templates/react-router-node/app/routes/protected.tsx @@ -14,7 +14,8 @@ export async function loader(args: Route.LoaderArgs) { const user = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.getUser(userId); return { - user, + firstName: user.firstName, + emailAddress: user.emailAddresses[0].emailAddress, }; } @@ -24,8 +25,8 @@ export default function Profile({ loaderData }: Route.ComponentProps) {

Protected

    -
  • First name: {loaderData.user.firstName}
  • -
  • Email: {loaderData.user.emailAddresses[0].emailAddress}
  • +
  • First name: {loaderData.firstName}
  • +
  • Email: {loaderData.emailAddress}
); diff --git a/integration/templates/react-router-node/package.json b/integration/templates/react-router-node/package.json index aabe6a20c32..3bcf6de6ba8 100644 --- a/integration/templates/react-router-node/package.json +++ b/integration/templates/react-router-node/package.json @@ -9,21 +9,20 @@ "typecheck": "react-router typegen && tsc --build --noEmit" }, "dependencies": { - "@clerk/react-router": "latest", - "@react-router/node": "^7.1.2", - "@react-router/serve": "^7.1.2", + "@react-router/node": "^7.9.1", + "@react-router/serve": "^7.9.1", "isbot": "^5.1.17", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router": "^7.1.2" + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router": "^7.9.1" }, "devDependencies": { - "@react-router/dev": "^7.1.2", + "@react-router/dev": "^7.9.1", "@types/node": "^20", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", "typescript": "^5.7.3", - "vite": "^5.4.11", - "vite-tsconfig-paths": "^5.1.2" + "vite": "^7.1.5", + "vite-tsconfig-paths": "^5.1.4" } } diff --git a/integration/templates/react-router-node/react-router.config.ts b/integration/templates/react-router-node/react-router.config.ts index 4f9a6ed5228..77f1c2cbc06 100644 --- a/integration/templates/react-router-node/react-router.config.ts +++ b/integration/templates/react-router-node/react-router.config.ts @@ -4,4 +4,8 @@ export default { // Config options... // Server-side render by default, to enable SPA mode set this to `false` ssr: true, + future: { + v8_middleware: true, + unstable_optimizeDeps: true, + }, } satisfies Config; diff --git a/integration/tests/react-router/basic.test.ts b/integration/tests/react-router/basic.test.ts index 595a724304b..e67921ef416 100644 --- a/integration/tests/react-router/basic.test.ts +++ b/integration/tests/react-router/basic.test.ts @@ -5,7 +5,7 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes], withPattern: ['react-router.node'] })( - 'basic tests for @react-router', + 'basic tests for @react-router with middleware', ({ app }) => { test.describe.configure({ mode: 'parallel' }); diff --git a/integration/tests/react-router/pre-middleware.test.ts b/integration/tests/react-router/pre-middleware.test.ts new file mode 100644 index 00000000000..3cb80691d2d --- /dev/null +++ b/integration/tests/react-router/pre-middleware.test.ts @@ -0,0 +1,169 @@ +import { expect, test } from '@playwright/test'; + +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; + +test.describe('basic tests for @react-router without middleware', () => { + test.describe.configure({ mode: 'parallel' }); + let app: Application; + let fakeUser: FakeUser; + + test.beforeAll(async () => { + test.setTimeout(90_000); // Wait for app to be ready + app = await appConfigs.reactRouter.reactRouterNode + .clone() + .addFile( + `app/root.tsx`, + () => `import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'; +import { rootAuthLoader } from '@clerk/react-router/ssr.server'; +import { ClerkProvider } from '@clerk/react-router'; + +import type { Route } from './+types/root'; + +export async function loader(args: Route.LoaderArgs) { + return rootAuthLoader(args); +} + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + {children} + + + + + ); +} + +export default function App({ loaderData }: Route.ComponentProps) { + return ( + +
+ +
+
+ ); +} + +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = 'Oops!'; + let details = 'An unexpected error occurred.'; + let stack: string | undefined; + + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? '404' : 'Error'; + details = error.status === 404 ? 'The requested page could not be found.' : error.statusText || details; + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message; + stack = error.stack; + } + + return ( +
+

{message}

+

{details}

+ {stack && ( +
+          {stack}
+        
+ )} +
+ ); +} +`, + ) + .commit(); + + await app.setup(); + await app.withEnv(appConfigs.envs.withEmailCodes); + await app.dev(); + + const u = createTestUtils({ app }); + fakeUser = u.services.users.createFakeUser({ + fictionalEmail: true, + withPhoneNumber: true, + withUsername: true, + }); + await u.services.users.createBapiUser(fakeUser); + }); + + test.afterAll(async () => { + await fakeUser.deleteIfExists(); + await app.teardown(); + }); + + test.afterEach(async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + await u.page.signOut(); + await u.page.context().clearCookies(); + }); + + test('can sign in and user button renders', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + await u.po.signIn.goTo(); + + await u.po.signIn.setIdentifier(fakeUser.email); + await u.po.signIn.setPassword(fakeUser.password); + await u.po.signIn.continue(); + await u.po.expect.toBeSignedIn(); + + await u.page.waitForAppUrl('/'); + + await u.po.userButton.waitForMounted(); + await u.po.userButton.toggleTrigger(); + await u.po.userButton.waitForPopover(); + + await u.po.userButton.toHaveVisibleMenuItems([/Manage account/i, /Sign out$/i]); + }); + + test('redirects to sign-in when unauthenticated', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + + await u.page.goToRelative('/protected'); + await u.page.waitForURL(`${app.serverUrl}/sign-in`); + await u.po.signIn.waitForMounted(); + }); + + test('renders control components contents', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + + await u.page.goToAppHome(); + await expect(u.page.getByText('SignedOut')).toBeVisible(); + + await u.page.goToRelative('/sign-in'); + await u.po.signIn.waitForMounted(); + await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); + await u.po.expect.toBeSignedIn(); + await expect(u.page.getByText('SignedIn')).toBeVisible(); + }); + + test('renders user profile with SSR data', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + + await u.page.goToRelative('/sign-in'); + await u.po.signIn.waitForMounted(); + await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); + await u.po.expect.toBeSignedIn(); + + await u.po.userButton.waitForMounted(); + await u.page.goToRelative('/protected'); + await u.po.userProfile.waitForMounted(); + + // Fetched from an API endpoint (/api/me), which is server-rendered. + // This also verifies that the server middleware is working. + await expect(u.page.getByText(`First name: ${fakeUser.firstName}`)).toBeVisible(); + await expect(u.page.getByText(`Email: ${fakeUser.email}`)).toBeVisible(); + }); +}); diff --git a/package.json b/package.json index e824b83e4e4..ba85402ae45 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "test:integration:nextjs": "E2E_APP_ID=next.appRouter.* pnpm test:integration:base --grep @nextjs", "test:integration:nuxt": "E2E_APP_ID=nuxt.node npm run test:integration:base -- --grep @nuxt", "test:integration:quickstart": "E2E_APP_ID=quickstart.* pnpm test:integration:base --grep @quickstart", - "test:integration:react-router": "E2E_APP_ID=react-router.* npm run test:integration:base -- --grep @react-router", + "test:integration:react-router": "E2E_APP_ID=react-router.* pnpm test:integration:base --grep @react-router", "test:integration:sessions": "DISABLE_WEB_SECURITY=true E2E_SESSIONS_APP_1_ENV_KEY=sessions-prod-1 E2E_SESSIONS_APP_2_ENV_KEY=sessions-prod-2 E2E_SESSIONS_APP_1_HOST=multiple-apps-e2e.clerk.app pnpm test:integration:base --grep @sessions", "test:integration:sessions:staging": "DISABLE_WEB_SECURITY=true E2E_SESSIONS_APP_1_ENV_KEY=clerkstage-sessions-prod-1 E2E_SESSIONS_APP_2_ENV_KEY=clerkstage-sessions-prod-2 E2E_SESSIONS_APP_1_HOST=clerkstage-sessions-prod-1-e2e.clerk.app pnpm test:integration:base --grep @sessions", "test:integration:tanstack-react-router": "E2E_APP_ID=tanstack.react-router pnpm test:integration:base --grep @tanstack-react-router", diff --git a/packages/react-router/package.json b/packages/react-router/package.json index bdfa38997d0..9d0922abfc4 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -30,6 +30,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, + "./server": { + "types": "./dist/server/index.d.ts", + "default": "./dist/server/index.js" + }, "./ssr.server": { "types": "./dist/ssr/index.d.ts", "default": "./dist/ssr/index.js" @@ -55,6 +59,9 @@ "dist/*.d.ts", "dist/index.d.ts" ], + "server": [ + "dist/server/index.d.ts" + ], "ssr.server": [ "dist/ssr/index.d.ts" ], @@ -97,7 +104,7 @@ "peerDependencies": { "react": "catalog:peer-react", "react-dom": "catalog:peer-react", - "react-router": "^7.1.2" + "react-router": "^7.9.0" }, "engines": { "node": ">=20.0.0" diff --git a/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap index 84aae121b9f..53f3377ed94 100644 --- a/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap @@ -1,5 +1,12 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`deprecated ssr public exports > should not change unexpectedly 1`] = ` +[ + "getAuth", + "rootAuthLoader", +] +`; + exports[`root public exports > should not change unexpectedly 1`] = ` [ "APIKeys", @@ -54,9 +61,13 @@ exports[`root public exports > should not change unexpectedly 1`] = ` ] `; -exports[`ssr public exports > should not change unexpectedly 1`] = ` +exports[`server public exports > should not change unexpectedly 1`] = ` [ + "clerkClient", + "clerkMiddleware", + "createClerkClient", "getAuth", "rootAuthLoader", + "verifyToken", ] `; diff --git a/packages/react-router/src/__tests__/exports.test.ts b/packages/react-router/src/__tests__/exports.test.ts index 4bba4ec2277..1d9551d96c7 100644 --- a/packages/react-router/src/__tests__/exports.test.ts +++ b/packages/react-router/src/__tests__/exports.test.ts @@ -1,5 +1,8 @@ +import { logger } from '@clerk/shared/logger'; +import { vi } from 'vitest'; + import * as publicExports from '../index'; -import * as ssrExports from '../ssr/index'; +import * as serverExports from '../server/index'; describe('root public exports', () => { it('should not change unexpectedly', () => { @@ -7,8 +10,18 @@ describe('root public exports', () => { }); }); -describe('ssr public exports', () => { +describe('server public exports', () => { it('should not change unexpectedly', () => { + expect(Object.keys(serverExports).sort()).toMatchSnapshot(); + }); +}); + +describe('deprecated ssr public exports', () => { + it('should not change unexpectedly', async () => { + const warnOnceSpy = vi.spyOn(logger, 'warnOnce').mockImplementation(() => {}); + const ssrExports = await import('../ssr/index'); expect(Object.keys(ssrExports).sort()).toMatchSnapshot(); + expect(warnOnceSpy).toHaveBeenCalled(); + warnOnceSpy.mockRestore(); }); }); diff --git a/packages/react-router/src/api/index.ts b/packages/react-router/src/api/index.ts index f5ce35a683b..cb3b0378663 100644 --- a/packages/react-router/src/api/index.ts +++ b/packages/react-router/src/api/index.ts @@ -1 +1,15 @@ export * from '@clerk/backend'; + +import { logger } from '@clerk/shared/logger'; + +logger.warnOnce(` +Clerk - DEPRECATION WARNING: \`@clerk/react-router/api.server\` has been deprecated and will be removed in the next major version. + +Import from \`@clerk/react-router/server\` instead. + +Before: + import { getAuth, clerkMiddleware, rootAuthLoader } from '@clerk/react-router/api.server'; + +After: + import { getAuth, clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server'; +`); diff --git a/packages/react-router/src/server/__tests__/clerkMiddleware.test.ts b/packages/react-router/src/server/__tests__/clerkMiddleware.test.ts new file mode 100644 index 00000000000..8be8e33c419 --- /dev/null +++ b/packages/react-router/src/server/__tests__/clerkMiddleware.test.ts @@ -0,0 +1,151 @@ +import type { ClerkClient } from '@clerk/backend'; +import { AuthStatus, TokenType } from '@clerk/backend/internal'; +import type { LoaderFunctionArgs } from 'react-router'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { clerkClient } from '../clerkClient'; +import { authFnContext, clerkMiddleware, requestStateContext } from '../clerkMiddleware'; +import { loadOptions } from '../loadOptions'; +import type { ClerkMiddlewareOptions } from '../types'; + +vi.mock('../clerkClient'); +vi.mock('../loadOptions'); + +const mockClerkClient = vi.mocked(clerkClient); +const mockLoadOptions = vi.mocked(loadOptions); + +describe('clerkMiddleware', () => { + const mockNext = vi.fn(); + const mockContext = { + get: vi.fn(), + set: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + process.env.CLERK_SECRET_KEY = 'sk_test_...'; + + mockLoadOptions.mockReturnValue({ + audience: '', + authorizedParties: [], + signInUrl: '', + signUpUrl: '', + afterSignInUrl: '', + afterSignUpUrl: '', + secretKey: 'sk_test_...', + publishableKey: 'pk_test_...', + } as unknown as ReturnType); + + mockClerkClient.mockReturnValue({ + authenticateRequest: vi.fn(), + } as unknown as ClerkClient); + }); + + it('should authenticate request and set context', async () => { + const mockRequestState = { + status: AuthStatus.SignedIn, + headers: new Headers(), + toAuth: vi.fn().mockReturnValue({ userId: 'user_xxx', tokenType: TokenType.SessionToken }), + }; + + const mockAuthenticateRequest = vi.fn().mockResolvedValue(mockRequestState); + mockClerkClient.mockReturnValue({ + authenticateRequest: mockAuthenticateRequest, + } as unknown as ClerkClient); + + const middleware = clerkMiddleware(); + const args = { + request: new Request('http://clerk.com'), + context: mockContext, + } as LoaderFunctionArgs; + + const mockResponse = new Response('OK'); + mockNext.mockResolvedValue(mockResponse); + + const result = await middleware(args, mockNext); + + expect(mockAuthenticateRequest).toHaveBeenCalledWith(expect.any(Object), { + audience: '', + authorizedParties: [], + signInUrl: '', + signUpUrl: '', + afterSignInUrl: '', + afterSignUpUrl: '', + acceptsToken: 'any', + }); + + expect(mockContext.set).toHaveBeenCalledWith(authFnContext, expect.any(Function)); + expect(mockContext.set).toHaveBeenCalledWith(requestStateContext, mockRequestState); + + expect(mockNext).toHaveBeenCalled(); + + expect(result).toBe(mockResponse); + }); + + it('should pass options to loadOptions', async () => { + const mockRequestState = { + status: AuthStatus.SignedIn, + headers: new Headers(), + toAuth: vi.fn().mockReturnValue({ userId: 'user_xxx', tokenType: TokenType.SessionToken }), + }; + + const mockAuthenticateRequest = vi.fn().mockResolvedValue(mockRequestState); + mockClerkClient.mockReturnValue({ + authenticateRequest: mockAuthenticateRequest, + } as unknown as ClerkClient); + + const options: ClerkMiddlewareOptions = { + audience: 'test-audience', + authorizedParties: ['https://example.com'], + signInUrl: '/sign-in', + signUpUrl: '/sign-up', + afterSignInUrl: '/dashboard', + afterSignUpUrl: '/welcome', + }; + + const middleware = clerkMiddleware(options); + const args = { + request: new Request('http://clerk.com'), + context: mockContext, + } as LoaderFunctionArgs; + + const mockResponse = new Response('OK'); + mockNext.mockResolvedValue(mockResponse); + + await middleware(args, mockNext); + + expect(mockLoadOptions).toHaveBeenCalledWith(args, options); + }); + + it('should append request state headers to response', async () => { + const mockRequestState = { + status: AuthStatus.SignedIn, + headers: new Headers({ + 'x-clerk-auth-status': 'signed-in', + 'x-clerk-auth-reason': 'auth-reason', + 'x-clerk-auth-message': 'auth-message', + }), + toAuth: vi.fn().mockReturnValue({ userId: 'user_xxx', tokenType: TokenType.SessionToken }), + }; + + const mockAuthenticateRequest = vi.fn().mockResolvedValue(mockRequestState); + mockClerkClient.mockReturnValue({ + authenticateRequest: mockAuthenticateRequest, + } as unknown as ClerkClient); + + const middleware = clerkMiddleware(); + const args = { + request: new Request('http://clerk.com'), + context: mockContext, + } as LoaderFunctionArgs; + + const mockResponse = new Response('OK'); + mockNext.mockResolvedValue(mockResponse); + + const result = (await middleware(args, mockNext)) as Response; + + expect(result.headers.get('x-clerk-auth-status')).toBe('signed-in'); + expect(result.headers.get('x-clerk-auth-reason')).toBe('auth-reason'); + expect(result.headers.get('x-clerk-auth-message')).toBe('auth-message'); + }); +}); diff --git a/packages/react-router/src/server/__tests__/getAuth.test.ts b/packages/react-router/src/server/__tests__/getAuth.test.ts new file mode 100644 index 00000000000..742fc6f0ae1 --- /dev/null +++ b/packages/react-router/src/server/__tests__/getAuth.test.ts @@ -0,0 +1,71 @@ +import { TokenType } from '@clerk/backend/internal'; +import type { LoaderFunctionArgs } from 'react-router'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { authFnContext } from '../clerkMiddleware'; +import { getAuth } from '../getAuth'; +import { legacyAuthenticateRequest } from '../legacyAuthenticateRequest'; + +vi.mock('../legacyAuthenticateRequest', () => { + return { + legacyAuthenticateRequest: vi.fn().mockResolvedValue({ + toAuth: vi.fn().mockImplementation(() => ({ + userId: 'user_xxx', + tokenType: TokenType.SessionToken, + })), + headers: new Headers(), + status: 'signed-in', + }), + }; +}); + +describe('getAuth', () => { + beforeEach(() => { + vi.clearAllMocks(); + process.env.CLERK_SECRET_KEY = 'sk_test_...'; + }); + + it('should not call legacyAuthenticateRequest when middleware context exists', async () => { + const mockContext = { + get: vi.fn().mockImplementation(contextKey => { + if (contextKey === authFnContext) { + return vi.fn().mockImplementation((options?: any) => ({ + userId: 'user_xxx', + tokenType: TokenType.SessionToken, + ...options, + })); + } + return null; + }), + set: vi.fn(), + }; + + const args = { + context: mockContext, + request: new Request('http://clerk.com'), + } as LoaderFunctionArgs; + + const auth = await getAuth(args); + + expect(legacyAuthenticateRequest).not.toHaveBeenCalled(); + expect(auth.userId).toBe('user_xxx'); + expect(auth.tokenType).toBe('session_token'); + }); + + it('should call legacyAuthenticateRequest when middleware context is missing', async () => { + const mockContext = { + get: vi.fn().mockReturnValue(null), + }; + + const args = { + context: mockContext, + request: new Request('http://clerk.com'), + } as LoaderFunctionArgs; + + const auth = await getAuth(args); + + expect(legacyAuthenticateRequest).toHaveBeenCalled(); + expect(auth.userId).toBe('user_xxx'); + expect(auth.tokenType).toBe('session_token'); + }); +}); diff --git a/packages/react-router/src/server/__tests__/rootAuthLoader.test.ts b/packages/react-router/src/server/__tests__/rootAuthLoader.test.ts new file mode 100644 index 00000000000..e8bc86e116b --- /dev/null +++ b/packages/react-router/src/server/__tests__/rootAuthLoader.test.ts @@ -0,0 +1,251 @@ +import { TokenType } from '@clerk/backend/internal'; +import { logger } from '@clerk/shared/logger'; +import { data, type LoaderFunctionArgs } from 'react-router'; +import type { MockInstance } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { middlewareMigrationWarning } from '../../utils/errors'; +import { authFnContext, requestStateContext } from '../clerkMiddleware'; +import { legacyAuthenticateRequest } from '../legacyAuthenticateRequest'; +import { rootAuthLoader } from '../rootAuthLoader'; + +vi.mock('../legacyAuthenticateRequest', () => { + return { + legacyAuthenticateRequest: vi.fn().mockResolvedValue({ + toAuth: vi.fn().mockImplementation(() => ({ + userId: 'user_xxx', + tokenType: TokenType.SessionToken, + })), + headers: new Headers({ + 'x-clerk-auth-status': 'signed-in', + 'x-clerk-auth-reason': 'auth-reason', + 'x-clerk-auth-message': 'auth-message', + }), + status: 'signed-in', + }), + }; +}); + +describe('rootAuthLoader', () => { + beforeEach(() => { + vi.clearAllMocks(); + process.env.CLERK_SECRET_KEY = 'sk_test_...'; + }); + + describe('with middleware context', () => { + const mockContext = { + get: vi.fn().mockImplementation(contextKey => { + if (contextKey === requestStateContext) { + return { + toAuth: vi.fn().mockImplementation(() => ({ + userId: 'user_xxx', + tokenType: TokenType.SessionToken, + })), + headers: new Headers(), + status: 'signed-in', + }; + } + if (contextKey === authFnContext) { + return vi.fn().mockImplementation((options?: any) => ({ + userId: 'user_xxx', + tokenType: TokenType.SessionToken, + ...options, + })); + } + return null; + }), + set: vi.fn(), + }; + + const args = { + context: mockContext, + request: new Request('http://clerk.com'), + } as LoaderFunctionArgs; + + it('should not call legacyAuthenticateRequest when middleware context exists', async () => { + const warnOnceSpy = vi.spyOn(logger, 'warnOnce').mockImplementation(() => {}); + + await rootAuthLoader(args, () => ({ data: 'test' })); + + expect(legacyAuthenticateRequest).not.toHaveBeenCalled(); + expect(warnOnceSpy).not.toHaveBeenCalled(); + + warnOnceSpy.mockRestore(); + }); + + it('should handle no callback', async () => { + const result = await rootAuthLoader(args); + + expect(result).toHaveProperty('clerkState'); + expect(legacyAuthenticateRequest).not.toHaveBeenCalled(); + }); + + it('should handle callback returning a Response', async () => { + const mockResponse = new Response(JSON.stringify({ message: 'Hello' }), { + headers: { 'Content-Type': 'application/json' }, + }); + + const response = await rootAuthLoader(args, () => mockResponse); + + expect(response).toBeInstanceOf(Response); + const json = await response.json(); + expect(json).toHaveProperty('message', 'Hello'); + expect(json).toHaveProperty('clerkState'); + + // Headers will be set by middleware + expect(response.headers.get('x-clerk-auth-reason')).toBeNull(); + expect(response.headers.get('x-clerk-auth-status')).toBeNull(); + expect(response.headers.get('x-clerk-auth-message')).toBeNull(); + + expect(legacyAuthenticateRequest).not.toHaveBeenCalled(); + }); + + it('should handle callback returning data()', async () => { + const result = await rootAuthLoader(args, () => data({ message: 'Hello from data()' })); + + const response = result as unknown as Response; + + expect(response).toBeInstanceOf(Response); + const json = await response.json(); + expect(json).toHaveProperty('message', 'Hello from data()'); + expect(json).toHaveProperty('clerkState'); + + // Headers will be set by middleware + expect(response.headers.get('x-clerk-auth-reason')).toBeNull(); + expect(response.headers.get('x-clerk-auth-status')).toBeNull(); + expect(response.headers.get('x-clerk-auth-message')).toBeNull(); + + expect(legacyAuthenticateRequest).not.toHaveBeenCalled(); + }); + + it('should handle callback returning plain object', async () => { + const nonCriticalData = new Promise(res => setTimeout(() => res('non-critical'), 5000)); + const plainObject = { message: 'Hello from plain object', nonCriticalData }; + + const result = await rootAuthLoader(args, () => plainObject); + + expect(result).toHaveProperty('message', 'Hello from plain object'); + expect(result).toHaveProperty('nonCriticalData', nonCriticalData); + expect(result).toHaveProperty('clerkState'); + + expect(legacyAuthenticateRequest).not.toHaveBeenCalled(); + }); + + it('should handle callback returning null', async () => { + const result = await rootAuthLoader(args, () => null); + + expect(result).toHaveProperty('clerkState'); + expect(legacyAuthenticateRequest).not.toHaveBeenCalled(); + }); + }); + + describe('without middleware context', () => { + const mockContext = { + // No get/set methods - simulates v8_middleware flag not enabled + }; + + const args = { + context: mockContext, + request: new Request('http://clerk.com'), + } as LoaderFunctionArgs; + + let warnOnceSpy: MockInstance<(msg: string) => void>; + + beforeEach(() => { + warnOnceSpy = vi.spyOn(logger, 'warnOnce').mockImplementation(() => {}); + }); + + afterEach(() => { + warnOnceSpy.mockRestore(); + }); + + it('should call legacyAuthenticateRequest when middleware context is missing', async () => { + await rootAuthLoader(args, () => ({ data: 'test' })); + + expect(legacyAuthenticateRequest).toHaveBeenCalled(); + expect(warnOnceSpy).toHaveBeenCalledWith(middlewareMigrationWarning); + }); + + it('should handle no callback', async () => { + const result = await rootAuthLoader(args); + + const response = result as Response; + + expect(result).toBeInstanceOf(Response); + expect(await response.json()).toHaveProperty('clerkState'); + expect(legacyAuthenticateRequest).toHaveBeenCalled(); + + expect(response.headers.get('x-clerk-auth-reason')).toBe('auth-reason'); + expect(response.headers.get('x-clerk-auth-status')).toBe('signed-in'); + expect(response.headers.get('x-clerk-auth-message')).toBe('auth-message'); + }); + + it('should handle callback returning Response', async () => { + const mockResponse = new Response(JSON.stringify({ message: 'Hello' })); + + const response = await rootAuthLoader(args, () => mockResponse); + + expect(response).toBeInstanceOf(Response); + expect(await response.json()).toHaveProperty('clerkState'); + + expect(response.headers.get('x-clerk-auth-reason')).toBe('auth-reason'); + expect(response.headers.get('x-clerk-auth-status')).toBe('signed-in'); + expect(response.headers.get('x-clerk-auth-message')).toBe('auth-message'); + + expect(legacyAuthenticateRequest).toHaveBeenCalled(); + }); + + it('should handle callback returning data()', async () => { + const result = await rootAuthLoader(args, () => data({ message: 'Hello from data()' })); + + const response = result as unknown as Response; + + expect(response).toBeInstanceOf(Response); + const json = await response.json(); + expect(json).toHaveProperty('message', 'Hello from data()'); + expect(json).toHaveProperty('clerkState'); + + expect(response.headers.get('x-clerk-auth-reason')).toBe('auth-reason'); + expect(response.headers.get('x-clerk-auth-status')).toBe('signed-in'); + expect(response.headers.get('x-clerk-auth-message')).toBe('auth-message'); + + expect(legacyAuthenticateRequest).toHaveBeenCalled(); + }); + + it('should handle callback returning plain object', async () => { + const nonCriticalData = new Promise(res => setTimeout(() => res('non-critical'), 5000)); + const plainObject = { message: 'Hello from plain object', nonCriticalData }; + + const result = await rootAuthLoader(args, () => plainObject); + + const response = result as unknown as Response; + + expect(result).toBeInstanceOf(Response); + const json = await response.json(); + expect(json).toHaveProperty('message', 'Hello from plain object'); + expect(json).toHaveProperty('nonCriticalData', {}); // serialized to {} + expect(json).toHaveProperty('clerkState'); + + expect(response.headers.get('x-clerk-auth-reason')).toBe('auth-reason'); + expect(response.headers.get('x-clerk-auth-status')).toBe('signed-in'); + expect(response.headers.get('x-clerk-auth-message')).toBe('auth-message'); + + expect(legacyAuthenticateRequest).toHaveBeenCalled(); + }); + + it('should handle callback returning null', async () => { + const result = await rootAuthLoader(args, () => null); + + const response = result as unknown as Response; + + expect(result).toBeInstanceOf(Response); + expect(await response.json()).toHaveProperty('clerkState'); + + expect(response.headers.get('x-clerk-auth-reason')).toBe('auth-reason'); + expect(response.headers.get('x-clerk-auth-status')).toBe('signed-in'); + expect(response.headers.get('x-clerk-auth-message')).toBe('auth-message'); + + expect(legacyAuthenticateRequest).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/react-router/src/server/clerkClient.ts b/packages/react-router/src/server/clerkClient.ts new file mode 100644 index 00000000000..3c52229da3b --- /dev/null +++ b/packages/react-router/src/server/clerkClient.ts @@ -0,0 +1,21 @@ +import { createClerkClient } from '@clerk/backend'; + +import { type DataFunctionArgs, loadOptions } from './loadOptions'; + +export const clerkClient = (args: DataFunctionArgs) => { + const options = loadOptions(args); + + const { apiUrl, secretKey, jwtKey, proxyUrl, isSatellite, domain, publishableKey, machineSecretKey } = options; + + return createClerkClient({ + apiUrl, + secretKey, + jwtKey, + proxyUrl, + isSatellite, + domain, + publishableKey, + machineSecretKey, + userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}`, + }); +}; diff --git a/packages/react-router/src/server/clerkMiddleware.ts b/packages/react-router/src/server/clerkMiddleware.ts new file mode 100644 index 00000000000..458fb7c7bf4 --- /dev/null +++ b/packages/react-router/src/server/clerkMiddleware.ts @@ -0,0 +1,77 @@ +import type { AuthObject } from '@clerk/backend'; +import type { RequestState } from '@clerk/backend/internal'; +import { AuthStatus, constants, createClerkRequest } from '@clerk/backend/internal'; +import { handleNetlifyCacheInDevInstance } from '@clerk/shared/netlifyCacheHandler'; +import type { PendingSessionOptions } from '@clerk/types'; +import type { MiddlewareFunction } from 'react-router'; +import { createContext } from 'react-router'; + +import { clerkClient } from './clerkClient'; +import { loadOptions } from './loadOptions'; +import type { ClerkMiddlewareOptions } from './types'; +import { patchRequest } from './utils'; + +export const authFnContext = createContext<((options?: PendingSessionOptions) => AuthObject) | null>(null); +export const requestStateContext = createContext | null>(null); + +/** + * Middleware that integrates Clerk authentication into your React Router application. + * It checks the request's cookies and headers for a session JWT and, if found, + * attaches the Auth object to a context. + * + * @example + * // react-router.config.ts + * export default { + * future: { + * v8_middleware: true, + * }, + * } + * + * // root.tsx + * export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()] + */ +export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareFunction => { + return async (args, next) => { + const clerkRequest = createClerkRequest(patchRequest(args.request)); + const loadedOptions = loadOptions(args, options); + const { audience, authorizedParties } = loadedOptions; + const { signInUrl, signUpUrl, afterSignInUrl, afterSignUpUrl } = loadedOptions; + const requestState = await clerkClient(args).authenticateRequest(clerkRequest, { + audience, + authorizedParties, + signInUrl, + signUpUrl, + afterSignInUrl, + afterSignUpUrl, + acceptsToken: 'any', + }); + + const locationHeader = requestState.headers.get(constants.Headers.Location); + if (locationHeader) { + handleNetlifyCacheInDevInstance({ + locationHeader, + requestStateHeaders: requestState.headers, + publishableKey: requestState.publishableKey, + }); + // Trigger a handshake redirect + return new Response(null, { status: 307, headers: requestState.headers }); + } + + if (requestState.status === AuthStatus.Handshake) { + throw new Error('Clerk: handshake status without redirect'); + } + + args.context.set(authFnContext, (options?: PendingSessionOptions) => requestState.toAuth(options)); + args.context.set(requestStateContext, requestState); + + const response = await next(); + + if (requestState.headers) { + requestState.headers.forEach((value, key) => { + response.headers.append(key, value); + }); + } + + return response; + }; +}; diff --git a/packages/react-router/src/server/getAuth.ts b/packages/react-router/src/server/getAuth.ts new file mode 100644 index 00000000000..0c3acbb171a --- /dev/null +++ b/packages/react-router/src/server/getAuth.ts @@ -0,0 +1,46 @@ +import { + type AuthenticateRequestOptions, + type GetAuthFn, + getAuthObjectForAcceptedToken, +} from '@clerk/backend/internal'; +import type { PendingSessionOptions } from '@clerk/types'; +import type { LoaderFunctionArgs } from 'react-router'; + +import { IsOptIntoMiddleware } from '../server/utils'; +import { noLoaderArgsPassedInGetAuth } from '../utils/errors'; +import { authFnContext } from './clerkMiddleware'; +import { legacyAuthenticateRequest } from './legacyAuthenticateRequest'; +import { loadOptions } from './loadOptions'; + +type GetAuthOptions = PendingSessionOptions & { acceptsToken?: AuthenticateRequestOptions['acceptsToken'] }; + +export const getAuth: GetAuthFn = (async ( + args: LoaderFunctionArgs, + opts?: GetAuthOptions, +) => { + if (!args || (args && (!args.request || !args.context))) { + throw new Error(noLoaderArgsPassedInGetAuth); + } + + const { acceptsToken, treatPendingAsSignedOut, ...restOptions } = opts || {}; + + // If the middleware is installed, use the auth function from the context + const authObjectFn = IsOptIntoMiddleware(args.context) && args.context.get(authFnContext); + if (authObjectFn) { + return getAuthObjectForAcceptedToken({ + authObject: authObjectFn({ treatPendingAsSignedOut }), + acceptsToken, + }); + } + + // Fallback to the legacy authenticateRequest if the middleware is not installed + const loadedOptions = loadOptions(args, restOptions); + const requestState = await legacyAuthenticateRequest(args, { + ...loadedOptions, + acceptsToken: 'any', + }); + + const authObject = requestState.toAuth({ treatPendingAsSignedOut }); + + return getAuthObjectForAcceptedToken({ authObject, acceptsToken }); +}) as GetAuthFn; diff --git a/packages/react-router/src/server/index.ts b/packages/react-router/src/server/index.ts new file mode 100644 index 00000000000..fb78cd1fed7 --- /dev/null +++ b/packages/react-router/src/server/index.ts @@ -0,0 +1,5 @@ +export * from '@clerk/backend'; +export { clerkMiddleware } from './clerkMiddleware'; +export { rootAuthLoader } from './rootAuthLoader'; +export { getAuth } from './getAuth'; +export { clerkClient } from './clerkClient'; diff --git a/packages/react-router/src/ssr/authenticateRequest.ts b/packages/react-router/src/server/legacyAuthenticateRequest.ts similarity index 86% rename from packages/react-router/src/ssr/authenticateRequest.ts rename to packages/react-router/src/server/legacyAuthenticateRequest.ts index a0c434ca881..7b2d704cee8 100644 --- a/packages/react-router/src/ssr/authenticateRequest.ts +++ b/packages/react-router/src/server/legacyAuthenticateRequest.ts @@ -1,12 +1,12 @@ -import { createClerkClient } from '@clerk/backend'; import type { AuthenticateRequestOptions, SignedInState, SignedOutState } from '@clerk/backend/internal'; import { AuthStatus, constants } from '@clerk/backend/internal'; import { handleNetlifyCacheInDevInstance } from '@clerk/shared/netlifyCacheHandler'; import type { LoaderFunctionArgs } from 'react-router'; +import { clerkClient } from './clerkClient'; import { patchRequest } from './utils'; -export async function authenticateRequest( +export async function legacyAuthenticateRequest( args: LoaderFunctionArgs, opts: AuthenticateRequestOptions, ): Promise { @@ -16,7 +16,7 @@ export async function authenticateRequest( const { apiUrl, secretKey, jwtKey, proxyUrl, isSatellite, domain, publishableKey, machineSecretKey } = opts; const { signInUrl, signUpUrl, afterSignInUrl, afterSignUpUrl } = opts; - const requestState = await createClerkClient({ + const requestState = await clerkClient(args).authenticateRequest(patchRequest(request), { apiUrl, secretKey, jwtKey, @@ -25,8 +25,6 @@ export async function authenticateRequest( domain, publishableKey, machineSecretKey, - userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}`, - }).authenticateRequest(patchRequest(request), { audience, authorizedParties, signInUrl, diff --git a/packages/react-router/src/ssr/loadOptions.ts b/packages/react-router/src/server/loadOptions.ts similarity index 90% rename from packages/react-router/src/ssr/loadOptions.ts rename to packages/react-router/src/server/loadOptions.ts index 969cce2dd12..6c64a7face8 100644 --- a/packages/react-router/src/ssr/loadOptions.ts +++ b/packages/react-router/src/server/loadOptions.ts @@ -4,14 +4,17 @@ import { getEnvVariable } from '@clerk/shared/getEnvVariable'; import { isDevelopmentFromSecretKey } from '@clerk/shared/keys'; import { isHttpOrHttps, isProxyUrlRelative } from '@clerk/shared/proxy'; import { handleValueOrFn } from '@clerk/shared/utils'; -import type { LoaderFunctionArgs } from 'react-router'; +import type { MiddlewareFunction } from 'react-router'; import { getPublicEnvVariables } from '../utils/env'; import { noSecretKeyError, satelliteAndMissingProxyUrlAndDomain, satelliteAndMissingSignInUrl } from '../utils/errors'; -import type { RootAuthLoaderOptions } from './types'; +import type { ClerkMiddlewareOptions } from './types'; import { patchRequest } from './utils'; -export const loadOptions = (args: LoaderFunctionArgs, overrides: RootAuthLoaderOptions = {}) => { +export type DataFunctionArgs = Parameters>[0]; + +export const loadOptions = (args: DataFunctionArgs, overrides: ClerkMiddlewareOptions = {}) => { + // see https://developers.cloudflare.com/workers/framework-guides/web-apps/react-router/#use-bindings-with-react-router const { request, context } = args; const clerkRequest = createClerkRequest(patchRequest(request)); diff --git a/packages/react-router/src/server/rootAuthLoader.ts b/packages/react-router/src/server/rootAuthLoader.ts new file mode 100644 index 00000000000..b3e108ca85b --- /dev/null +++ b/packages/react-router/src/server/rootAuthLoader.ts @@ -0,0 +1,172 @@ +import type { RequestState } from '@clerk/backend/internal'; +import { decorateObjectWithResources } from '@clerk/backend/internal'; +import { logger } from '@clerk/shared/logger'; +import type { LoaderFunctionArgs } from 'react-router'; + +import { invalidRootLoaderCallbackReturn, middlewareMigrationWarning } from '../utils/errors'; +import { authFnContext, requestStateContext } from './clerkMiddleware'; +import { legacyAuthenticateRequest } from './legacyAuthenticateRequest'; +import { loadOptions } from './loadOptions'; +import type { + LoaderFunctionArgsWithAuth, + LoaderFunctionReturn, + RootAuthLoaderCallback, + RootAuthLoaderOptions, +} from './types'; +import { + getResponseClerkState, + injectRequestStateIntoResponse, + isDataWithResponseInit, + IsOptIntoMiddleware, + isRedirect, + isResponse, +} from './utils'; + +interface RootAuthLoader { + >( + /** + * Arguments passed to the loader function. + */ + args: LoaderFunctionArgs, + /** + * A loader function with authentication state made available to it. Allows you to fetch route data based on the user's authentication state. + */ + callback: Callback, + options?: Options, + ): Promise>; + + (args: LoaderFunctionArgs, options?: RootAuthLoaderOptions): Promise; +} + +/** + * Shared logic for processing the root auth loader with a given request state + */ +async function processRootAuthLoader( + args: LoaderFunctionArgs, + requestState: RequestState, + handler?: RootAuthLoaderCallback, +): Promise { + const hasMiddleware = IsOptIntoMiddleware(args.context) && !!args.context.get(authFnContext); + const includeClerkHeaders = !hasMiddleware; + + if (!handler) { + // if the user did not provide a handler, simply inject requestState into an empty response + const { clerkState } = getResponseClerkState(requestState, args.context); + return { + ...clerkState, + }; + } + + // Create args that has the auth object in the request for backward compatibility + const argsWithAuth = { + ...args, + request: Object.assign(args.request, { auth: requestState.toAuth() }), + } as LoaderFunctionArgsWithAuth; + + const handlerResult = await handler(argsWithAuth); + + if (isResponse(handlerResult)) { + try { + // respect and pass-through any redirects without modifying them + if (isRedirect(handlerResult)) { + return handlerResult; + } + // clone and try to inject requestState into all json-like responses + // if this fails, the user probably didn't return a json object or a valid json string + return injectRequestStateIntoResponse(handlerResult, requestState, args.context, includeClerkHeaders); + } catch { + throw new Error(invalidRootLoaderCallbackReturn); + } + } + + if (isDataWithResponseInit(handlerResult)) { + try { + // clone and try to inject requestState into all json-like responses + // if this fails, the user probably didn't return a json object or a valid json string + return injectRequestStateIntoResponse( + new Response(JSON.stringify(handlerResult.data), handlerResult.init ?? undefined), + requestState, + args.context, + includeClerkHeaders, + ); + } catch { + throw new Error(invalidRootLoaderCallbackReturn); + } + } + + // If the return value of the user's handler is null or a plain object + if (includeClerkHeaders) { + // Legacy path: return Response with headers + const responseBody = JSON.stringify(handlerResult ?? {}); + return injectRequestStateIntoResponse(new Response(responseBody), requestState, args.context, includeClerkHeaders); + } + + // Middleware path: return plain object with streaming support + const { clerkState } = getResponseClerkState(requestState, args.context); + + return { + ...(handlerResult ?? {}), + ...clerkState, + }; +} + +/** + * Makes authorization state available in your application by wrapping the root loader. + * + * @see https://clerk.com/docs/references/react-router/root-auth-loader + */ +export const rootAuthLoader: RootAuthLoader = async ( + args: LoaderFunctionArgs, + handlerOrOptions: any, + options?: any, +): Promise => { + const handler = typeof handlerOrOptions === 'function' ? handlerOrOptions : undefined; + const opts: RootAuthLoaderOptions = options + ? options + : !!handlerOrOptions && typeof handlerOrOptions !== 'function' + ? handlerOrOptions + : {}; + + const hasMiddlewareFlag = IsOptIntoMiddleware(args.context); + const requestState = hasMiddlewareFlag && args.context.get(requestStateContext); + + if (!requestState) { + logger.warnOnce(middlewareMigrationWarning); + return legacyRootAuthLoader(args, handlerOrOptions, opts); + } + + return processRootAuthLoader(args, requestState, handler); +}; + +/** + * Legacy implementation that authenticates requests without middleware. + * This maintains backward compatibility for users who haven't migrated to the new middleware system. + */ +const legacyRootAuthLoader: RootAuthLoader = async ( + args: LoaderFunctionArgs, + handlerOrOptions: any, + options?: any, +): Promise => { + const handler = typeof handlerOrOptions === 'function' ? handlerOrOptions : undefined; + const opts: RootAuthLoaderOptions = options + ? options + : !!handlerOrOptions && typeof handlerOrOptions !== 'function' + ? handlerOrOptions + : {}; + + const loadedOptions = loadOptions(args, opts); + // Note: legacyAuthenticateRequest() will throw a redirect if the auth state is determined to be handshake + const _requestState = await legacyAuthenticateRequest(args, loadedOptions); + const requestState = { ...loadedOptions, ..._requestState }; + + if (!handler) { + // if the user did not provide a handler, simply inject requestState into an empty response + return injectRequestStateIntoResponse(new Response(JSON.stringify({})), requestState, args.context, true); + } + + const authObj = requestState.toAuth(); + const requestWithAuth = Object.assign(args.request, { auth: authObj }); + await decorateObjectWithResources(requestWithAuth, authObj, loadedOptions); + + return processRootAuthLoader(args, requestState, handler); +}; diff --git a/packages/react-router/src/ssr/types.ts b/packages/react-router/src/server/types.ts similarity index 96% rename from packages/react-router/src/ssr/types.ts rename to packages/react-router/src/server/types.ts index 87d54d5994a..9f8e0847496 100644 --- a/packages/react-router/src/ssr/types.ts +++ b/packages/react-router/src/server/types.ts @@ -12,7 +12,7 @@ import type { LoaderFunction, LoaderFunctionArgs, UNSAFE_DataWithResponseInit } export type GetAuthReturn = Promise; -export type RootAuthLoaderOptions = { +export type ClerkMiddlewareOptions = { /** * Used to override the default VITE_CLERK_PUBLISHABLE_KEY env variable if needed. */ @@ -29,6 +29,17 @@ export type RootAuthLoaderOptions = { * Used to override the CLERK_MACHINE_SECRET_KEY env variable if needed. */ machineSecretKey?: string; + signInUrl?: string; + signUpUrl?: string; +} & Pick & + MultiDomainAndOrProxy & + SignInForceRedirectUrl & + SignInFallbackRedirectUrl & + SignUpForceRedirectUrl & + SignUpFallbackRedirectUrl & + LegacyRedirectProps; + +export type RootAuthLoaderOptions = ClerkMiddlewareOptions & { /** * @deprecated Use [session token claims](https://clerk.com/docs/backend-requests/making/custom-session-token) instead. */ @@ -41,15 +52,7 @@ export type RootAuthLoaderOptions = { * @deprecated Use [session token claims](https://clerk.com/docs/backend-requests/making/custom-session-token) instead. */ loadOrganization?: boolean; - signInUrl?: string; - signUpUrl?: string; -} & Pick & - MultiDomainAndOrProxy & - SignInForceRedirectUrl & - SignInFallbackRedirectUrl & - SignUpForceRedirectUrl & - SignUpFallbackRedirectUrl & - LegacyRedirectProps; +}; export type RequestStateWithRedirectUrls = RequestState & SignInForceRedirectUrl & diff --git a/packages/react-router/src/ssr/utils.ts b/packages/react-router/src/server/utils.ts similarity index 89% rename from packages/react-router/src/ssr/utils.ts rename to packages/react-router/src/server/utils.ts index 376539c3bc9..6b5552da0d6 100644 --- a/packages/react-router/src/ssr/utils.ts +++ b/packages/react-router/src/server/utils.ts @@ -40,10 +40,19 @@ export function assertValidHandlerResult(val: any, error?: string): asserts val } } +/** + * `get` and `set` properties will only be available if v8_middleware flag is enabled + * See: https://reactrouter.com/upgrading/future#futurev8_middleware + */ +export const IsOptIntoMiddleware = (context: AppLoadContext) => { + return 'get' in context && 'set' in context; +}; + export const injectRequestStateIntoResponse = async ( response: Response, requestState: RequestStateWithRedirectUrls, context: AppLoadContext, + includeClerkHeaders = false, ) => { const clone = new Response(response.body, response); const data = await clone.json(); @@ -52,9 +61,13 @@ export const injectRequestStateIntoResponse = async ( // set the correct content-type header in case the user returned a `Response` directly clone.headers.set(constants.Headers.ContentType, constants.ContentTypes.Json); - headers.forEach((value, key) => { - clone.headers.append(key, value); - }); + + // Only add Clerk headers if requested (for legacy mode) + if (includeClerkHeaders) { + headers.forEach((value, key) => { + clone.headers.append(key, value); + }); + } return Response.json({ ...(data || {}), ...clerkState }, clone); }; diff --git a/packages/react-router/src/ssr/getAuth.ts b/packages/react-router/src/ssr/getAuth.ts deleted file mode 100644 index bc488619444..00000000000 --- a/packages/react-router/src/ssr/getAuth.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - type AuthenticateRequestOptions, - type GetAuthFn, - getAuthObjectForAcceptedToken, -} from '@clerk/backend/internal'; -import type { LoaderFunctionArgs } from 'react-router'; - -import { noLoaderArgsPassedInGetAuth } from '../utils/errors'; -import { authenticateRequest } from './authenticateRequest'; -import { loadOptions } from './loadOptions'; -import type { RootAuthLoaderOptions } from './types'; - -type GetAuthOptions = { acceptsToken?: AuthenticateRequestOptions['acceptsToken'] } & Pick< - RootAuthLoaderOptions, - 'secretKey' ->; - -export const getAuth: GetAuthFn = (async ( - args: LoaderFunctionArgs, - opts?: GetAuthOptions, -) => { - if (!args || (args && (!args.request || !args.context))) { - throw new Error(noLoaderArgsPassedInGetAuth); - } - - const { acceptsToken, ...restOptions } = opts || {}; - - const loadedOptions = loadOptions(args, restOptions); - // Note: authenticateRequest() will throw a redirect if the auth state is determined to be handshake - const requestState = await authenticateRequest(args, { - ...loadedOptions, - acceptsToken: 'any', - }); - - const authObject = requestState.toAuth(); - - return getAuthObjectForAcceptedToken({ authObject, acceptsToken }); -}) as GetAuthFn; diff --git a/packages/react-router/src/ssr/index.ts b/packages/react-router/src/ssr/index.ts index fcd02aa9159..28e761a5b0f 100644 --- a/packages/react-router/src/ssr/index.ts +++ b/packages/react-router/src/ssr/index.ts @@ -1,5 +1,18 @@ -export * from './rootAuthLoader'; -export * from './getAuth'; +export { rootAuthLoader } from '../server/rootAuthLoader'; +export { getAuth } from '../server/getAuth'; +import { logger } from '@clerk/shared/logger'; + +logger.warnOnce(` +Clerk - DEPRECATION WARNING: \`@clerk/react-router/ssr.server\` has been deprecated and will be removed in the next major version. + +Import from \`@clerk/react-router/server\` instead. + +Before: + import { getAuth, clerkMiddleware, rootAuthLoader } from '@clerk/react-router/ssr.server'; + +After: + import { getAuth, clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server'; +`); /** * Re-export resource types from @clerk/backend diff --git a/packages/react-router/src/ssr/rootAuthLoader.ts b/packages/react-router/src/ssr/rootAuthLoader.ts deleted file mode 100644 index 688df50fbdf..00000000000 --- a/packages/react-router/src/ssr/rootAuthLoader.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { decorateObjectWithResources } from '@clerk/backend/internal'; -import type { LoaderFunctionArgs } from 'react-router'; - -import { invalidRootLoaderCallbackReturn } from '../utils/errors'; -import { authenticateRequest } from './authenticateRequest'; -import { loadOptions } from './loadOptions'; -import type { LoaderFunctionReturn, RootAuthLoaderCallback, RootAuthLoaderOptions } from './types'; -import { - assertValidHandlerResult, - injectRequestStateIntoResponse, - isDataWithResponseInit, - isRedirect, - isResponse, -} from './utils'; - -interface RootAuthLoader { - >( - /** - * Arguments passed to the loader function. - */ - args: LoaderFunctionArgs, - /** - * A loader function with authentication state made available to it. Allows you to fetch route data based on the user's authentication state. - */ - callback: Callback, - options?: Options, - ): Promise>; - - (args: LoaderFunctionArgs, options?: RootAuthLoaderOptions): Promise; -} - -/** - * Makes authorization state available in your application by wrapping the root loader. - * - * @see https://clerk.com/docs/quickstarts/react-router - */ -export const rootAuthLoader: RootAuthLoader = async ( - args: LoaderFunctionArgs, - handlerOrOptions: any, - options?: any, -): Promise => { - const handler = typeof handlerOrOptions === 'function' ? handlerOrOptions : undefined; - const opts: RootAuthLoaderOptions = options - ? options - : !!handlerOrOptions && typeof handlerOrOptions !== 'function' - ? handlerOrOptions - : {}; - - const loadedOptions = loadOptions(args, opts); - // Note: authenticateRequest() will throw a redirect if the auth state is determined to be handshake - const _requestState = await authenticateRequest(args, loadedOptions); - // TODO: Investigate if `authenticateRequest` needs to return the loadedOptions (the new request urls in particular) - const requestState = { ...loadedOptions, ..._requestState }; - - if (!handler) { - // if the user did not provide a handler, simply inject requestState into an empty response - return injectRequestStateIntoResponse(new Response(JSON.stringify({})), requestState, args.context); - } - - const authObj = requestState.toAuth(); - const requestWithAuth = Object.assign(args.request, { auth: authObj }); - await decorateObjectWithResources(requestWithAuth, authObj, loadedOptions); - const handlerResult = await handler(args); - assertValidHandlerResult(handlerResult, invalidRootLoaderCallbackReturn); - - if (isResponse(handlerResult)) { - try { - // respect and pass-through any redirects without modifying them - if (isRedirect(handlerResult)) { - return handlerResult; - } - // clone and try to inject requestState into all json-like responses - // if this fails, the user probably didn't return a json object or a valid json string - return injectRequestStateIntoResponse(handlerResult, requestState, args.context); - } catch { - throw new Error(invalidRootLoaderCallbackReturn); - } - } - - if (isDataWithResponseInit(handlerResult)) { - try { - // clone and try to inject requestState into all json-like responses - // if this fails, the user probably didn't return a json object or a valid json string - return injectRequestStateIntoResponse( - new Response(JSON.stringify(handlerResult.data), handlerResult.init ?? undefined), - requestState, - args.context, - ); - } catch { - throw new Error(invalidRootLoaderCallbackReturn); - } - } - - // if the return value of the user's handler is null or a plain object, create an empty response to inject Clerk's state into - const responseBody = JSON.stringify(handlerResult ?? {}); - - return injectRequestStateIntoResponse(new Response(responseBody), requestState, args.context); -}; diff --git a/packages/react-router/src/utils/errors.ts b/packages/react-router/src/utils/errors.ts index 8e0a7682f2b..be88c9880ae 100644 --- a/packages/react-router/src/utils/errors.ts +++ b/packages/react-router/src/utils/errors.ts @@ -94,3 +94,38 @@ Example: `); + +const middlewareMigrationExample = `To use the new middleware system, you need to: + +1. Enable the 'v8_middleware' future flag in your config: + +// react-router.config.ts +export default { + future: { + v8_middleware: true, + }, +} satisfies Config; + +2. Install the clerkMiddleware: + +import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server' +import { ClerkProvider } from '@clerk/react-router' + +export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()] + +export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args) + +export default function App({ loaderData }: Route.ComponentProps) { + return ( + + + + ) +} +`; + +export const middlewareMigrationWarning = createErrorMessage(` +'"clerkMiddleware()" not detected. + +${middlewareMigrationExample} +`); From f77df91f90f40b238a0cb1cc9b7d512149292943 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 22:44:03 +0000 Subject: [PATCH 17/33] chore(repo): Update pnpm to v10.17.1 (#6847) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jacek Radko --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ba85402ae45..ce30eb562e0 100644 --- a/package.json +++ b/package.json @@ -152,10 +152,10 @@ "yalc": "1.0.0-pre.53", "zx": "catalog:repo" }, - "packageManager": "pnpm@10.17.0", + "packageManager": "pnpm@10.17.1", "engines": { "node": ">=18.17.0", - "pnpm": ">=10.17.0" + "pnpm": ">=10.17.1" }, "pnpm": { "onlyBuiltDependencies": [ From 900ac85f1551d489c9161b724ce963d239e80df9 Mon Sep 17 00:00:00 2001 From: Clerk Cookie <136073014+clerk-cookie@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:04:04 -0400 Subject: [PATCH 18/33] ci(repo): Version packages (#6844) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/empty-melons-beg.md | 11 ---- .changeset/fast-dolls-float.md | 5 -- .changeset/fuzzy-books-win.md | 2 - .changeset/lucky-showers-guess.md | 5 -- .changeset/mean-bikes-stop.md | 2 - .changeset/mighty-frogs-brake.md | 2 - .changeset/nice-moons-repeat.md | 2 - .changeset/pretty-rings-compare.md | 5 -- .changeset/purple-apples-read.md | 13 ----- .changeset/quiet-bats-protect.md | 54 ------------------ .changeset/silly-rooms-sleep.md | 5 -- .changeset/silver-tools-clean.md | 5 -- .changeset/slow-rules-travel.md | 2 - .changeset/tangy-bees-follow.md | 5 -- .changeset/wild-fans-love.md | 2 - .changeset/wild-shoes-divide.md | 5 -- .changeset/yummy-years-feel.md | 2 - packages/agent-toolkit/CHANGELOG.md | 9 +++ packages/agent-toolkit/package.json | 2 +- packages/astro/CHANGELOG.md | 9 +++ packages/astro/package.json | 2 +- packages/backend/CHANGELOG.md | 14 +++++ packages/backend/package.json | 2 +- packages/chrome-extension/CHANGELOG.md | 13 +++++ packages/chrome-extension/package.json | 2 +- packages/clerk-js/CHANGELOG.md | 17 ++++++ packages/clerk-js/package.json | 2 +- packages/elements/CHANGELOG.md | 9 +++ packages/elements/package.json | 2 +- packages/expo-passkeys/CHANGELOG.md | 8 +++ packages/expo-passkeys/package.json | 2 +- packages/expo/CHANGELOG.md | 10 ++++ packages/expo/package.json | 2 +- packages/express/CHANGELOG.md | 9 +++ packages/express/package.json | 2 +- packages/fastify/CHANGELOG.md | 9 +++ packages/fastify/package.json | 2 +- packages/localizations/CHANGELOG.md | 7 +++ packages/localizations/package.json | 2 +- packages/nextjs/CHANGELOG.md | 18 ++++++ packages/nextjs/package.json | 2 +- packages/nuxt/CHANGELOG.md | 10 ++++ packages/nuxt/package.json | 2 +- packages/react-router/CHANGELOG.md | 65 ++++++++++++++++++++++ packages/react-router/package.json | 2 +- packages/react/CHANGELOG.md | 14 +++++ packages/react/package.json | 2 +- packages/remix/CHANGELOG.md | 14 +++++ packages/remix/package.json | 2 +- packages/shared/CHANGELOG.md | 15 +++++ packages/shared/package.json | 2 +- packages/tanstack-react-start/CHANGELOG.md | 16 ++++++ packages/tanstack-react-start/package.json | 2 +- packages/testing/CHANGELOG.md | 13 +++++ packages/testing/package.json | 2 +- packages/themes/CHANGELOG.md | 7 +++ packages/themes/package.json | 2 +- packages/types/CHANGELOG.md | 8 +++ packages/types/package.json | 2 +- packages/vue/CHANGELOG.md | 14 +++++ packages/vue/package.json | 2 +- 61 files changed, 330 insertions(+), 149 deletions(-) delete mode 100644 .changeset/empty-melons-beg.md delete mode 100644 .changeset/fast-dolls-float.md delete mode 100644 .changeset/fuzzy-books-win.md delete mode 100644 .changeset/lucky-showers-guess.md delete mode 100644 .changeset/mean-bikes-stop.md delete mode 100644 .changeset/mighty-frogs-brake.md delete mode 100644 .changeset/nice-moons-repeat.md delete mode 100644 .changeset/pretty-rings-compare.md delete mode 100644 .changeset/purple-apples-read.md delete mode 100644 .changeset/quiet-bats-protect.md delete mode 100644 .changeset/silly-rooms-sleep.md delete mode 100644 .changeset/silver-tools-clean.md delete mode 100644 .changeset/slow-rules-travel.md delete mode 100644 .changeset/tangy-bees-follow.md delete mode 100644 .changeset/wild-fans-love.md delete mode 100644 .changeset/wild-shoes-divide.md delete mode 100644 .changeset/yummy-years-feel.md diff --git a/.changeset/empty-melons-beg.md b/.changeset/empty-melons-beg.md deleted file mode 100644 index bb040006d7d..00000000000 --- a/.changeset/empty-melons-beg.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@clerk/clerk-js': minor -'@clerk/backend': minor -'@clerk/nextjs': minor -'@clerk/shared': minor -'@clerk/clerk-react': minor -'@clerk/types': minor -'@clerk/vue': minor ---- - -Udpate Tyepdoc links to fix temporary ignore warnings diff --git a/.changeset/fast-dolls-float.md b/.changeset/fast-dolls-float.md deleted file mode 100644 index 9a5431b335d..00000000000 --- a/.changeset/fast-dolls-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@clerk/tanstack-react-start": minor ---- - -Constrained TanStack dependencies to pre-RC versions. Please ensure you're using `@tanstack/*` versions below `1.132.0`. diff --git a/.changeset/fuzzy-books-win.md b/.changeset/fuzzy-books-win.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/fuzzy-books-win.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/lucky-showers-guess.md b/.changeset/lucky-showers-guess.md deleted file mode 100644 index 3a21df15d8d..00000000000 --- a/.changeset/lucky-showers-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@clerk/clerk-js': patch ---- - -Update active context cookie to properly set `Secure` attribute. diff --git a/.changeset/mean-bikes-stop.md b/.changeset/mean-bikes-stop.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/mean-bikes-stop.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/mighty-frogs-brake.md b/.changeset/mighty-frogs-brake.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/mighty-frogs-brake.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/nice-moons-repeat.md b/.changeset/nice-moons-repeat.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/nice-moons-repeat.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/pretty-rings-compare.md b/.changeset/pretty-rings-compare.md deleted file mode 100644 index a46279fd7b8..00000000000 --- a/.changeset/pretty-rings-compare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@clerk/shared": patch ---- - -Increase sampling for high-signal auth components on mount. diff --git a/.changeset/purple-apples-read.md b/.changeset/purple-apples-read.md deleted file mode 100644 index 110f645d9d2..00000000000 --- a/.changeset/purple-apples-read.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@clerk/tanstack-react-start': minor -'@clerk/chrome-extension': minor -'@clerk/react-router': minor -'@clerk/clerk-js': minor -'@clerk/nextjs': minor -'@clerk/clerk-react': minor -'@clerk/remix': minor -'@clerk/types': minor -'@clerk/vue': minor ---- - -Add new component diff --git a/.changeset/quiet-bats-protect.md b/.changeset/quiet-bats-protect.md deleted file mode 100644 index 73f732b19fe..00000000000 --- a/.changeset/quiet-bats-protect.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -'@clerk/react-router': major ---- - -Introduce [React Router middleware](https://reactrouter.com/how-to/middleware) support with `clerkMiddleware()` for improved performance and streaming capabilities. - -Usage of `rootAuthLoader` without the `clerkMiddleware()` installed is now deprecated and will be removed in the next major version. - -**Before (Deprecated - will be removed):** - -```tsx -import { rootAuthLoader } from '@clerk/react-router/ssr.server' - -export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args) -``` - -**After (Recommended):** - -1. Enable the `v8_middleware` future flag: - -```ts -// react-router.config.ts -export default { - future: { - v8_middleware: true, - }, -} satisfies Config; -``` - -2. Use the middleware in your app: - -```tsx -import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server' - -export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()] - -export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args) -``` - -**Streaming Support (with middleware):** - -```tsx -export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()] - -export const loader = (args: Route.LoaderArgs) => { - const nonCriticalData = new Promise((res) => - setTimeout(() => res('non-critical'), 5000), - ) - - return rootAuthLoader(args, () => ({ - nonCriticalData - })) -} -``` \ No newline at end of file diff --git a/.changeset/silly-rooms-sleep.md b/.changeset/silly-rooms-sleep.md deleted file mode 100644 index d217e19ef8d..00000000000 --- a/.changeset/silly-rooms-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@clerk/backend': minor ---- - -[Billing Beta] Update subscription item `plan` and `planId` properties to be `null`. diff --git a/.changeset/silver-tools-clean.md b/.changeset/silver-tools-clean.md deleted file mode 100644 index 8580550fcad..00000000000 --- a/.changeset/silver-tools-clean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@clerk/testing': minor ---- - -Add Playwright testing helpers under unstable page-objects: `userAvatar.goTo()`, `userAvatar.waitForMounted()`, and `userAvatar.toBeVisible()` for . diff --git a/.changeset/slow-rules-travel.md b/.changeset/slow-rules-travel.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/slow-rules-travel.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/tangy-bees-follow.md b/.changeset/tangy-bees-follow.md deleted file mode 100644 index 2f0b945d942..00000000000 --- a/.changeset/tangy-bees-follow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@clerk/shared': minor ---- - -Improve error handling when loading clerk-js. diff --git a/.changeset/wild-fans-love.md b/.changeset/wild-fans-love.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/wild-fans-love.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/wild-shoes-divide.md b/.changeset/wild-shoes-divide.md deleted file mode 100644 index 8cdf0600ce1..00000000000 --- a/.changeset/wild-shoes-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@clerk/nextjs": patch ---- - -feat(nextjs): Add CI environment detection header for Next.js keyless app creation diff --git a/.changeset/yummy-years-feel.md b/.changeset/yummy-years-feel.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/yummy-years-feel.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/packages/agent-toolkit/CHANGELOG.md b/packages/agent-toolkit/CHANGELOG.md index a1d4545e1b7..72c9ae40462 100644 --- a/packages/agent-toolkit/CHANGELOG.md +++ b/packages/agent-toolkit/CHANGELOG.md @@ -1,5 +1,14 @@ # @clerk/agent-toolkit +## 0.1.35 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 0.1.34 ### Patch Changes diff --git a/packages/agent-toolkit/package.json b/packages/agent-toolkit/package.json index 0b028247b85..116c628a0be 100644 --- a/packages/agent-toolkit/package.json +++ b/packages/agent-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/agent-toolkit", - "version": "0.1.34", + "version": "0.1.35", "description": "Clerk Toolkit for AI Agents", "homepage": "https://clerk.com/", "bugs": { diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index 91edf693aa4..7f8c09a5244 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,14 @@ # @clerk/astro +## 2.13.3 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 2.13.2 ### Patch Changes diff --git a/packages/astro/package.json b/packages/astro/package.json index 74f6e82e7dd..d237428a38b 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/astro", - "version": "2.13.2", + "version": "2.13.3", "description": "Clerk SDK for Astro", "keywords": [ "auth", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 2509a301b03..1ec19a9b52d 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## 2.16.0 + +### Minor Changes + +- Udpate Tyepdoc links to fix temporary ignore warnings ([#6846](https://github.com/clerk/javascript/pull/6846)) by [@SarahSoutoul](https://github.com/SarahSoutoul) + +- [Billing Beta] Update subscription item `plan` and `planId` properties to be `null`. ([#6839](https://github.com/clerk/javascript/pull/6839)) by [@paddycarver](https://github.com/paddycarver) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 2.15.0 ### Minor Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 333086fc82e..fd5048b92f9 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/backend", - "version": "2.15.0", + "version": "2.16.0", "description": "Clerk Backend SDK - REST Client for Backend API & JWT verification utilities", "homepage": "https://clerk.com/", "bugs": { diff --git a/packages/chrome-extension/CHANGELOG.md b/packages/chrome-extension/CHANGELOG.md index d4d00169b5b..9c3cd102cca 100644 --- a/packages/chrome-extension/CHANGELOG.md +++ b/packages/chrome-extension/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## 2.7.0 + +### Minor Changes + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`33efaad`](https://github.com/clerk/javascript/commit/33efaad1479daaffd125e3e380c791e70f18d43f), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/clerk-js@5.96.0 + - @clerk/shared@3.27.0 + - @clerk/clerk-react@5.49.0 + ## 2.6.2 ### Patch Changes diff --git a/packages/chrome-extension/package.json b/packages/chrome-extension/package.json index ea2f391d58c..d68c0bb83d0 100644 --- a/packages/chrome-extension/package.json +++ b/packages/chrome-extension/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/chrome-extension", - "version": "2.6.2", + "version": "2.7.0", "description": "Clerk SDK for Chrome extensions", "keywords": [ "auth", diff --git a/packages/clerk-js/CHANGELOG.md b/packages/clerk-js/CHANGELOG.md index b29094b0725..a7feaed421e 100644 --- a/packages/clerk-js/CHANGELOG.md +++ b/packages/clerk-js/CHANGELOG.md @@ -1,5 +1,22 @@ # Change Log +## 5.96.0 + +### Minor Changes + +- Udpate Tyepdoc links to fix temporary ignore warnings ([#6846](https://github.com/clerk/javascript/pull/6846)) by [@SarahSoutoul](https://github.com/SarahSoutoul) + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Update active context cookie to properly set `Secure` attribute. ([#6851](https://github.com/clerk/javascript/pull/6851)) by [@brkalow](https://github.com/brkalow) + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + - @clerk/localizations@3.25.4 + ## 5.95.0 ### Minor Changes diff --git a/packages/clerk-js/package.json b/packages/clerk-js/package.json index 3155f1e1333..1aa363e319a 100644 --- a/packages/clerk-js/package.json +++ b/packages/clerk-js/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/clerk-js", - "version": "5.95.0", + "version": "5.96.0", "description": "Clerk JS library", "keywords": [ "clerk", diff --git a/packages/elements/CHANGELOG.md b/packages/elements/CHANGELOG.md index c466069ef2c..6875bb495a0 100644 --- a/packages/elements/CHANGELOG.md +++ b/packages/elements/CHANGELOG.md @@ -1,5 +1,14 @@ # @clerk/elements +## 0.23.66 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/shared@3.27.0 + - @clerk/clerk-react@5.49.0 + - @clerk/types@4.89.0 + ## 0.23.65 ### Patch Changes diff --git a/packages/elements/package.json b/packages/elements/package.json index c08deca5af9..f19e2bf0e2c 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/elements", - "version": "0.23.65", + "version": "0.23.66", "description": "Clerk Elements", "keywords": [ "clerk", diff --git a/packages/expo-passkeys/CHANGELOG.md b/packages/expo-passkeys/CHANGELOG.md index 76d8c5080f8..2a68768f3f1 100644 --- a/packages/expo-passkeys/CHANGELOG.md +++ b/packages/expo-passkeys/CHANGELOG.md @@ -1,5 +1,13 @@ # @clerk/expo-passkeys +## 0.4.3 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 0.4.2 ### Patch Changes diff --git a/packages/expo-passkeys/package.json b/packages/expo-passkeys/package.json index a1bba11903f..912aa6e7578 100644 --- a/packages/expo-passkeys/package.json +++ b/packages/expo-passkeys/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/expo-passkeys", - "version": "0.4.2", + "version": "0.4.3", "description": "Passkeys library to be used with Clerk for expo", "keywords": [ "react-native", diff --git a/packages/expo/CHANGELOG.md b/packages/expo/CHANGELOG.md index dec330aabdd..332988e9b31 100644 --- a/packages/expo/CHANGELOG.md +++ b/packages/expo/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## 2.15.3 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`33efaad`](https://github.com/clerk/javascript/commit/33efaad1479daaffd125e3e380c791e70f18d43f), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/clerk-js@5.96.0 + - @clerk/shared@3.27.0 + - @clerk/clerk-react@5.49.0 + - @clerk/types@4.89.0 + ## 2.15.2 ### Patch Changes diff --git a/packages/expo/package.json b/packages/expo/package.json index 46c38399f37..cec2a8206e2 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/clerk-expo", - "version": "2.15.2", + "version": "2.15.3", "description": "Clerk React Native/Expo library", "keywords": [ "react", diff --git a/packages/express/CHANGELOG.md b/packages/express/CHANGELOG.md index 38b8ed0def7..61ce3ef8981 100644 --- a/packages/express/CHANGELOG.md +++ b/packages/express/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 1.7.34 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 1.7.33 ### Patch Changes diff --git a/packages/express/package.json b/packages/express/package.json index 6a196c5fee2..7311c26b2a1 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/express", - "version": "1.7.33", + "version": "1.7.34", "description": "Clerk server SDK for usage with Express", "keywords": [ "clerk", diff --git a/packages/fastify/CHANGELOG.md b/packages/fastify/CHANGELOG.md index 6176c93b682..e08b1c4f456 100644 --- a/packages/fastify/CHANGELOG.md +++ b/packages/fastify/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 2.4.34 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 2.4.33 ### Patch Changes diff --git a/packages/fastify/package.json b/packages/fastify/package.json index d419b0827eb..d8245d9318b 100644 --- a/packages/fastify/package.json +++ b/packages/fastify/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/fastify", - "version": "2.4.33", + "version": "2.4.34", "description": "Clerk SDK for Fastify", "keywords": [ "auth", diff --git a/packages/localizations/CHANGELOG.md b/packages/localizations/CHANGELOG.md index 4f6f2cac957..fbb0e741128 100644 --- a/packages/localizations/CHANGELOG.md +++ b/packages/localizations/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 3.25.4 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4)]: + - @clerk/types@4.89.0 + ## 3.25.3 ### Patch Changes diff --git a/packages/localizations/package.json b/packages/localizations/package.json index c1dac29c375..db54a528c8e 100644 --- a/packages/localizations/package.json +++ b/packages/localizations/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/localizations", - "version": "3.25.3", + "version": "3.25.4", "description": "Localizations for the Clerk components", "keywords": [ "react", diff --git a/packages/nextjs/CHANGELOG.md b/packages/nextjs/CHANGELOG.md index 518c4a5c54f..107c7c1516f 100644 --- a/packages/nextjs/CHANGELOG.md +++ b/packages/nextjs/CHANGELOG.md @@ -1,5 +1,23 @@ # Change Log +## 6.33.0 + +### Minor Changes + +- Udpate Tyepdoc links to fix temporary ignore warnings ([#6846](https://github.com/clerk/javascript/pull/6846)) by [@SarahSoutoul](https://github.com/SarahSoutoul) + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- feat(nextjs): Add CI environment detection header for Next.js keyless app creation ([#6852](https://github.com/clerk/javascript/pull/6852)) by [@heatlikeheatwave](https://github.com/heatlikeheatwave) + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/clerk-react@5.49.0 + - @clerk/types@4.89.0 + ## 6.32.2 ### Patch Changes diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index c6d5c825e86..2d97c37776e 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/nextjs", - "version": "6.32.2", + "version": "6.33.0", "description": "Clerk SDK for NextJS", "keywords": [ "clerk", diff --git a/packages/nuxt/CHANGELOG.md b/packages/nuxt/CHANGELOG.md index d8112f5ca00..41eda8c94ce 100644 --- a/packages/nuxt/CHANGELOG.md +++ b/packages/nuxt/CHANGELOG.md @@ -1,5 +1,15 @@ # @clerk/nuxt +## 1.9.1 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + - @clerk/vue@1.14.0 + ## 1.9.0 ### Minor Changes diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index ba183fc3c2a..9cb31664b1c 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/nuxt", - "version": "1.9.0", + "version": "1.9.1", "description": "Clerk SDK for Nuxt", "keywords": [ "clerk", diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index 24f46d687e7..74d813f4593 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,70 @@ # Change Log +## 2.0.0 + +### Major Changes + +- Introduce [React Router middleware](https://reactrouter.com/how-to/middleware) support with `clerkMiddleware()` for improved performance and streaming capabilities. ([#6660](https://github.com/clerk/javascript/pull/6660)) by [@wobsoriano](https://github.com/wobsoriano) + + Usage of `rootAuthLoader` without the `clerkMiddleware()` installed is now deprecated and will be removed in the next major version. + + **Before (Deprecated - will be removed):** + + ```tsx + import { rootAuthLoader } from '@clerk/react-router/ssr.server'; + + export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args); + ``` + + **After (Recommended):** + + 1. Enable the `v8_middleware` future flag: + + ```ts + // react-router.config.ts + export default { + future: { + v8_middleware: true, + }, + } satisfies Config; + ``` + + 2. Use the middleware in your app: + + ```tsx + import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server'; + + export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]; + + export const loader = (args: Route.LoaderArgs) => rootAuthLoader(args); + ``` + + **Streaming Support (with middleware):** + + ```tsx + export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]; + + export const loader = (args: Route.LoaderArgs) => { + const nonCriticalData = new Promise(res => setTimeout(() => res('non-critical'), 5000)); + + return rootAuthLoader(args, () => ({ + nonCriticalData, + })); + }; + ``` + +### Minor Changes + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/clerk-react@5.49.0 + - @clerk/types@4.89.0 + ## 1.10.2 ### Patch Changes diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 9d0922abfc4..fdf9557f301 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/react-router", - "version": "1.10.2", + "version": "2.0.0", "description": "Clerk SDK for React Router", "keywords": [ "clerk", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 0e09e1640be..576fd69da0a 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## 5.49.0 + +### Minor Changes + +- Udpate Tyepdoc links to fix temporary ignore warnings ([#6846](https://github.com/clerk/javascript/pull/6846)) by [@SarahSoutoul](https://github.com/SarahSoutoul) + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 5.48.1 ### Patch Changes diff --git a/packages/react/package.json b/packages/react/package.json index b2cfba5b476..23758eab9e8 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/clerk-react", - "version": "5.48.1", + "version": "5.49.0", "description": "Clerk React library", "keywords": [ "clerk", diff --git a/packages/remix/CHANGELOG.md b/packages/remix/CHANGELOG.md index 41b0ef1774f..6a97039c517 100644 --- a/packages/remix/CHANGELOG.md +++ b/packages/remix/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## 4.13.0 + +### Minor Changes + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/clerk-react@5.49.0 + - @clerk/types@4.89.0 + ## 4.12.2 ### Patch Changes diff --git a/packages/remix/package.json b/packages/remix/package.json index 3e165a6f11d..0837aa1ced0 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/remix", - "version": "4.12.2", + "version": "4.13.0", "description": "Clerk SDK for Remix", "keywords": [ "clerk", diff --git a/packages/shared/CHANGELOG.md b/packages/shared/CHANGELOG.md index d48a1c85710..d927354b041 100644 --- a/packages/shared/CHANGELOG.md +++ b/packages/shared/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## 3.27.0 + +### Minor Changes + +- Udpate Tyepdoc links to fix temporary ignore warnings ([#6846](https://github.com/clerk/javascript/pull/6846)) by [@SarahSoutoul](https://github.com/SarahSoutoul) + +- Improve error handling when loading clerk-js. ([#6856](https://github.com/clerk/javascript/pull/6856)) by [@brkalow](https://github.com/brkalow) + +### Patch Changes + +- Increase sampling for high-signal auth components on mount. ([#6858](https://github.com/clerk/javascript/pull/6858)) by [@heatlikeheatwave](https://github.com/heatlikeheatwave) + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4)]: + - @clerk/types@4.89.0 + ## 3.26.1 ### Patch Changes diff --git a/packages/shared/package.json b/packages/shared/package.json index 9f050fdbbc6..a51d657a0e4 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/shared", - "version": "3.26.1", + "version": "3.27.0", "description": "Internal package utils used by the Clerk SDKs", "repository": { "type": "git", diff --git a/packages/tanstack-react-start/CHANGELOG.md b/packages/tanstack-react-start/CHANGELOG.md index 3a6e44d4bc0..f44d3bf383f 100644 --- a/packages/tanstack-react-start/CHANGELOG.md +++ b/packages/tanstack-react-start/CHANGELOG.md @@ -1,5 +1,21 @@ # @clerk/tanstack-react-start +## 0.25.0 + +### Minor Changes + +- Constrained TanStack dependencies to pre-RC versions. Please ensure you're using `@tanstack/*` versions below `1.132.0`. ([#6845](https://github.com/clerk/javascript/pull/6845)) by [@wobsoriano](https://github.com/wobsoriano) + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/clerk-react@5.49.0 + - @clerk/types@4.89.0 + ## 0.24.2 ### Patch Changes diff --git a/packages/tanstack-react-start/package.json b/packages/tanstack-react-start/package.json index 9b854867379..cfde3fe122c 100644 --- a/packages/tanstack-react-start/package.json +++ b/packages/tanstack-react-start/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/tanstack-react-start", - "version": "0.24.2", + "version": "0.25.0", "description": "Clerk SDK for TanStack React Start", "keywords": [ "clerk", diff --git a/packages/testing/CHANGELOG.md b/packages/testing/CHANGELOG.md index 47209f73476..c30b6a14d13 100644 --- a/packages/testing/CHANGELOG.md +++ b/packages/testing/CHANGELOG.md @@ -1,5 +1,18 @@ # @clerk/testing +## 1.13.0 + +### Minor Changes + +- Add Playwright testing helpers under unstable page-objects: `userAvatar.goTo()`, `userAvatar.waitForMounted()`, and `userAvatar.toBeVisible()` for . ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`b8fbadd`](https://github.com/clerk/javascript/commit/b8fbadd95652b08ecea23fdbc7e352e3e7297b2d), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/backend@2.16.0 + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 1.12.8 ### Patch Changes diff --git a/packages/testing/package.json b/packages/testing/package.json index aab96bda0b8..782408b2d5f 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/testing", - "version": "1.12.8", + "version": "1.13.0", "description": "Utilities to help you create E2E test suites for apps using Clerk", "keywords": [ "auth", diff --git a/packages/themes/CHANGELOG.md b/packages/themes/CHANGELOG.md index 9aa8911ce50..fc4171622ac 100644 --- a/packages/themes/CHANGELOG.md +++ b/packages/themes/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 2.4.22 + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4)]: + - @clerk/types@4.89.0 + ## 2.4.21 ### Patch Changes diff --git a/packages/themes/package.json b/packages/themes/package.json index 7249db9d2e4..bd562d8e18b 100644 --- a/packages/themes/package.json +++ b/packages/themes/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/themes", - "version": "2.4.21", + "version": "2.4.22", "description": "Themes for the Clerk auth components", "keywords": [ "react", diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 928c61372dc..fa725973672 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## 4.89.0 + +### Minor Changes + +- Udpate Tyepdoc links to fix temporary ignore warnings ([#6846](https://github.com/clerk/javascript/pull/6846)) by [@SarahSoutoul](https://github.com/SarahSoutoul) + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + ## 4.88.0 ### Minor Changes diff --git a/packages/types/package.json b/packages/types/package.json index bd5f002919a..41affb51245 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/types", - "version": "4.88.0", + "version": "4.89.0", "description": "Typings for Clerk libraries.", "keywords": [ "clerk", diff --git a/packages/vue/CHANGELOG.md b/packages/vue/CHANGELOG.md index 0c3583535f0..c283ac6e1af 100644 --- a/packages/vue/CHANGELOG.md +++ b/packages/vue/CHANGELOG.md @@ -1,5 +1,19 @@ # @clerk/vue +## 1.14.0 + +### Minor Changes + +- Udpate Tyepdoc links to fix temporary ignore warnings ([#6846](https://github.com/clerk/javascript/pull/6846)) by [@SarahSoutoul](https://github.com/SarahSoutoul) + +- Add new component ([#6808](https://github.com/clerk/javascript/pull/6808)) by [@tmilewski](https://github.com/tmilewski) + +### Patch Changes + +- Updated dependencies [[`e3e77eb`](https://github.com/clerk/javascript/commit/e3e77eb277c6b36847265db7b863c418e3708ab6), [`9cf89cd`](https://github.com/clerk/javascript/commit/9cf89cd3402c278e8d5bfcd8277cee292bc45333), [`090ca74`](https://github.com/clerk/javascript/commit/090ca742c590bc4f369cf3e1ca2ec9917410ffe4), [`5546352`](https://github.com/clerk/javascript/commit/55463527df9a710ef3215c353bab1ef423d1de62)]: + - @clerk/shared@3.27.0 + - @clerk/types@4.89.0 + ## 1.13.3 ### Patch Changes diff --git a/packages/vue/package.json b/packages/vue/package.json index 9b6c09a0c27..15011e611f6 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@clerk/vue", - "version": "1.13.3", + "version": "1.14.0", "description": "Clerk SDK for Vue", "keywords": [ "clerk", From 01ec8eeb30fc7070789909612c669f68123230da Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 26 Sep 2025 17:44:02 -0700 Subject: [PATCH 19/33] dedupe --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bb2737c099..c608c69a763 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2755,7 +2755,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.22.26': resolution: {integrity: sha512-I689wc8Fn/AX7aUGiwrh3HnssiORMJtR2fpksX+JIe8Cj/EDleblYMSwRPd0025wrwOV9UN1KM/RuEt/QjCS3Q==} From 86cb36924c97231b6350160ec95bf07766295406 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 07:56:29 -0700 Subject: [PATCH 20/33] chore: update tests --- .../templates/tanstack-react-start/package.json | 6 +++--- .../tanstack-react-start/src/routes/user.tsx | 7 ++----- .../templates/tanstack-react-start/src/server.tsx | 14 -------------- .../templates/tanstack-react-start/src/start.ts | 7 +++++++ 4 files changed, 12 insertions(+), 22 deletions(-) delete mode 100644 integration/templates/tanstack-react-start/src/server.tsx create mode 100644 integration/templates/tanstack-react-start/src/start.ts diff --git a/integration/templates/tanstack-react-start/package.json b/integration/templates/tanstack-react-start/package.json index cae4bc234ee..c07c89d0bf0 100644 --- a/integration/templates/tanstack-react-start/package.json +++ b/integration/templates/tanstack-react-start/package.json @@ -8,9 +8,9 @@ "start": "vite start --port=$PORT" }, "dependencies": { - "@tanstack/react-router": "1.131.27", - "@tanstack/react-router-devtools": "1.131.27", - "@tanstack/react-start": "1.131.27", + "@tanstack/react-router": "1.132.37", + "@tanstack/react-router-devtools": "1.132.37", + "@tanstack/react-start": "1.132.38", "react": "18.3.1", "react-dom": "18.3.1", "tailwind-merge": "^2.5.4" diff --git a/integration/templates/tanstack-react-start/src/routes/user.tsx b/integration/templates/tanstack-react-start/src/routes/user.tsx index 4fd5f6ccf2c..8140f5dc03d 100644 --- a/integration/templates/tanstack-react-start/src/routes/user.tsx +++ b/integration/templates/tanstack-react-start/src/routes/user.tsx @@ -1,13 +1,10 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { createServerFn } from '@tanstack/react-start'; import { getAuth } from '@clerk/tanstack-react-start/server'; -import { getWebRequest } from '@tanstack/react-start/server'; +import { getRequest } from '@tanstack/react-start/server'; const fetchClerkAuth = createServerFn({ method: 'GET' }).handler(async () => { - const request = getWebRequest(); - if (!request) throw new Error('No request found'); - - const { userId } = await getAuth(request); + const { userId } = await getAuth(getRequest()); return { userId, diff --git a/integration/templates/tanstack-react-start/src/server.tsx b/integration/templates/tanstack-react-start/src/server.tsx deleted file mode 100644 index 1f4daa692e3..00000000000 --- a/integration/templates/tanstack-react-start/src/server.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { createStartHandler, defineHandlerCallback, defaultStreamHandler } from '@tanstack/react-start/server'; -import { createRouter } from './router'; -import { createClerkHandler } from '@clerk/tanstack-react-start/server'; - -const handlerFactory = createClerkHandler( - createStartHandler({ - createRouter, - }), -); - -export default defineHandlerCallback(async event => { - const startHandler = await handlerFactory(defaultStreamHandler); - return startHandler(event); -}); diff --git a/integration/templates/tanstack-react-start/src/start.ts b/integration/templates/tanstack-react-start/src/start.ts new file mode 100644 index 00000000000..61571c1900b --- /dev/null +++ b/integration/templates/tanstack-react-start/src/start.ts @@ -0,0 +1,7 @@ +import { clerkMiddleware } from '@clerk/tanstack-react-start/server'; + +export const startInstance = createStart(() => { + return { + requestMiddleware: [clerkMiddleware()], + }; +}); From d8a96ab0c6e5535653d969a975918e64f605d997 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Sun, 5 Oct 2025 08:14:28 -0700 Subject: [PATCH 21/33] chore: edit changeset --- .changeset/spotty-cooks-march.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/spotty-cooks-march.md b/.changeset/spotty-cooks-march.md index 994d8fe3caf..55b29094186 100644 --- a/.changeset/spotty-cooks-march.md +++ b/.changeset/spotty-cooks-march.md @@ -2,4 +2,4 @@ "@clerk/tanstack-react-start": minor --- -placeholder +placeholder trigger test From 5427d6e5262d90b6e67642f1348c1282f418de4a Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 08:15:03 -0700 Subject: [PATCH 22/33] chore: remove unused file --- packages/tanstack-react-start/src/server/errors.ts | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 packages/tanstack-react-start/src/server/errors.ts diff --git a/packages/tanstack-react-start/src/server/errors.ts b/packages/tanstack-react-start/src/server/errors.ts deleted file mode 100644 index 4843add9e8a..00000000000 --- a/packages/tanstack-react-start/src/server/errors.ts +++ /dev/null @@ -1,11 +0,0 @@ -export class ClerkHandshakeRedirect extends Error { - constructor( - public status: number, - public headers: Headers, - ) { - super('Clerk handshake redirect required'); - this.name = 'ClerkHandshakeRedirect'; - this.status = status; - this.headers = headers; - } -} From da42e5aba705b7915860e07026b7f5940a4d0e5d Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 08:57:33 -0700 Subject: [PATCH 23/33] chore: set test version to 7 days ago --- .../tanstack-react-start/package.json | 6 +- .../__snapshots__/exports.test.ts.snap | 2 +- .../src/server/clerkMiddleware.ts | 108 +++++++++--------- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/integration/templates/tanstack-react-start/package.json b/integration/templates/tanstack-react-start/package.json index c07c89d0bf0..89f3ed126bd 100644 --- a/integration/templates/tanstack-react-start/package.json +++ b/integration/templates/tanstack-react-start/package.json @@ -8,9 +8,9 @@ "start": "vite start --port=$PORT" }, "dependencies": { - "@tanstack/react-router": "1.132.37", - "@tanstack/react-router-devtools": "1.132.37", - "@tanstack/react-start": "1.132.38", + "@tanstack/react-router": "1.132.23", + "@tanstack/react-router-devtools": "1.132.23", + "@tanstack/react-start": "1.132.23", "react": "18.3.1", "react-dom": "18.3.1", "tailwind-merge": "^2.5.4" diff --git a/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap index f49dd011405..7cf8f38656e 100644 --- a/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap @@ -69,7 +69,7 @@ exports[`root public exports > should not change unexpectedly 1`] = ` exports[`server public exports > should not change unexpectedly 1`] = ` [ "clerkClient", - "createClerkHandler", + "clerkMiddleware", "getAuth", ] `; diff --git a/packages/tanstack-react-start/src/server/clerkMiddleware.ts b/packages/tanstack-react-start/src/server/clerkMiddleware.ts index 9daec9e0736..ecd6d140200 100644 --- a/packages/tanstack-react-start/src/server/clerkMiddleware.ts +++ b/packages/tanstack-react-start/src/server/clerkMiddleware.ts @@ -1,54 +1,54 @@ -import type { RequestState } from '@clerk/backend/internal'; -import { AuthStatus, constants } from '@clerk/backend/internal'; -import { handleNetlifyCacheInDevInstance } from '@clerk/shared/netlifyCacheHandler'; -import type { PendingSessionOptions } from '@clerk/types'; -import type { AnyRequestMiddleware } from '@tanstack/react-start'; -import { createMiddleware, json } from '@tanstack/react-start'; - -import { clerkClient } from './clerkClient'; -import { loadOptions } from './loadOptions'; -import type { ClerkMiddlewareOptions } from './types'; -import { getResponseClerkState } from './utils'; - -export const clerkMiddleware = (options?: ClerkMiddlewareOptions): AnyRequestMiddleware => { - return createMiddleware().server(async args => { - const loadedOptions = loadOptions(args.request, options); - const requestState = await clerkClient().authenticateRequest(args.request, { - ...loadedOptions, - acceptsToken: 'any', - }); - - const locationHeader = requestState.headers.get(constants.Headers.Location); - if (locationHeader) { - handleNetlifyCacheInDevInstance({ - locationHeader, - requestStateHeaders: requestState.headers, - publishableKey: requestState.publishableKey, - }); - // Trigger a handshake redirect - // eslint-disable-next-line @typescript-eslint/only-throw-error - throw json(null, { status: 307, headers: requestState.headers }); - } - - if (requestState.status === AuthStatus.Handshake) { - throw new Error('Clerk: handshake status without redirect'); - } - - const clerkInitialState = getResponseClerkState(requestState as RequestState, loadedOptions); - - const result = await args.next({ - context: { - clerkInitialState, - auth: (opts?: PendingSessionOptions) => requestState.toAuth(opts), - }, - }); - - if (requestState.headers) { - requestState.headers.forEach((value, key) => { - result.response.headers.append(key, value); - }); - } - - return result; - }); -}; +import type { RequestState } from '@clerk/backend/internal'; +import { AuthStatus, constants } from '@clerk/backend/internal'; +import { handleNetlifyCacheInDevInstance } from '@clerk/shared/netlifyCacheHandler'; +import type { PendingSessionOptions } from '@clerk/types'; +import type { AnyRequestMiddleware } from '@tanstack/react-start'; +import { createMiddleware, json } from '@tanstack/react-start'; + +import { clerkClient } from './clerkClient'; +import { loadOptions } from './loadOptions'; +import type { ClerkMiddlewareOptions } from './types'; +import { getResponseClerkState } from './utils'; + +export const clerkMiddleware = (options?: ClerkMiddlewareOptions): AnyRequestMiddleware => { + return createMiddleware().server(async args => { + const loadedOptions = loadOptions(args.request, options); + const requestState = await clerkClient().authenticateRequest(args.request, { + ...loadedOptions, + acceptsToken: 'any', + }); + + const locationHeader = requestState.headers.get(constants.Headers.Location); + if (locationHeader) { + handleNetlifyCacheInDevInstance({ + locationHeader, + requestStateHeaders: requestState.headers, + publishableKey: requestState.publishableKey, + }); + // Trigger a handshake redirect + // eslint-disable-next-line @typescript-eslint/only-throw-error + throw json(null, { status: 307, headers: requestState.headers }); + } + + if (requestState.status === AuthStatus.Handshake) { + throw new Error('Clerk: handshake status without redirect'); + } + + const clerkInitialState = getResponseClerkState(requestState as RequestState, loadedOptions); + + const result = await args.next({ + context: { + clerkInitialState, + auth: (opts?: PendingSessionOptions) => requestState.toAuth(opts), + }, + }); + + if (requestState.headers) { + requestState.headers.forEach((value, key) => { + result.response.headers.append(key, value); + }); + } + + return result; + }); +}; From 33b064b0fcf2c84e346eb0e5d17d112c4c45ab81 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 08:58:53 -0700 Subject: [PATCH 24/33] skip tanstack router test --- integration/tests/tanstack-router/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/tanstack-router/basic.test.ts b/integration/tests/tanstack-router/basic.test.ts index 488db6f3bb0..c72fd98c12b 100644 --- a/integration/tests/tanstack-router/basic.test.ts +++ b/integration/tests/tanstack-router/basic.test.ts @@ -5,7 +5,7 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( - 'basic tests for TanStack Router @tanstack-react-router', + 'basic tests for TanStack Router @xtanstack-react-router', ({ app }) => { test.describe.configure({ mode: 'parallel' }); From 52459e0759be10e5c6462bb223f9ed8efb22f572 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 12:12:32 -0700 Subject: [PATCH 25/33] skip tanstack router test --- integration/presets/react-router.ts | 20 +++++++++---------- integration/templates/index.ts | 2 +- .../tanstack-react-start/src/start.ts | 1 + .../tests/react-router/library-mode.test.ts | 3 ++- .../tests/tanstack-router/basic.test.ts | 2 +- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/integration/presets/react-router.ts b/integration/presets/react-router.ts index 6dc759ddae7..7183f152303 100644 --- a/integration/presets/react-router.ts +++ b/integration/presets/react-router.ts @@ -13,17 +13,17 @@ const reactRouterNode = applicationConfig() .addScript('serve', 'pnpm start') .addDependency('@clerk/react-router', constants.E2E_CLERK_VERSION || linkPackage('react-router')); -const reactRouterLibrary = applicationConfig() - .setName('react-router-library') - .useTemplate(templates['react-router-library']) - .setEnvFormatter('public', key => `VITE_${key}`) - .addScript('setup', 'pnpm install') - .addScript('dev', 'pnpm dev') - .addScript('build', 'pnpm build') - .addScript('serve', 'pnpm preview') - .addDependency('@clerk/react-router', constants.E2E_CLERK_VERSION || linkPackage('react-router')); +// const reactRouterLibrary = applicationConfig() +// .setName('react-router-library') +// .useTemplate(templates['react-router-library']) +// .setEnvFormatter('public', key => `VITE_${key}`) +// .addScript('setup', 'pnpm install') +// .addScript('dev', 'pnpm dev') +// .addScript('build', 'pnpm build') +// .addScript('serve', 'pnpm preview') +// .addDependency('@clerk/react-router', constants.E2E_CLERK_VERSION || linkPackage('react-router')); export const reactRouter = { reactRouterNode, - reactRouterLibrary, + // reactRouterLibrary, } as const; diff --git a/integration/templates/index.ts b/integration/templates/index.ts index 5c117a1f523..aab725d812b 100644 --- a/integration/templates/index.ts +++ b/integration/templates/index.ts @@ -18,7 +18,7 @@ export const templates = { 'vue-vite': resolve(__dirname, './vue-vite'), 'nuxt-node': resolve(__dirname, './nuxt-node'), 'react-router-node': resolve(__dirname, './react-router-node'), - 'react-router-library': resolve(__dirname, './react-router-library'), + // 'react-router-library': resolve(__dirname, './react-router-library'), 'custom-flows-react-vite': resolve(__dirname, './custom-flows-react-vite'), } as const; diff --git a/integration/templates/tanstack-react-start/src/start.ts b/integration/templates/tanstack-react-start/src/start.ts index 61571c1900b..6f0d68a73f1 100644 --- a/integration/templates/tanstack-react-start/src/start.ts +++ b/integration/templates/tanstack-react-start/src/start.ts @@ -1,4 +1,5 @@ import { clerkMiddleware } from '@clerk/tanstack-react-start/server'; +import { createStart } from '@tanstack/react-start'; export const startInstance = createStart(() => { return { diff --git a/integration/tests/react-router/library-mode.test.ts b/integration/tests/react-router/library-mode.test.ts index 6f5af6f63b5..48747424783 100644 --- a/integration/tests/react-router/library-mode.test.ts +++ b/integration/tests/react-router/library-mode.test.ts @@ -5,13 +5,14 @@ import { appConfigs } from '../../presets'; import type { FakeOrganization, FakeUser } from '../../testUtils'; import { createTestUtils } from '../../testUtils'; -test.describe('Library Mode basic tests for @react-router', () => { +test.skip('Library Mode basic tests for @react-router', () => { test.describe.configure({ mode: 'parallel' }); let app: Application; let fakeUser: FakeUser; let fakeOrganization: FakeOrganization; test.beforeAll(async () => { + // @ts-expect-error: Exclude test for now app = await appConfigs.reactRouter.reactRouterLibrary.commit(); await app.setup(); diff --git a/integration/tests/tanstack-router/basic.test.ts b/integration/tests/tanstack-router/basic.test.ts index c72fd98c12b..488db6f3bb0 100644 --- a/integration/tests/tanstack-router/basic.test.ts +++ b/integration/tests/tanstack-router/basic.test.ts @@ -5,7 +5,7 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( - 'basic tests for TanStack Router @xtanstack-react-router', + 'basic tests for TanStack Router @tanstack-react-router', ({ app }) => { test.describe.configure({ mode: 'parallel' }); From 80a30188e146920adc15b1a0de739b817158952f Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 12:29:35 -0700 Subject: [PATCH 26/33] skip tanstack router test --- integration/tests/tanstack-router/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/tanstack-router/basic.test.ts b/integration/tests/tanstack-router/basic.test.ts index 488db6f3bb0..9ae897e7bb7 100644 --- a/integration/tests/tanstack-router/basic.test.ts +++ b/integration/tests/tanstack-router/basic.test.ts @@ -5,7 +5,7 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( - 'basic tests for TanStack Router @tanstack-react-router', + 'basic tests for TanStack Router @skip-tanstack-react-router', ({ app }) => { test.describe.configure({ mode: 'parallel' }); From ec71828315367c80b8840f0247b872374565f931 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 12:43:27 -0700 Subject: [PATCH 27/33] chore: skip tanstack router test --- .github/workflows/ci.yml | 2 +- integration/presets/react-router.ts | 20 +++++++++---------- integration/templates/index.ts | 2 +- .../tests/react-router/library-mode.test.ts | 3 +-- .../tests/tanstack-router/basic.test.ts | 2 +- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b7fec1f7de..5b14d83bd02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -292,7 +292,7 @@ jobs: 'astro', 'expo-web', 'tanstack-react-start', - 'tanstack-react-router', + # 'tanstack-react-router', 'vue', 'nuxt', 'react-router', diff --git a/integration/presets/react-router.ts b/integration/presets/react-router.ts index 7183f152303..6dc759ddae7 100644 --- a/integration/presets/react-router.ts +++ b/integration/presets/react-router.ts @@ -13,17 +13,17 @@ const reactRouterNode = applicationConfig() .addScript('serve', 'pnpm start') .addDependency('@clerk/react-router', constants.E2E_CLERK_VERSION || linkPackage('react-router')); -// const reactRouterLibrary = applicationConfig() -// .setName('react-router-library') -// .useTemplate(templates['react-router-library']) -// .setEnvFormatter('public', key => `VITE_${key}`) -// .addScript('setup', 'pnpm install') -// .addScript('dev', 'pnpm dev') -// .addScript('build', 'pnpm build') -// .addScript('serve', 'pnpm preview') -// .addDependency('@clerk/react-router', constants.E2E_CLERK_VERSION || linkPackage('react-router')); +const reactRouterLibrary = applicationConfig() + .setName('react-router-library') + .useTemplate(templates['react-router-library']) + .setEnvFormatter('public', key => `VITE_${key}`) + .addScript('setup', 'pnpm install') + .addScript('dev', 'pnpm dev') + .addScript('build', 'pnpm build') + .addScript('serve', 'pnpm preview') + .addDependency('@clerk/react-router', constants.E2E_CLERK_VERSION || linkPackage('react-router')); export const reactRouter = { reactRouterNode, - // reactRouterLibrary, + reactRouterLibrary, } as const; diff --git a/integration/templates/index.ts b/integration/templates/index.ts index aab725d812b..5c117a1f523 100644 --- a/integration/templates/index.ts +++ b/integration/templates/index.ts @@ -18,7 +18,7 @@ export const templates = { 'vue-vite': resolve(__dirname, './vue-vite'), 'nuxt-node': resolve(__dirname, './nuxt-node'), 'react-router-node': resolve(__dirname, './react-router-node'), - // 'react-router-library': resolve(__dirname, './react-router-library'), + 'react-router-library': resolve(__dirname, './react-router-library'), 'custom-flows-react-vite': resolve(__dirname, './custom-flows-react-vite'), } as const; diff --git a/integration/tests/react-router/library-mode.test.ts b/integration/tests/react-router/library-mode.test.ts index 48747424783..6f5af6f63b5 100644 --- a/integration/tests/react-router/library-mode.test.ts +++ b/integration/tests/react-router/library-mode.test.ts @@ -5,14 +5,13 @@ import { appConfigs } from '../../presets'; import type { FakeOrganization, FakeUser } from '../../testUtils'; import { createTestUtils } from '../../testUtils'; -test.skip('Library Mode basic tests for @react-router', () => { +test.describe('Library Mode basic tests for @react-router', () => { test.describe.configure({ mode: 'parallel' }); let app: Application; let fakeUser: FakeUser; let fakeOrganization: FakeOrganization; test.beforeAll(async () => { - // @ts-expect-error: Exclude test for now app = await appConfigs.reactRouter.reactRouterLibrary.commit(); await app.setup(); diff --git a/integration/tests/tanstack-router/basic.test.ts b/integration/tests/tanstack-router/basic.test.ts index 9ae897e7bb7..488db6f3bb0 100644 --- a/integration/tests/tanstack-router/basic.test.ts +++ b/integration/tests/tanstack-router/basic.test.ts @@ -5,7 +5,7 @@ import type { FakeUser } from '../../testUtils'; import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })( - 'basic tests for TanStack Router @skip-tanstack-react-router', + 'basic tests for TanStack Router @tanstack-react-router', ({ app }) => { test.describe.configure({ mode: 'parallel' }); From 5848fa53cdffbc47ababcc2c7c1353c31fbcea2e Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 12:46:58 -0700 Subject: [PATCH 28/33] chore: bump integration tanstack versions --- integration/presets/utils.ts | 12 +++++------- .../templates/tanstack-react-start/package.json | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/integration/presets/utils.ts b/integration/presets/utils.ts index f7831c39663..7c790879e9e 100644 --- a/integration/presets/utils.ts +++ b/integration/presets/utils.ts @@ -1,10 +1,8 @@ -import path from 'node:path'; - export function linkPackage(pkg: string) { - // eslint-disable-next-line turbo/no-undeclared-env-vars - if (process.env.CI === 'true') { - return '*'; - } + // if (process.env.CI === 'true') { + // return '*'; + // } - return `link:${path.resolve(process.cwd(), `packages/${pkg}`)}`; + // return `link:${path.resolve(process.cwd(), `packages/${pkg}`)}`; + return '*'; } diff --git a/integration/templates/tanstack-react-start/package.json b/integration/templates/tanstack-react-start/package.json index 89f3ed126bd..708304cc5ad 100644 --- a/integration/templates/tanstack-react-start/package.json +++ b/integration/templates/tanstack-react-start/package.json @@ -8,9 +8,9 @@ "start": "vite start --port=$PORT" }, "dependencies": { - "@tanstack/react-router": "1.132.23", - "@tanstack/react-router-devtools": "1.132.23", - "@tanstack/react-start": "1.132.23", + "@tanstack/react-router": "1.132.31", + "@tanstack/react-router-devtools": "1.132.31", + "@tanstack/react-start": "1.132.31", "react": "18.3.1", "react-dom": "18.3.1", "tailwind-merge": "^2.5.4" From 8ef7ac6f733db8f5a53ee1c5b43fc63a541b5733 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 12:51:12 -0700 Subject: [PATCH 29/33] revert --- integration/presets/utils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/integration/presets/utils.ts b/integration/presets/utils.ts index 7c790879e9e..f7831c39663 100644 --- a/integration/presets/utils.ts +++ b/integration/presets/utils.ts @@ -1,8 +1,10 @@ +import path from 'node:path'; + export function linkPackage(pkg: string) { - // if (process.env.CI === 'true') { - // return '*'; - // } + // eslint-disable-next-line turbo/no-undeclared-env-vars + if (process.env.CI === 'true') { + return '*'; + } - // return `link:${path.resolve(process.cwd(), `packages/${pkg}`)}`; - return '*'; + return `link:${path.resolve(process.cwd(), `packages/${pkg}`)}`; } From 525dd91f6b145d22a531feb9068a60422fc7248f Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 5 Oct 2025 13:07:48 -0700 Subject: [PATCH 30/33] fix integration router --- integration/templates/tanstack-react-start/src/router.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration/templates/tanstack-react-start/src/router.tsx b/integration/templates/tanstack-react-start/src/router.tsx index 5664a8f649b..998658822da 100644 --- a/integration/templates/tanstack-react-start/src/router.tsx +++ b/integration/templates/tanstack-react-start/src/router.tsx @@ -1,8 +1,8 @@ -import { createRouter as createTanStackRouter } from '@tanstack/react-router'; +import { createRouter } from '@tanstack/react-router'; import { routeTree } from './routeTree.gen'; -export function createRouter() { - const router = createTanStackRouter({ +export function getRouter() { + const router = createRouter({ routeTree, defaultPreload: 'intent', defaultErrorComponent: err =>

{err.error.stack}

, @@ -15,6 +15,6 @@ export function createRouter() { declare module '@tanstack/react-router' { interface Register { - router: ReturnType; + router: ReturnType; } } From 40cf986e44b9cd4819c85ebc809f64f049a3d5d7 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 7 Oct 2025 12:19:12 -0700 Subject: [PATCH 31/33] chore: Use shared type helper --- .changeset/spotty-cooks-march.md | 67 ++++++++++++++++++- .../tanstack-react-start/src/routes/user.tsx | 3 +- .../src/server/getAuth.ts | 8 ++- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/.changeset/spotty-cooks-march.md b/.changeset/spotty-cooks-march.md index 55b29094186..efe67c9914d 100644 --- a/.changeset/spotty-cooks-march.md +++ b/.changeset/spotty-cooks-march.md @@ -2,4 +2,69 @@ "@clerk/tanstack-react-start": minor --- -placeholder trigger test +Added support for [TanStack Start v1 RC](https://tanstack.com/blog/announcing-tanstack-start-v1)! Includes a new `clerkMiddleware()` global middleware replacing the custom server handler. + +Usage: + +1. Create a `src/start.ts` file and add `clerkMiddleware()` to the list of request middlewares: + +```ts +// src/start.ts +import { clerkMiddleware } from '@clerk/tanstack-react-start/server' +import { createStart } from '@tanstack/react-start' + +export const startInstance = createStart(() => { + return { + requestMiddleware: [clerkMiddleware()], + } +}) +``` + +2. Add `` to your root route + +```tsx +// src/routes/__root.tsx +import { ClerkProvider } from '@clerk/tanstack-react-start' + +export const Route = createRootRoute({...}) + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + + + {children} + + + + + ) +} +``` + +The `getAuth()` helper can now be called within server routes and functions, without passing a Request object: + +```ts +const authStateFn = createServerFn().handler(async () => { + const { userId } = await getAuth() + + if (!userId) { + throw redirect({ + to: '/sign-in', + }) + } + + return { userId } +}) + +export const Route = createFileRoute('/')({ + component: Home, + beforeLoad: async () => await authStateFn(), + loader: async ({ context }) => { + return { userId: context.userId } + }, +}) +``` \ No newline at end of file diff --git a/integration/templates/tanstack-react-start/src/routes/user.tsx b/integration/templates/tanstack-react-start/src/routes/user.tsx index 8140f5dc03d..51ff8e51ec3 100644 --- a/integration/templates/tanstack-react-start/src/routes/user.tsx +++ b/integration/templates/tanstack-react-start/src/routes/user.tsx @@ -1,10 +1,9 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { createServerFn } from '@tanstack/react-start'; import { getAuth } from '@clerk/tanstack-react-start/server'; -import { getRequest } from '@tanstack/react-start/server'; const fetchClerkAuth = createServerFn({ method: 'GET' }).handler(async () => { - const { userId } = await getAuth(getRequest()); + const { userId } = await getAuth(); return { userId, diff --git a/packages/tanstack-react-start/src/server/getAuth.ts b/packages/tanstack-react-start/src/server/getAuth.ts index 7502ba12f5b..e8074cf35c6 100644 --- a/packages/tanstack-react-start/src/server/getAuth.ts +++ b/packages/tanstack-react-start/src/server/getAuth.ts @@ -1,10 +1,12 @@ +import type { SessionAuthObject } from '@clerk/backend'; import type { AuthOptions, GetAuthFnNoRequest } from '@clerk/backend/internal'; import { getAuthObjectForAcceptedToken } from '@clerk/backend/internal'; import { getGlobalStartContext } from '@tanstack/react-start'; -import { clerkMiddlewareNotConfigured, noFetchFnCtxPassedInGetAuth } from '../utils/errors'; +import { errorThrower } from '../utils'; +import { clerkMiddlewareNotConfigured } from '../utils/errors'; -export const getAuth: GetAuthFnNoRequest<{}, true> = (async (opts?: AuthOptions) => { +export const getAuth: GetAuthFnNoRequest = (async (opts?: AuthOptions) => { // @ts-expect-error: Untyped internal Clerk start context const authObjectFn = getGlobalStartContext().auth; @@ -16,4 +18,4 @@ export const getAuth: GetAuthFnNoRequest<{}, true> = (async (opts?: AuthOptions) const authObject = await Promise.resolve(authObjectFn({ treatPendingAsSignedOut: opts?.treatPendingAsSignedOut })); return getAuthObjectForAcceptedToken({ authObject, acceptsToken: opts?.acceptsToken }); -}) as GetAuthFn; +}) as GetAuthFnNoRequest; From b971abcc114218acd0ba80b9d14401c0bf62d839 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 7 Oct 2025 12:26:50 -0700 Subject: [PATCH 32/33] chore: fix middleware missing message --- packages/tanstack-react-start/src/utils/errors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tanstack-react-start/src/utils/errors.ts b/packages/tanstack-react-start/src/utils/errors.ts index 7731579f598..c813fb23e09 100644 --- a/packages/tanstack-react-start/src/utils/errors.ts +++ b/packages/tanstack-react-start/src/utils/errors.ts @@ -21,4 +21,4 @@ export const noFetchFnCtxPassedInGetAuth = createErrorMessage(` export const clerkMiddlewareNotConfigured = createErrorMessage(` It looks like you're trying to use Clerk without configuring the middleware. -To fix this, make sure you have the \`clerkMiddleware()\` configured in your \`createStart()\` function in your \`src/start.ts()\` file.`); +To fix this, make sure you have the \`clerkMiddleware()\` configured in your \`createStart()\` function in your \`src/start.ts\` file.`); From b19fa85f4c885ee9f309d9a969dce3976c255a51 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 7 Oct 2025 12:41:19 -0700 Subject: [PATCH 33/33] chore: update integration deps --- integration/templates/tanstack-react-start/package.json | 3 ++- integration/templates/tanstack-react-start/vite.config.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/integration/templates/tanstack-react-start/package.json b/integration/templates/tanstack-react-start/package.json index 708304cc5ad..45f452ea75a 100644 --- a/integration/templates/tanstack-react-start/package.json +++ b/integration/templates/tanstack-react-start/package.json @@ -20,9 +20,10 @@ "@types/node": "^22.5.4", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", "tailwindcss": "^4.0.8", "typescript": "^5.7.2", - "vite": "^6.0.11", + "vite": "^7.1.7", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/integration/templates/tanstack-react-start/vite.config.ts b/integration/templates/tanstack-react-start/vite.config.ts index 09afa5c1d2f..bce0dc21dd9 100644 --- a/integration/templates/tanstack-react-start/vite.config.ts +++ b/integration/templates/tanstack-react-start/vite.config.ts @@ -2,6 +2,7 @@ import { tanstackStart } from '@tanstack/react-start/plugin/vite'; import { defineConfig } from 'vite'; import tsConfigPaths from 'vite-tsconfig-paths'; import tailwindcss from '@tailwindcss/vite'; +import viteReact from '@vitejs/plugin-react'; export default defineConfig({ plugins: [ @@ -10,5 +11,6 @@ export default defineConfig({ }), tanstackStart(), tailwindcss(), + viteReact(), ], });