Skip to content

Commit

Permalink
feat(hc): add init option (#2592)
Browse files Browse the repository at this point in the history
  • Loading branch information
NamesMT authored May 3, 2024
1 parent 11a20a7 commit fb07fb4
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 8 deletions.
1 change: 1 addition & 0 deletions deno_dist/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class ClientRequestImpl {
body: setBody ? this.rBody : undefined,
method: methodUpperCase,
headers: headers,
...opt?.init,
})
}
}
Expand Down
15 changes: 11 additions & 4 deletions deno_dist/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ import type { HasRequiredKeys } from '../utils/types.ts'

type HonoRequest = (typeof Hono.prototype)['request']

export type ClientRequestOptions<T = unknown> = keyof T extends never
export type ClientRequestOptions<T = unknown> = {
fetch?: typeof fetch | HonoRequest
/**
* Standard `RequestInit`, caution that this take highest priority
* and could be used to overwrite things that Hono sets for you, like `body | method | headers`.
*
* If you want to add some headers, use in `headers` instead of `init`
*/
init?: RequestInit
} & (keyof T extends never
? {
headers?:
| Record<string, string>
| (() => Record<string, string> | Promise<Record<string, string>>)
fetch?: typeof fetch | HonoRequest
}
: {
headers: T | (() => T | Promise<T>)
fetch?: typeof fetch | HonoRequest
}
})

export type ClientRequest<S extends Schema> = {
[M in keyof S]: S[M] extends Endpoint & { input: infer R }
Expand Down
91 changes: 91 additions & 0 deletions src/client/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,97 @@ describe('Dynamic headers', () => {
})
})

describe('RequestInit work as expected', () => {
const app = new Hono()

const route = app
.get('/credentials', (c) => {
return c.text('' as RequestCredentials)
})
.get('/headers', (c) => {
return c.json({} as Record<string, string>)
})
.post('/headers', (c) => c.text('Not found', 404))

type AppType = typeof route

const server = setupServer(
rest.get('http://localhost/credentials', async (req, res, ctx) => {
return res(ctx.status(200), ctx.text(req.credentials))
}),
rest.get('http://localhost/headers', async (req, res, ctx) => {
const allHeaders: Record<string, string> = {}
for (const [k, v] of req.headers.entries()) {
allHeaders[k] = v
}

return res(ctx.status(200), ctx.json(allHeaders))
}),
rest.post('http://localhost/headers', async (req, res, ctx) => {
return res(ctx.status(400), ctx.text('Should not be here'))
})
)

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

const client = hc<AppType>('http://localhost', {
headers: { 'x-hono': 'fire' },
init: {
credentials: 'include',
},
})

it('Should overwrite method and fail', async () => {
const res = await client.headers.$get(undefined, { init: { method: 'POST' } })

expect(res.ok).toBe(false)
})

it('Should clear headers', async () => {
const res = await client.headers.$get(undefined, { init: { headers: undefined } })

expect(res.ok).toBe(true)
const data = await res.json()
expect(data).toEqual({})
})

it('Should overwrite headers', async () => {
const res = await client.headers.$get(undefined, {
init: { headers: new Headers({ 'x-hono': 'awesome' }) },
})

expect(res.ok).toBe(true)
const data = await res.json()
expect(data).toEqual({ 'x-hono': 'awesome' })
})

it('credentials is include', async () => {
const res = await client.credentials.$get()

expect(res.ok).toBe(true)
const data = await res.text()
expect(data).toEqual('include')
})

it('deepMerge should works and not unset credentials', async () => {
const res = await client.credentials.$get(undefined, { init: { headers: { hi: 'hello' } } })

expect(res.ok).toBe(true)
const data = await res.text()
expect(data).toEqual('include')
})

it('Should unset credentials', async () => {
const res = await client.credentials.$get(undefined, { init: { credentials: undefined } })

expect(res.ok).toBe(true)
const data = await res.text()
expect(data).toEqual('same-origin')
})
})

describe('WebSocket URL Protocol Translation', () => {
const app = new Hono()
const route = app.get(
Expand Down
1 change: 1 addition & 0 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class ClientRequestImpl {
body: setBody ? this.rBody : undefined,
method: methodUpperCase,
headers: headers,
...opt?.init,
})
}
}
Expand Down
15 changes: 11 additions & 4 deletions src/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ import type { HasRequiredKeys } from '../utils/types'

type HonoRequest = (typeof Hono.prototype)['request']

export type ClientRequestOptions<T = unknown> = keyof T extends never
export type ClientRequestOptions<T = unknown> = {
fetch?: typeof fetch | HonoRequest
/**
* Standard `RequestInit`, caution that this take highest priority
* and could be used to overwrite things that Hono sets for you, like `body | method | headers`.
*
* If you want to add some headers, use in `headers` instead of `init`
*/
init?: RequestInit
} & (keyof T extends never
? {
headers?:
| Record<string, string>
| (() => Record<string, string> | Promise<Record<string, string>>)
fetch?: typeof fetch | HonoRequest
}
: {
headers: T | (() => T | Promise<T>)
fetch?: typeof fetch | HonoRequest
}
})

export type ClientRequest<S extends Schema> = {
[M in keyof S]: S[M] extends Endpoint & { input: infer R }
Expand Down

0 comments on commit fb07fb4

Please sign in to comment.