@basaltkit/hono 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Machize Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,267 @@
1
+ # @basaltkit/hono
2
+
3
+ The Basalt adapter for [Hono](https://hono.dev): the same typed routes, enrichers, and guards you'd use with Fastify or Express, running on Hono — on Node.js, Bun, Deno, or *edge* platforms (Cloudflare Workers, Vercel Edge, …). You need this when you want to take your Basalt API outside classic Node, or when you're already using Hono.
4
+
5
+ ## What this module solves
6
+
7
+ [Hono](https://hono.dev) is a small, very fast web framework built on top of standard web APIs (`fetch`'s `Request`/`Response`). Because of that, it runs almost everywhere: Node.js, Bun, Deno, and *edge runtimes* — servers that execute your code in hundreds of locations close to users. But, like other frameworks, Hono on its own doesn't validate data or standardize errors.
8
+
9
+ This module connects Hono to Basalt. **Routes** (a path + HTTP method, e.g. `POST /echo`) are defined with the `route()` function from `@basaltkit/http`, with [Zod](https://zod.dev) schemas that validate the body, query, and URL parameters — and give you TypeScript types for free. The adapter converts each Hono request into Basalt's neutral format, runs the shared pipeline (validation, *enrichers* — functions that enrich the request context — and *guards* — functions that can reject it, e.g. authentication), and returns responses with errors in a stable format.
10
+
11
+ The main benefit is **portability**: a route written here runs unchanged on `@basaltkit/fastify` and `@basaltkit/express`; and the neutral edge plugins (security, health, metrics, tracing, OpenAPI) from `@basaltkit/http` work on Hono exactly the same way.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm add @basaltkit/hono @basaltkit/core @basaltkit/http hono zod
17
+ ```
18
+
19
+ `hono` (version 4+) is a *peer dependency* — you install it. To serve on Node.js you also need `pnpm add @hono/node-server`; on Bun, Deno, or edge nothing extra is needed.
20
+
21
+ ## Get started in 5 minutes
22
+
23
+ **Step 1** — install the packages (command above, plus `@hono/node-server` if you're on Node).
24
+
25
+ **Step 2** — create a `server.ts` file:
26
+
27
+ ```ts
28
+ import { serve } from '@hono/node-server'
29
+ import { createApp } from '@basaltkit/core'
30
+ import { route } from '@basaltkit/http'
31
+ import { HONO, honoPlugin } from '@basaltkit/hono'
32
+ import { z } from 'zod'
33
+
34
+ // 1. Define a route: method, URL, validation, and handler (the function that responds).
35
+ const echo = route({
36
+ method: 'POST',
37
+ url: '/echo',
38
+ body: z.object({ n: z.number() }), // the body must have a number n
39
+ async handler({ body, reply }) {
40
+ // body.n is already validated and typed as number
41
+ return reply.code(201).send({ doubled: body.n * 2 })
42
+ },
43
+ })
44
+
45
+ // 2. Create the Basalt app with the Hono plugin and boot it.
46
+ const app = await createApp({ plugins: [honoPlugin({ routes: [echo] })] }).boot()
47
+
48
+ // 3. Get the Hono app from the container and serve it on Node.
49
+ serve({ fetch: app.container.get(HONO).fetch, port: 3000 })
50
+ console.log('Listening on http://localhost:3000')
51
+ ```
52
+
53
+ **Step 3** — run it and test:
54
+
55
+ ```bash
56
+ npx tsx server.ts
57
+ curl -X POST http://localhost:3000/echo \
58
+ -H 'content-type: application/json' \
59
+ -d '{"n":21}'
60
+ # → {"doubled":42} (status 201)
61
+
62
+ curl -X POST http://localhost:3000/echo \
63
+ -H 'content-type: application/json' \
64
+ -d '{"n":"nope"}'
65
+ # → 400 {"error":{"code":"HTTP_VALIDATION","part":"body","issues":[...]}}
66
+ ```
67
+
68
+ **On an edge runtime** (Cloudflare Workers, Bun, Deno) you export `fetch` instead of calling `serve`:
69
+
70
+ ```ts
71
+ const app = await createApp({ plugins: [honoPlugin({ routes: [echo] })] }).boot()
72
+ export default app.container.get(HONO) // the runtime calls .fetch for you
73
+ ```
74
+
75
+ ## Usage guide
76
+
77
+ ### Routes with params, query, and errors
78
+
79
+ ```ts
80
+ import { HttpError, route } from '@basaltkit/http'
81
+ import { z } from 'zod'
82
+
83
+ const hello = route({
84
+ method: 'GET',
85
+ url: '/hello/:name', // :name is a dynamic URL parameter
86
+ params: z.object({ name: z.string() }),
87
+ async handler({ params }) {
88
+ return { hello: params.name }
89
+ },
90
+ })
91
+
92
+ const boom = route({
93
+ method: 'GET',
94
+ url: '/boom',
95
+ async handler() {
96
+ // Intentional error: becomes a 418 response with a stable code
97
+ throw new HttpError(418, 'TEAPOT', "I'm a teapot")
98
+ },
99
+ })
100
+ ```
101
+
102
+ Unexpected errors respond with `500` and `{ error: { code: 'INTERNAL_ERROR', ... } }` — the same format as the other adapters, without exposing internal details.
103
+
104
+ ### Request body: what the adapter interprets
105
+
106
+ The adapter reads the body based on `Content-Type`: `application/json` → JSON object; forms (`form`) → Hono's `parseBody()`; other text → string; empty or invalid body → `undefined` (Zod validation handles the rest). `GET`/`HEAD` requests never have a body.
107
+
108
+ ### Enrichers and guards (authentication, tenancy, …)
109
+
110
+ Plugins register these functions in the container's metadata "buckets"; the adapter applies them to every route. Real example (from the package's tests):
111
+
112
+ ```ts
113
+ import { createApp, definePlugin, ensureMetadata, tryCtx } from '@basaltkit/core'
114
+ import { HttpError, route, type RequestEnricher, type RouteGuard } from '@basaltkit/http'
115
+ import { HONO, honoPlugin } from '@basaltkit/hono'
116
+
117
+ // Enricher: attaches the tenant to the request context.
118
+ const enricher: RequestEnricher = ({ request, context }) => {
119
+ const tenant = request.headers['x-tenant-id']
120
+ if (typeof tenant === 'string') (context as { tenant?: unknown }).tenant = { id: tenant }
121
+ }
122
+
123
+ // Guard: rejects the request by throwing an error; reads the route's meta.
124
+ const guard: RouteGuard = ({ route: def, request }) => {
125
+ if (def.meta?.['auth'] && !request.headers['authorization']) {
126
+ throw new HttpError(401, 'AUTH_REQUIRED', 'Authentication required.')
127
+ }
128
+ }
129
+
130
+ const myPlugin = definePlugin({
131
+ name: 'my:http',
132
+ register({ container }) {
133
+ const metadata = ensureMetadata(container)
134
+ metadata.add('http:enrichers', enricher)
135
+ metadata.add('http:guards', guard)
136
+ },
137
+ })
138
+
139
+ const secure = route({
140
+ method: 'GET',
141
+ url: '/secure',
142
+ meta: { auth: true }, // the guard reads this
143
+ async handler() {
144
+ const tenant = (tryCtx() as { tenant?: { id: string } })?.tenant?.id ?? null
145
+ return { ok: true, tenant }
146
+ },
147
+ })
148
+
149
+ const app = await createApp({ plugins: [myPlugin, honoPlugin({ routes: [secure] })] }).boot()
150
+ ```
151
+
152
+ Without `Authorization` → `401 AUTH_REQUIRED`; with `x-tenant-id: acme` the handler sees `tenant: 'acme'` through the request context.
153
+
154
+ ### Neutral edge plugins
155
+
156
+ Imported from `@basaltkit/http` and work on Hono without changes:
157
+
158
+ ```ts
159
+ import { createApp } from '@basaltkit/core'
160
+ import { healthPlugin, metricsPlugin, route, securityPlugin } from '@basaltkit/http'
161
+ import { HONO, honoPlugin } from '@basaltkit/hono'
162
+
163
+ const ping = route({ method: 'GET', url: '/ping', async handler() { return { pong: true } } })
164
+
165
+ const app = await createApp({
166
+ plugins: [
167
+ honoPlugin({ routes: [ping] }),
168
+ securityPlugin({ rateLimit: { limit: 100, windowMs: 60_000 } }), // secure headers + 429 above limit
169
+ healthPlugin({ checks: { db: () => ({ ok: true }) } }), // GET /livez and /readyz
170
+ metricsPlugin(), // GET /metrics (Prometheus)
171
+ ],
172
+ }).boot()
173
+ ```
174
+
175
+ All options for these plugins are documented in the [`@basaltkit/http`](../http/README.md) README.
176
+
177
+ > On distributed edge runtimes, remember that in-memory stores (rate limit, metrics) live per instance — use shared stores (e.g. Redis/KV) when you need global values.
178
+
179
+ ### Testing without opening ports
180
+
181
+ Since Hono speaks `fetch`, testing means calling `hono.fetch` with a normal `Request` — that's how this package's own tests work:
182
+
183
+ ```ts
184
+ import { createApp } from '@basaltkit/core'
185
+ import { route } from '@basaltkit/http'
186
+ import { HONO, honoPlugin } from '@basaltkit/hono'
187
+
188
+ const ping = route({ method: 'GET', url: '/ping', async handler() { return { pong: true } } })
189
+ const app = await createApp({ plugins: [honoPlugin({ routes: [ping] })] }).boot()
190
+ const hono = app.container.get(HONO)
191
+
192
+ const res = await hono.fetch(new Request('http://local/ping'))
193
+ console.log(res.status, await res.json()) // 200 { pong: true }
194
+ await app.shutdown()
195
+ ```
196
+
197
+ ### Advanced: `registerRoutes()` without the plugin
198
+
199
+ Mount Basalt routes onto an existing Hono app, without the Basalt lifecycle:
200
+
201
+ ```ts
202
+ import { Hono } from 'hono'
203
+ import { route } from '@basaltkit/http'
204
+ import { registerRoutes } from '@basaltkit/hono'
205
+
206
+ const app = new Hono()
207
+ const ping = route({ method: 'GET', url: '/ping', async handler() { return { pong: true } } })
208
+ registerRoutes(app, [ping]) // container, enrichers, and guards are optional
209
+ export default app
210
+ ```
211
+
212
+ In this mode errors are still standardized (each handler wraps `toErrorResponse`), but there are no edge plugins and no route registration for OpenAPI/CLI.
213
+
214
+ ## API reference
215
+
216
+ ### `honoPlugin(options?)` → Basalt plugin (`basalt:hono`)
217
+
218
+ | Option | Type | Required? | Default | Description |
219
+ |---|---|---|---|---|
220
+ | `routes` | `BasaltRoute[]` | No | `[]` | Routes (created with `route()` from `@basaltkit/http`) to mount. |
221
+ | `app` | `Hono` | No | `new Hono()` | Bring your own Hono app; otherwise a new one is created. |
222
+
223
+ Behavior: registers the Hono app on the `HONO` token and an `HttpServerCollector` on the `HTTP_SERVER` token. On the `app:booted` event it mounts, in order: *after-hooks* middleware (metrics/tracing, measuring duration), *pre-hooks* middleware (security/CORS/rate limit; if one of these responds, the route doesn't run), the Basalt routes, and the extra edge plugin routes (`/livez`, `/metrics`, `/openapi.json`, …). Publishes the routes to the `'http:routes'` metadata bucket for OpenAPI/CLI/SDK.
224
+
225
+ > Note: this plugin has no `shutdown` step of its own — stopping the server (`serve` from `@hono/node-server`, etc.) is your responsibility.
226
+
227
+ ### `HONO`
228
+
229
+ Dependency injection token (`Token<Hono>`): `app.container.get(HONO)` returns the Hono app — use `hono.fetch` to serve (on Node via `@hono/node-server`, or export it in an edge runtime) and for testing.
230
+
231
+ ### `registerRoutes(app, routes, container?, enrichers?, guards?)`
232
+
233
+ | Parameter | Type | Required? | Default | Description |
234
+ |---|---|---|---|---|
235
+ | `app` | `Hono` | Yes | — | Hono app to mount onto. |
236
+ | `routes` | `BasaltRoute[]` | Yes | — | Routes to mount (via `app.on(method, url, handler)`). |
237
+ | `container` | `Container` | No | — | DI container; without it there's no per-request scope or enrichers/guards. |
238
+ | `enrichers` | `RequestEnricher[]` | No | `[]` | Functions that enrich the context before the guards. |
239
+ | `guards` | `RouteGuard[]` | No | `[]` | Functions that can reject the request (by throwing an error). |
240
+
241
+ ### What to import from where
242
+
243
+ This package only exports `honoPlugin`, `registerRoutes`, `HONO`, and `HonoPluginOptions`. Everything else — `route`, `HttpError`, `RequestValidationError`, `securityPlugin`, `healthPlugin`, `metricsPlugin`, `tracingPlugin`, `openapiPlugin`, types like `RequestEnricher`/`RouteGuard` — is imported from **`@basaltkit/http`**.
244
+
245
+ ## Common errors and solutions (FAQ)
246
+
247
+ **"`Cannot find module 'hono'`."** Hono is a *peer dependency*: `pnpm add hono`.
248
+
249
+ **"Nothing responds on Node."** Hono doesn't open ports on its own on Node — you need `@hono/node-server`: `serve({ fetch: hono.fetch, port: 3000 })`.
250
+
251
+ **"I tried `import { route } from '@basaltkit/hono'` and it failed."** The `route()` function isn't exported by this package — import it from `@basaltkit/http` (it's neutral by design: the same route runs on Fastify and Express).
252
+
253
+ **"`body` arrives as `undefined`."** Send the `Content-Type: application/json` header; without it the adapter doesn't parse the body as JSON. Also note that `GET`/`HEAD` never have a body.
254
+
255
+ **"400 `HTTP_VALIDATION` on a GET with a correct query."** In the query, everything arrives as text — use `z.coerce.number()` / `z.coerce.boolean()` in your schemas.
256
+
257
+ **"The edge plugins don't respond (`/metrics` returns 404)."** They're mounted on the `app:booted` event: make sure you call `await createApp({...}).boot()` before serving, and that `honoPlugin` is in the plugins list (it's the one that registers `HTTP_SERVER`).
258
+
259
+ **"The rate limit resets out of nowhere on edge."** Each edge instance has its own memory; `MemoryRateLimitStore` isn't shared across locations. Implement `RateLimitStore` on top of shared storage.
260
+
261
+ ## How it connects to other modules
262
+
263
+ - **`@basaltkit/core`** — `honoPlugin` is a Basalt plugin (`definePlugin`) in the `createApp → boot` lifecycle; uses the `Container` (tokens `HONO`, `HTTP_SERVER`), the metadata buckets, and the per-request context (`ctx()`/`tryCtx()`).
264
+ - **`@basaltkit/http`** — provides `route()`, the `runRoute()` pipeline (validation, enrichers, guards), `toErrorResponse()`, and the edge plugins. This adapter converts Hono's `Context` into the neutral `HttpRequest`/`HttpReply` and turns the result into a standard web `Response`.
265
+ - **`@basaltkit/fastify` / `@basaltkit/express`** — sibling adapters: the same routes, enrichers, guards, and edge plugins run on any of them without changes; switching frameworks (or runtime — Node → edge) is just switching the plugin.
266
+ - **`@basaltkit/auth` / `@basaltkit/tenancy` / `@basaltkit/permissions`** — register guards/enrichers on `'http:guards'`/`'http:enrichers'` and read the routes' `meta` (e.g. `meta: { auth: true }`); this adapter applies them automatically.
267
+ - **`@basaltkit/sdk` and the CLI** — consume the `'http:routes'` bucket (routes + Zod schemas) that this plugin publishes.
@@ -0,0 +1,22 @@
1
+ import * as _basaltkit_core from '@basaltkit/core';
2
+ import { Container } from '@basaltkit/core';
3
+ import * as hono_types from 'hono/types';
4
+ import { BasaltRoute, RequestEnricher, RouteGuard } from '@basaltkit/http';
5
+ import { Hono } from 'hono';
6
+
7
+ declare const HONO: _basaltkit_core.Token<Hono<any, hono_types.BlankSchema, "/">>;
8
+ /** Mounts Basalt routes on a Hono app (usable without the plugin). */
9
+ declare function registerRoutes(app: Hono<any>, routes: BasaltRoute[], container?: Container, enrichers?: RequestEnricher[], guards?: RouteGuard[]): void;
10
+ interface HonoPluginOptions {
11
+ routes?: BasaltRoute[];
12
+ /** Bring your own Hono app; otherwise a fresh one is created. */
13
+ app?: Hono<any>;
14
+ }
15
+ /**
16
+ * Runs Basalt on Hono (Node, Bun, Deno, edge). The same routes, enrichers and
17
+ * guards you register for Fastify work unchanged — resolve `HONO` for the app
18
+ * to serve (e.g. `@hono/node-server` or an edge runtime's `fetch` export).
19
+ */
20
+ declare function honoPlugin(options?: HonoPluginOptions): _basaltkit_core.BasaltPlugin<unknown>;
21
+
22
+ export { HONO, type HonoPluginOptions, honoPlugin, registerRoutes };
package/dist/index.js ADDED
@@ -0,0 +1,163 @@
1
+ // src/index.ts
2
+ import { createToken, definePlugin, ensureMetadata } from "@basaltkit/core";
3
+ import {
4
+ HttpServerCollector,
5
+ HTTP_SERVER,
6
+ runRoute,
7
+ toErrorResponse
8
+ } from "@basaltkit/http";
9
+ import { Hono } from "hono";
10
+ var HONO = createToken("hono");
11
+ async function parseBody(context) {
12
+ const method = context.req.method;
13
+ if (method === "GET" || method === "HEAD") return void 0;
14
+ const contentType = context.req.header("content-type") ?? "";
15
+ try {
16
+ if (contentType.includes("application/json")) return await context.req.json();
17
+ if (contentType.includes("form")) return await context.req.parseBody();
18
+ const text = await context.req.text();
19
+ return text || void 0;
20
+ } catch {
21
+ return void 0;
22
+ }
23
+ }
24
+ async function toNeutralRequest(context) {
25
+ return {
26
+ method: context.req.method,
27
+ url: context.req.url,
28
+ headers: Object.fromEntries(context.req.raw.headers.entries()),
29
+ params: context.req.param(),
30
+ query: context.req.query(),
31
+ body: await parseBody(context),
32
+ ...context.req.routePath ? { routePattern: context.req.routePath } : {},
33
+ raw: context
34
+ };
35
+ }
36
+ var HonoReply = class {
37
+ constructor(context) {
38
+ this.context = context;
39
+ }
40
+ context;
41
+ _status = 200;
42
+ _sent = false;
43
+ _payload;
44
+ get sent() {
45
+ return this._sent;
46
+ }
47
+ get statusCode() {
48
+ return this._status;
49
+ }
50
+ get payload() {
51
+ return this._payload;
52
+ }
53
+ get raw() {
54
+ return this.context;
55
+ }
56
+ code(status) {
57
+ this._status = status;
58
+ return this;
59
+ }
60
+ header(name, value) {
61
+ this.context.header(name, value);
62
+ return this;
63
+ }
64
+ send(payload) {
65
+ this._sent = true;
66
+ this._payload = payload;
67
+ return this;
68
+ }
69
+ };
70
+ function toResponse(reply, payload) {
71
+ const headers = new Headers(reply.context.res?.headers);
72
+ let body;
73
+ if (payload === void 0 || payload === null) {
74
+ body = null;
75
+ } else if (typeof payload === "string") {
76
+ body = payload;
77
+ if (!headers.has("content-type")) headers.set("content-type", "text/plain; charset=utf-8");
78
+ } else {
79
+ body = JSON.stringify(payload);
80
+ headers.set("content-type", "application/json");
81
+ }
82
+ return new Response(body, { status: reply.statusCode, headers });
83
+ }
84
+ function handlerFor(definition, container, enrichers, guards) {
85
+ return async (context) => {
86
+ const reply = new HonoReply(context);
87
+ try {
88
+ const result = await runRoute(definition, await toNeutralRequest(context), reply, {
89
+ ...container ? { container } : {},
90
+ enrichers,
91
+ guards
92
+ });
93
+ return toResponse(reply, reply.sent ? reply.payload : result);
94
+ } catch (error) {
95
+ const { status, body } = toErrorResponse(error);
96
+ return new Response(JSON.stringify(body), {
97
+ status,
98
+ headers: { "content-type": "application/json" }
99
+ });
100
+ }
101
+ };
102
+ }
103
+ function registerRoutes(app, routes, container, enrichers = [], guards = []) {
104
+ for (const definition of routes) {
105
+ app.on(definition.method, definition.url, handlerFor(definition, container, enrichers, guards));
106
+ }
107
+ }
108
+ function honoPlugin(options = {}) {
109
+ const collector = new HttpServerCollector();
110
+ return definePlugin({
111
+ name: "basalt:hono",
112
+ register({ container }) {
113
+ container.singleton(HONO, () => options.app ?? new Hono());
114
+ container.singleton(HTTP_SERVER, () => collector);
115
+ },
116
+ boot({ container, hooks }) {
117
+ const app = container.get(HONO);
118
+ const routes = options.routes ?? [];
119
+ const metadata = ensureMetadata(container);
120
+ const enrichers = metadata.get("http:enrichers");
121
+ const guards = metadata.get("http:guards");
122
+ hooks.on("app:booted", () => {
123
+ if (collector.afterHooks.length) {
124
+ app.use(async (context, next) => {
125
+ const start = Date.now();
126
+ await next();
127
+ await collector.runAfter(await toNeutralRequest(context), new HonoReply(context), context.res.status, Date.now() - start);
128
+ });
129
+ }
130
+ app.use(async (context, next) => {
131
+ const reply = new HonoReply(context);
132
+ if (await collector.runPre(await toNeutralRequest(context), reply)) return toResponse(reply, reply.payload);
133
+ await next();
134
+ return void 0;
135
+ });
136
+ registerRoutes(app, routes, container, enrichers, guards);
137
+ for (const { method, url, handler } of collector.extraRoutes) {
138
+ app.on(method, url, async (context) => {
139
+ const reply = new HonoReply(context);
140
+ const result = await handler({ request: await toNeutralRequest(context), reply });
141
+ return toResponse(reply, reply.sent ? reply.payload : result);
142
+ });
143
+ }
144
+ });
145
+ for (const definition of routes) {
146
+ metadata.add("http:routes", {
147
+ method: definition.method,
148
+ url: definition.url,
149
+ meta: definition.meta ?? {},
150
+ body: definition.body,
151
+ query: definition.query,
152
+ params: definition.params,
153
+ response: definition.response
154
+ });
155
+ }
156
+ }
157
+ });
158
+ }
159
+ export {
160
+ HONO,
161
+ honoPlugin,
162
+ registerRoutes
163
+ };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@basaltkit/hono",
3
+ "version": "1.0.0",
4
+ "description": "Hono adapter for Basalt: run the same typed routes, enrichers and guards on Hono (Node, Bun, Deno, edge) as on Fastify or Express.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "dependencies": {
17
+ "@basaltkit/core": "^1.0.0",
18
+ "@basaltkit/http": "^1.0.0"
19
+ },
20
+ "peerDependencies": {
21
+ "hono": "^4.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.15.0",
25
+ "hono": "^4.6.0",
26
+ "tsup": "^8.4.0",
27
+ "typescript": "^5.8.0",
28
+ "vitest": "^3.1.0",
29
+ "zod": "^3.24.0",
30
+ "@basaltkit/tsconfig": "^0.24.0"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/Zebedeu/basalt.git",
38
+ "directory": "packages/hono"
39
+ },
40
+ "homepage": "https://github.com/Zebedeu/basalt/tree/main/packages/hono#readme",
41
+ "bugs": "https://github.com/Zebedeu/basalt/issues",
42
+ "keywords": [
43
+ "basalt",
44
+ "typescript",
45
+ "http",
46
+ "hono",
47
+ "adapter",
48
+ "edge"
49
+ ],
50
+ "scripts": {
51
+ "build": "tsup src/index.ts --format esm --dts --clean",
52
+ "test": "vitest run",
53
+ "typecheck": "tsc --noEmit"
54
+ }
55
+ }