Skip to content

Commit 6d66d7e

Browse files
committed
fix(client): clarify isolated connection defaults and auth flow
1 parent 22c3206 commit 6d66d7e

4 files changed

Lines changed: 44 additions & 8 deletions

File tree

‎packages/devframe/src/client/connection-isolation.test.ts‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
DevframeConnection,
3+
SetupDevframeConnectionOptions,
34
} from './index'
45
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
56
import { setupDevframeConnection } from './index'
@@ -89,7 +90,7 @@ describe('isolated connection setup', () => {
8990
expect(readGlobal('__DEVFRAME_CONNECTION_META__')).toBe(storedConnection.connectionMeta)
9091
})
9192

92-
it.each([{}, { connection: { isolated: false } }])(
93+
it.each([{}, { connection: {} }, { connection: { isolated: false } }])(
9394
'retains default cache discovery with %j',
9495
async (options) => {
9596
expect.assertions(5)
@@ -130,3 +131,33 @@ describe('isolated connection setup', () => {
130131
expect(parentWindow.__DEVFRAME_CONNECTION__).toBe(storedConnection)
131132
})
132133
})
134+
135+
describe.each(['provided', 'fetched'] as const)('shared credentials with %s metadata', (metadataSource) => {
136+
it.each([
137+
{ name: 'explicit token overrides metadata and storage', authToken: 'explicit-token', metadataToken: 'metadata-token', storedToken: 'stored-token', expectedToken: 'explicit-token' },
138+
{ name: 'metadata token overrides storage', authToken: undefined, metadataToken: 'metadata-token', storedToken: 'stored-token', expectedToken: 'metadata-token' },
139+
{ name: 'local storage supplies a missing token', authToken: undefined, metadataToken: undefined, storedToken: 'stored-token', expectedToken: 'stored-token' },
140+
{ name: 'window storage supplies a missing token', authToken: undefined, metadataToken: undefined, storedToken: null, expectedToken: 'window-token' },
141+
])('$name', async ({ authToken, metadataToken, storedToken, expectedToken }) => {
142+
expect.assertions(4)
143+
vi.stubGlobal('__DEVFRAME_CONNECTION__', undefined)
144+
vi.stubGlobal('__DEVFRAME_CONNECTION_META__', undefined)
145+
vi.stubGlobal('__DEVFRAME_CONNECTION_AUTH_TOKEN__', 'window-token')
146+
getItem.mockReturnValue(storedToken)
147+
const connectionMeta = { backend: 'static' as const, authToken: metadataToken }
148+
const options: SetupDevframeConnectionOptions = {
149+
baseURL: 'http://requested.example/',
150+
authToken,
151+
}
152+
if (metadataSource === 'provided')
153+
options.connectionMeta = connectionMeta
154+
else
155+
fetchMetadata.mockResolvedValue(Response.json(connectionMeta))
156+
157+
const connection = await setupDevframeConnection(options)
158+
expect(connection.authToken).toBe(expectedToken)
159+
expect(readGlobal('__DEVFRAME_CONNECTION__')).toStrictEqual(connection)
160+
expect(setItem).toHaveBeenCalledExactlyOnceWith('__DEVFRAME_CONNECTION_AUTH_TOKEN__', expectedToken)
161+
expect(fetchMetadata).toHaveBeenCalledTimes(metadataSource === 'fetched' ? 1 : 0)
162+
})
163+
})

‎packages/devframe/src/client/connection.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface DevframeConnection {
3030
export interface SetupDevframeConnectionOptions {
3131
/** Reuse a prepared connection, or configure isolation before resolving its metadata. */
3232
connection?: DevframeConnection | {
33-
isolated: boolean
33+
isolated?: boolean
3434
connectionMeta?: never
3535
metaBaseUrl?: never
3636
authToken?: never
@@ -119,6 +119,7 @@ export async function setupDevframeConnection(
119119
options: SetupDevframeConnectionOptions = {},
120120
): Promise<DevframeConnection> {
121121
const connection = await resolveDevframeConnection(options)
122+
/** Apply token precedence once, regardless of how the connection metadata was resolved. */
122123
const authToken = options.authToken ?? connection.authToken ?? connection.connectionMeta.authToken
123124
const resolvedConnection = withAuthToken(
124125
connection,

‎packages/devframe/src/client/rpc.ts‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -427,15 +427,19 @@ export async function getDevframeRpcClient(
427427

428428
/** Channel name kept for cross-tab interop with the Vite DevTools auth page. */
429429
let authChannel: BroadcastChannel | undefined
430-
try {
431-
authChannel = connection.isolated ? undefined : new BroadcastChannel('devframe-auth')
430+
if (!connection.isolated) {
431+
try {
432+
authChannel = new BroadcastChannel('devframe-auth')
433+
}
434+
catch {}
432435
}
433-
catch {}
434436

435437
function updateAuthToken(token: string): void {
436438
connection = { ...connection, authToken: token }
437-
if (!connection.isolated)
438-
storeAuthToken(token)
439+
if (connection.isolated)
440+
return
441+
442+
storeAuthToken(token)
439443
}
440444

441445
// Gate outbound calls behind the auth bootstrap below. Without it, a

‎tests/__snapshots__/tsnapi/devframe/client.snapshot.d.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export interface RpcStreamingClientHost {
127127
}
128128
export interface SetupDevframeConnectionOptions {
129129
connection?: DevframeConnection | {
130-
isolated: boolean;
130+
isolated?: boolean;
131131
connectionMeta?: never;
132132
metaBaseUrl?: never;
133133
authToken?: never;

0 commit comments

Comments
 (0)