Skip to content

Commit

Permalink
Merge branch 'honojs:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
k2tzumi authored Oct 12, 2024
2 parents a27e3ca + f1a7267 commit 6b36a6c
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 30 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hono",
"version": "4.6.3",
"version": "4.6.4",
"description": "Web framework built on Web Standards",
"main": "dist/cjs/index.js",
"type": "module",
Expand Down Expand Up @@ -645,4 +645,4 @@
"engines": {
"node": ">=16.9.0"
}
}
}
38 changes: 38 additions & 0 deletions src/adapter/service-worker/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,31 @@ import { Hono } from '../../hono'
import { handle } from './handler'
import type { FetchEvent } from './types'

beforeAll(() => {
// fetch errors when it's not bound to globalThis in service worker
// set a fetch stub to emulate that behavior
vi.stubGlobal(
'fetch',
function fetch(this: undefined | typeof globalThis, arg0: string | Request) {
if (this !== globalThis) {
const error = new Error(
// eslint-disable-next-line quotes
"Failed to execute 'fetch' on 'WorkerGlobalScope': Illegal invocation"
)
error.name = 'TypeError'
throw error
}
if (arg0 instanceof Request && arg0.url === 'http://localhost/fallback') {
return new Response('hello world')
}
return Response.error()
}
)
})
afterAll(() => {
vi.unstubAllGlobals()
})

describe('handle', () => {
it('Success to fetch', async () => {
const app = new Hono()
Expand All @@ -20,6 +45,19 @@ describe('handle', () => {
expect(json).toStrictEqual({ hello: 'world' })
})
it('Fallback 404', async () => {
const app = new Hono()
const handler = handle(app)
const text = await new Promise<Response>((resolve) => {
handler({
request: new Request('http://localhost/fallback'),
respondWith(res) {
resolve(res)
},
} as FetchEvent)
}).then((res) => res.text())
expect(text).toBe('hello world')
})
it('Fallback 404 with explicit fetch', async () => {
const app = new Hono()
const handler = handle(app, {
async fetch() {
Expand Down
4 changes: 2 additions & 2 deletions src/adapter/service-worker/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const handle = (
opts: {
fetch?: typeof fetch
} = {
// To use `fetch` on a Service Worker correctly, first refer to `self.fetch`.
fetch: globalThis.self !== undefined ? globalThis.self.fetch : fetch,
// To use `fetch` on a Service Worker correctly, bind it to `globalThis`.
fetch: globalThis.fetch.bind(globalThis),
}
): Handler => {
return (evt) => {
Expand Down
30 changes: 30 additions & 0 deletions src/helper/factory/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,36 @@ describe('createMiddleware', () => {
const url = client.message.$url()
expect(url.pathname).toBe('/message')
})

describe('Relax types for Bindings and Variables', () => {
it('Should not throw a type error', () => {
type Bindings = {
MY_VAR_IN_BINDINGS: string
}

const app = new Hono<{ Bindings: Bindings }>()

type Variables = {
MY_VAR: string
}

const middleware = (_variable: string) =>
createMiddleware<{ Variables: Variables }>(async (c, next) => {
await next()
})

app.get(
'/',
createMiddleware<{ Bindings: Bindings }>(async (c, next) => {
const mw = middleware(c.env.MY_VAR_IN_BINDINGS)
await mw(c, next) // `c` does not throw an error
}),
(c) => {
return c.json({})
}
)
})
})
})

describe('createHandler', () => {
Expand Down
17 changes: 14 additions & 3 deletions src/helper/factory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Hono } from '../../hono'
import type { Env, H, HandlerResponse, Input, MiddlewareHandler } from '../../types'
import type { Simplify } from '../../utils/types'

type InitApp<E extends Env = Env> = (app: Hono<E>) => void

Expand Down Expand Up @@ -237,10 +238,20 @@ export const createFactory = <E extends Env = any, P extends string = any>(init?
initApp?: InitApp<E>
}): Factory<E, P> => new Factory<E, P>(init)

// Add `any` if it's undefined to relax types
// { Variables: Variables } => { Bindings: any, Variables: any }
// { Bindings: Bindings } => { Bindings: Bindings, Variables: any }

type AddAnyToEnv<T extends { Variables?: object; Bindings?: object }> = {
Bindings: undefined extends T['Bindings'] ? any : T['Bindings']
Variables: undefined extends T['Variables'] ? any : T['Variables']
}

export const createMiddleware = <
E extends Env = any,
P extends string = string,
I extends Input = {}
I extends Input = {},
E2 extends Env = Simplify<AddAnyToEnv<E>>
>(
middleware: MiddlewareHandler<E, P, I>
): MiddlewareHandler<E, P, I> => createFactory<E, P>().createMiddleware<I>(middleware)
middleware: MiddlewareHandler<E2, P, I>
): MiddlewareHandler<E2, P, I> => createFactory<E2, P>().createMiddleware<I>(middleware)
7 changes: 6 additions & 1 deletion src/hono-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
RouterRoute,
Schema,
} from './types'
import type { ExcludeEmptyObject } from './utils/types'
import { getPath, getPathNoStrict, mergePath } from './utils/url'

/**
Expand Down Expand Up @@ -212,7 +213,11 @@ class Hono<E extends Env = Env, S extends Schema = {}, BasePath extends string =
>(
path: SubPath,
app: Hono<SubEnv, SubSchema, SubBasePath>
): Hono<E, MergeSchemaPath<SubSchema, MergePath<BasePath, SubPath>> & S, BasePath> {
): Hono<
E,
ExcludeEmptyObject<MergeSchemaPath<SubSchema, MergePath<BasePath, SubPath>> | S>,
BasePath
> {
const subApp = this.basePath(path)
app.routes.map((r) => {
let handler
Expand Down
4 changes: 2 additions & 2 deletions src/jsx/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export class JSXNode implements HtmlEscaped {
}

class JSXFunctionNode extends JSXNode {
toStringToBuffer(buffer: StringBufferWithCallbacks): void {
override toStringToBuffer(buffer: StringBufferWithCallbacks): void {
const { children } = this

const res = (this.tag as Function).call(null, {
Expand Down Expand Up @@ -284,7 +284,7 @@ class JSXFunctionNode extends JSXNode {
}

export class JSXFragmentNode extends JSXNode {
toStringToBuffer(buffer: StringBufferWithCallbacks): void {
override toStringToBuffer(buffer: StringBufferWithCallbacks): void {
childrenToStringToBuffer(this.children, buffer)
}
}
Expand Down
36 changes: 16 additions & 20 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1817,24 +1817,21 @@ type ExtractParams<Path extends string> = string extends Path

type FlattenIfIntersect<T> = T extends infer O ? { [K in keyof O]: O[K] } : never

export type MergeSchemaPath<OrigSchema extends Schema, SubPath extends string> = Simplify<{
[P in keyof OrigSchema as MergePath<SubPath, P & string>]: {
[M in keyof OrigSchema[P]]: MergeEndpointParamsWithPath<OrigSchema[P][M], SubPath>
}
}>

type MergeEndpointParamsWithPath<T, SubPath extends string> = T extends {
input: infer Input
output: infer Output
outputFormat: infer OutputFormat
status: infer Status
export type MergeSchemaPath<OrigSchema extends Schema, SubPath extends string> = {
[P in keyof OrigSchema as MergePath<SubPath, P & string>]: [OrigSchema[P]] extends [
Record<string, Endpoint>
]
? { [M in keyof OrigSchema[P]]: MergeEndpointParamsWithPath<OrigSchema[P][M], SubPath> }
: never
}

type MergeEndpointParamsWithPath<T extends Endpoint, SubPath extends string> = T extends unknown
? {
input: Input extends { param: infer _ }
input: T['input'] extends { param: infer _ }
? ExtractParams<SubPath> extends never
? Input
? T['input']
: FlattenIfIntersect<
Input & {
T['input'] & {
param: {
// Maps extracted keys, stripping braces, to a string-typed record.
[K in keyof ExtractParams<SubPath> as K extends `${infer Prefix}{${infer _}}`
Expand All @@ -1844,21 +1841,20 @@ type MergeEndpointParamsWithPath<T, SubPath extends string> = T extends {
}
>
: RemoveBlankRecord<ExtractParams<SubPath>> extends never
? Input
: Input & {
? T['input']
: T['input'] & {
// Maps extracted keys, stripping braces, to a string-typed record.
param: {
[K in keyof ExtractParams<SubPath> as K extends `${infer Prefix}{${infer _}}`
? Prefix
: K]: string
}
}
output: Output
outputFormat: OutputFormat
status: Status
output: T['output']
outputFormat: T['outputFormat']
status: T['status']
}
: never

export type AddParam<I, P extends string> = ParamKeys<P> extends never
? I
: I extends { param: infer _ }
Expand Down
2 changes: 2 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,5 @@ export type IsAny<T> = boolean extends (T extends never ? true : false) ? true :
* @see https://github.com/Microsoft/TypeScript/issues/29729
*/
export type StringLiteralUnion<T> = T | (string & Record<never, never>)

export type ExcludeEmptyObject<T> = T extends {} ? ({} extends T ? never : T) : T

0 comments on commit 6b36a6c

Please sign in to comment.