diff --git a/src/client/client.test.ts b/src/client/client.test.ts index defedcde4..141e3c97c 100644 --- a/src/client/client.test.ts +++ b/src/client/client.test.ts @@ -885,6 +885,20 @@ describe('Use custom fetch (app.request) method', () => { const res = await client.search.$get() expect(res.ok).toBe(true) }) + + it.each([false, 0, '', null])('Should send falsy JSON value: %j', async (json) => { + const app = new Hono().post( + '/json', + validator('json', (value) => value as boolean | number | string | null), + (c) => c.json(c.req.valid('json')) + ) + const client = hc('', { fetch: app.request }) + + const res = await client.json.$post({ json }) + + expect(res.status).toBe(200) + expect(await res.json()).toBe(json) + }) }) describe('Optional parameters in JSON response', () => { diff --git a/src/client/client.ts b/src/client/client.ts index 0a6144830..1ce6d60a1 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -78,7 +78,7 @@ class ClientRequestImpl { this.rBody = form } - if (args.json) { + if (args.json !== undefined) { this.rBody = JSON.stringify(args.json) this.cType = 'application/json' } diff --git a/src/middleware/cors/index.test.ts b/src/middleware/cors/index.test.ts index 9ea184c70..ee9efdf2d 100644 --- a/src/middleware/cors/index.test.ts +++ b/src/middleware/cors/index.test.ts @@ -382,6 +382,17 @@ describe('CORS by Middleware', () => { expect(res2.headers.get('Access-Control-Allow-Methods')).toBe('GET,HEAD') }) + it('Does not set allow methods when function returns an empty array', async () => { + for (const allowMethods of [() => [], async () => []]) { + const app = new Hono() + app.use(cors({ allowMethods })) + + const res = await app.request('http://localhost/', { method: 'OPTIONS' }) + + expect(res.headers.get('Access-Control-Allow-Methods')).toBeNull() + } + }) + it('Emits the wildcard, not the reflected origin, when credentials is true with wildcard origin', async () => { const res = await app.request('http://localhost/api10/abc', { headers: { diff --git a/src/middleware/cors/index.ts b/src/middleware/cors/index.ts index e4a45400a..1ea2e012a 100644 --- a/src/middleware/cors/index.ts +++ b/src/middleware/cors/index.ts @@ -69,6 +69,9 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => { ...options, } satisfies CORSOptions + const exposeHeadersStr = opts.exposeHeaders?.length ? opts.exposeHeaders.join(',') : undefined + const allowHeadersStr = opts.allowHeaders?.length ? opts.allowHeaders.join(',') : undefined + const findAllowOrigin = ((optsOrigin) => { if (typeof optsOrigin === 'string') { if (optsOrigin === '*') { @@ -85,11 +88,12 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => { const findAllowMethods = ((optsAllowMethods) => { if (typeof optsAllowMethods === 'function') { - return optsAllowMethods + return async (origin: string, c: Context) => (await optsAllowMethods(origin, c)).join(',') } else if (Array.isArray(optsAllowMethods)) { - return () => optsAllowMethods + const methodsStr = optsAllowMethods.join(',') + return () => methodsStr } else { - return () => [] + return () => '' } })(opts.allowMethods) @@ -107,8 +111,8 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => { set('Access-Control-Allow-Credentials', 'true') } - if (opts.exposeHeaders?.length) { - set('Access-Control-Expose-Headers', opts.exposeHeaders.join(',')) + if (exposeHeadersStr) { + set('Access-Control-Expose-Headers', exposeHeadersStr) } if (c.req.method === 'OPTIONS') { @@ -121,19 +125,22 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => { } const allowMethods = await findAllowMethods(c.req.header('origin') || '', c) - if (allowMethods.length) { - set('Access-Control-Allow-Methods', allowMethods.join(',')) + if (allowMethods) { + set('Access-Control-Allow-Methods', allowMethods) } - let headers = opts.allowHeaders - if (!headers?.length) { + let headersStr = allowHeadersStr + if (!headersStr) { const requestHeaders = c.req.header('Access-Control-Request-Headers') if (requestHeaders) { - headers = requestHeaders.split(',').map((h) => h.trim()) + headersStr = requestHeaders + .split(',') + .map((h) => h.trim()) + .join(',') } } - if (headers?.length) { - set('Access-Control-Allow-Headers', headers.join(',')) + if (headersStr) { + set('Access-Control-Allow-Headers', headersStr) c.res.headers.append('Vary', 'Access-Control-Request-Headers') } diff --git a/src/utils/url.test.ts b/src/utils/url.test.ts index 7274d5db7..2fcd70d72 100644 --- a/src/utils/url.test.ts +++ b/src/utils/url.test.ts @@ -253,6 +253,7 @@ describe('url', () => { '/api/:version/animal', '/api/:version/animal/:type', ]) + expect(checkOptionalParameter('/api/:id{[0-9]?}?')).toEqual(['/api', '/api/:id{[0-9]?}']) }) }) diff --git a/src/utils/url.ts b/src/utils/url.ts index ea92ff935..124936482 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -187,13 +187,13 @@ export const checkOptionalParameter = (path: string): string[] | null => { if (segment !== '' && !/\:/.test(segment)) { basePath += '/' + segment } else if (/\:/.test(segment)) { - if (/\?/.test(segment)) { + if (segment.charCodeAt(segment.length - 1) === 63) { if (results.length === 0 && basePath === '') { results.push('/') } else { results.push(basePath) } - const optionalSegment = segment.replace('?', '') + const optionalSegment = segment.slice(0, -1) basePath += '/' + optionalSegment results.push(basePath) } else {