b1f7116231
Three small defects found underneath error clusters that are themselves benign. bounceToAuth returned a bare NextResponse.redirect and never copied the cookies off supabaseResponse, so the Set-Cookie headers that clear a dead session were thrown away on the first bounce and the browser replayed the dead refresh token once more on /login. That doubled both the GoTrue 400s and the log volume. The AuthApiError itself is left alone: it is correct session-expiry handling that auth-js logs from inside node_modules, and getUser() returns it as a value. "The destination stream closed early." is a client disconnecting mid-stream, produced inside React's Flight server. Next's own isAbortError filter does not recognise React's cancel error, so instrumentation.ts reported it to PostHog Error Tracking as a real exception against real users' session replays and paid an awaited flush on an otherwise-healthy request. A narrow predicate now early-returns before PostHog is touched. This cannot remove the line from Vercel's runtime-error table, which is fed by Next's stderr. other-account-hint.ts issued an unfiltered journal_entries probe on the blocking Hem render path, inside a render Promise.all: roughly 1 in 20 Hem loads waited an extra 2.5 s for an advisory nudge. An unfiltered probe on a multi-tenant table is also a correctness smell. It is now company-scoped and off the blocking path. Claude-Session: https://claude.ai/code/session_016ifKg6Ec67A39oxfGPU1yc Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
56 lines
2.1 KiB
TypeScript
56 lines
2.1 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { isClientDisconnectError } from '../is-client-disconnect'
|
|
|
|
describe('isClientDisconnectError', () => {
|
|
it('matches React\'s cancel error for a stream the client closed', () => {
|
|
expect(isClientDisconnectError(new Error('The destination stream closed early.'))).toBe(true)
|
|
})
|
|
|
|
it('matches the sibling cancel error React raises on a write failure', () => {
|
|
expect(
|
|
isClientDisconnectError(new Error('The destination stream errored while writing data.')),
|
|
).toBe(true)
|
|
})
|
|
|
|
it('matches Next\'s own ResponseAborted error by name', () => {
|
|
const aborted = Object.assign(new Error('The response was aborted'), {
|
|
name: 'ResponseAborted',
|
|
})
|
|
expect(isClientDisconnectError(aborted)).toBe(true)
|
|
})
|
|
|
|
it('does not match an ordinary error', () => {
|
|
expect(isClientDisconnectError(new Error('boom'))).toBe(false)
|
|
expect(isClientDisconnectError(new TypeError('fetch failed'))).toBe(false)
|
|
})
|
|
|
|
it('does not match a real provider timeout or socket reset', () => {
|
|
// AbortSignal.timeout throws this shape all over lib/providers; treating
|
|
// it as a client disconnect would hide genuine integration failures.
|
|
expect(isClientDisconnectError(new DOMException('Aborted', 'AbortError'))).toBe(false)
|
|
expect(
|
|
isClientDisconnectError(Object.assign(new Error('socket hang up'), { code: 'ECONNRESET' })),
|
|
).toBe(false)
|
|
expect(
|
|
isClientDisconnectError(
|
|
Object.assign(new Error('Premature close'), { code: 'ERR_STREAM_PREMATURE_CLOSE' }),
|
|
),
|
|
).toBe(false)
|
|
})
|
|
|
|
it('matches the message exactly, so a real error cannot hide behind the phrase', () => {
|
|
expect(
|
|
isClientDisconnectError(
|
|
new Error('Supabase query failed: The destination stream closed early.'),
|
|
),
|
|
).toBe(false)
|
|
})
|
|
|
|
it('is false for anything that is not an error object', () => {
|
|
expect(isClientDisconnectError(null)).toBe(false)
|
|
expect(isClientDisconnectError(undefined)).toBe(false)
|
|
expect(isClientDisconnectError('The destination stream closed early.')).toBe(false)
|
|
expect(isClientDisconnectError(42)).toBe(false)
|
|
})
|
|
})
|