@bunary/http 0.0.1
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/CHANGELOG.md +27 -0
- package/README.md +180 -0
- package/dist/app.d.ts +55 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +139 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +51 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@bunary/http` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.0.1] - 2025-01-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release of `@bunary/http`
|
|
13
|
+
- `createApp()` factory function for creating Bunary applications
|
|
14
|
+
- HTTP method helpers: `get()`, `post()`, `put()`, `delete()`, `patch()`
|
|
15
|
+
- Path parameter extraction (`:param` syntax)
|
|
16
|
+
- Query parameter parsing via `ctx.query`
|
|
17
|
+
- Automatic JSON serialization for objects and arrays
|
|
18
|
+
- String responses returned as `text/plain`
|
|
19
|
+
- `null`/`undefined` returns converted to 204 No Content
|
|
20
|
+
- Response objects passed through unchanged
|
|
21
|
+
- `404 Not Found` for unregistered routes
|
|
22
|
+
- `405 Method Not Allowed` for wrong HTTP methods
|
|
23
|
+
- Error handling with 500 responses for uncaught exceptions
|
|
24
|
+
- `listen()` method for starting the Bun server
|
|
25
|
+
- `fetch()` method for testing without starting a server
|
|
26
|
+
- Full TypeScript type definitions with JSDoc documentation
|
|
27
|
+
- Exported types: `BunaryApp`, `BunaryServer`, `RequestContext`, `RouteHandler`, `Middleware`
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# @bunary/http
|
|
2
|
+
|
|
3
|
+
A lightweight, type-safe HTTP framework built exclusively for [Bun](https://bun.sh).
|
|
4
|
+
|
|
5
|
+
Part of the [Bunary](https://github.com/bunary-dev) ecosystem - a Bun-first backend platform inspired by Laravel.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🚀 **Bun-native** - Uses `Bun.serve()` directly, no Node.js compatibility layer
|
|
10
|
+
- 📦 **Zero dependencies** - Only depends on `@bunary/core`
|
|
11
|
+
- 🔒 **Type-safe** - Full TypeScript support with strict types
|
|
12
|
+
- âš¡ **Fast** - Minimal overhead, direct routing
|
|
13
|
+
- 🧩 **Simple API** - Chainable route registration with automatic JSON serialization
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add @bunary/http
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { createApp } from '@bunary/http';
|
|
25
|
+
|
|
26
|
+
const app = createApp();
|
|
27
|
+
|
|
28
|
+
app.get('/hello', () => ({ message: 'Hello, Bun!' }));
|
|
29
|
+
|
|
30
|
+
app.listen({ port: 3000 });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API
|
|
34
|
+
|
|
35
|
+
### `createApp()`
|
|
36
|
+
|
|
37
|
+
Creates a new Bunary application instance.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { createApp } from '@bunary/http';
|
|
41
|
+
|
|
42
|
+
const app = createApp();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Route Registration
|
|
46
|
+
|
|
47
|
+
Register routes using chainable HTTP method helpers:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
app
|
|
51
|
+
.get('/users', () => ({ users: [] }))
|
|
52
|
+
.post('/users', async (ctx) => {
|
|
53
|
+
const body = await ctx.request.json();
|
|
54
|
+
return { id: 1, ...body };
|
|
55
|
+
})
|
|
56
|
+
.put('/users/:id', (ctx) => {
|
|
57
|
+
return { id: ctx.params.id, updated: true };
|
|
58
|
+
})
|
|
59
|
+
.delete('/users/:id', (ctx) => {
|
|
60
|
+
return { deleted: ctx.params.id };
|
|
61
|
+
})
|
|
62
|
+
.patch('/users/:id', (ctx) => {
|
|
63
|
+
return { patched: ctx.params.id };
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Path Parameters
|
|
68
|
+
|
|
69
|
+
Path parameters are extracted automatically:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
app.get('/users/:id', (ctx) => {
|
|
73
|
+
return { userId: ctx.params.id };
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
app.get('/posts/:postId/comments/:commentId', (ctx) => {
|
|
77
|
+
const { postId, commentId } = ctx.params;
|
|
78
|
+
return { postId, commentId };
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Query Parameters
|
|
83
|
+
|
|
84
|
+
Query parameters are parsed from the URL:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
app.get('/search', (ctx) => {
|
|
88
|
+
const { q, page, limit } = ctx.query;
|
|
89
|
+
return { query: q, page, limit };
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Request Context
|
|
94
|
+
|
|
95
|
+
Route handlers receive a `RequestContext` object:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface RequestContext {
|
|
99
|
+
request: Request; // Original Bun Request object
|
|
100
|
+
params: Record<string, string>; // Path parameters
|
|
101
|
+
query: Record<string, string>; // Query parameters
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Response Handling
|
|
106
|
+
|
|
107
|
+
Handlers can return various types - they're automatically serialized:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Objects/Arrays → JSON with Content-Type: application/json
|
|
111
|
+
app.get('/json', () => ({ data: 'value' }));
|
|
112
|
+
|
|
113
|
+
// Strings → text/plain
|
|
114
|
+
app.get('/text', () => 'Hello, world!');
|
|
115
|
+
|
|
116
|
+
// Response objects passed through unchanged
|
|
117
|
+
app.get('/custom', () => new Response('Custom', { status: 201 }));
|
|
118
|
+
|
|
119
|
+
// null/undefined → 204 No Content
|
|
120
|
+
app.get('/empty', () => null);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Starting the Server
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const server = app.listen({ port: 3000, hostname: 'localhost' });
|
|
127
|
+
|
|
128
|
+
console.log(`Server running on ${server.hostname}:${server.port}`);
|
|
129
|
+
|
|
130
|
+
// Stop the server when done
|
|
131
|
+
server.stop();
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Testing Without Server
|
|
135
|
+
|
|
136
|
+
Use `app.fetch()` to test handlers directly:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const app = createApp();
|
|
140
|
+
app.get('/hello', () => ({ message: 'hi' }));
|
|
141
|
+
|
|
142
|
+
const response = await app.fetch(new Request('http://localhost/hello'));
|
|
143
|
+
const data = await response.json();
|
|
144
|
+
// { message: 'hi' }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Error Handling
|
|
148
|
+
|
|
149
|
+
Uncaught errors in handlers return a 500 response:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
app.get('/error', () => {
|
|
153
|
+
throw new Error('Something went wrong');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Returns: 500 Internal Server Error
|
|
157
|
+
// Body: { error: "Internal Server Error" }
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Types
|
|
161
|
+
|
|
162
|
+
All types are exported for TypeScript users:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import type {
|
|
166
|
+
BunaryApp,
|
|
167
|
+
BunaryServer,
|
|
168
|
+
RequestContext,
|
|
169
|
+
RouteHandler,
|
|
170
|
+
Middleware
|
|
171
|
+
} from '@bunary/http';
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Requirements
|
|
175
|
+
|
|
176
|
+
- Bun ≥ 1.0.0
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a new Bunary HTTP application instance.
|
|
3
|
+
*
|
|
4
|
+
* @returns BunaryApp instance with routing and middleware support
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createApp } from "@bunary/http";
|
|
9
|
+
*
|
|
10
|
+
* const app = createApp();
|
|
11
|
+
*
|
|
12
|
+
* app.get("/", () => ({ message: "Hello!" }));
|
|
13
|
+
* app.get("/users/:id", (ctx) => ({ id: ctx.params.id }));
|
|
14
|
+
*
|
|
15
|
+
* app.listen(3000);
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { BunaryApp } from "./types.js";
|
|
19
|
+
/**
|
|
20
|
+
* Create a new Bunary HTTP application instance.
|
|
21
|
+
*
|
|
22
|
+
* Provides a simple, chainable API for defining routes and middleware.
|
|
23
|
+
* Objects returned from handlers are automatically serialized to JSON.
|
|
24
|
+
*
|
|
25
|
+
* @returns BunaryApp instance
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { createApp } from "@bunary/http";
|
|
30
|
+
*
|
|
31
|
+
* const app = createApp();
|
|
32
|
+
*
|
|
33
|
+
* // Simple JSON response
|
|
34
|
+
* app.get("/", () => ({ message: "Hello, Bunary!" }));
|
|
35
|
+
*
|
|
36
|
+
* // Path parameters
|
|
37
|
+
* app.get("/users/:id", (ctx) => {
|
|
38
|
+
* return { id: ctx.params.id };
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Query parameters
|
|
42
|
+
* app.get("/search", (ctx) => {
|
|
43
|
+
* return { query: ctx.query.get("q") };
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* // Custom Response
|
|
47
|
+
* app.get("/custom", () => {
|
|
48
|
+
* return new Response("Custom", { status: 201 });
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* app.listen(3000);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function createApp(): BunaryApp;
|
|
55
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EACX,SAAS,EAQT,MAAM,YAAY,CAAC;AA0FpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,SAAS,IAAI,SAAS,CAuOrC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bunary/http - HTTP routing and middleware for Bunary
|
|
3
|
+
*
|
|
4
|
+
* A Bun-first HTTP layer providing routing, middleware, and server functionality.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createApp } from "@bunary/http";
|
|
9
|
+
*
|
|
10
|
+
* const app = createApp();
|
|
11
|
+
*
|
|
12
|
+
* app.get("/", () => ({ message: "Hello, Bunary!" }));
|
|
13
|
+
* app.get("/users/:id", (ctx) => ({ id: ctx.params.id }));
|
|
14
|
+
*
|
|
15
|
+
* app.listen(3000);
|
|
16
|
+
* console.log("Server running on http://localhost:3000");
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
*/
|
|
21
|
+
export type { AppOptions, BunaryApp, BunaryServer, HandlerResponse, HttpMethod, Middleware, PathParams, QueryParams, RequestContext, Route, RouteHandler, } from "./types.js";
|
|
22
|
+
export { createApp } from "./app.js";
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,YAAY,EACX,UAAU,EACV,SAAS,EACT,YAAY,EACZ,eAAe,EACf,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,KAAK,EACL,YAAY,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/app.ts
|
|
3
|
+
function compilePath(path) {
|
|
4
|
+
const paramNames = [];
|
|
5
|
+
const regexString = path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_match, paramName) => {
|
|
6
|
+
paramNames.push(paramName);
|
|
7
|
+
return "([^/]+)";
|
|
8
|
+
});
|
|
9
|
+
return {
|
|
10
|
+
pattern: new RegExp(`^${regexString}$`),
|
|
11
|
+
paramNames
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function extractParams(path, route) {
|
|
15
|
+
const match = path.match(route.pattern);
|
|
16
|
+
if (!match)
|
|
17
|
+
return {};
|
|
18
|
+
const params = {};
|
|
19
|
+
for (let i = 0;i < route.paramNames.length; i++) {
|
|
20
|
+
params[route.paramNames[i]] = match[i + 1];
|
|
21
|
+
}
|
|
22
|
+
return params;
|
|
23
|
+
}
|
|
24
|
+
function toResponse(result) {
|
|
25
|
+
if (result instanceof Response) {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
if (result === null || result === undefined) {
|
|
29
|
+
return new Response(null, { status: 204 });
|
|
30
|
+
}
|
|
31
|
+
if (typeof result === "string") {
|
|
32
|
+
return new Response(result, {
|
|
33
|
+
status: 200,
|
|
34
|
+
headers: { "Content-Type": "text/plain;charset=utf-8" }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (typeof result === "number") {
|
|
38
|
+
return new Response(String(result), {
|
|
39
|
+
status: 200,
|
|
40
|
+
headers: { "Content-Type": "text/plain;charset=utf-8" }
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return new Response(JSON.stringify(result), {
|
|
44
|
+
status: 200,
|
|
45
|
+
headers: { "Content-Type": "application/json" }
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function createApp() {
|
|
49
|
+
const routes = [];
|
|
50
|
+
const middlewares = [];
|
|
51
|
+
function addRoute(method, path, handler) {
|
|
52
|
+
const { pattern, paramNames } = compilePath(path);
|
|
53
|
+
routes.push({ method, path, pattern, paramNames, handler });
|
|
54
|
+
return app;
|
|
55
|
+
}
|
|
56
|
+
function findRoute(method, path) {
|
|
57
|
+
for (const route of routes) {
|
|
58
|
+
if (route.pattern.test(path)) {
|
|
59
|
+
if (route.method === method) {
|
|
60
|
+
return { route, params: extractParams(path, route) };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function hasMatchingPath(path) {
|
|
67
|
+
return routes.some((route) => route.pattern.test(path));
|
|
68
|
+
}
|
|
69
|
+
async function handleRequest(request) {
|
|
70
|
+
const url = new URL(request.url);
|
|
71
|
+
const path = url.pathname;
|
|
72
|
+
const method = request.method;
|
|
73
|
+
const match = findRoute(method, path);
|
|
74
|
+
if (!match) {
|
|
75
|
+
if (hasMatchingPath(path)) {
|
|
76
|
+
return new Response(JSON.stringify({ error: "Method not allowed" }), {
|
|
77
|
+
status: 405,
|
|
78
|
+
headers: { "Content-Type": "application/json" }
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return new Response(JSON.stringify({ error: "Not found" }), {
|
|
82
|
+
status: 404,
|
|
83
|
+
headers: { "Content-Type": "application/json" }
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const ctx = {
|
|
87
|
+
request,
|
|
88
|
+
params: match.params,
|
|
89
|
+
query: url.searchParams
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
let index = 0;
|
|
93
|
+
const next = async () => {
|
|
94
|
+
if (index < middlewares.length) {
|
|
95
|
+
const middleware = middlewares[index++];
|
|
96
|
+
return await middleware(ctx, next);
|
|
97
|
+
}
|
|
98
|
+
return await match.route.handler(ctx);
|
|
99
|
+
};
|
|
100
|
+
const result = await next();
|
|
101
|
+
return toResponse(result);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
const message = error instanceof Error ? error.message : "Internal server error";
|
|
104
|
+
return new Response(JSON.stringify({ error: message }), {
|
|
105
|
+
status: 500,
|
|
106
|
+
headers: { "Content-Type": "application/json" }
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const app = {
|
|
111
|
+
get: (path, handler) => addRoute("GET", path, handler),
|
|
112
|
+
post: (path, handler) => addRoute("POST", path, handler),
|
|
113
|
+
put: (path, handler) => addRoute("PUT", path, handler),
|
|
114
|
+
delete: (path, handler) => addRoute("DELETE", path, handler),
|
|
115
|
+
patch: (path, handler) => addRoute("PATCH", path, handler),
|
|
116
|
+
use: (middleware) => {
|
|
117
|
+
middlewares.push(middleware);
|
|
118
|
+
return app;
|
|
119
|
+
},
|
|
120
|
+
listen: (port = 3000, hostname = "localhost") => {
|
|
121
|
+
const server = Bun.serve({
|
|
122
|
+
port,
|
|
123
|
+
hostname,
|
|
124
|
+
fetch: handleRequest
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
server,
|
|
128
|
+
port: server.port ?? port,
|
|
129
|
+
hostname: server.hostname ?? hostname,
|
|
130
|
+
stop: () => server.stop()
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
fetch: handleRequest
|
|
134
|
+
};
|
|
135
|
+
return app;
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
createApp
|
|
139
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP method types supported by the router.
|
|
3
|
+
*/
|
|
4
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
|
5
|
+
/**
|
|
6
|
+
* Query parameters parsed from the URL search string.
|
|
7
|
+
*/
|
|
8
|
+
export type QueryParams = Record<string, string | string[]>;
|
|
9
|
+
/**
|
|
10
|
+
* Path parameters extracted from route patterns (e.g., `/users/:id` → `{ id: "123" }`).
|
|
11
|
+
*/
|
|
12
|
+
export type PathParams = Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Context object passed to route handlers containing request data.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* app.get("/users/:id", (ctx) => {
|
|
19
|
+
* console.log(ctx.params.id); // "123"
|
|
20
|
+
* console.log(ctx.query.get("sort")); // "name"
|
|
21
|
+
* return { id: ctx.params.id };
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export interface RequestContext {
|
|
26
|
+
/** The original Bun Request object */
|
|
27
|
+
request: Request;
|
|
28
|
+
/** Path parameters extracted from the route pattern */
|
|
29
|
+
params: PathParams;
|
|
30
|
+
/** Query parameters from the URL search string */
|
|
31
|
+
query: URLSearchParams;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Return type for route handlers.
|
|
35
|
+
* - Objects/arrays are automatically serialized to JSON.
|
|
36
|
+
* - Response objects are passed through unchanged.
|
|
37
|
+
* - Primitives (string, number) are converted to text responses.
|
|
38
|
+
*/
|
|
39
|
+
export type HandlerResponse = Response | object | string | number | null | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Route handler function that processes incoming requests.
|
|
42
|
+
*
|
|
43
|
+
* @param ctx - The request context containing request, params, and query
|
|
44
|
+
* @returns Response data (object for JSON, Response for custom, or primitive)
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const handler: RouteHandler = (ctx) => {
|
|
49
|
+
* return { message: "Hello, World!" };
|
|
50
|
+
* };
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export type RouteHandler = (ctx: RequestContext) => HandlerResponse | Promise<HandlerResponse>;
|
|
54
|
+
/**
|
|
55
|
+
* Middleware function for processing requests in a pipeline.
|
|
56
|
+
*
|
|
57
|
+
* @param ctx - The request context
|
|
58
|
+
* @param next - Function to call the next middleware or route handler
|
|
59
|
+
* @returns Response data or void (if next() handles it)
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const logger: Middleware = async (ctx, next) => {
|
|
64
|
+
* console.log(`${ctx.request.method} ${ctx.request.url}`);
|
|
65
|
+
* const response = await next();
|
|
66
|
+
* console.log("Response sent");
|
|
67
|
+
* return response;
|
|
68
|
+
* };
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export type Middleware = (ctx: RequestContext, next: () => Promise<HandlerResponse>) => HandlerResponse | Promise<HandlerResponse>;
|
|
72
|
+
/**
|
|
73
|
+
* Internal route definition stored by the router.
|
|
74
|
+
*/
|
|
75
|
+
export interface Route {
|
|
76
|
+
/** HTTP method for this route */
|
|
77
|
+
method: HttpMethod;
|
|
78
|
+
/** Route path pattern (e.g., "/users/:id") */
|
|
79
|
+
path: string;
|
|
80
|
+
/** Compiled regex for matching */
|
|
81
|
+
pattern: RegExp;
|
|
82
|
+
/** Parameter names extracted from path */
|
|
83
|
+
paramNames: string[];
|
|
84
|
+
/** Handler function for this route */
|
|
85
|
+
handler: RouteHandler;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Configuration options for creating a Bunary app.
|
|
89
|
+
*/
|
|
90
|
+
export interface AppOptions {
|
|
91
|
+
/** Base path prefix for all routes (default: "") */
|
|
92
|
+
basePath?: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Server instance returned by app.listen().
|
|
96
|
+
*/
|
|
97
|
+
export interface BunaryServer {
|
|
98
|
+
/** The underlying Bun server */
|
|
99
|
+
server: ReturnType<typeof Bun.serve>;
|
|
100
|
+
/** Stop the server */
|
|
101
|
+
stop: () => void;
|
|
102
|
+
/** Port the server is listening on */
|
|
103
|
+
port: number;
|
|
104
|
+
/** Hostname the server is bound to */
|
|
105
|
+
hostname: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The Bunary application instance for HTTP routing and middleware.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* const app = createApp();
|
|
113
|
+
*
|
|
114
|
+
* app.get("/", () => ({ message: "Hello!" }));
|
|
115
|
+
* app.get("/users/:id", (ctx) => ({ id: ctx.params.id }));
|
|
116
|
+
*
|
|
117
|
+
* app.listen(3000);
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export interface BunaryApp {
|
|
121
|
+
/**
|
|
122
|
+
* Register a GET route.
|
|
123
|
+
* @param path - URL path pattern (supports :param syntax)
|
|
124
|
+
* @param handler - Function to handle requests
|
|
125
|
+
*/
|
|
126
|
+
get: (path: string, handler: RouteHandler) => BunaryApp;
|
|
127
|
+
/**
|
|
128
|
+
* Register a POST route.
|
|
129
|
+
* @param path - URL path pattern (supports :param syntax)
|
|
130
|
+
* @param handler - Function to handle requests
|
|
131
|
+
*/
|
|
132
|
+
post: (path: string, handler: RouteHandler) => BunaryApp;
|
|
133
|
+
/**
|
|
134
|
+
* Register a PUT route.
|
|
135
|
+
* @param path - URL path pattern (supports :param syntax)
|
|
136
|
+
* @param handler - Function to handle requests
|
|
137
|
+
*/
|
|
138
|
+
put: (path: string, handler: RouteHandler) => BunaryApp;
|
|
139
|
+
/**
|
|
140
|
+
* Register a DELETE route.
|
|
141
|
+
* @param path - URL path pattern (supports :param syntax)
|
|
142
|
+
* @param handler - Function to handle requests
|
|
143
|
+
*/
|
|
144
|
+
delete: (path: string, handler: RouteHandler) => BunaryApp;
|
|
145
|
+
/**
|
|
146
|
+
* Register a PATCH route.
|
|
147
|
+
* @param path - URL path pattern (supports :param syntax)
|
|
148
|
+
* @param handler - Function to handle requests
|
|
149
|
+
*/
|
|
150
|
+
patch: (path: string, handler: RouteHandler) => BunaryApp;
|
|
151
|
+
/**
|
|
152
|
+
* Add middleware to the request pipeline.
|
|
153
|
+
* Middleware executes in registration order.
|
|
154
|
+
* @param middleware - Middleware function
|
|
155
|
+
*/
|
|
156
|
+
use: (middleware: Middleware) => BunaryApp;
|
|
157
|
+
/**
|
|
158
|
+
* Start the HTTP server.
|
|
159
|
+
* @param port - Port number to listen on (default: 3000)
|
|
160
|
+
* @param hostname - Hostname to bind to (default: "localhost")
|
|
161
|
+
* @returns Server instance with stop() method
|
|
162
|
+
*/
|
|
163
|
+
listen: (port?: number, hostname?: string) => BunaryServer;
|
|
164
|
+
/**
|
|
165
|
+
* Handle an incoming request (used internally and for testing).
|
|
166
|
+
* @param request - The incoming Request object
|
|
167
|
+
* @returns Response object
|
|
168
|
+
*/
|
|
169
|
+
fetch: (request: Request) => Promise<Response>;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bunary/http",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "HTTP routing and middleware for Bunary - a Bun-first backend framework inspired by Laravel",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun && bunx tsc -p tsconfig.build.json",
|
|
22
|
+
"typecheck": "bunx tsc --noEmit",
|
|
23
|
+
"test": "bun test",
|
|
24
|
+
"lint": "bunx biome check ./src"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"bunary",
|
|
28
|
+
"bun",
|
|
29
|
+
"http",
|
|
30
|
+
"router",
|
|
31
|
+
"middleware",
|
|
32
|
+
"framework"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/bunary-dev/http.git"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"bun": ">=1.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@bunary/core": "^0.0.2"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@biomejs/biome": "^1.9.4",
|
|
48
|
+
"bun-types": "^1.0.0",
|
|
49
|
+
"typescript": "^5.0.0"
|
|
50
|
+
}
|
|
51
|
+
}
|