From 783a082c12e45f127fd8916cfad7f824c69a048f Mon Sep 17 00:00:00 2001 From: Jonathan Haines Date: Wed, 19 Mar 2025 19:53:11 +1100 Subject: [PATCH] chore: add coverage badges (#1023) * chore: add coverage badges * ci(casbin): fix spelling --- .github/workflows/ci-casbin.yml | 14 +- .github/workflows/ci-coverage.yml | 23 -- packages/ajv-validator/README.md | 26 +-- packages/arktype-validator/README.md | 4 +- packages/auth-js/README.md | 3 + packages/bun-transpiler/README.md | 2 + packages/casbin/README.md | 31 +-- packages/class-validator/README.md | 26 +-- packages/clerk-auth/README.md | 15 +- packages/cloudflare-access/README.md | 4 +- packages/conform-validator/README.md | 2 + packages/effect-validator/README.md | 2 + packages/esbuild-transpiler/README.md | 2 + packages/event-emitter/README.md | 294 +++++++++++++++----------- packages/firebase-auth/README.md | 40 ++-- packages/graphql-server/README.md | 2 + packages/hello/README.md | 2 + packages/medley-router/README.md | 2 + packages/node-ws/README.md | 13 +- packages/oauth-providers/README.md | 28 +-- packages/oidc-auth/README.md | 4 +- packages/otel/README.md | 2 + packages/prometheus/README.md | 37 ++-- packages/qwik-city/README.md | 2 + packages/react-compat/README.md | 2 + packages/react-renderer/README.md | 2 + packages/sentry/README.md | 2 + packages/standard-validator/README.md | 14 +- packages/swagger-editor/README.md | 5 +- packages/swagger-ui/README.md | 27 +-- packages/trpc-server/README.md | 15 +- packages/tsyringe/README.md | 25 ++- packages/typebox-validator/README.md | 2 + packages/typia-validator/README.md | 6 +- packages/valibot-validator/README.md | 2 + packages/zod-openapi/README.md | 2 + packages/zod-validator/README.md | 17 +- 37 files changed, 402 insertions(+), 299 deletions(-) delete mode 100644 .github/workflows/ci-coverage.yml diff --git a/.github/workflows/ci-casbin.yml b/.github/workflows/ci-casbin.yml index 5240ff18..1b659927 100644 --- a/.github/workflows/ci-casbin.yml +++ b/.github/workflows/ci-casbin.yml @@ -1,13 +1,13 @@ -name: ci-cabin +name: ci-casbin on: push: branches: [main] paths: - - 'packages/cabin/**' + - 'packages/casbin/**' pull_request: branches: ['*'] paths: - - 'packages/cabin/**' + - 'packages/casbin/**' jobs: ci: @@ -17,13 +17,13 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 20.x - - run: yarn workspaces focus hono-middleware @hono/ci-cabin - - run: yarn workspace @hono/ci-cabin build - - run: yarn test --coverage --project @hono/ci-cabin + - run: yarn workspaces focus hono-middleware @hono/casbin + - run: yarn workspace @hono/casbin build + - run: yarn test --coverage --project @hono/casbin - uses: codecov/codecov-action@v5 with: fail_ci_if_error: true directory: ./coverage - flags: ci-cabin + flags: casbin env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/ci-coverage.yml b/.github/workflows/ci-coverage.yml deleted file mode 100644 index 9610084d..00000000 --- a/.github/workflows/ci-coverage.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: initial-coverage -on: - push: - branches: [main] - pull_request: - branches: ['*'] - -jobs: - ci: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20.x - - run: yarn install - - run: yarn vitest --coverage - - uses: codecov/codecov-action@v5 - with: - fail_ci_if_error: true - directory: ./coverage - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/packages/ajv-validator/README.md b/packages/ajv-validator/README.md index cd02581f..233fe4f4 100644 --- a/packages/ajv-validator/README.md +++ b/packages/ajv-validator/README.md @@ -1,5 +1,7 @@ # Ajv validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=ajv-validator)](https://codecov.io/github/honojs/middleware) + Validator middleware using [Ajv](https://github.com/ajv-validator/ajv) for [Hono](https://honojs.dev) applications. Define your schema with Ajv and validate incoming requests. @@ -8,9 +10,9 @@ Define your schema with Ajv and validate incoming requests. No Hook: ```ts -import { type JSONSchemaType } from 'ajv'; -import { ajvValidator } from '@hono/ajv-validator'; - +import { type JSONSchemaType } from 'ajv' +import { ajvValidator } from '@hono/ajv-validator' + const schema: JSONSchemaType<{ name: string; age: number }> = { type: 'object', properties: { @@ -19,19 +21,19 @@ const schema: JSONSchemaType<{ name: string; age: number }> = { }, required: ['name', 'age'], additionalProperties: false, -} as const; +} as const const route = app.post('/user', ajvValidator('json', schema), (c) => { - const user = c.req.valid('json'); - return c.json({ success: true, message: `${user.name} is ${user.age}` }); -}); + const user = c.req.valid('json') + return c.json({ success: true, message: `${user.name} is ${user.age}` }) +}) ``` Hook: ```ts -import { type JSONSchemaType } from 'ajv'; -import { ajvValidator } from '@hono/ajv-validator'; +import { type JSONSchemaType } from 'ajv' +import { ajvValidator } from '@hono/ajv-validator' const schema: JSONSchemaType<{ name: string; age: number }> = { type: 'object', @@ -41,17 +43,17 @@ const schema: JSONSchemaType<{ name: string; age: number }> = { }, required: ['name', 'age'], additionalProperties: false, -}; +} app.post( '/user', ajvValidator('json', schema, (result, c) => { if (!result.success) { - return c.text('Invalid!', 400); + return c.text('Invalid!', 400) } }) //... -); +) ``` ## Author diff --git a/packages/arktype-validator/README.md b/packages/arktype-validator/README.md index 0537cc74..f177f2cd 100644 --- a/packages/arktype-validator/README.md +++ b/packages/arktype-validator/README.md @@ -1,5 +1,7 @@ # ArkType validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=arktype-validator)](https://codecov.io/github/honojs/middleware) + The validator middleware using [ArkType](https://arktype.io/) for [Hono](https://honojs.dev) applications. You can write a schema with ArkType and validate the incoming values. @@ -11,7 +13,7 @@ import { arktypeValidator } from '@hono/arktype-validator' const schema = type({ name: 'string', - age: 'number' + age: 'number', }) app.post('/author', arktypeValidator('json', schema), (c) => { diff --git a/packages/auth-js/README.md b/packages/auth-js/README.md index 3457bfaa..4dbccd91 100644 --- a/packages/auth-js/README.md +++ b/packages/auth-js/README.md @@ -1,5 +1,7 @@ # Auth.js middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=auth-js)](https://codecov.io/github/honojs/middleware) + This is a [Auth.js](https://authjs.dev) third-party middleware for [Hono](https://github.com/honojs/hono). This middleware can be used to inject the Auth.js session into the request context. @@ -112,6 +114,7 @@ const useSession = () => { return { session: data, status } } ``` + For more details on how to Popup Oauth Login see [example](https://github.com/divyam234/next-auth-hono-react) ## Author diff --git a/packages/bun-transpiler/README.md b/packages/bun-transpiler/README.md index 9192bf49..11c9d965 100644 --- a/packages/bun-transpiler/README.md +++ b/packages/bun-transpiler/README.md @@ -1,5 +1,7 @@ # Bun Transpiler middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=bun-transpiler)](https://codecov.io/github/honojs/middleware) + The Bun Transpiler middleware is a Hono middleware designed to transpile content such as TypeScript or TSX. You can place your script written in TypeScript in a directory and serve it using `serveStatic`. When you apply this middleware, your script will automatically be served transpiled into JavaScript code. This middleware works only with [Bun](https://bun.sh/). diff --git a/packages/casbin/README.md b/packages/casbin/README.md index d6a64540..e5e3ab4a 100644 --- a/packages/casbin/README.md +++ b/packages/casbin/README.md @@ -1,5 +1,7 @@ # Casbin Middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=casbin)](https://codecov.io/github/honojs/middleware) + This is a third-party [Casbin](https://casbin.org) middleware for [Hono](https://github.com/honojs/hono). This middleware can be used to enforce authorization policies defined using Casbin in your Hono routes. @@ -56,7 +58,8 @@ import { casbin } from '@hono/casbin' import { basicAuthorizer } from '@hono/casbin/helper' const app = new Hono() -app.use('*', +app.use( + '*', basicAuth( { username: 'alice', // alice has full access to /dataset1/test @@ -69,7 +72,7 @@ app.use('*', ), casbin({ newEnforcer: newEnforcer('examples/model.conf', 'examples/policy.csv'), - authorizer: basicAuthorizer + authorizer: basicAuthorizer, }) ) app.get('/dataset1/test', (c) => c.text('dataset1 test')) // alice and bob can access /dataset1/test @@ -89,13 +92,14 @@ import { casbin } from '@hono/casbin' import { jwtAuthorizer } from '@hono/casbin/helper' const app = new Hono() -app.use('*', +app.use( + '*', jwt({ secret: 'it-is-very-secret', }), casbin({ newEnforcer: newEnforcer('examples/model.conf', 'examples/policy.csv'), - authorizer: jwtAuthorizer + authorizer: jwtAuthorizer, }) ) app.get('/dataset1/test', (c) => c.text('dataset1 test')) // alice and bob can access /dataset1/test @@ -112,7 +116,7 @@ const claimMapping = { // ... casbin({ newEnforcer: newEnforcer('examples/model.conf', 'examples/policy.csv'), - authorizer: (c, e) => jwtAuthorizer(c, e, claimMapping) + authorizer: (c, e) => jwtAuthorizer(c, e, claimMapping), }) ``` @@ -126,13 +130,16 @@ import { newEnforcer } from 'casbin' import { casbin } from '@hono/casbin' const app = new Hono() -app.use('*', casbin({ - newEnforcer: newEnforcer('path-to-your-model.conf', 'path-to-your-policy.csv'), - authorizer: async (c, enforcer) => { - const { user, path, method } = c - return await enforcer.enforce(user, path, method) - } -})) +app.use( + '*', + casbin({ + newEnforcer: newEnforcer('path-to-your-model.conf', 'path-to-your-policy.csv'), + authorizer: async (c, enforcer) => { + const { user, path, method } = c + return await enforcer.enforce(user, path, method) + }, + }) +) ``` ## Author diff --git a/packages/class-validator/README.md b/packages/class-validator/README.md index c12325b1..65d17421 100644 --- a/packages/class-validator/README.md +++ b/packages/class-validator/README.md @@ -1,5 +1,7 @@ # Class-validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=class-validator)](https://codecov.io/github/honojs/middleware) + The validator middleware using [class-validator](https://github.com/typestack/class-validator) for [Hono](https://github.com/honojs/hono) applications. ## Usage @@ -7,16 +9,15 @@ The validator middleware using [class-validator](https://github.com/typestack/cl ```ts import { classValidator } from '@hono/class-validator' import { IsInt, IsString } from 'class-validator' - + class CreateUserDto { @IsString() - name!: string; - + name!: string + @IsInt() - age!: number; + age!: number } - - + const route = app.post('/user', classValidator('json', CreateUserDto), (c) => { const user = c.req.valid('json') return c.json({ success: true, message: `${user.name} is ${user.age}` }) @@ -31,17 +32,18 @@ import { IsInt, IsString } from 'class-validator' class CreateUserDto { @IsString() - name!: string; + name!: string - @IsInt() - age!: number; + @IsInt() + age!: number } app.post( - '/user', classValidator('json', CreateUserDto, (result, c) => { + '/user', + classValidator('json', CreateUserDto, (result, c) => { if (!result.success) { return c.text('Invalid!', 400) - } + } }) //... ) @@ -53,4 +55,4 @@ app.post( ## License -MIT \ No newline at end of file +MIT diff --git a/packages/clerk-auth/README.md b/packages/clerk-auth/README.md index a5ad8e82..63dc8326 100644 --- a/packages/clerk-auth/README.md +++ b/packages/clerk-auth/README.md @@ -1,5 +1,7 @@ # Clerk middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=clerk-auth)](https://codecov.io/github/honojs/middleware) + This is a [Clerk](https://clerk.com) third-party middleware for [Hono](https://github.com/honojs/hono). This middleware can be used to inject the active Clerk session into the request context. @@ -33,13 +35,13 @@ app.get('/', (c) => { if (!auth?.userId) { return c.json({ - message: 'You are not logged in.' + message: 'You are not logged in.', }) } return c.json({ message: 'You are logged in!', - userId: auth.userId + userId: auth.userId, }) }) @@ -65,9 +67,12 @@ app.get('/', async (c) => { user, }) } catch (e) { - return c.json({ - message: 'User not found.' - }, 404) + return c.json( + { + message: 'User not found.', + }, + 404 + ) } }) diff --git a/packages/cloudflare-access/README.md b/packages/cloudflare-access/README.md index 533786a6..575fc5ab 100644 --- a/packages/cloudflare-access/README.md +++ b/packages/cloudflare-access/README.md @@ -1,5 +1,7 @@ # Cloudflare Access middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=cloudflare-access)](https://codecov.io/github/honojs/middleware) + This is a [Cloudflare Access](https://www.cloudflare.com/zero-trust/products/access/) third-party middleware for [Hono](https://github.com/honojs/hono). @@ -48,7 +50,7 @@ export default app ## Errors throw by the middleware | Error | HTTP Code | -|--------------------------------------------------------------------------------------------------------|-----------| +| ------------------------------------------------------------------------------------------------------ | --------- | | Authentication error: Missing bearer token | 401 | | Authentication error: Unable to decode Bearer token | 401 | | Authentication error: Token is expired | 401 | diff --git a/packages/conform-validator/README.md b/packages/conform-validator/README.md index a3fbc8b8..5e61ce36 100644 --- a/packages/conform-validator/README.md +++ b/packages/conform-validator/README.md @@ -1,5 +1,7 @@ # Conform validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=conform-validator)](https://codecov.io/github/honojs/middleware) + The validator middleware using [conform](https://conform.guide) for [Hono](https://honojs.dev) applications. This middleware allows you to validate submitted FormValue and making better use of [Hono RPC](https://hono.dev/docs/guides/rpc). ## Usage diff --git a/packages/effect-validator/README.md b/packages/effect-validator/README.md index 2d386e21..84e1c694 100644 --- a/packages/effect-validator/README.md +++ b/packages/effect-validator/README.md @@ -1,5 +1,7 @@ # Effect Schema Validator Middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=effect-validator)](https://codecov.io/github/honojs/middleware) + This package provides a validator middleware using [Effect Schema](https://github.com/Effect-TS/effect/blob/main/packages/schema/README.md) for [Hono](https://honojs.dev) applications. With this middleware, you can define schemas using Effect Schema and validate incoming data in your Hono routes. ## Why Effect Schema? diff --git a/packages/esbuild-transpiler/README.md b/packages/esbuild-transpiler/README.md index 52388835..44de0f3b 100644 --- a/packages/esbuild-transpiler/README.md +++ b/packages/esbuild-transpiler/README.md @@ -1,5 +1,7 @@ # esbuild Transpiler Middleware +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=esbuild-transpiler)](https://codecov.io/github/honojs/middleware) + The **esbuild Transpiler Middleware** is a Hono Middleware designed to transpile content such as TypeScript or TSX. You can place your script written in TypeScript in a directory and serve it using `serveStatic`. When you apply this Middleware, the script will be transpiled into JavaScript code. diff --git a/packages/event-emitter/README.md b/packages/event-emitter/README.md index e316d635..7c7a64c2 100644 --- a/packages/event-emitter/README.md +++ b/packages/event-emitter/README.md @@ -1,23 +1,26 @@ # Event Emitter middleware for Hono + +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=event-emitter)](https://codecov.io/github/honojs/middleware) + ### Minimal, lightweight and edge compatible Event Emitter middleware for [Hono](https://github.com/honojs/hono). ## Table of Contents + 1. [Introduction](#introduction) 2. [Installation](#installation) 3. [Usage Examples](#usage-examples) - - [1. As Hono middleware](#1-as-hono-middleware) - - [2. Standalone](#2-standalone) + - [1. As Hono middleware](#1-as-hono-middleware) + - [2. Standalone](#2-standalone) 4. [API Reference](#api-reference) - - [emitter](#emitter) - - [createEmitter](#createemitter) - - [defineHandler](#definehandler) - - [defineHandlers](#definehandlers) - - [Emitter API Documentation](#emitter) + - [emitter](#emitter) + - [createEmitter](#createemitter) + - [defineHandler](#definehandler) + - [defineHandlers](#definehandlers) + - [Emitter API Documentation](#emitter) 5. [Types](#types) ## Introduction - This library provides an event emitter middleware for Hono, allowing you to easily implement and manage event-driven architectures in your Hono applications. It enables event driven logic flow, allowing you to decouple your code and make it more modular and maintainable. @@ -52,13 +55,13 @@ bun install @hono/event-emitter // Define event handlers export const handlers = { 'user:created': [ - (c, payload) => {} // c is current Context, payload is whatever the emit method passes + (c, payload) => {}, // c is current Context, payload is whatever the emit method passes ], 'user:deleted': [ - async (c, payload) => {} // c is current Context, payload is whatever the emit method passes + async (c, payload) => {}, // c is current Context, payload is whatever the emit method passes ], - 'foo': [ - (c, payload) => {} // c is current Context, payload is whatever the emit method passes + foo: [ + (c, payload) => {}, // c is current Context, payload is whatever the emit method passes ], } @@ -68,7 +71,6 @@ export const handlers = { // // ... // console.log('New foo created:', payload) // } - ``` ```js @@ -115,7 +117,6 @@ but because middlewares are called on every request, you can only use named func ### 2 Standalone - ```js // events.js @@ -124,11 +125,11 @@ import { createEmitter } from '@hono/event-emitter' // Define event handlers export const handlers = { 'user:created': [ - (c, payload) => {} // c is current Context, payload will be whatever you pass to emit method + (c, payload) => {}, // c is current Context, payload will be whatever you pass to emit method ], 'user:deleted': [ - async (c, payload) => {} // c is current Context, payload will be whatever you pass to emit method - ] + async (c, payload) => {}, // c is current Context, payload will be whatever you pass to emit method + ], } // Initialize emitter with handlers @@ -141,7 +142,6 @@ const ee = createEmitter(handlers) // }) export default ee - ``` ```js @@ -154,17 +154,17 @@ import ee from './events' const app = new Hono() app.post('/users', async (c) => { - // ... - // Emit event and pass current context plus the payload - ee.emit(c, 'user:created', user) - // ... + // ... + // Emit event and pass current context plus the payload + ee.emit(c, 'user:created', user) + // ... }) app.delete('/users/:id', async (c) => { - // ... - // Emit event and pass current context plus the payload - await ee.emitAsync(c, 'user:deleted', id ) - // ... + // ... + // Emit event and pass current context plus the payload + await ee.emitAsync(c, 'user:deleted', id) + // ... }) export default app @@ -180,27 +180,25 @@ export default app import type { Emitter } from '@hono/event-emitter' export type User = { - id: string, - title: string, - role: string + id: string + title: string + role: string } export type AvailableEvents = { - // event key: payload type - 'user:created': User; - 'user:deleted': string; - 'foo': { bar: number }; -}; + // event key: payload type + 'user:created': User + 'user:deleted': string + foo: { bar: number } +} export type Env = { - Bindings: {}; - Variables: { - // Define emitter variable type - emitter: Emitter; - }; -}; - - + Bindings: {} + Variables: { + // Define emitter variable type + emitter: Emitter + } +} ``` ```ts @@ -212,11 +210,11 @@ import { AvailableEvents } from './types' // Define event handlers export const handlers = defineHandlers({ 'user:created': [ - (c, user) => {} // c is current Context, payload will be correctly inferred as User + (c, user) => {}, // c is current Context, payload will be correctly inferred as User ], 'user:deleted': [ - async (c, payload) => {} // c is current Context, payload will be inferred as string - ] + async (c, payload) => {}, // c is current Context, payload will be inferred as string + ], }) // You can also define single event handler as named function using defineHandler to leverage typings @@ -225,7 +223,6 @@ export const handlers = defineHandlers({ // // ... // console.log('Foo:', payload) // }) - ``` ```ts @@ -277,35 +274,39 @@ but because middlewares are called on every request, you can only use named func // types.ts type User = { - id: string, - title: string, + id: string + title: string role: string } type AvailableEvents = { // event key: payload type - 'user:created': User; - 'user:updated': User; - 'user:deleted': string, - 'foo': { bar: number }; + 'user:created': User + 'user:updated': User + 'user:deleted': string + foo: { bar: number } } - ``` ```ts // events.ts -import { createEmitter, defineHandlers, type Emitter, type EventHandlers } from '@hono/event-emitter' +import { + createEmitter, + defineHandlers, + type Emitter, + type EventHandlers, +} from '@hono/event-emitter' import { AvailableEvents } from './types' // Define event handlers export const handlers = defineHandlers({ 'user:created': [ - (c, user) => {} // c is current Context, payload will be correctly inferred as User + (c, user) => {}, // c is current Context, payload will be correctly inferred as User ], 'user:deleted': [ - async (c, payload) => {} // c is current Context, payload will be inferred as string - ] + async (c, payload) => {}, // c is current Context, payload will be inferred as string + ], }) // You can also define single event handler using defineHandler to leverage typings @@ -318,12 +319,12 @@ const ee = createEmitter(handlers) // And you can add more listeners on the fly. // Here you can use anonymous or closure function because .on() is only called once. -ee.on('foo', async (c, payload) => { // Payload will be correctly inferred as User - console.log('User updated:', payload) +ee.on('foo', async (c, payload) => { + // Payload will be correctly inferred as User + console.log('User updated:', payload) }) export default ee - ``` ```ts @@ -345,7 +346,7 @@ app.post('/user', async (c) => { app.delete('/user/:id', async (c) => { // ... // Emit event and pass current context plus the payload (string) - ee.emit(c, 'user:deleted', id ) + ee.emit(c, 'user:deleted', id) // ... }) @@ -355,26 +356,29 @@ export default app ## API Reference ### emitter + Creates a Hono middleware that adds an event emitter to the context. ```ts function emitter( - eventHandlers?: EventHandlers, - options?: EventEmitterOptions + eventHandlers?: EventHandlers, + options?: EventEmitterOptions ): MiddlewareHandler ``` #### Parameters + - `eventHandlers` - (optional): An object containing initial event handlers. Each key is event name and value is array of event handlers. Use `defineHandlers` function to create fully typed event handlers. - `options` - (optional): An object containing options for the emitter. Currently, the only option is `maxHandlers`, which is the maximum number of handlers that can be added to an event. The default is `10`. #### Returns + A Hono middleware function that adds an `Emitter` instance to the context under the key 'emitter'. #### Example ```ts -app.use(emitter(eventHandlers)); +app.use(emitter(eventHandlers)) ``` ### createEmitter @@ -383,12 +387,13 @@ Creates new instance of event emitter with provided handlers. This is usefull wh ```ts function createEmitter( - eventHandlers?: EventHandlers, - options?: EventEmitterOptions + eventHandlers?: EventHandlers, + options?: EventEmitterOptions ): Emitter ``` #### Parameters + - `eventHandlers` - (optional): An object containing initial event handlers. Each key is event name and value is array of event handlers. - `options` - (optional): An object containing options for the emitter. Currently, the only option is `maxHandlers`, which is the maximum number of handlers that can be added to an event. The default is `10`. @@ -399,7 +404,7 @@ An `Emitter` instance: #### Example ```ts -const ee = createEmitter(eventHandlers); +const ee = createEmitter(eventHandlers) ``` ### defineHandler @@ -408,14 +413,16 @@ A utility function to define a typed event handler. ```ts function defineHandler( - handler: EventHandler, + handler: EventHandler ): EventHandler ``` #### Parameters + - `handler`: The event handler function to be defined. #### Type parameters + - `EPMap`: The available event key to payload map i.e.: `type AvailableEvents = { 'user:created': { name: string } };`. - `Key`: The key of the event type. - `E`: (optional) - The Hono environment, so that the context within the handler has the right info. @@ -428,11 +435,11 @@ The same event handler function with proper type inference. ```ts type AvailableEvents = { - 'user:created': { name: string }; -}; + 'user:created': { name: string } +} const handler = defineHandler((c, payload) => { - console.log('New user created:', payload) + console.log('New user created:', payload) }) ``` @@ -441,15 +448,17 @@ const handler = defineHandler((c, payload) => { A utility function to define multiple typed event handlers. ```ts -function defineHandlers( - handlers: { [K in keyof EPMap]?: EventHandler[] }, -): { [K in keyof EPMap]?: EventHandler[] } +function defineHandlers(handlers: { + [K in keyof EPMap]?: EventHandler[] +}): { [K in keyof EPMap]?: EventHandler[] } ``` #### Parameters + - `handlers`: An object containing event handlers for multiple event types/keys. #### Type parameters + - `EPMap`: The available event key to payload map i.e.: `type AvailableEvents = { 'user:created': { name: string } };`. - `E`: (optional) - The Hono environment, so that the context within the handler has the right info. @@ -461,18 +470,20 @@ The same handlers object with proper type inference. ```ts type AvailableEvents = { - 'user:created': { name: string }; -}; + 'user:created': { name: string } +} const handlers = defineHandlers({ - 'user:created': [ - (c, payload) => { - console.log('New user created:', pyload) - } - ] + 'user:created': [ + (c, payload) => { + console.log('New user created:', pyload) + }, + ], }) ``` + ## Emitter instance methods + The `Emitter` interface provides methods for managing and triggering events. Here's a detailed look at each method: ### on @@ -483,8 +494,8 @@ Adds an event handler for the specified event key. ```ts function on( - key: Key, - handler: EventHandler + key: Key, + handler: EventHandler ): void ``` @@ -493,8 +504,8 @@ function on( - `key`: The event key to listen for. Must be a key of `EventHandlerPayloads`. - `handler`: The function to be called when the event is emitted. If using within a Hono middleware or request handler, do not use anonymous or closure functions! It should accept two parameters: - - `c`: The current Hono context object. - - `payload`: The payload passed when the event is emitted. The type of the payload is inferred from the `EventHandlerPayloads` type. + - `c`: The current Hono context object. + - `payload`: The payload passed when the event is emitted. The type of the payload is inferred from the `EventHandlerPayloads` type. #### Returns @@ -503,33 +514,36 @@ function on( #### Example Using outside the Hono middleware or request handler: + ```ts type AvailableEvents = { - 'user:created': { name: string }; -}; -const ee = createEmitter(); + 'user:created': { name: string } +} +const ee = createEmitter() // If adding event handler outside of Hono middleware or request handler, you can use both, named or anonymous function. ee.on('user:created', (c, user) => { - console.log('New user created:', user) + console.log('New user created:', user) }) ``` + Using within Hono middleware or request handler: + ```ts type AvailableEvents = { - 'user:created': { name: string }; -}; + 'user:created': { name: string } +} // Define event handler as named function, outside of the Hono middleware or request handler to prevent duplicates/memory leaks const namedHandler = defineHandler((c, user) => { - console.log('New user created:', user) + console.log('New user created:', user) }) -app.use(emitter()); +app.use(emitter()) app.use((c, next) => { - c.get('emitter').on('user:created', namedHandler) - return next() + c.get('emitter').on('user:created', namedHandler) + return next() }) ``` @@ -541,41 +555,42 @@ Removes an event handler for the specified event key. ```ts function off( - key: Key, - handler?: EventHandler + key: Key, + handler?: EventHandler ): void ``` #### Parameters + - `key`: The event key to remove the handler from. Must be a key of `EventPayloadMap`. - `handler` (optional): The specific handler function to remove. If not provided, all handlers for the given key will be removed. #### Returns + `void` #### Example ```ts type AvailableEvents = { - 'user:created': { name: string }; -}; + 'user:created': { name: string } +} -const ee = createEmitter(); +const ee = createEmitter() const logUser = defineHandler((c, user) => { - console.log(`User: ${user.name}`); -}); + console.log(`User: ${user.name}`) +}) -ee.on('user:created', logUser); +ee.on('user:created', logUser) // Later, to remove the specific handler: -ee.off('user:created', logUser); +ee.off('user:created', logUser) // Or to remove all handlers for 'user:created': -ee.off('user:created'); +ee.off('user:created') ``` - ### emit Synchronously emits an event with the specified key and payload. @@ -591,6 +606,7 @@ emit( ``` #### Parameters + - `c`: The current Hono context object. - `key`: The event key to emit. Must be a key of `EventPayloadMap`. - `payload`: The payload to pass to the event handlers. The type of the payload is inferred from the `EventPayloadMap` type. @@ -603,9 +619,9 @@ emit( ```ts app.post('/users', (c) => { - const user = { name: 'Alice' }; - c.get('emitter').emit(c, 'user:created', user); -}); + const user = { name: 'Alice' } + c.get('emitter').emit(c, 'user:created', user) +}) ``` ### emitAsync @@ -624,13 +640,14 @@ emitAsync( ``` #### Parameters + - `c`: The current Hono context object. - `key`: The event key to emit. Must be a key of `EventPayloadMap`. - `payload`: The payload to pass to the event handlers. The type of the payload is inferred from the `EventPayloadMap` type. - `options` (optional): An object containing options for the asynchronous emission. Currently, the only option is `mode`, which can be `'concurrent'` (default) or `'sequencial'`. - - The `'concurrent'` mode will call all handlers concurrently (at the same time) and resolve or reject (with aggregated errors) after all handlers settle. - - The `'sequencial'` mode will call handlers one by one and resolve when all handlers are done or reject when the first error is thrown, not executing rest of the handlers. + - The `'concurrent'` mode will call all handlers concurrently (at the same time) and resolve or reject (with aggregated errors) after all handlers settle. + - The `'sequencial'` mode will call handlers one by one and resolve when all handlers are done or reject when the first error is thrown, not executing rest of the handlers. #### Returns @@ -640,15 +657,16 @@ emitAsync( ```ts app.post('/users', async (c) => { - const user = { name: 'Alice' }; - await c.get('emitter').emitAsync(c, 'user:created', user); - // await c.get('emitter').emitAsync(c, 'user:created', user, { mode: 'sequencial' }); -}); + const user = { name: 'Alice' } + await c.get('emitter').emitAsync(c, 'user:created', user) + // await c.get('emitter').emitAsync(c, 'user:created', user, { mode: 'sequencial' }); +}) ``` ## Types ### EventKey + A string literal type representing an event key. ```ts @@ -656,6 +674,7 @@ type EventKey = string | symbol ``` ### EventHandler + A function type that handles an event. ```ts @@ -663,6 +682,7 @@ type EventHandler = (c: Context, payload: T) => void ``` ### EventHandlers + An object type containing event handlers for multiple event types/keys. ```ts @@ -670,6 +690,7 @@ type EventHandlers = { [K in keyof T]?: EventHandler An object type containing options for the `Emitter` class. ```ts -type EventEmitterOptions = { maxHandlers?: number }; +type EventEmitterOptions = { maxHandlers?: number } ``` ### EmitAsyncOptions + An object type containing options for the `emitAsync` method. ```ts type EmitAsyncOptions = { - mode?: 'concurrent' | 'sequencial' + mode?: 'concurrent' | 'sequencial' } ``` @@ -699,45 +721,65 @@ An interface representing an event emitter. ```ts interface Emitter { - on(key: Key, handler: EventHandler): void; - off(key: Key, handler?: EventHandler): void; - emit(c: Context, key: Key, payload: EventPayloadMap[Key]): void; - emitAsync( - c: Context, - key: Key, - payload: EventPayloadMap[Key], - options?: EmitAsyncOptions - ): Promise; + on(key: Key, handler: EventHandler): void + off( + key: Key, + handler?: EventHandler + ): void + emit(c: Context, key: Key, payload: EventPayloadMap[Key]): void + emitAsync( + c: Context, + key: Key, + payload: EventPayloadMap[Key], + options?: EmitAsyncOptions + ): Promise } ``` For more usage examples, see the [tests](src/index.test.ts) or [Hono REST API starter kit](https://github.com/DavidHavl/hono-rest-api-starter) ## FAQ + ### What the heck is event emitter and why should I use it? + Event emitter is a pattern that allows you to decouple your code and make it more modular and maintainable. It's a way to implement the observer pattern in your application. It's especially useful in larger projects or projects with a lot of interactions between features. Just imagine you have a user registration feature, and you want to send a welcome email after the user is created. You can do this by emitting an event `user:created` and then listen to this event in another part of your application (e.g. email service). + ### How is this different to the built-in EventEmitter in Node.js? + The build-in EventEmitter has huge API surface, weak TypeScript support and does only synchronous event emitting. Hono's event emitter is designed to be minimal, lightweight, edge compatible and fully typed. Additionally, it supports async event handlers. + ### Is there a way to define event handlers with types? + Yes, you can use `defineHandlers` and `defineHandler` functions to define event handlers with types. This way you can leverage TypeScript's type inference and get better type checking. + ### Does it support async event handlers? + Yes, it does. You can use async functions as event handlers and emit the events using `emitAsync` method. + ### What happens if I emit an event that has no handlers? + Nothing. The event will be emitted, but no handlers will be called. + ### Using `emitAsync` function, what happens if one or more of the handlers reject? + - If using `{ mode = 'concurrent' }` in the options (which is the default), it will call all handlers concurrently (at the same time) and resolve or reject (with aggregated errors) after all handlers settle. - If using `{ mode = 'sequencial' }` in the options, it will call handlers one by one and resolve when all handlers are done or reject when the first error is thrown, not executing rest of the handlers. + ### Is it request scoped? + No, by design it's not request scoped. The same Emitter instance is shared across all requests. This aproach prevents memory leaks (especially when using closures or dealing with large data structures within the handlers) and additional strain on Javascript garbage collector. + ### Why can't I use anonymous functions or closures as event handlers when adding them inside of middleware? + This is because middleware or request handlers run repeatedly on every request, and because anonymous functions are created as new unique object in memory every time, you would be instructing the event emitter to add new handler for same key every time the request/middleware runs. Since they are each different objects in memory they can't be checked for equality and would result in memory leaks and duplicate handlers. You should use named functions if you really want to use the `on()` method inside of middleware or request handler. + ## Author David Havl diff --git a/packages/firebase-auth/README.md b/packages/firebase-auth/README.md index 60adf094..84b9e9df 100644 --- a/packages/firebase-auth/README.md +++ b/packages/firebase-auth/README.md @@ -1,5 +1,7 @@ # Hono Firebase Auth middleware for Cloudflare Workers +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=firebase-auth)](https://codecov.io/github/honojs/middleware) + This is a Firebase Auth middleware library for [Hono](https://github.com/honojs/hono) which is used [firebase-auth-cloudflare-workers](https://github.com/Code-Hex/firebase-auth-cloudflare-workers). Currently only Cloudflare Workers are supported officially. However, it may work in other environments as well, so please let us know in an issue if it works. @@ -106,23 +108,23 @@ If not specified, check the [`FIREBASE_AUTH_EMULATOR_HOST` environment variable ```ts import { Hono } from 'hono' -import { setCookie } from 'hono/cookie'; -import { csrf } from 'hono/csrf'; -import { html } from 'hono/html'; +import { setCookie } from 'hono/cookie' +import { csrf } from 'hono/csrf' +import { html } from 'hono/html' import { VerifySessionCookieFirebaseAuthConfig, VerifyFirebaseAuthEnv, verifySessionCookieFirebaseAuth, getFirebaseToken, } from '@hono/firebase-auth' -import { AdminAuthApiClient, ServiceAccountCredential } from 'firebase-auth-cloudflare-workers'; +import { AdminAuthApiClient, ServiceAccountCredential } from 'firebase-auth-cloudflare-workers' const config: VerifySessionCookieFirebaseAuthConfig = { // specify your firebase project ID. projectId: 'your-project-id', redirects: { - signIn: "/login" - } + signIn: '/login', + }, } // You can specify here the extended VerifyFirebaseAuthEnv type. @@ -140,7 +142,7 @@ type MyEnv = VerifyFirebaseAuthEnv & { const app = new Hono<{ Bindings: MyEnv }>() // set middleware -app.get('/login', csrf(), async c => { +app.get('/login', csrf(), async (c) => { // You can copy code from here // https://github.com/Code-Hex/firebase-auth-cloudflare-workers/blob/0ce610fff257b0b60e2f8e38d89c8e012497d537/example/index.ts#L63C25-L63C37 const content = await html`...` @@ -148,13 +150,13 @@ app.get('/login', csrf(), async c => { }) app.post('/login_session', csrf(), (c) => { - const json = await c.req.json(); - const idToken = json.idToken; + const json = await c.req.json() + const idToken = json.idToken if (!idToken || typeof idToken !== 'string') { - return c.json({ message: 'invalid idToken' }, 400); + return c.json({ message: 'invalid idToken' }, 400) } // Set session expiration to 5 days. - const expiresIn = 60 * 60 * 24 * 5 * 1000; + const expiresIn = 60 * 60 * 24 * 5 * 1000 // Create the session cookie. This will also verify the ID token in the process. // The session cookie will have the same claims as the ID token. @@ -163,26 +165,22 @@ app.post('/login_session', csrf(), (c) => { const auth = AdminAuthApiClient.getOrInitialize( c.env.PROJECT_ID, new ServiceAccountCredential(c.env.SERVICE_ACCOUNT_JSON) - ); - const sessionCookie = await auth.createSessionCookie( - idToken, - expiresIn, - ); + ) + const sessionCookie = await auth.createSessionCookie(idToken, expiresIn) setCookie(c, 'session', sessionCookie, { maxAge: expiresIn, httpOnly: true, - secure: true - }); - return c.json({ message: 'success' }); + secure: true, + }) + return c.json({ message: 'success' }) }) -app.use('/admin/*', csrf(), verifySessionCookieFirebaseAuth(config)); +app.use('/admin/*', csrf(), verifySessionCookieFirebaseAuth(config)) app.get('/admin/hello', (c) => { const idToken = getFirebaseToken(c) // get id-token object. return c.json(idToken) }) - export default app ``` diff --git a/packages/graphql-server/README.md b/packages/graphql-server/README.md index a8caf992..a0f2d84e 100644 --- a/packages/graphql-server/README.md +++ b/packages/graphql-server/README.md @@ -1,5 +1,7 @@ # GraphQL Server Middleware +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=graphql-server)](https://codecov.io/github/honojs/middleware) + ## Requirements This middleware depends on [GraphQL.js](https://www.npmjs.com/package/graphql). diff --git a/packages/hello/README.md b/packages/hello/README.md index 99cda50b..29b390bb 100644 --- a/packages/hello/README.md +++ b/packages/hello/README.md @@ -1,5 +1,7 @@ # Hello middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=hello)](https://codecov.io/github/honojs/middleware) + An example project of the third-party middleware for [Hono](https://github.com/honojs/hono). This middleware add `X-Message` header to the Response. diff --git a/packages/medley-router/README.md b/packages/medley-router/README.md index 0c10a3bb..4e4deebf 100644 --- a/packages/medley-router/README.md +++ b/packages/medley-router/README.md @@ -1,5 +1,7 @@ # Router using @medley/router +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=medley-router)](https://codecov.io/github/honojs/middleware) + Just a PoC. ## Usage diff --git a/packages/node-ws/README.md b/packages/node-ws/README.md index 47fa20b4..44aa060a 100644 --- a/packages/node-ws/README.md +++ b/packages/node-ws/README.md @@ -1,5 +1,7 @@ # WebSocket helper for Node.js +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=node-ws)](https://codecov.io/github/honojs/middleware) + A WebSocket helper for Node.js ## Usage @@ -13,9 +15,12 @@ const app = new Hono() const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app }) -app.get('/ws', upgradeWebSocket((c) => ({ - // https://hono.dev/helpers/websocket -}))) +app.get( + '/ws', + upgradeWebSocket((c) => ({ + // https://hono.dev/helpers/websocket + })) +) const server = serve(app) injectWebSocket(server) @@ -27,4 +32,4 @@ Shotaro Nakamura ## License -MIT \ No newline at end of file +MIT diff --git a/packages/oauth-providers/README.md b/packages/oauth-providers/README.md index 61dd147a..fd0449ce 100644 --- a/packages/oauth-providers/README.md +++ b/packages/oauth-providers/README.md @@ -1,5 +1,7 @@ # OAuth Providers Middleware +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=oauth-providers)](https://codecov.io/github/honojs/middleware) + Authentication middleware for [Hono](https://github.com/honojs/hono). This package offers a straightforward API for social login with platforms such as Facebook, GitHub, Google, LinkedIn and X(Twitter). ## Installation @@ -1079,7 +1081,6 @@ You can validate a Twitch access token to verify it's still valid or to obtain i You can use `validateToken` method, which accepts the `token` to be validated as parameter and returns `TwitchValidateSuccess` if valid or throws `HTTPException` upon failure. - > **IMPORTANT:** Twitch requires applications to validate OAuth tokens when they start and on an hourly basis thereafter. Failure to validate tokens may result in Twitch taking punitive action, such as revoking API keys or throttling performance. When a token becomes invalid, your app should terminate all sessions using that token immediately. [Read more](https://dev.twitch.tv/docs/authentication/validate-tokens) The validation endpoint helps your application detect when tokens become invalid for reasons other than expiration, such as when users disconnect your integration from their Twitch account. When a token becomes invalid, your app should terminate all sessions using that token. @@ -1099,36 +1100,39 @@ This parameters can be useful if 3. Or, in need to encode more info into `redirect_uri`. ```ts -const app = new Hono(); +const app = new Hono() -const SITE_ORIGIN = `https://my-site.com`; -const OAUTH_CALLBACK_PATH = `/oauth/google`; +const SITE_ORIGIN = `https://my-site.com` +const OAUTH_CALLBACK_PATH = `/oauth/google` -app.get('/*', +app.get( + '/*', async (c, next) => { - const session = readSession(c); + const session = readSession(c) if (!session) { // start oauth flow - const redirectUri = `${SITE_ORIGIN}${OAUTH_CALLBACK_PATH}?redirect=${encodeURIComponent(c.req.path)}`; - const oauth = googleAuth({ redirect_uri: redirectUri, ...more }); + const redirectUri = `${SITE_ORIGIN}${OAUTH_CALLBACK_PATH}?redirect=${encodeURIComponent( + c.req.path + )}` + const oauth = googleAuth({ redirect_uri: redirectUri, ...more }) return await oauth(c, next) } }, async (c, next) => { // if we are here, the req should contain either a valid session or a valid auth code - const session = readSession(c); + const session = readSession(c) const authedGoogleUser = c.get('user-google') if (authedGoogleUser) { - await saveSession(c, authedGoogleUser); + await saveSession(c, authedGoogleUser) } else if (!session) { throw new HttpException(401) } - return next(); + return next() }, async (c, next) => { // serve protected content } -); +) ``` ## Author diff --git a/packages/oidc-auth/README.md b/packages/oidc-auth/README.md index 10158dd5..2c89669b 100644 --- a/packages/oidc-auth/README.md +++ b/packages/oidc-auth/README.md @@ -1,5 +1,7 @@ # OpenID Connect Authentication middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=oidc-auth)](https://codecov.io/github/honojs/middleware) + This is an OpenID Connect (OIDC) authentication third-party middleware for [Hono](https://github.com/honojs/hono), which depends on [oauth4webapi](https://www.npmjs.com/package/oauth4webapi). This middleware provides storage-less login sessions. @@ -144,7 +146,7 @@ If the middleware is applied to the callback URL, the default callback handling ```typescript // Before other oidc-auth APIs are used -app.use(initOidcAuthMiddleware(config)); +app.use(initOidcAuthMiddleware(config)) ``` Or to leverage context, use the [`Context access inside Middleware arguments`](https://hono.dev/docs/guides/middleware#context-access-inside-middleware-arguments) pattern. diff --git a/packages/otel/README.md b/packages/otel/README.md index b85f7f47..95927b69 100644 --- a/packages/otel/README.md +++ b/packages/otel/README.md @@ -1,5 +1,7 @@ # OpenTelemetry middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=otel)](https://codecov.io/github/honojs/middleware) + This package provides a [Hono](https://hono.dev/) middleware that instruments your application with [OpenTelemetry](https://opentelemetry.io/). ## Usage diff --git a/packages/prometheus/README.md b/packages/prometheus/README.md index 741feb50..4b0a5373 100644 --- a/packages/prometheus/README.md +++ b/packages/prometheus/README.md @@ -1,5 +1,7 @@ # Prometheus middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=prometheus)](https://codecov.io/github/honojs/middleware) + This middleware adds basic [RED metrics](https://www.weave.works/blog/the-red-method-key-metrics-for-microservices-architecture/) to your Hono application, and exposes them on the `/metrics` endpoint for Prometheus to scrape. ## Installation @@ -81,13 +83,13 @@ An options object can be passed in the `prometheus()` middleware factory to conf ### `prefix` -Type: *string* +Type: _string_ Prefix all metrics with this string. ### `registry` -Type: *[Registry](https://www.npmjs.com/package/prom-client)* +Type: _[Registry](https://www.npmjs.com/package/prom-client)_ A prom-client Registry instance to store the metrics. If not provided, a new one will be created. @@ -95,7 +97,7 @@ Useful when you want to register some custom metrics while exposing them on the ### `collectDefaultMetrics` -Type: *boolean | [CollectDefaultMetricsOptions](https://www.npmjs.com/package/prom-client#default-metrics)* +Type: _boolean | [CollectDefaultMetricsOptions](https://www.npmjs.com/package/prom-client#default-metrics)_ There are some default metrics recommended by prom-client, like event loop delay, garbage collection statistics etc. @@ -103,34 +105,37 @@ To enable these metrics, set this option to `true`. To configure the default met ### `metricOptions` -Type: *object (see below)* +Type: _object (see below)_ -Modify the standard metrics (*requestDuration* and *requestsTotal*) with any of the [Counter](https://www.npmjs.com/package/prom-client#counter) / [Histogram](https://www.npmjs.com/package/prom-client#histogram) metric options, including: +Modify the standard metrics (_requestDuration_ and _requestsTotal_) with any of the [Counter](https://www.npmjs.com/package/prom-client#counter) / [Histogram](https://www.npmjs.com/package/prom-client#histogram) metric options, including: #### `disabled` -Type: *boolean* +Type: _boolean_ Disables the metric. #### `customLabels` -Type: *Record string>* +Type: _Record string>_ A record where the keys are the labels to add to the metrics, and the values are functions that receive the Hono context and return the value for that label. This is useful when adding labels to the metrics that are specific to your application or your needs. These functions are executed after all the other middlewares finished. -The following example adds a label to the *requestsTotal* metric with the `contentType` name where the value is the content type of the response: +The following example adds a label to the _requestsTotal_ metric with the `contentType` name where the value is the content type of the response: ```ts -app.use('*', prometheus({ - metricOptions: { - requestsTotal: { - customLabels: { - content_type: (c) => c.res.headers.get('content-type'), - } +app.use( + '*', + prometheus({ + metricOptions: { + requestsTotal: { + customLabels: { + content_type: (c) => c.res.headers.get('content-type'), + }, + }, }, - } -})) + }) +) ``` ## Examples diff --git a/packages/qwik-city/README.md b/packages/qwik-city/README.md index ee8921fb..d73f6db7 100644 --- a/packages/qwik-city/README.md +++ b/packages/qwik-city/README.md @@ -1,5 +1,7 @@ # Qwik City middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=qwik-city)](https://codecov.io/github/honojs/middleware) + **WIP** ## Usage diff --git a/packages/react-compat/README.md b/packages/react-compat/README.md index 750b2334..f5199350 100644 --- a/packages/react-compat/README.md +++ b/packages/react-compat/README.md @@ -1,5 +1,7 @@ # Alias of hono/jsx for replacement of React +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=react-compat)](https://codecov.io/github/honojs/middleware) + This package is used to install the React compatibility API provided by [Hono](https://github.com/honojs/hono). This package allows you to replace the "react" and "react-dom" entities with "@hono/react-compat". ## Usage diff --git a/packages/react-renderer/README.md b/packages/react-renderer/README.md index 1adad50f..bcfca20c 100644 --- a/packages/react-renderer/README.md +++ b/packages/react-renderer/README.md @@ -1,5 +1,7 @@ # React Renderer Middleware +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=react-renderer)](https://codecov.io/github/honojs/middleware) + React Renderer Middleware allows for the easy creation of a renderer based on React for Hono. ## Installation diff --git a/packages/sentry/README.md b/packages/sentry/README.md index 43a5df47..ed115e91 100644 --- a/packages/sentry/README.md +++ b/packages/sentry/README.md @@ -1,5 +1,7 @@ # Sentry Middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=sentry)](https://codecov.io/github/honojs/middleware) + This middleware integrates [Hono](https://github.com/honojs/hono) with Sentry. It captures exceptions and sends them to the specified Sentry data source name (DSN) using [toucan-js](https://github.com/robertcepa/toucan-js). ## Installation diff --git a/packages/standard-validator/README.md b/packages/standard-validator/README.md index 3df6e9d1..d29ea21f 100644 --- a/packages/standard-validator/README.md +++ b/packages/standard-validator/README.md @@ -1,12 +1,14 @@ # Standard Schema validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=standard-validator)](https://codecov.io/github/honojs/middleware) + The validator middleware using [Standard Schema Spec](https://github.com/standard-schema/standard-schema) for [Hono](https://honojs.dev) applications. You can write a schema with any validation library supporting Standard Schema and validate the incoming values. ## Usage - ### Basic: + ```ts import { z } from 'zod' import { sValidator } from '@hono/standard-validator' @@ -14,7 +16,7 @@ import { sValidator } from '@hono/standard-validator' const schema = z.object({ name: z.string(), age: z.number(), -}); +}) app.post('/author', sValidator('json', schema), (c) => { const data = c.req.valid('json') @@ -26,6 +28,7 @@ app.post('/author', sValidator('json', schema), (c) => { ``` ### Hook: + ```ts app.post( '/post', @@ -39,15 +42,17 @@ app.post( ``` ### Headers: + Headers are internally transformed to lower-case in Hono. Hence, you will have to make them lower-cased in validation object. + ```ts import { object, string } from 'valibot' import { sValidator } from '@hono/standard-validator' const schema = object({ 'content-type': string(), - 'user-agent': string() -}); + 'user-agent': string(), +}) app.post('/author', sValidator('header', schema), (c) => { const headers = c.req.valid('header') @@ -55,7 +60,6 @@ app.post('/author', sValidator('header', schema), (c) => { }) ``` - ## Author Rokas Muningis diff --git a/packages/swagger-editor/README.md b/packages/swagger-editor/README.md index b92df570..c97de2e1 100644 --- a/packages/swagger-editor/README.md +++ b/packages/swagger-editor/README.md @@ -1,5 +1,7 @@ # Swagger Editor Middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=swagger-editor)](https://codecov.io/github/honojs/middleware) + This library, `@hono/swagger-editor` is the middleware for integrating Swagger Editor with Hono applications. The Swagger Editor is an open source editor to design, define and document RESTful APIs in the Swagger Specification. ## Installation @@ -14,7 +16,6 @@ yarn add @hono/swagger-editor You can use the `swaggerEditor` middleware to serve Swagger Editor on a specific route in your Hono application. Here's how you can do it: - ```ts import { Hono } from 'hono' import { swaggerUI } from '@hono/swagger-ui' @@ -37,4 +38,4 @@ Middleware supports almost all swagger-editor options. See full documentation: < ## License -MIT \ No newline at end of file +MIT diff --git a/packages/swagger-ui/README.md b/packages/swagger-ui/README.md index 25a9f338..23560384 100644 --- a/packages/swagger-ui/README.md +++ b/packages/swagger-ui/README.md @@ -1,5 +1,7 @@ # Swagger UI Middleware and Component for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=swagger-ui)](https://codecov.io/github/honojs/middleware) + This library, `@hono/swagger-ui`, provides a middleware and a component for integrating Swagger UI with Hono applications. Swagger UI is an interactive documentation interface for APIs compliant with the OpenAPI Specification, making it easier to understand and test API endpoints. ## Installation @@ -83,16 +85,16 @@ app.openapi( content: { 'application/json': { schema: z.object({ - message: z.string() - }) - } - } - } - } + message: z.string(), + }), + }, + }, + }, + }, }), (c) => { return c.json({ - message: 'hello' + message: 'hello', }) } ) @@ -100,16 +102,16 @@ app.openapi( app.get( '/ui', swaggerUI({ - url: '/doc' + url: '/doc', }) ) app.doc('/doc', { info: { title: 'An API', - version: 'v1' + version: 'v1', }, - openapi: '3.1.0' + openapi: '3.1.0', }) export default app @@ -124,11 +126,10 @@ The following options are available: - `version` (string, optional): The version of Swagger UI to use, defaults to `latest`. - `manuallySwaggerUIHtml` (string, optional): If you want to use your own custom HTML, you can specify it here. If this option is specified, the all options except `version` will be ignored. -and most of options from [Swagger UI]( - https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/ -) are supported as well. +and most of options from [Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) are supported as well. such as: + - `url` (string, optional): The URL pointing to the OpenAPI definition (v2 or v3) that describes the API. - `urls` (array, optional): An array of OpenAPI definitions (v2 or v3) that describe the APIs. Each definition must have a `name` and `url`. - `presets` (array, optional): An array of presets to use for Swagger UI. diff --git a/packages/trpc-server/README.md b/packages/trpc-server/README.md index 34b65d65..e443dd7d 100644 --- a/packages/trpc-server/README.md +++ b/packages/trpc-server/README.md @@ -1,5 +1,7 @@ # tRPC Server Middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=trpc-server)](https://codecov.io/github/honojs/middleware) + tRPC Server Middleware adapts a [tRPC](https://trpc.io) server as middleware for Hono. Hono works on almost any JavaScript runtime, including Cloudflare Workers, Deno, and Bun. So, with this middleware, the same code will run as tRPC server. @@ -76,11 +78,11 @@ import { initTRPC } from '@trpc/server' import { z } from 'zod' type Env = { - DB: D1Database; + DB: D1Database } type HonoContext = { - env: Env, -}; + env: Env +} const t = initTRPC.context().create() @@ -89,8 +91,8 @@ const router = t.router export const appRouter = router({ usersCount: publicProcedure.query(({ input, ctx }) => { - const result = await ctx.env.DB.prepare("SELECT count(*) from user;").all(); - return result.results[0].count; + const result = await ctx.env.DB.prepare('SELECT count(*) from user;').all() + return result.results[0].count }), }) @@ -108,10 +110,11 @@ app.use( // c is the hono context var1: c.env.MY_VAR1, var2: c.req.header('X-VAR2'), - }) + }), }) ) ``` + ## Custom Endpoints To set up custom endpoints ensure the endpoint parameter matches the middleware's path. This alignment allows `@trpc/server` to accurately extract your procedure paths. diff --git a/packages/tsyringe/README.md b/packages/tsyringe/README.md index 1c8fcaf6..face7e92 100644 --- a/packages/tsyringe/README.md +++ b/packages/tsyringe/README.md @@ -1,11 +1,13 @@ # tsyringe middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=tsyringe)](https://codecov.io/github/honojs/middleware) + The [tsyringe](https://github.com/microsoft/tsyringe) middleware provides a way to use dependency injection in [Hono](https://hono.dev/). ## Usage ```ts -import "reflect-metadata" // tsyringe requires reflect-metadata or polyfill +import 'reflect-metadata' // tsyringe requires reflect-metadata or polyfill import { container, inject, injectable } from 'tsyringe' import { tsyringe } from '@hono/tsyringe' import { Hono } from 'hono' @@ -21,13 +23,16 @@ class Hello { const app = new Hono() -app.use('*', tsyringe((container) => { +app.use( + '*', + tsyringe((container) => { container.register('name', { useValue: 'world' }) -})) + }) +) app.get('/', (c) => { - const hello = container.resolve(Hello) - return c.text(hello.greet()) + const hello = container.resolve(Hello) + return c.text(hello.greet()) }) export default app @@ -39,12 +44,12 @@ export default app const app = new Hono() app.use('/tenant/:name/*', async (c, next) => { - await tsyringe((container) => { - // Allowing to inject `c.var` or `c.req.param` in the providers - const tenantName = c.req.param('name') + await tsyringe((container) => { + // Allowing to inject `c.var` or `c.req.param` in the providers + const tenantName = c.req.param('name') - container.register(Config, { useFactory: () => new Config(tenantName) }) - })(c, next) + container.register(Config, { useFactory: () => new Config(tenantName) }) + })(c, next) }) ``` diff --git a/packages/typebox-validator/README.md b/packages/typebox-validator/README.md index 3a513fc4..6db897a6 100644 --- a/packages/typebox-validator/README.md +++ b/packages/typebox-validator/README.md @@ -1,5 +1,7 @@ # TypeBox validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?typebox-validator)](https://codecov.io/github/honojs/middleware) + Validator middleware using [TypeBox](https://github.com/sinclairzx81/typebox) for [Hono](https://honojs.dev) applications. Define your schema with TypeBox and validate incoming requests. diff --git a/packages/typia-validator/README.md b/packages/typia-validator/README.md index 79b950d3..8776bdd4 100644 --- a/packages/typia-validator/README.md +++ b/packages/typia-validator/README.md @@ -1,5 +1,7 @@ # Typia validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=typia-validator)](https://codecov.io/github/honojs/middleware) + The validator middleware using [Typia](https://typia.io/docs/) for [Hono](https://honojs.dev) applications. ## Usage @@ -83,7 +85,8 @@ const validate = typia.createValidate() const validateQuery = typia.http.createValidateQuery() const validateHeaders = typia.http.createValidateHeaders() -app.get('/items', +app.get( + '/items', typiaValidator('json', validate), typiaValidator('query', validateQuery), typiaValidator('header', validateHeaders), @@ -98,6 +101,7 @@ app.get('/items', } ) ``` + ## Author Patryk Dwórznik diff --git a/packages/valibot-validator/README.md b/packages/valibot-validator/README.md index f700de69..1eb10dcd 100644 --- a/packages/valibot-validator/README.md +++ b/packages/valibot-validator/README.md @@ -1,5 +1,7 @@ # Valibot validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=valibot-validator)](https://codecov.io/github/honojs/middleware) + The validator middleware using [Valibot](https://valibot.dev) for [Hono](https://honojs.dev) applications. You can write a schema with Valibot and validate the incoming values. diff --git a/packages/zod-openapi/README.md b/packages/zod-openapi/README.md index a6a4adf1..dd3d874b 100644 --- a/packages/zod-openapi/README.md +++ b/packages/zod-openapi/README.md @@ -1,5 +1,7 @@ # Zod OpenAPI Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=zod-openapi)](https://codecov.io/github/honojs/middleware) + **Zod OpenAPI Hono** is an extended Hono class that supports OpenAPI. With it, you can validate values and types using [**Zod**](https://zod.dev/) and generate OpenAPI Swagger documentation. This is based on [**Zod to OpenAPI**](https://github.com/asteasolutions/zod-to-openapi) (thanks a lot!). For details on creating schemas and defining routes, please refer to [the "Zod to OpenAPI" resource](https://github.com/asteasolutions/zod-to-openapi). _Note: This is not standalone middleware but is hosted on the monorepo "[github.com/honojs/middleware](https://github.com/honojs/middleware)"._ diff --git a/packages/zod-validator/README.md b/packages/zod-validator/README.md index 384b42e7..5a5f5acc 100644 --- a/packages/zod-validator/README.md +++ b/packages/zod-validator/README.md @@ -1,5 +1,7 @@ # Zod validator middleware for Hono +[![codecov](https://codecov.io/github/honojs/middleware/graph/badge.svg?flag=zod-validator)](https://codecov.io/github/honojs/middleware) + The validator middleware using [Zod](https://zod.dev) for [Hono](https://honojs.dev) applications. You can write a schema with Zod and validate the incoming values. @@ -43,22 +45,19 @@ throw a zod validate error instead of directly returning an error response. ```ts // file: validator-wrapper.ts -import { ZodSchema } from "zod"; -import type { ValidationTargets } from "hono"; -import { zValidator as zv } from "@hono/zod-validator"; +import { ZodSchema } from 'zod' +import type { ValidationTargets } from 'hono' +import { zValidator as zv } from '@hono/zod-validator' -export const zValidator = < - T extends ZodSchema, - Target extends keyof ValidationTargets ->( +export const zValidator = ( target: Target, schema: T ) => zv(target, schema, (result, c) => { if (!result.success) { - throw new HTTPException(400, { cause: result.error }); + throw new HTTPException(400, { cause: result.error }) } - }); + }) // usage import { zValidator } from './validator-wrapper'