Skip to content

Commit

Permalink
feat(types): Add DefaultEnv for zero runtime factory with interface m…
Browse files Browse the repository at this point in the history
…erging
  • Loading branch information
miyaji255 committed Jan 11, 2025
1 parent 2ead4d8 commit 9bcd09f
Show file tree
Hide file tree
Showing 24 changed files with 259 additions and 67 deletions.
3 changes: 3 additions & 0 deletions jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@
"./deno": "./src/adapter/deno/index.ts",
"./bun": "./src/adapter/bun/index.ts",
"./aws-lambda": "./src/adapter/aws-lambda/index.ts",
"./aws-lambda/alb": "./src/adapter/aws-lambda/alb.ts",
"./aws-lambda/api-gateway":"./src/adapter/aws-lambda/api-gateway.ts",
"./aws-lambda/api-gateway-v2":"./src/adapter/aws-lambda/api-gateway-v2.ts",
"./vercel": "./src/adapter/vercel/index.ts",
"./netlify": "./src/adapter/netlify/index.ts",
"./lambda-edge": "./src/adapter/lambda-edge/index.ts",
Expand Down
24 changes: 24 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,21 @@
"import": "./dist/adapter/aws-lambda/index.js",
"require": "./dist/cjs/adapter/aws-lambda/index.js"
},
"./aws-lambda/alb": {
"types": "./dist/types/adapter/aws-lambda/alb.d.ts",
"import": "./dist/adapter/aws-lambda/alb.js",
"require": "./dist/cjs/adapter/aws-lambda/alb.js"
},
"./aws-lambda/api-gateway": {
"types": "./dist/types/adapter/aws-lambda/api-gateway.d.ts",
"import": "./dist/adapter/aws-lambda/api-gateway.js",
"require": "./dist/cjs/adapter/aws-lambda/api-gateway.js"
},
"./aws-lambda/api-gateway-v2": {
"types": "./dist/types/adapter/aws-lambda/api-gateway-v2.d.ts",
"import": "./dist/adapter/aws-lambda/api-gateway-v2.js",
"require": "./dist/cjs/adapter/aws-lambda/api-gateway-v2.js"
},
"./vercel": {
"types": "./dist/types/adapter/vercel/index.d.ts",
"import": "./dist/adapter/vercel/index.js",
Expand Down Expand Up @@ -575,6 +590,15 @@
"aws-lambda": [
"./dist/types/adapter/aws-lambda"
],
"aws-lambda/alb": [
"./dist/types/adapter/aws-lambda/alb.d.ts"
],
"aws-lambda/api-gateway":[
"./dist/types/adapter/aws-lambda/api-gateway.d.ts"
],
"aws-lambda/api-gateway-v2":[
"./dist/types/adapter/aws-lambda/api-gateway-v2.d.ts"
],
"vercel": [
"./dist/types/adapter/vercel"
],
Expand Down
24 changes: 24 additions & 0 deletions src/adapter/aws-lambda/alb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @module
* AWS Lambda Adapter for Hono. Invoked by ALB Event.
*/

export { handle, streamHandle } from './handler'
export type { APIGatewayProxyResult, ALBProxyEvent } from './handler'
export type { ALBRequestContext, LambdaContext } from './types'

import type { ALBProxyEvent } from './handler'
import type { LambdaContext } from './types'

declare module '../../types' {

Check failure on line 13 in src/adapter/aws-lambda/alb.ts

View workflow job for this annotation

GitHub Actions / Checking if it's valid for JSR

found an ambient module, which is a global augmentation, which are not unsupported
interface DefaultEnv {
Bindings: DefaultBindings
}

interface DefaultBindings {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore On development, this line will raise an error because of interface merging.
event: ALBProxyEvent
lambdaContext: LambdaContext
}
}
24 changes: 24 additions & 0 deletions src/adapter/aws-lambda/api-gateway-v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @module
* AWS Lambda Adapter for Hono. Invoked by AWS API Gateway Proxy Event V2.
*/

export { handle, streamHandle } from './handler'
export type { APIGatewayProxyResult, APIGatewayProxyEventV2 } from './handler'
export type { ApiGatewayRequestContext, LambdaContext } from './types'

import type { APIGatewayProxyEventV2 } from './handler'
import type { LambdaContext } from './types'

declare module '../../types' {

Check failure on line 13 in src/adapter/aws-lambda/api-gateway-v2.ts

View workflow job for this annotation

GitHub Actions / Checking if it's valid for JSR

found an ambient module, which is a global augmentation, which are not unsupported
interface DefaultEnv {
Bindings: DefaultBindings
}

interface DefaultBindings {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore On development, this line will raise an error because of interface merging.
event: APIGatewayProxyEventV2
lambdaContext: LambdaContext
}
}
24 changes: 24 additions & 0 deletions src/adapter/aws-lambda/api-gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @module
* AWS Lambda Adapter for Hono. Invoked by AWS API Gateway Proxy Event.
*/

export { handle, streamHandle } from './handler'
export type { APIGatewayProxyResult, APIGatewayProxyEvent } from './handler'
export type { ApiGatewayRequestContextV2, LambdaContext } from './types'

import type { APIGatewayProxyEvent } from './handler'
import type { LambdaContext } from './types'

declare module '../../types' {

Check failure on line 13 in src/adapter/aws-lambda/api-gateway.ts

View workflow job for this annotation

GitHub Actions / Checking if it's valid for JSR

found an ambient module, which is a global augmentation, which are not unsupported
interface DefaultEnv {
Bindings: DefaultBindings
}

interface DefaultBindings {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore On development, this line will raise an error because of interface merging.
event: APIGatewayProxyEvent
lambdaContext: LambdaContext
}
}
10 changes: 7 additions & 3 deletions src/adapter/aws-lambda/handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import crypto from 'node:crypto'
import type { Hono } from '../../hono'
import type { Env, Schema } from '../../types'
import type { DefaultEnv, Env, Schema } from '../../types'
import { decodeBase64, encodeBase64 } from '../../utils/encode'
import type {
ALBRequestContext,
Expand Down Expand Up @@ -106,7 +106,7 @@ const streamToNodeStream = async (
}

export const streamHandle = <
E extends Env = Env,
E extends Env = DefaultEnv,
S extends Schema = {},
BasePath extends string = '/'
>(
Expand Down Expand Up @@ -165,7 +165,11 @@ export const streamHandle = <
/**
* Accepts events from API Gateway/ELB(`APIGatewayProxyEvent`) and directly through Function Url(`APIGatewayProxyEventV2`)
*/
export const handle = <E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'>(
export const handle = <
E extends Env = DefaultEnv,
S extends Schema = {},
BasePath extends string = '/'
>(
app: Hono<E, S, BasePath>
): ((event: LambdaEvent, lambdaContext?: LambdaContext) => Promise<APIGatewayProxyResult>) => {
return async (event, lambdaContext?) => {
Expand Down
8 changes: 4 additions & 4 deletions src/adapter/bun/serve-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import { stat } from 'node:fs/promises'
import { serveStatic as baseServeStatic } from '../../middleware/serve-static'
import type { ServeStaticOptions } from '../../middleware/serve-static'
import type { Env, MiddlewareHandler } from '../../types'
import type { DefaultEnv, Env, MiddlewareHandler } from '../../types'

export const serveStatic = <E extends Env = Env>(
export const serveStatic = <E extends Env = DefaultEnv>(
options: ServeStaticOptions<E>
): MiddlewareHandler => {
): MiddlewareHandler<E> => {
return async function serveStatic(c, next) {
const getContent = async (path: string) => {
path = path.startsWith('/') ? path : `./${path}`
Expand All @@ -25,7 +25,7 @@ export const serveStatic = <E extends Env = Env>(
} catch {}
return isDir
}
return baseServeStatic({
return baseServeStatic<E>({
...options,
getContent,
pathResolve,
Expand Down
2 changes: 1 addition & 1 deletion src/adapter/cloudflare-pages/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('Adapter for Cloudflare Pages', () => {
it('Should not use `basePath()` if path argument is not passed', async () => {
const request = new Request('http://localhost/api/error')
const eventContext = createEventContext({ request })
const app = new Hono().basePath('/api')
const app = new Hono<Env>().basePath('/api')

app.onError((e) => {
throw e
Expand Down
6 changes: 3 additions & 3 deletions src/adapter/cloudflare-pages/handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Context } from '../../context'
import type { Hono } from '../../hono'
import { HTTPException } from '../../http-exception'
import type { BlankSchema, Env, Input, MiddlewareHandler, Schema } from '../../types'
import type { BlankSchema, DefaultEnv, Env, Input, MiddlewareHandler, Schema } from '../../types'

// Ref: https://github.com/cloudflare/workerd/blob/main/types/defines/pages.d.ts

Expand All @@ -28,7 +28,7 @@ declare type PagesFunction<
> = (context: EventContext<Env, Params, Data>) => Response | Promise<Response>

export const handle =
<E extends Env = Env, S extends Schema = BlankSchema, BasePath extends string = '/'>(
<E extends Env = DefaultEnv, S extends Schema = BlankSchema, BasePath extends string = '/'>(
app: Hono<E, S, BasePath>
): PagesFunction<E['Bindings']> =>
(eventContext) => {
Expand Down Expand Up @@ -110,7 +110,7 @@ declare abstract class FetcherLike {
*/
export const serveStatic = (): MiddlewareHandler => {
return async (c) => {
const env = c.env as { ASSETS: FetcherLike }
const env = c.env as unknown as { ASSETS: FetcherLike }
const res = await env.ASSETS.fetch(c.req.raw)
if (res.status === 404) {
return c.notFound()
Expand Down
12 changes: 12 additions & 0 deletions src/adapter/cloudflare-pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,15 @@

export { handle, handleMiddleware, serveStatic } from './handler'
export type { EventContext } from './handler'
import type { EventContext } from './handler'

declare module '../../types' {

Check failure on line 10 in src/adapter/cloudflare-pages/index.ts

View workflow job for this annotation

GitHub Actions / Checking if it's valid for JSR

found an ambient module, which is a global augmentation, which are not unsupported
interface DefaultEnv {
Bindings: DefaultBindings
}

interface DefaultBindings {
eventContext: EventContext<DefaultEnv>
ASSETS: { fetch: typeof fetch }
}
}
6 changes: 3 additions & 3 deletions src/adapter/cloudflare-workers/serve-static-module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// For ES module mode
import type { Env, MiddlewareHandler } from '../../types'
import type { DefaultEnv, Env, MiddlewareHandler } from '../../types'
import type { ServeStaticOptions } from './serve-static'
import { serveStatic } from './serve-static'

const module = <E extends Env = Env>(
const module = <E extends Env = DefaultEnv>(
options: Omit<ServeStaticOptions<E>, 'namespace'>
): MiddlewareHandler => {
): MiddlewareHandler<E> => {
return serveStatic<E>(options)
}

Expand Down
13 changes: 7 additions & 6 deletions src/adapter/cloudflare-workers/serve-static.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { serveStatic as baseServeStatic } from '../../middleware/serve-static'
import type { ServeStaticOptions as BaseServeStaticOptions } from '../../middleware/serve-static'
import type { Env, MiddlewareHandler } from '../../types'
import type { DefaultEnv, Env, MiddlewareHandler } from '../../types'
import { getContentFromKVAsset } from './utils'

export type ServeStaticOptions<E extends Env = Env> = BaseServeStaticOptions<E> & {
export type ServeStaticOptions<E extends Env = DefaultEnv> = BaseServeStaticOptions<E> & {
// namespace is KVNamespace
namespace?: unknown
manifest: object | string
Expand All @@ -18,9 +18,9 @@ export type ServeStaticOptions<E extends Env = Env> = BaseServeStaticOptions<E>
* please consider using Cloudflare Pages. You can start to create the Cloudflare Pages
* application with the `npm create hono@latest` command.
*/
export const serveStatic = <E extends Env = Env>(
export const serveStatic = <E extends Env = DefaultEnv>(
options: ServeStaticOptions<E>
): MiddlewareHandler => {
): MiddlewareHandler<E> => {
return async function serveStatic(c, next) {
const getContent = async (path: string) => {
return getContentFromKVAsset(path, {
Expand All @@ -30,11 +30,12 @@ export const serveStatic = <E extends Env = Env>(
namespace: options.namespace
? options.namespace
: c.env
? c.env.__STATIC_CONTENT
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
(c.env as any).__STATIC_CONTENT
: undefined,
})
}
return baseServeStatic({
return baseServeStatic<E>({
...options,
getContent,
})(c, next)
Expand Down
8 changes: 4 additions & 4 deletions src/adapter/deno/serve-static.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { ServeStaticOptions } from '../../middleware/serve-static'
import { serveStatic as baseServeStatic } from '../../middleware/serve-static'
import type { Env, MiddlewareHandler } from '../../types'
import type { DefaultEnv, Env, MiddlewareHandler } from '../../types'

const { open, lstatSync, errors } = Deno

export const serveStatic = <E extends Env = Env>(
export const serveStatic = <E extends Env = DefaultEnv>(
options: ServeStaticOptions<E>
): MiddlewareHandler => {
): MiddlewareHandler<E> => {
return async function serveStatic(c, next) {
const getContent = async (path: string) => {
try {
Expand Down Expand Up @@ -35,7 +35,7 @@ export const serveStatic = <E extends Env = Env>(
return isDir
}

return baseServeStatic({
return baseServeStatic<E>({
...options,
getContent,
pathResolve,
Expand Down
25 changes: 25 additions & 0 deletions src/adapter/lambda-edge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,28 @@ export type {
CloudFrontResponse,
CloudFrontEdgeEvent,
} from './handler'

import type {
Callback,
CloudFrontConfig,
CloudFrontRequest,
CloudFrontResponse,
CloudFrontEdgeEvent,
} from './handler'

declare module '../../types' {

Check failure on line 24 in src/adapter/lambda-edge/index.ts

View workflow job for this annotation

GitHub Actions / Checking if it's valid for JSR

found an ambient module, which is a global augmentation, which are not unsupported
interface DefaultEnv {
Bindings: DefaultBindings
}

interface DefaultBindings {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore On development, this line will raise an error because of interface merging.
event: CloudFrontEdgeEvent
context?: {}
callback: Callback
config: CloudFrontConfig
request: CloudFrontRequest
response?: CloudFrontResponse
}
}
4 changes: 2 additions & 2 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ type ContextOptions<E extends Env> = {
* Handler for not found responses.
*/
notFoundHandler?: NotFoundHandler<E>
matchResult?: Result<[H, RouterRoute]>
matchResult?: Result<[H<E>, RouterRoute<E>]>
path?: string
}

Expand Down Expand Up @@ -339,7 +339,7 @@ export class Context<
#renderer: Renderer | undefined
#notFoundHandler: NotFoundHandler<E> | undefined

#matchResult: Result<[H, RouterRoute]> | undefined
#matchResult: Result<[H<E>, RouterRoute<E>]> | undefined
#path: string | undefined

/**
Expand Down
Loading

0 comments on commit 9bcd09f

Please sign in to comment.