@byline/host-tanstack-start 2.3.1 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,7 +20,7 @@ async function getAdminRequestContext() {
20
20
  } catch {}
21
21
  const refreshToken = readRefreshTokenCookie();
22
22
  if (!refreshToken) {
23
- clearSessionCookies();
23
+ if (accessToken) clearSessionCookies();
24
24
  throw ERR_UNAUTHENTICATED({
25
25
  message: 'no admin session'
26
26
  });
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "private": false,
4
4
  "type": "module",
5
5
  "license": "MPL-2.0",
6
- "version": "2.3.1",
6
+ "version": "2.3.2",
7
7
  "engines": {
8
8
  "node": ">=20.9.0"
9
9
  },
@@ -107,12 +107,12 @@
107
107
  "react-swipeable": "^7.0.2",
108
108
  "uuid": "^14.0.0",
109
109
  "zod": "^4.4.3",
110
- "@byline/ai": "2.3.1",
111
- "@byline/client": "2.3.1",
112
- "@byline/core": "2.3.1",
113
- "@byline/admin": "2.3.1",
114
- "@byline/auth": "2.3.1",
115
- "@byline/ui": "2.3.1"
110
+ "@byline/auth": "2.3.2",
111
+ "@byline/admin": "2.3.2",
112
+ "@byline/core": "2.3.2",
113
+ "@byline/client": "2.3.2",
114
+ "@byline/ai": "2.3.2",
115
+ "@byline/ui": "2.3.2"
116
116
  },
117
117
  "peerDependencies": {
118
118
  "@tanstack/react-router": "^1.167.0",
@@ -137,7 +137,7 @@ describe('getAdminRequestContext', () => {
137
137
  expect(verifyAccessToken).toHaveBeenCalledWith('fresh-access')
138
138
  })
139
139
 
140
- it('throws ERR_UNAUTHENTICATED and clears cookies when no session exists', async () => {
140
+ it('throws ERR_UNAUTHENTICATED without emitting Set-Cookie when no cookies are sent', async () => {
141
141
  cookiesReturn({})
142
142
 
143
143
  try {
@@ -147,7 +147,22 @@ describe('getAdminRequestContext', () => {
147
147
  expect(err).toBeInstanceOf(AuthError)
148
148
  expect((err as AuthError).code).toBe(AuthErrorCodes.UNAUTHENTICATED)
149
149
  }
150
- // Cookie clear = setCookie with empty value and maxAge 0 for both names.
150
+ // Anonymous visitors must produce zero Set-Cookie headers so shared
151
+ // caches (Cloudflare) can cache public pages — a Set-Cookie on the
152
+ // response is a hard bypass signal for CDNs.
153
+ expect(setCookie).not.toHaveBeenCalled()
154
+ })
155
+
156
+ it('clears the stale access cookie when only an access token was sent', async () => {
157
+ cookiesReturn({ byline_access_token: 'stale' })
158
+ verifyAccessToken.mockRejectedValueOnce(new Error('expired'))
159
+
160
+ try {
161
+ await getAdminRequestContext()
162
+ expect.fail('expected ERR_UNAUTHENTICATED')
163
+ } catch (err) {
164
+ expect((err as AuthError).code).toBe(AuthErrorCodes.UNAUTHENTICATED)
165
+ }
151
166
  const clears = setCookie.mock.calls.filter((c) => c[2]?.maxAge === 0)
152
167
  const clearedNames = new Set(clears.map((c) => c[0]))
153
168
  expect(clearedNames.has('byline_access_token')).toBe(true)
@@ -80,8 +80,13 @@ export async function getAdminRequestContext(): Promise<RequestContext> {
80
80
  // Refresh path: swap the refresh cookie for a fresh token pair.
81
81
  const refreshToken = readRefreshTokenCookie()
82
82
  if (!refreshToken) {
83
- // No session at all clear anything stale and reject.
84
- clearSessionCookies()
83
+ // Only emit cookie clears when the browser actually sent a stale access
84
+ // cookie — otherwise the response carries no Set-Cookie at all, which
85
+ // lets shared caches (Cloudflare) cache public pages for anonymous
86
+ // visitors. Set-Cookie on the response is a hard bypass signal for CDNs.
87
+ if (accessToken) {
88
+ clearSessionCookies()
89
+ }
85
90
  throw ERR_UNAUTHENTICATED({ message: 'no admin session' })
86
91
  }
87
92