astro-routify 1.0.0 → 1.1.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/README.md +132 -60
- package/dist/core/RouterBuilder.d.ts +114 -3
- package/dist/core/RouterBuilder.js +133 -6
- package/dist/core/defineGroup.d.ts +87 -0
- package/dist/core/defineGroup.js +111 -0
- package/dist/core/defineHandler.d.ts +1 -0
- package/dist/core/defineHandler.js +16 -0
- package/dist/core/defineRouter.d.ts +1 -0
- package/dist/core/defineRouter.js +7 -2
- package/dist/core/responseHelpers.d.ts +2 -1
- package/dist/core/responseHelpers.js +14 -0
- package/dist/index.d.ts +202 -4
- package/dist/index.js +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,44 +3,75 @@
|
|
|
3
3
|
**A high-performance API router for [Astro](https://astro.build/) built on a Trie matcher.**
|
|
4
4
|
Define API routes using clean, flat structures — no folders or boilerplate logic.
|
|
5
5
|
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
|
|
6
11
|
---
|
|
7
12
|
|
|
13
|
+
## Installing
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
npm install astro-routify
|
|
17
|
+
```
|
|
18
|
+
|
|
8
19
|
## ⚡️ Quickstart
|
|
9
20
|
|
|
10
21
|
```ts
|
|
11
22
|
// src/pages/api/index.ts
|
|
12
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
defineRoute,
|
|
25
|
+
defineRouter,
|
|
26
|
+
defineGroup,
|
|
27
|
+
HttpMethod,
|
|
28
|
+
ok,
|
|
29
|
+
} from 'astro-routify';
|
|
30
|
+
|
|
31
|
+
const userGroup = defineGroup('/users', (group) => {
|
|
32
|
+
group.addGet('/:id', ({ params }) => ok({ id: params.id }));
|
|
33
|
+
});
|
|
13
34
|
|
|
14
35
|
export const GET = defineRouter([
|
|
15
|
-
defineRoute(HttpMethod.GET,
|
|
16
|
-
|
|
36
|
+
defineRoute(HttpMethod.GET, '/ping', () => ok('pong')),
|
|
37
|
+
...userGroup.getRoutes(),
|
|
17
38
|
]);
|
|
18
39
|
```
|
|
19
40
|
|
|
20
41
|
Or to handle everything in a single place:
|
|
21
42
|
|
|
22
43
|
```ts
|
|
23
|
-
import { RouterBuilder,
|
|
44
|
+
import { RouterBuilder, ok } from 'astro-routify';
|
|
24
45
|
|
|
25
46
|
const builder = new RouterBuilder();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
|
|
48
|
+
builder
|
|
49
|
+
.addGet('/ping', () => ok('pong'))
|
|
50
|
+
.addPost('/submit', async ({ request }) => {
|
|
29
51
|
const body = await request.json();
|
|
30
52
|
return ok({ received: body });
|
|
31
|
-
})
|
|
32
|
-
]);
|
|
53
|
+
});
|
|
33
54
|
|
|
34
55
|
export const ALL = builder.build(); // catch-all
|
|
35
56
|
```
|
|
36
57
|
|
|
58
|
+
## 💡 Full Example
|
|
59
|
+
|
|
60
|
+
You can find an implementation example in the [astro-routify-example](https://github.com/oamm/astro-routify-example) repository.
|
|
61
|
+
It showcases a minimal Astro app with API endpoints configured under:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
/src/pages/api/[...path].ts
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This setup demonstrates how to route requests dynamically using astro-routify, while still leveraging Astro's native endpoint system.
|
|
68
|
+
|
|
37
69
|
---
|
|
38
70
|
|
|
39
71
|
## 🚀 Features
|
|
40
72
|
|
|
41
73
|
- ⚡ Fully compatible with Astro’s native APIContext — no extra setup needed.
|
|
42
|
-
- 🧩 Use middleware, access cookies, headers, and request bodies exactly as you would in a normal Astro
|
|
43
|
-
|
|
74
|
+
- 🧩 Use middleware, access cookies, headers, and request bodies exactly as you would in a normal Astro endpoint.
|
|
44
75
|
- ✅ Flat-file, code-based routing (no folders required)
|
|
45
76
|
- ✅ Dynamic segments (`:id`)
|
|
46
77
|
- ✅ ALL-mode for monolithic routing (`RouterBuilder`)
|
|
@@ -48,6 +79,8 @@ export const ALL = builder.build(); // catch-all
|
|
|
48
79
|
- ✅ Trie-based matcher for fast route lookup
|
|
49
80
|
- ✅ Fully typed — no magic strings
|
|
50
81
|
|
|
82
|
+
> 🔄 See [CHANGELOG.md](./CHANGELOG.md) for recent updates and improvements.
|
|
83
|
+
|
|
51
84
|
---
|
|
52
85
|
|
|
53
86
|
## 🧠 Core Concepts
|
|
@@ -57,8 +90,8 @@ export const ALL = builder.build(); // catch-all
|
|
|
57
90
|
Declare a single route:
|
|
58
91
|
|
|
59
92
|
```ts
|
|
60
|
-
defineRoute(HttpMethod.GET, "/users/:id", ({
|
|
61
|
-
return ok({
|
|
93
|
+
defineRoute(HttpMethod.GET, "/users/:id", ({params}) => {
|
|
94
|
+
return ok({userId: params.id});
|
|
62
95
|
});
|
|
63
96
|
```
|
|
64
97
|
|
|
@@ -74,18 +107,34 @@ export const GET = defineRouter([
|
|
|
74
107
|
|
|
75
108
|
> 🧠 `defineRouter()` supports all HTTP methods — but Astro only executes the method you export (`GET`, `POST`, etc.)
|
|
76
109
|
|
|
77
|
-
### `RouterBuilder` (Catch-All)
|
|
110
|
+
### `RouterBuilder` (Catch-All & Fluent Builder)
|
|
78
111
|
|
|
79
|
-
|
|
112
|
+
Use `RouterBuilder` when you want to build routes dynamically, catch all HTTP methods via `ALL`, or organize routes more fluently with helpers.
|
|
80
113
|
|
|
81
114
|
```ts
|
|
82
115
|
const builder = new RouterBuilder();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
116
|
+
|
|
117
|
+
builder
|
|
118
|
+
.addGet("/ping", () => ok("pong"))
|
|
119
|
+
.addPost("/submit", async ({request}) => {
|
|
120
|
+
const body = await request.json();
|
|
121
|
+
return ok({received: body});
|
|
122
|
+
});
|
|
123
|
+
|
|
86
124
|
export const ALL = builder.build();
|
|
87
125
|
```
|
|
88
126
|
|
|
127
|
+
You can also group routes:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const users = defineGroup("/users")
|
|
131
|
+
.addGet("/:id", ({params}) => ok({id: params.id}));
|
|
132
|
+
|
|
133
|
+
builder.addGroup(users);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
> 🔁 While `.register()` is still available, it's **deprecated** in favor of `.addGroup()` and `.addRoute()` for better structure and reusability.
|
|
137
|
+
|
|
89
138
|
---
|
|
90
139
|
|
|
91
140
|
## 🔁 Response Helpers
|
|
@@ -93,11 +142,19 @@ export const ALL = builder.build();
|
|
|
93
142
|
Avoid boilerplate `new Response(JSON.stringify(...))`:
|
|
94
143
|
|
|
95
144
|
```ts
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
145
|
+
import {fileResponse} from 'astro-routify';
|
|
146
|
+
|
|
147
|
+
ok(data); // 200 OK
|
|
148
|
+
created(data); // 201 Created
|
|
149
|
+
noContent(); // 204
|
|
150
|
+
notFound("Missing"); // 404
|
|
151
|
+
internalError(err); // 500
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### File downloads
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
fileResponse(content, "application/pdf", "report.pdf"); // sets Content-Type and Content-Disposition
|
|
101
158
|
```
|
|
102
159
|
|
|
103
160
|
---
|
|
@@ -107,9 +164,17 @@ internalError(err); // 500
|
|
|
107
164
|
Any route param like `:id` is extracted into `ctx.params`:
|
|
108
165
|
|
|
109
166
|
```ts
|
|
110
|
-
|
|
111
|
-
|
|
167
|
+
const builder = new RouterBuilder();
|
|
168
|
+
|
|
169
|
+
builder.addGet("/users/:id", ({params}) => ok({userId: params.id}));
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
//OR
|
|
173
|
+
|
|
174
|
+
defineRoute(HttpMethod.GET, "/items/:id", ({params}) => {
|
|
175
|
+
return ok({itemId: params.id});
|
|
112
176
|
});
|
|
177
|
+
|
|
113
178
|
```
|
|
114
179
|
|
|
115
180
|
---
|
|
@@ -121,35 +186,33 @@ defineRoute(HttpMethod.GET, "/items/:id", ({ params }) => {
|
|
|
121
186
|
```ts
|
|
122
187
|
// src/pages/api/[...slug].ts
|
|
123
188
|
export const GET = async ({request}) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return new Response('Not Found', {status: 404});
|
|
189
|
+
const url = new URL(request.url);
|
|
190
|
+
const path = url.pathname;
|
|
191
|
+
|
|
192
|
+
if (path.startsWith('/api/users/')) {
|
|
193
|
+
// Try to extract ID
|
|
194
|
+
const id = path.split('/').pop();
|
|
195
|
+
return new Response(JSON.stringify({id}), {
|
|
196
|
+
status: 200,
|
|
197
|
+
headers: {'Content-Type': 'application/json'},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (path === '/api/users') {
|
|
202
|
+
return new Response(JSON.stringify([{id: 1}, {id: 2}]), {
|
|
203
|
+
status: 200,
|
|
204
|
+
headers: {'Content-Type': 'application/json'},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (path === '/api/ping') {
|
|
209
|
+
return new Response(JSON.stringify({pong: true}), {
|
|
210
|
+
status: 200,
|
|
211
|
+
headers: {'Content-Type': 'application/json'}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return new Response('Not Found', {status: 404});
|
|
153
216
|
};
|
|
154
217
|
```
|
|
155
218
|
|
|
@@ -169,11 +232,19 @@ src/
|
|
|
169
232
|
### ✅ With `astro-routify`
|
|
170
233
|
|
|
171
234
|
```ts
|
|
172
|
-
//src/pages/api/[...slug].ts
|
|
235
|
+
// src/pages/api/[...slug].ts
|
|
236
|
+
|
|
237
|
+
const builder = new RouterBuilder();
|
|
238
|
+
builder.addGet("/ping", () => ok({pong: true}));
|
|
239
|
+
builder.addGet("/users/:id", ({params}) => ok({userId: params.id}));
|
|
240
|
+
|
|
241
|
+
// OR
|
|
242
|
+
|
|
173
243
|
export const ALL = defineRouter([
|
|
174
|
-
defineRoute(HttpMethod.GET, "/ping", () => ok({
|
|
175
|
-
defineRoute(HttpMethod.GET, "/users/:id", ({
|
|
244
|
+
defineRoute(HttpMethod.GET, "/ping", () => ok({pong: true})),
|
|
245
|
+
defineRoute(HttpMethod.GET, "/users/:id", ({params}) => ok({id: params.id}))
|
|
176
246
|
]);
|
|
247
|
+
|
|
177
248
|
```
|
|
178
249
|
|
|
179
250
|
---
|
|
@@ -196,11 +267,10 @@ Tests ran on a mid-range development setup:
|
|
|
196
267
|
- **GPU**: NVIDIA GeForce GTX 1080 (8 GB)
|
|
197
268
|
- **OS**: Windows 10 Pro 64-bit
|
|
198
269
|
- **Node.js**: v20.x
|
|
199
|
-
- **Benchmark Tool**: [Vitest Bench](https://vitest.dev/guide/
|
|
270
|
+
- **Benchmark Tool**: [Vitest Bench](https://vitest.dev/guide/features.html#benchmarking)
|
|
200
271
|
|
|
201
272
|
Results may vary slightly on different hardware.
|
|
202
273
|
|
|
203
|
-
|
|
204
274
|
### 🔬 Realistic route shapes (5000 registered routes):
|
|
205
275
|
|
|
206
276
|
```
|
|
@@ -225,11 +295,13 @@ Results may vary slightly on different hardware.
|
|
|
225
295
|
```
|
|
226
296
|
|
|
227
297
|
> ⚡ Performance stays consistently fast even with 10k+ routes
|
|
298
|
+
|
|
228
299
|
---
|
|
229
300
|
|
|
230
301
|
## 🛠 Designed to Scale
|
|
231
302
|
|
|
232
|
-
While focused on simplicity and speed today, `astro-routify` is designed to evolve — enabling more advanced routing
|
|
303
|
+
While focused on simplicity and speed today, `astro-routify` is designed to evolve — enabling more advanced routing
|
|
304
|
+
patterns in the future.
|
|
233
305
|
|
|
234
306
|
---
|
|
235
307
|
|
|
@@ -1,8 +1,119 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Route } from './defineRoute';
|
|
2
2
|
import { type RouterOptions } from './defineRouter';
|
|
3
|
+
import { RouteGroup } from './defineGroup';
|
|
4
|
+
import { type Handler } from './defineHandler';
|
|
5
|
+
/**
|
|
6
|
+
* A fluent builder for creating and composing API routes in Astro.
|
|
7
|
+
*
|
|
8
|
+
* `RouterBuilder` supports both simple method-based additions (`addGet`, `addPost`, etc.)
|
|
9
|
+
* and organized groups via `defineGroup()` + `addGroup()` for scalable applications.
|
|
10
|
+
*
|
|
11
|
+
* @example Basic usage:
|
|
12
|
+
* ```ts
|
|
13
|
+
* const router = new RouterBuilder()
|
|
14
|
+
* .addGet('/ping', () => ok('pong'))
|
|
15
|
+
* .addPost('/submit', handler);
|
|
16
|
+
*
|
|
17
|
+
* export const ALL = router.build();
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @example Using groups:
|
|
21
|
+
* ```ts
|
|
22
|
+
* const users = defineGroup('/users')
|
|
23
|
+
* .addGet('/:id', userHandler);
|
|
24
|
+
*
|
|
25
|
+
* const router = new RouterBuilder({ basePath: '/api' })
|
|
26
|
+
* .addGroup(users);
|
|
27
|
+
*
|
|
28
|
+
* export const GET = router.build();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
3
31
|
export declare class RouterBuilder {
|
|
4
|
-
private
|
|
32
|
+
private _options;
|
|
33
|
+
private _routes;
|
|
34
|
+
private static _registerWarned;
|
|
35
|
+
constructor(options?: RouterOptions);
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated Prefer `addGroup()` or `addRoute()` for structured routing.
|
|
38
|
+
*
|
|
39
|
+
* Registers a single route manually.
|
|
40
|
+
*
|
|
41
|
+
* This method is deprecated in favor of a more scalable and organized approach using
|
|
42
|
+
* `defineGroup()` + `addGroup()` or `addRoute()` for ad-hoc additions.
|
|
43
|
+
*
|
|
44
|
+
* @param route A single `Route` object to register.
|
|
45
|
+
*/
|
|
5
46
|
register(route: Route): void;
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated Prefer `addGroup()` or `addRoute()` for structured routing.
|
|
49
|
+
*
|
|
50
|
+
* Registers multiple routes at once.
|
|
51
|
+
*
|
|
52
|
+
* This method is deprecated and discouraged for larger codebases in favor of group-based composition.
|
|
53
|
+
*
|
|
54
|
+
* @param routes An array of `Route` objects to register.
|
|
55
|
+
*/
|
|
6
56
|
register(routes: Route[]): void;
|
|
7
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Adds a single route to the router.
|
|
59
|
+
*
|
|
60
|
+
* @param route The route to add.
|
|
61
|
+
* @returns The current builder instance (for chaining).
|
|
62
|
+
*/
|
|
63
|
+
addRoute(route: Route): this;
|
|
64
|
+
/**
|
|
65
|
+
* Adds a group of pre-defined routes (from `defineGroup()`).
|
|
66
|
+
*
|
|
67
|
+
* @param group A `RouteGroup` instance.
|
|
68
|
+
* @returns The current builder instance (for chaining).
|
|
69
|
+
*/
|
|
70
|
+
addGroup(group: RouteGroup): this;
|
|
71
|
+
/**
|
|
72
|
+
* Adds a GET route.
|
|
73
|
+
* @param path Route path (e.g., `/items/:id`)
|
|
74
|
+
* @param handler Request handler function.
|
|
75
|
+
*/
|
|
76
|
+
addGet(path: string, handler: Handler): this;
|
|
77
|
+
/**
|
|
78
|
+
* Adds a POST route.
|
|
79
|
+
* @param path Route path.
|
|
80
|
+
* @param handler Request handler function.
|
|
81
|
+
*/
|
|
82
|
+
addPost(path: string, handler: Handler): this;
|
|
83
|
+
/**
|
|
84
|
+
* Adds a PUT route.
|
|
85
|
+
* @param path Route path.
|
|
86
|
+
* @param handler Request handler function.
|
|
87
|
+
*/
|
|
88
|
+
addPut(path: string, handler: Handler): this;
|
|
89
|
+
/**
|
|
90
|
+
* Adds a DELETE route.
|
|
91
|
+
* @param path Route path.
|
|
92
|
+
* @param handler Request handler function.
|
|
93
|
+
*/
|
|
94
|
+
addDelete(path: string, handler: Handler): this;
|
|
95
|
+
/**
|
|
96
|
+
* Adds a PATCH route.
|
|
97
|
+
* @param path Route path.
|
|
98
|
+
* @param handler Request handler function.
|
|
99
|
+
*/
|
|
100
|
+
addPatch(path: string, handler: Handler): this;
|
|
101
|
+
/**
|
|
102
|
+
* Internal helper to add a route with any HTTP method.
|
|
103
|
+
*
|
|
104
|
+
* @param method The HTTP method.
|
|
105
|
+
* @param subPath Path segment (can be relative or absolute).
|
|
106
|
+
* @param handler Request handler.
|
|
107
|
+
* @returns The current builder instance (for chaining).
|
|
108
|
+
*/
|
|
109
|
+
private add;
|
|
110
|
+
/**
|
|
111
|
+
* Finalizes the router and returns an Astro-compatible route handler.
|
|
112
|
+
*
|
|
113
|
+
* This function should be used to export a method like `GET`, `POST`, or `ALL`
|
|
114
|
+
* inside Astro API endpoints.
|
|
115
|
+
*
|
|
116
|
+
* @returns A fully resolved Astro route handler.
|
|
117
|
+
*/
|
|
118
|
+
build(): import("astro").APIRoute;
|
|
8
119
|
}
|
|
@@ -1,17 +1,144 @@
|
|
|
1
|
+
import { defineRoute } from './defineRoute';
|
|
1
2
|
import { defineRouter } from './defineRouter';
|
|
3
|
+
import { HttpMethod } from './HttpMethod';
|
|
4
|
+
/**
|
|
5
|
+
* A fluent builder for creating and composing API routes in Astro.
|
|
6
|
+
*
|
|
7
|
+
* `RouterBuilder` supports both simple method-based additions (`addGet`, `addPost`, etc.)
|
|
8
|
+
* and organized groups via `defineGroup()` + `addGroup()` for scalable applications.
|
|
9
|
+
*
|
|
10
|
+
* @example Basic usage:
|
|
11
|
+
* ```ts
|
|
12
|
+
* const router = new RouterBuilder()
|
|
13
|
+
* .addGet('/ping', () => ok('pong'))
|
|
14
|
+
* .addPost('/submit', handler);
|
|
15
|
+
*
|
|
16
|
+
* export const ALL = router.build();
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example Using groups:
|
|
20
|
+
* ```ts
|
|
21
|
+
* const users = defineGroup('/users')
|
|
22
|
+
* .addGet('/:id', userHandler);
|
|
23
|
+
*
|
|
24
|
+
* const router = new RouterBuilder({ basePath: '/api' })
|
|
25
|
+
* .addGroup(users);
|
|
26
|
+
*
|
|
27
|
+
* export const GET = router.build();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
2
30
|
export class RouterBuilder {
|
|
3
|
-
constructor() {
|
|
4
|
-
this.
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this._routes = [];
|
|
33
|
+
this._options = {
|
|
34
|
+
basePath: 'api',
|
|
35
|
+
...options,
|
|
36
|
+
};
|
|
5
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Prefer `addGroup()` or `addRoute()` for structured routing.
|
|
40
|
+
*
|
|
41
|
+
* Registers one or more routes, supporting both a single `Route` or an array of them.
|
|
42
|
+
*
|
|
43
|
+
* Internally used by the two overloads above. Emits a console warning once per runtime.
|
|
44
|
+
*
|
|
45
|
+
* @param routeOrRoutes Either a single `Route` or an array of `Route`s.
|
|
46
|
+
*/
|
|
6
47
|
register(routeOrRoutes) {
|
|
48
|
+
if (!RouterBuilder._registerWarned) {
|
|
49
|
+
console.warn('[RouterBuilder] register() is deprecated. Use defineGroup() + addGroup() for route grouping and better structure.');
|
|
50
|
+
RouterBuilder._registerWarned = true;
|
|
51
|
+
}
|
|
7
52
|
if (Array.isArray(routeOrRoutes)) {
|
|
8
|
-
this.
|
|
53
|
+
this._routes.push(...routeOrRoutes);
|
|
9
54
|
}
|
|
10
55
|
else {
|
|
11
|
-
this.
|
|
56
|
+
this._routes.push(routeOrRoutes);
|
|
12
57
|
}
|
|
13
58
|
}
|
|
14
|
-
|
|
15
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Adds a single route to the router.
|
|
61
|
+
*
|
|
62
|
+
* @param route The route to add.
|
|
63
|
+
* @returns The current builder instance (for chaining).
|
|
64
|
+
*/
|
|
65
|
+
addRoute(route) {
|
|
66
|
+
this._routes.push(route);
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Adds a group of pre-defined routes (from `defineGroup()`).
|
|
71
|
+
*
|
|
72
|
+
* @param group A `RouteGroup` instance.
|
|
73
|
+
* @returns The current builder instance (for chaining).
|
|
74
|
+
*/
|
|
75
|
+
addGroup(group) {
|
|
76
|
+
this._routes.push(...group.getRoutes());
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Adds a GET route.
|
|
81
|
+
* @param path Route path (e.g., `/items/:id`)
|
|
82
|
+
* @param handler Request handler function.
|
|
83
|
+
*/
|
|
84
|
+
addGet(path, handler) {
|
|
85
|
+
return this.add(HttpMethod.GET, path, handler);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Adds a POST route.
|
|
89
|
+
* @param path Route path.
|
|
90
|
+
* @param handler Request handler function.
|
|
91
|
+
*/
|
|
92
|
+
addPost(path, handler) {
|
|
93
|
+
return this.add(HttpMethod.POST, path, handler);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Adds a PUT route.
|
|
97
|
+
* @param path Route path.
|
|
98
|
+
* @param handler Request handler function.
|
|
99
|
+
*/
|
|
100
|
+
addPut(path, handler) {
|
|
101
|
+
return this.add(HttpMethod.PUT, path, handler);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Adds a DELETE route.
|
|
105
|
+
* @param path Route path.
|
|
106
|
+
* @param handler Request handler function.
|
|
107
|
+
*/
|
|
108
|
+
addDelete(path, handler) {
|
|
109
|
+
return this.add(HttpMethod.DELETE, path, handler);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Adds a PATCH route.
|
|
113
|
+
* @param path Route path.
|
|
114
|
+
* @param handler Request handler function.
|
|
115
|
+
*/
|
|
116
|
+
addPatch(path, handler) {
|
|
117
|
+
return this.add(HttpMethod.PATCH, path, handler);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Internal helper to add a route with any HTTP method.
|
|
121
|
+
*
|
|
122
|
+
* @param method The HTTP method.
|
|
123
|
+
* @param subPath Path segment (can be relative or absolute).
|
|
124
|
+
* @param handler Request handler.
|
|
125
|
+
* @returns The current builder instance (for chaining).
|
|
126
|
+
*/
|
|
127
|
+
add(method, subPath, handler) {
|
|
128
|
+
const normalizedPath = subPath.startsWith('/') ? subPath : `/${subPath}`;
|
|
129
|
+
this._routes.push(defineRoute(method, normalizedPath, handler));
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Finalizes the router and returns an Astro-compatible route handler.
|
|
134
|
+
*
|
|
135
|
+
* This function should be used to export a method like `GET`, `POST`, or `ALL`
|
|
136
|
+
* inside Astro API endpoints.
|
|
137
|
+
*
|
|
138
|
+
* @returns A fully resolved Astro route handler.
|
|
139
|
+
*/
|
|
140
|
+
build() {
|
|
141
|
+
return defineRouter(this._routes, this._options);
|
|
16
142
|
}
|
|
17
143
|
}
|
|
144
|
+
RouterBuilder._registerWarned = false;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type Route } from './defineRoute';
|
|
2
|
+
import { Handler } from "./defineHandler";
|
|
3
|
+
/**
|
|
4
|
+
* Represents a group of routes under a shared base path.
|
|
5
|
+
*
|
|
6
|
+
* Use this class to organize related endpoints, applying a consistent prefix
|
|
7
|
+
* and reducing duplication when defining similar routes.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const users = new RouteGroup('/users')
|
|
11
|
+
* .addGet('/:id', handler)
|
|
12
|
+
* .addPost('/', createUser);
|
|
13
|
+
*/
|
|
14
|
+
export declare class RouteGroup {
|
|
15
|
+
private basePath;
|
|
16
|
+
private routes;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new route group with the specified base path.
|
|
19
|
+
* Trailing slashes are automatically removed.
|
|
20
|
+
*
|
|
21
|
+
* @param basePath - The common prefix for all routes in the group (e.g. "/users")
|
|
22
|
+
*/
|
|
23
|
+
constructor(basePath: string);
|
|
24
|
+
/**
|
|
25
|
+
* Returns the normalized base path used by the group.
|
|
26
|
+
*/
|
|
27
|
+
getBasePath(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Registers a GET route under the group's base path.
|
|
30
|
+
*
|
|
31
|
+
* @param path - Path relative to the base path (e.g. "/:id")
|
|
32
|
+
* @param handler - The handler function for this route
|
|
33
|
+
*/
|
|
34
|
+
addGet(path: string, handler: Handler): this;
|
|
35
|
+
/**
|
|
36
|
+
* Registers a POST route under the group's base path.
|
|
37
|
+
*
|
|
38
|
+
* @param path - Path relative to the base path
|
|
39
|
+
* @param handler - The handler function for this route
|
|
40
|
+
*/
|
|
41
|
+
addPost(path: string, handler: Handler): this;
|
|
42
|
+
/**
|
|
43
|
+
* Registers a PUT route under the group's base path.
|
|
44
|
+
*
|
|
45
|
+
* @param path - Path relative to the base path
|
|
46
|
+
* @param handler - The handler function for this route
|
|
47
|
+
*/
|
|
48
|
+
addPut(path: string, handler: Handler): this;
|
|
49
|
+
/**
|
|
50
|
+
* Registers a DELETE route under the group's base path.
|
|
51
|
+
*
|
|
52
|
+
* @param path - Path relative to the base path
|
|
53
|
+
* @param handler - The handler function for this route
|
|
54
|
+
*/
|
|
55
|
+
addDelete(path: string, handler: Handler): this;
|
|
56
|
+
/**
|
|
57
|
+
* Registers a PATCH route under the group's base path.
|
|
58
|
+
*
|
|
59
|
+
* @param path - Path relative to the base path
|
|
60
|
+
* @param handler - The handler function for this route
|
|
61
|
+
*/
|
|
62
|
+
addPatch(path: string, handler: Handler): this;
|
|
63
|
+
/**
|
|
64
|
+
* Internal method to register a route under the group with any HTTP method.
|
|
65
|
+
*
|
|
66
|
+
* @param method - HTTP verb
|
|
67
|
+
* @param subPath - Route path relative to the base
|
|
68
|
+
* @param handler - The handler function for this route
|
|
69
|
+
*/
|
|
70
|
+
private add;
|
|
71
|
+
/**
|
|
72
|
+
* Returns all the registered routes in the group.
|
|
73
|
+
*/
|
|
74
|
+
getRoutes(): Route[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Helper to define a `RouteGroup` with optional inline configuration.
|
|
78
|
+
*
|
|
79
|
+
* @param basePath - The base path prefix for all routes
|
|
80
|
+
* @param configure - Optional callback to configure the group inline
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const users = defineGroup('/users', (group) => {
|
|
84
|
+
* group.addGet('/:id', handler);
|
|
85
|
+
* });
|
|
86
|
+
*/
|
|
87
|
+
export declare function defineGroup(basePath: string, configure?: (group: RouteGroup) => void): RouteGroup;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { HttpMethod } from './HttpMethod';
|
|
2
|
+
import { defineRoute } from './defineRoute';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a group of routes under a shared base path.
|
|
5
|
+
*
|
|
6
|
+
* Use this class to organize related endpoints, applying a consistent prefix
|
|
7
|
+
* and reducing duplication when defining similar routes.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const users = new RouteGroup('/users')
|
|
11
|
+
* .addGet('/:id', handler)
|
|
12
|
+
* .addPost('/', createUser);
|
|
13
|
+
*/
|
|
14
|
+
export class RouteGroup {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new route group with the specified base path.
|
|
17
|
+
* Trailing slashes are automatically removed.
|
|
18
|
+
*
|
|
19
|
+
* @param basePath - The common prefix for all routes in the group (e.g. "/users")
|
|
20
|
+
*/
|
|
21
|
+
constructor(basePath) {
|
|
22
|
+
this.routes = [];
|
|
23
|
+
this.basePath = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns the normalized base path used by the group.
|
|
27
|
+
*/
|
|
28
|
+
getBasePath() {
|
|
29
|
+
return this.basePath;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Registers a GET route under the group's base path.
|
|
33
|
+
*
|
|
34
|
+
* @param path - Path relative to the base path (e.g. "/:id")
|
|
35
|
+
* @param handler - The handler function for this route
|
|
36
|
+
*/
|
|
37
|
+
addGet(path, handler) {
|
|
38
|
+
return this.add(HttpMethod.GET, path, handler);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Registers a POST route under the group's base path.
|
|
42
|
+
*
|
|
43
|
+
* @param path - Path relative to the base path
|
|
44
|
+
* @param handler - The handler function for this route
|
|
45
|
+
*/
|
|
46
|
+
addPost(path, handler) {
|
|
47
|
+
return this.add(HttpMethod.POST, path, handler);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Registers a PUT route under the group's base path.
|
|
51
|
+
*
|
|
52
|
+
* @param path - Path relative to the base path
|
|
53
|
+
* @param handler - The handler function for this route
|
|
54
|
+
*/
|
|
55
|
+
addPut(path, handler) {
|
|
56
|
+
return this.add(HttpMethod.PUT, path, handler);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Registers a DELETE route under the group's base path.
|
|
60
|
+
*
|
|
61
|
+
* @param path - Path relative to the base path
|
|
62
|
+
* @param handler - The handler function for this route
|
|
63
|
+
*/
|
|
64
|
+
addDelete(path, handler) {
|
|
65
|
+
return this.add(HttpMethod.DELETE, path, handler);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Registers a PATCH route under the group's base path.
|
|
69
|
+
*
|
|
70
|
+
* @param path - Path relative to the base path
|
|
71
|
+
* @param handler - The handler function for this route
|
|
72
|
+
*/
|
|
73
|
+
addPatch(path, handler) {
|
|
74
|
+
return this.add(HttpMethod.PATCH, path, handler);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Internal method to register a route under the group with any HTTP method.
|
|
78
|
+
*
|
|
79
|
+
* @param method - HTTP verb
|
|
80
|
+
* @param subPath - Route path relative to the base
|
|
81
|
+
* @param handler - The handler function for this route
|
|
82
|
+
*/
|
|
83
|
+
add(method, subPath, handler) {
|
|
84
|
+
const normalizedPath = subPath.startsWith('/') ? subPath : `/${subPath}`;
|
|
85
|
+
this.routes.push(defineRoute(method, `${this.basePath}${normalizedPath}`, handler));
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns all the registered routes in the group.
|
|
90
|
+
*/
|
|
91
|
+
getRoutes() {
|
|
92
|
+
return this.routes;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Helper to define a `RouteGroup` with optional inline configuration.
|
|
97
|
+
*
|
|
98
|
+
* @param basePath - The base path prefix for all routes
|
|
99
|
+
* @param configure - Optional callback to configure the group inline
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const users = defineGroup('/users', (group) => {
|
|
103
|
+
* group.addGet('/:id', handler);
|
|
104
|
+
* });
|
|
105
|
+
*/
|
|
106
|
+
export function defineGroup(basePath, configure) {
|
|
107
|
+
const group = new RouteGroup(basePath);
|
|
108
|
+
if (configure)
|
|
109
|
+
configure(group);
|
|
110
|
+
return group;
|
|
111
|
+
}
|
|
@@ -2,3 +2,4 @@ import type { APIContext, APIRoute } from 'astro';
|
|
|
2
2
|
import { type ResultResponse } from './responseHelpers';
|
|
3
3
|
export type Handler = (ctx: APIContext) => Promise<ResultResponse | Response> | ResultResponse | Response;
|
|
4
4
|
export declare function defineHandler(handler: Handler): APIRoute;
|
|
5
|
+
export declare function isReadableStream(value: unknown): value is ReadableStream<Uint8Array>;
|
|
@@ -16,6 +16,17 @@ export function defineHandler(handler) {
|
|
|
16
16
|
logResponse(result.status, start);
|
|
17
17
|
return result;
|
|
18
18
|
}
|
|
19
|
+
// If it's a file response (manual BodyInit with appropriate headers)
|
|
20
|
+
if (result?.body instanceof Blob ||
|
|
21
|
+
result?.body instanceof ArrayBuffer ||
|
|
22
|
+
isReadableStream(result?.body)) {
|
|
23
|
+
const res = new Response(result.body, {
|
|
24
|
+
status: result.status,
|
|
25
|
+
headers: result.headers,
|
|
26
|
+
});
|
|
27
|
+
logResponse(res.status, start);
|
|
28
|
+
return res;
|
|
29
|
+
}
|
|
19
30
|
const finalResponse = toAstroResponse(result);
|
|
20
31
|
logResponse(finalResponse.status, start);
|
|
21
32
|
return finalResponse;
|
|
@@ -28,3 +39,8 @@ export function defineHandler(handler) {
|
|
|
28
39
|
}
|
|
29
40
|
};
|
|
30
41
|
}
|
|
42
|
+
export function isReadableStream(value) {
|
|
43
|
+
return (typeof value === 'object' &&
|
|
44
|
+
value !== null &&
|
|
45
|
+
typeof value.getReader === 'function');
|
|
46
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { APIRoute } from 'astro';
|
|
|
2
2
|
import { notFound } from './responseHelpers';
|
|
3
3
|
import type { Route } from './defineRoute';
|
|
4
4
|
export interface RouterOptions {
|
|
5
|
+
basePath?: string;
|
|
5
6
|
/** Custom 404 handler */
|
|
6
7
|
onNotFound?: () => ReturnType<typeof notFound>;
|
|
7
8
|
}
|
|
@@ -7,9 +7,14 @@ export function defineRouter(routes, options = {}) {
|
|
|
7
7
|
for (const route of routes) {
|
|
8
8
|
trie.insert(route.path, route.method, route.handler);
|
|
9
9
|
}
|
|
10
|
-
// Wrap every user handler through defineHandler for uniform logging & error handling
|
|
11
10
|
return async (ctx) => {
|
|
12
|
-
const
|
|
11
|
+
const pathname = new URL(ctx.request.url).pathname;
|
|
12
|
+
let basePath = options.basePath ?? '/api';
|
|
13
|
+
if (!basePath.startsWith('/')) {
|
|
14
|
+
basePath = '/' + basePath;
|
|
15
|
+
}
|
|
16
|
+
const basePathRegex = new RegExp(`^${basePath}`);
|
|
17
|
+
const path = pathname.replace(basePathRegex, '');
|
|
13
18
|
const method = normalizeMethod(ctx.request.method);
|
|
14
19
|
const { handler, allowed, params } = trie.find(path, method);
|
|
15
20
|
if (!handler) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HeadersInit } from "undici";
|
|
1
|
+
import { BodyInit, HeadersInit } from "undici";
|
|
2
2
|
export interface ResultResponse<T = unknown> {
|
|
3
3
|
body?: T;
|
|
4
4
|
status: number;
|
|
@@ -14,4 +14,5 @@ export declare const forbidden: <T = string>(body?: T, headers?: HeadersInit) =>
|
|
|
14
14
|
export declare const notFound: <T = string>(body?: T, headers?: HeadersInit) => ResultResponse<T>;
|
|
15
15
|
export declare const methodNotAllowed: <T = string>(body?: T, headers?: HeadersInit) => ResultResponse<T>;
|
|
16
16
|
export declare const internalError: (err: unknown, headers?: HeadersInit) => ResultResponse<string>;
|
|
17
|
+
export declare const fileResponse: (content: Blob | ArrayBuffer | ReadableStream<Uint8Array>, contentType: string, fileName?: string, headers?: HeadersInit) => ResultResponse<BodyInit>;
|
|
17
18
|
export declare function toAstroResponse(result: ResultResponse | undefined): Response;
|
|
@@ -11,6 +11,20 @@ export const forbidden = (body = 'Forbidden', headers) => createResponse(403, bo
|
|
|
11
11
|
export const notFound = (body = 'Not Found', headers) => createResponse(404, body, headers);
|
|
12
12
|
export const methodNotAllowed = (body = 'Method Not Allowed', headers) => createResponse(405, body, headers);
|
|
13
13
|
export const internalError = (err, headers) => createResponse(500, err instanceof Error ? err.message : String(err), headers);
|
|
14
|
+
export const fileResponse = (content, contentType, fileName, headers) => {
|
|
15
|
+
const disposition = fileName
|
|
16
|
+
? { 'Content-Disposition': `attachment; filename="${fileName}"` }
|
|
17
|
+
: {};
|
|
18
|
+
return {
|
|
19
|
+
status: 200,
|
|
20
|
+
body: content,
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': contentType,
|
|
23
|
+
...disposition,
|
|
24
|
+
...headers,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
14
28
|
export function toAstroResponse(result) {
|
|
15
29
|
if (!result)
|
|
16
30
|
return new Response(null, { status: 204 });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as astro from 'astro';
|
|
2
2
|
import { APIContext, APIRoute } from 'astro';
|
|
3
|
-
import { HeadersInit } from 'undici';
|
|
3
|
+
import { HeadersInit, BodyInit } from 'undici';
|
|
4
4
|
|
|
5
5
|
declare enum HttpMethod {
|
|
6
6
|
GET = "GET",
|
|
@@ -31,10 +31,12 @@ declare const forbidden: <T = string>(body?: T, headers?: HeadersInit) => Result
|
|
|
31
31
|
declare const notFound: <T = string>(body?: T, headers?: HeadersInit) => ResultResponse<T>;
|
|
32
32
|
declare const methodNotAllowed: <T = string>(body?: T, headers?: HeadersInit) => ResultResponse<T>;
|
|
33
33
|
declare const internalError: (err: unknown, headers?: HeadersInit) => ResultResponse<string>;
|
|
34
|
+
declare const fileResponse: (content: Blob | ArrayBuffer | ReadableStream<Uint8Array>, contentType: string, fileName?: string, headers?: HeadersInit) => ResultResponse<BodyInit>;
|
|
34
35
|
declare function toAstroResponse(result: ResultResponse | undefined): Response;
|
|
35
36
|
|
|
36
37
|
type Handler = (ctx: APIContext) => Promise<ResultResponse | Response> | ResultResponse | Response;
|
|
37
38
|
declare function defineHandler(handler: Handler): APIRoute;
|
|
39
|
+
declare function isReadableStream(value: unknown): value is ReadableStream<Uint8Array>;
|
|
38
40
|
|
|
39
41
|
interface Route {
|
|
40
42
|
method: HttpMethod;
|
|
@@ -45,11 +47,98 @@ declare function defineRoute(route: Route): Route;
|
|
|
45
47
|
declare function defineRoute(method: HttpMethod, path: string, handler: Handler): Route;
|
|
46
48
|
|
|
47
49
|
interface RouterOptions {
|
|
50
|
+
basePath?: string;
|
|
48
51
|
/** Custom 404 handler */
|
|
49
52
|
onNotFound?: () => ReturnType<typeof notFound>;
|
|
50
53
|
}
|
|
51
54
|
declare function defineRouter(routes: Route[], options?: RouterOptions): APIRoute;
|
|
52
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Represents a group of routes under a shared base path.
|
|
58
|
+
*
|
|
59
|
+
* Use this class to organize related endpoints, applying a consistent prefix
|
|
60
|
+
* and reducing duplication when defining similar routes.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const users = new RouteGroup('/users')
|
|
64
|
+
* .addGet('/:id', handler)
|
|
65
|
+
* .addPost('/', createUser);
|
|
66
|
+
*/
|
|
67
|
+
declare class RouteGroup {
|
|
68
|
+
private basePath;
|
|
69
|
+
private routes;
|
|
70
|
+
/**
|
|
71
|
+
* Creates a new route group with the specified base path.
|
|
72
|
+
* Trailing slashes are automatically removed.
|
|
73
|
+
*
|
|
74
|
+
* @param basePath - The common prefix for all routes in the group (e.g. "/users")
|
|
75
|
+
*/
|
|
76
|
+
constructor(basePath: string);
|
|
77
|
+
/**
|
|
78
|
+
* Returns the normalized base path used by the group.
|
|
79
|
+
*/
|
|
80
|
+
getBasePath(): string;
|
|
81
|
+
/**
|
|
82
|
+
* Registers a GET route under the group's base path.
|
|
83
|
+
*
|
|
84
|
+
* @param path - Path relative to the base path (e.g. "/:id")
|
|
85
|
+
* @param handler - The handler function for this route
|
|
86
|
+
*/
|
|
87
|
+
addGet(path: string, handler: Handler): this;
|
|
88
|
+
/**
|
|
89
|
+
* Registers a POST route under the group's base path.
|
|
90
|
+
*
|
|
91
|
+
* @param path - Path relative to the base path
|
|
92
|
+
* @param handler - The handler function for this route
|
|
93
|
+
*/
|
|
94
|
+
addPost(path: string, handler: Handler): this;
|
|
95
|
+
/**
|
|
96
|
+
* Registers a PUT route under the group's base path.
|
|
97
|
+
*
|
|
98
|
+
* @param path - Path relative to the base path
|
|
99
|
+
* @param handler - The handler function for this route
|
|
100
|
+
*/
|
|
101
|
+
addPut(path: string, handler: Handler): this;
|
|
102
|
+
/**
|
|
103
|
+
* Registers a DELETE route under the group's base path.
|
|
104
|
+
*
|
|
105
|
+
* @param path - Path relative to the base path
|
|
106
|
+
* @param handler - The handler function for this route
|
|
107
|
+
*/
|
|
108
|
+
addDelete(path: string, handler: Handler): this;
|
|
109
|
+
/**
|
|
110
|
+
* Registers a PATCH route under the group's base path.
|
|
111
|
+
*
|
|
112
|
+
* @param path - Path relative to the base path
|
|
113
|
+
* @param handler - The handler function for this route
|
|
114
|
+
*/
|
|
115
|
+
addPatch(path: string, handler: Handler): this;
|
|
116
|
+
/**
|
|
117
|
+
* Internal method to register a route under the group with any HTTP method.
|
|
118
|
+
*
|
|
119
|
+
* @param method - HTTP verb
|
|
120
|
+
* @param subPath - Route path relative to the base
|
|
121
|
+
* @param handler - The handler function for this route
|
|
122
|
+
*/
|
|
123
|
+
private add;
|
|
124
|
+
/**
|
|
125
|
+
* Returns all the registered routes in the group.
|
|
126
|
+
*/
|
|
127
|
+
getRoutes(): Route[];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Helper to define a `RouteGroup` with optional inline configuration.
|
|
131
|
+
*
|
|
132
|
+
* @param basePath - The base path prefix for all routes
|
|
133
|
+
* @param configure - Optional callback to configure the group inline
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* const users = defineGroup('/users', (group) => {
|
|
137
|
+
* group.addGet('/:id', handler);
|
|
138
|
+
* });
|
|
139
|
+
*/
|
|
140
|
+
declare function defineGroup(basePath: string, configure?: (group: RouteGroup) => void): RouteGroup;
|
|
141
|
+
|
|
53
142
|
interface RouteMatch {
|
|
54
143
|
handler: Handler | null;
|
|
55
144
|
allowed?: HttpMethod[];
|
|
@@ -62,12 +151,121 @@ declare class RouteTrie {
|
|
|
62
151
|
private segmentize;
|
|
63
152
|
}
|
|
64
153
|
|
|
154
|
+
/**
|
|
155
|
+
* A fluent builder for creating and composing API routes in Astro.
|
|
156
|
+
*
|
|
157
|
+
* `RouterBuilder` supports both simple method-based additions (`addGet`, `addPost`, etc.)
|
|
158
|
+
* and organized groups via `defineGroup()` + `addGroup()` for scalable applications.
|
|
159
|
+
*
|
|
160
|
+
* @example Basic usage:
|
|
161
|
+
* ```ts
|
|
162
|
+
* const router = new RouterBuilder()
|
|
163
|
+
* .addGet('/ping', () => ok('pong'))
|
|
164
|
+
* .addPost('/submit', handler);
|
|
165
|
+
*
|
|
166
|
+
* export const ALL = router.build();
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* @example Using groups:
|
|
170
|
+
* ```ts
|
|
171
|
+
* const users = defineGroup('/users')
|
|
172
|
+
* .addGet('/:id', userHandler);
|
|
173
|
+
*
|
|
174
|
+
* const router = new RouterBuilder({ basePath: '/api' })
|
|
175
|
+
* .addGroup(users);
|
|
176
|
+
*
|
|
177
|
+
* export const GET = router.build();
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
65
180
|
declare class RouterBuilder {
|
|
66
|
-
private
|
|
181
|
+
private _options;
|
|
182
|
+
private _routes;
|
|
183
|
+
private static _registerWarned;
|
|
184
|
+
constructor(options?: RouterOptions);
|
|
185
|
+
/**
|
|
186
|
+
* @deprecated Prefer `addGroup()` or `addRoute()` for structured routing.
|
|
187
|
+
*
|
|
188
|
+
* Registers a single route manually.
|
|
189
|
+
*
|
|
190
|
+
* This method is deprecated in favor of a more scalable and organized approach using
|
|
191
|
+
* `defineGroup()` + `addGroup()` or `addRoute()` for ad-hoc additions.
|
|
192
|
+
*
|
|
193
|
+
* @param route A single `Route` object to register.
|
|
194
|
+
*/
|
|
67
195
|
register(route: Route): void;
|
|
196
|
+
/**
|
|
197
|
+
* @deprecated Prefer `addGroup()` or `addRoute()` for structured routing.
|
|
198
|
+
*
|
|
199
|
+
* Registers multiple routes at once.
|
|
200
|
+
*
|
|
201
|
+
* This method is deprecated and discouraged for larger codebases in favor of group-based composition.
|
|
202
|
+
*
|
|
203
|
+
* @param routes An array of `Route` objects to register.
|
|
204
|
+
*/
|
|
68
205
|
register(routes: Route[]): void;
|
|
69
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Adds a single route to the router.
|
|
208
|
+
*
|
|
209
|
+
* @param route The route to add.
|
|
210
|
+
* @returns The current builder instance (for chaining).
|
|
211
|
+
*/
|
|
212
|
+
addRoute(route: Route): this;
|
|
213
|
+
/**
|
|
214
|
+
* Adds a group of pre-defined routes (from `defineGroup()`).
|
|
215
|
+
*
|
|
216
|
+
* @param group A `RouteGroup` instance.
|
|
217
|
+
* @returns The current builder instance (for chaining).
|
|
218
|
+
*/
|
|
219
|
+
addGroup(group: RouteGroup): this;
|
|
220
|
+
/**
|
|
221
|
+
* Adds a GET route.
|
|
222
|
+
* @param path Route path (e.g., `/items/:id`)
|
|
223
|
+
* @param handler Request handler function.
|
|
224
|
+
*/
|
|
225
|
+
addGet(path: string, handler: Handler): this;
|
|
226
|
+
/**
|
|
227
|
+
* Adds a POST route.
|
|
228
|
+
* @param path Route path.
|
|
229
|
+
* @param handler Request handler function.
|
|
230
|
+
*/
|
|
231
|
+
addPost(path: string, handler: Handler): this;
|
|
232
|
+
/**
|
|
233
|
+
* Adds a PUT route.
|
|
234
|
+
* @param path Route path.
|
|
235
|
+
* @param handler Request handler function.
|
|
236
|
+
*/
|
|
237
|
+
addPut(path: string, handler: Handler): this;
|
|
238
|
+
/**
|
|
239
|
+
* Adds a DELETE route.
|
|
240
|
+
* @param path Route path.
|
|
241
|
+
* @param handler Request handler function.
|
|
242
|
+
*/
|
|
243
|
+
addDelete(path: string, handler: Handler): this;
|
|
244
|
+
/**
|
|
245
|
+
* Adds a PATCH route.
|
|
246
|
+
* @param path Route path.
|
|
247
|
+
* @param handler Request handler function.
|
|
248
|
+
*/
|
|
249
|
+
addPatch(path: string, handler: Handler): this;
|
|
250
|
+
/**
|
|
251
|
+
* Internal helper to add a route with any HTTP method.
|
|
252
|
+
*
|
|
253
|
+
* @param method The HTTP method.
|
|
254
|
+
* @param subPath Path segment (can be relative or absolute).
|
|
255
|
+
* @param handler Request handler.
|
|
256
|
+
* @returns The current builder instance (for chaining).
|
|
257
|
+
*/
|
|
258
|
+
private add;
|
|
259
|
+
/**
|
|
260
|
+
* Finalizes the router and returns an Astro-compatible route handler.
|
|
261
|
+
*
|
|
262
|
+
* This function should be used to export a method like `GET`, `POST`, or `ALL`
|
|
263
|
+
* inside Astro API endpoints.
|
|
264
|
+
*
|
|
265
|
+
* @returns A fully resolved Astro route handler.
|
|
266
|
+
*/
|
|
267
|
+
build(): astro.APIRoute;
|
|
70
268
|
}
|
|
71
269
|
|
|
72
|
-
export { ALLOWED_HTTP_METHODS, HttpMethod, RouteTrie, RouterBuilder, badRequest, created, defineHandler, defineRoute, defineRouter, forbidden, internalError, methodNotAllowed, noContent, normalizeMethod, notFound, notModified, ok, toAstroResponse, unauthorized };
|
|
270
|
+
export { ALLOWED_HTTP_METHODS, HttpMethod, RouteGroup, RouteTrie, RouterBuilder, badRequest, created, defineGroup, defineHandler, defineRoute, defineRouter, fileResponse, forbidden, internalError, isReadableStream, methodNotAllowed, noContent, normalizeMethod, notFound, notModified, ok, toAstroResponse, unauthorized };
|
|
73
271
|
export type { Handler, ResultResponse, Route, RouterOptions };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-routify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A high-performance API router for Astro using a Trie-based matcher.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"rimraf": "^6.0.1",
|
|
34
|
-
"rollup": "^4.
|
|
34
|
+
"rollup": "^4.46.2",
|
|
35
35
|
"rollup-plugin-dts": "^6.2.1",
|
|
36
36
|
"typescript": "^5.3.3",
|
|
37
|
-
"undici": "^7.
|
|
37
|
+
"undici": "^7.13.0",
|
|
38
38
|
"vitest": "^3.2.4"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|