@opensourceframework/next-connect 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) 2019 Hoang Vo
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,565 @@
1
+ # @opensourceframework/next-connect
2
+
3
+ [![npm](https://badgen.net/npm/v/@opensourceframework/next-connect)](https://www.npmjs.com/package/@opensourceframework/next-connect)
4
+ [![CircleCI](https://circleci.com/gh/hoangvvo/@opensourceframework/next-connect.svg?style=svg)](https://circleci.com/gh/hoangvvo/@opensourceframework/next-connect)
5
+ [![codecov](https://codecov.io/gh/hoangvvo/@opensourceframework/next-connect/branch/main/graph/badge.svg?token=LGIyl3Ti4H)](https://codecov.io/gh/hoangvvo/@opensourceframework/next-connect)
6
+ [![minified size](https://badgen.net/bundlephobia/min/@opensourceframework/next-connect)](https://bundlephobia.com/result?p=@opensourceframework/next-connect)
7
+ [![download/year](https://badgen.net/npm/dy/@opensourceframework/next-connect)](https://www.npmjs.com/package/@opensourceframework/next-connect)
8
+
9
+ The promise-based method routing and middleware layer for [Next.js](https://nextjs.org/) [API Routes](#nextjs-api-routes), [Edge API Routes](#nextjs-edge-api-routes), [Middleware](#nextjs-middleware), [Next.js App Router](#nextjs-app-router), and [getServerSideProps](#nextjs-getserversideprops).
10
+
11
+ ### Attribution
12
+
13
+ - **Original Author**: Unknown
14
+ - **Original Repository**: https://github.com/hoangvvo/next-connect
15
+ - **Original License**: MIT
16
+
17
+
18
+
19
+ - Async middleware
20
+ - [Lightweight](https://bundlephobia.com/scan-results?packages=express,@opensourceframework/next-connect,koa,micro) => Suitable for serverless environment
21
+ - [way faster](https://github.com/hoangvvo/@opensourceframework/next-connect/tree/main/bench) than Express.js. Compatible with Express.js via [a wrapper](#expressjs-compatibility).
22
+ - Works with async handlers (with error catching)
23
+ - TypeScript support
24
+
25
+ ## Installation
26
+
27
+ ```sh
28
+ npm install @opensourceframework/next-connect@next
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ Also check out the [examples](./examples/) folder.
34
+
35
+ ### Next.js API Routes
36
+
37
+ `@opensourceframework/next-connect` can be used in [API Routes](https://nextjs.org/docs/api-routes/introduction).
38
+
39
+ ```typescript
40
+ // pages/api/user/[id].ts
41
+ import type { NextApiRequest, NextApiResponse } from "next";
42
+ import { createRouter, expressWrapper } from "@opensourceframework/next-connect";
43
+ import cors from "cors";
44
+
45
+ const router = createRouter<NextApiRequest, NextApiResponse>();
46
+
47
+ router
48
+ // Use express middleware in @opensourceframework/next-connect with expressWrapper function
49
+ .use(expressWrapper(passport.session()))
50
+ // A middleware example
51
+ .use(async (req, res, next) => {
52
+ const start = Date.now();
53
+ await next(); // call next in chain
54
+ const end = Date.now();
55
+ console.log(`Request took ${end - start}ms`);
56
+ })
57
+ .get((req, res) => {
58
+ const user = getUser(req.query.id);
59
+ res.json({ user });
60
+ })
61
+ .put((req, res) => {
62
+ if (req.user.id !== req.query.id) {
63
+ throw new ForbiddenError("You can't update other user's profile");
64
+ }
65
+ const user = await updateUser(req.body.user);
66
+ res.json({ user });
67
+ });
68
+
69
+ export const config = {
70
+ runtime: "edge",
71
+ };
72
+
73
+ export default router.handler({
74
+ onError: (err, req, res) => {
75
+ console.error(err.stack);
76
+ res.status(err.statusCode || 500).end(err.message);
77
+ },
78
+ });
79
+ ```
80
+
81
+ ### Next.js Edge API Routes
82
+
83
+ `@opensourceframework/next-connect` can be used in [Edge API Routes](https://nextjs.org/docs/api-routes/edge-api-routes)
84
+
85
+ ```typescript
86
+ // pages/api/user/[id].ts
87
+ import type { NextFetchEvent, NextRequest } from "next/server";
88
+ import { createEdgeRouter } from "@opensourceframework/next-connect";
89
+ import cors from "cors";
90
+
91
+ const router = createEdgeRouter<NextRequest, NextFetchEvent>();
92
+
93
+ router
94
+ // A middleware example
95
+ .use(async (req, event, next) => {
96
+ const start = Date.now();
97
+ await next(); // call next in chain
98
+ const end = Date.now();
99
+ console.log(`Request took ${end - start}ms`);
100
+ })
101
+ .get((req) => {
102
+ const id = req.nextUrl.searchParams.get("id");
103
+ const user = getUser(id);
104
+ return NextResponse.json({ user });
105
+ })
106
+ .put((req) => {
107
+ const id = req.nextUrl.searchParams.get("id");
108
+ if (req.user.id !== id) {
109
+ throw new ForbiddenError("You can't update other user's profile");
110
+ }
111
+ const user = await updateUser(req.body.user);
112
+ return NextResponse.json({ user });
113
+ });
114
+
115
+ export default router.handler({
116
+ onError: (err, req, event) => {
117
+ console.error(err.stack);
118
+ return new NextResponse("Something broke!", {
119
+ status: err.statusCode || 500,
120
+ });
121
+ },
122
+ });
123
+ ```
124
+
125
+ ### Next.js App Router
126
+
127
+ `@opensourceframework/next-connect` can be used in [Next.js 13 Route Handler](https://beta.nextjs.org/docs/routing/route-handlers). The way handlers are written is almost the same to [Next.js Edge API Routes](#nextjs-edge-api-routes) by using `createEdgeRouter`.
128
+
129
+ ```typescript
130
+ // app/api/user/[id]/route.ts
131
+
132
+ import type { NextFetchEvent, NextRequest } from "next/server";
133
+ import { createEdgeRouter } from "@opensourceframework/next-connect";
134
+ import cors from "cors";
135
+
136
+ interface RequestContext {
137
+ params: {
138
+ id: string;
139
+ };
140
+ }
141
+
142
+ const router = createEdgeRouter<NextRequest, RequestContext>();
143
+
144
+ router
145
+ // A middleware example
146
+ .use(async (req, event, next) => {
147
+ const start = Date.now();
148
+ await next(); // call next in chain
149
+ const end = Date.now();
150
+ console.log(`Request took ${end - start}ms`);
151
+ })
152
+ .get((req) => {
153
+ const id = req.params.id;
154
+ const user = getUser(id);
155
+ return NextResponse.json({ user });
156
+ })
157
+ .put((req) => {
158
+ const id = req.params.id;
159
+ if (req.user.id !== id) {
160
+ throw new ForbiddenError("You can't update other user's profile");
161
+ }
162
+ const user = await updateUser(req.body.user);
163
+ return NextResponse.json({ user });
164
+ });
165
+
166
+ export async function GET(request: NextRequest, ctx: RequestContext) {
167
+ return router.run(request, ctx);
168
+ }
169
+
170
+ export async function PUT(request: NextRequest, ctx: RequestContext) {
171
+ return router.run(request, ctx);
172
+ }
173
+ ```
174
+
175
+ ### Next.js Middleware
176
+
177
+ `@opensourceframework/next-connect` can be used in [Next.js Middleware](https://nextjs.org/docs/advanced-features/middleware)
178
+
179
+ ```typescript
180
+ // middleware.ts
181
+ import { NextResponse } from "next/server";
182
+ import type { NextRequest, NextFetchEvent } from "next/server";
183
+ import { createEdgeRouter } from "@opensourceframework/next-connect";
184
+
185
+ const router = createEdgeRouter<NextRequest, NextFetchEvent>();
186
+
187
+ router.use(async (request, event, next) => {
188
+ // logging request example
189
+ console.log(`${request.method} ${request.url}`);
190
+ return next();
191
+ });
192
+
193
+ router.get("/about", (request) => {
194
+ return NextResponse.redirect(new URL("/about-2", request.url));
195
+ });
196
+
197
+ router.use("/dashboard", (request) => {
198
+ if (!isAuthenticated(request)) {
199
+ return NextResponse.redirect(new URL("/login", request.url));
200
+ }
201
+ return NextResponse.next();
202
+ });
203
+
204
+ router.all(() => {
205
+ // default if none of the above matches
206
+ return NextResponse.next();
207
+ });
208
+
209
+ export function middleware(request: NextRequest, event: NextFetchEvent) {
210
+ return router.run(request, event);
211
+ }
212
+
213
+ export const config = {
214
+ matcher: [
215
+ /*
216
+ * Match all request paths except for the ones starting with:
217
+ * - api (API routes)
218
+ * - _next/static (static files)
219
+ * - _next/image (image optimization files)
220
+ * - favicon.ico (favicon file)
221
+ */
222
+ "/((?!api|_next/static|_next/image|favicon.ico).*)",
223
+ ],
224
+ };
225
+ ```
226
+
227
+ ### Next.js getServerSideProps
228
+
229
+ `@opensourceframework/next-connect` can be used in [getServerSideProps](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props).
230
+
231
+ ```jsx
232
+ // pages/users/[id].js
233
+ import { createRouter } from "@opensourceframework/next-connect";
234
+
235
+ export default function Page({ user, updated }) {
236
+ return (
237
+ <div>
238
+ {updated && <p>User has been updated</p>}
239
+ <div>{JSON.stringify(user)}</div>
240
+ <form method="POST">{/* User update form */}</form>
241
+ </div>
242
+ );
243
+ }
244
+
245
+ const router = createRouter()
246
+ .use(async (req, res, next) => {
247
+ // this serve as the error handling middleware
248
+ try {
249
+ return await next();
250
+ } catch (e) {
251
+ return {
252
+ props: { error: e.message },
253
+ };
254
+ }
255
+ })
256
+ .get(async (req, res) => {
257
+ const user = await getUser(req.params.id);
258
+ if (!user) {
259
+ // https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#notfound
260
+ return { props: { notFound: true } };
261
+ }
262
+ return { props: { user } };
263
+ })
264
+ .put(async (req, res) => {
265
+ const user = await updateUser(req);
266
+ return { props: { user, updated: true } };
267
+ });
268
+
269
+ export async function getServerSideProps({ req, res }) {
270
+ return router.run(req, res);
271
+ }
272
+ ```
273
+
274
+ ## API
275
+
276
+ The following APIs are rewritten in term of `NodeRouter` (`createRouter`), but they apply to `EdgeRouter` (`createEdgeRouter`) as well.
277
+
278
+ ### router = createRouter()
279
+
280
+ Create an instance Node.js router.
281
+
282
+ ### router.use(base, ...fn)
283
+
284
+ `base` (optional) - match all routes to the right of `base` or match all if omitted. (Note: If used in Next.js, this is often omitted)
285
+
286
+ `fn`(s) can either be:
287
+
288
+ - functions of `(req, res[, next])`
289
+ - **or** a router instance
290
+
291
+ ```javascript
292
+ // Mount a middleware function
293
+ router1.use(async (req, res, next) => {
294
+ req.hello = "world";
295
+ await next(); // call to proceed to the next in chain
296
+ console.log("request is done"); // call after all downstream handler has run
297
+ });
298
+
299
+ // Or include a base
300
+ router2.use("/foo", fn); // Only run in /foo/**
301
+
302
+ // mount an instance of router
303
+ const sub1 = createRouter().use(fn1, fn2);
304
+ const sub2 = createRouter().use("/dashboard", auth);
305
+ const sub3 = createRouter()
306
+ .use("/waldo", subby)
307
+ .get(getty)
308
+ .post("/baz", posty)
309
+ .put("/", putty);
310
+ router3
311
+ // - fn1 and fn2 always run
312
+ // - auth runs only on /dashboard
313
+ .use(sub1, sub2)
314
+ // `subby` runs on ANY /foo/waldo?/*
315
+ // `getty` runs on GET /foo/*
316
+ // `posty` runs on POST /foo/baz
317
+ // `putty` runs on PUT /foo
318
+ .use("/foo", sub3);
319
+ ```
320
+
321
+ ### router.METHOD(pattern, ...fns)
322
+
323
+ `METHOD` is an HTTP method (`GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `TRACE`) in lowercase.
324
+
325
+ `pattern` (optional) - match routes based on [supported pattern](https://github.com/lukeed/regexparam#regexparam-) or match any if omitted.
326
+
327
+ `fn`(s) are functions of `(req, res[, next])`.
328
+
329
+ ```javascript
330
+ router.get("/api/user", (req, res, next) => {
331
+ res.json(req.user);
332
+ });
333
+ router.post("/api/users", (req, res, next) => {
334
+ res.end("User created");
335
+ });
336
+ router.put("/api/user/:id", (req, res, next) => {
337
+ // https://nextjs.org/docs/routing/dynamic-routes
338
+ res.end(`User ${req.params.id} updated`);
339
+ });
340
+
341
+ // Next.js already handles routing (including dynamic routes), we often
342
+ // omit `pattern` in `.METHOD`
343
+ router.get((req, res, next) => {
344
+ res.end("This matches whatever route");
345
+ });
346
+ ```
347
+
348
+ > **Note**
349
+ > You should understand Next.js [file-system based routing](https://nextjs.org/docs/routing/introduction). For example, having a `router.put("/api/foo", handler)` inside `page/api/index.js` _does not_ serve that handler at `/api/foo`.
350
+
351
+ ### router.all(pattern, ...fns)
352
+
353
+ Same as [.METHOD](#methodpattern-fns) but accepts _any_ methods.
354
+
355
+ ### router.handler(options)
356
+
357
+ Create a handler to handle incoming requests.
358
+
359
+ **options.onError**
360
+
361
+ Accepts a function as a catch-all error handler; executed whenever a handler throws an error.
362
+ By default, it responds with a generic `500 Internal Server Error` while logging the error to `console`.
363
+
364
+ ```javascript
365
+ function onError(err, req, res) {
366
+ logger.log(err);
367
+ // OR: console.error(err);
368
+
369
+ res.status(500).end("Internal server error");
370
+ }
371
+
372
+ export default router.handler({ onError });
373
+ ```
374
+
375
+ **options.onNoMatch**
376
+
377
+ Accepts a function of `(req, res)` as a handler when no route is matched.
378
+ By default, it responds with a `404` status and a `Route [Method] [Url] not found` body.
379
+
380
+ ```javascript
381
+ function onNoMatch(req, res) {
382
+ res.status(404).end("page is not found... or is it!?");
383
+ }
384
+
385
+ export default router.handler({ onNoMatch });
386
+ ```
387
+
388
+ ### router.run(req, res)
389
+
390
+ Runs `req` and `res` through the middleware chain and returns a **promise**. It resolves with the value returned from handlers.
391
+
392
+ ```js
393
+ router
394
+ .use(async (req, res, next) => {
395
+ return (await next()) + 1;
396
+ })
397
+ .use(async () => {
398
+ return (await next()) + 2;
399
+ })
400
+ .use(async () => {
401
+ return 3;
402
+ });
403
+
404
+ console.log(await router.run(req, res));
405
+ // The above will print "6"
406
+ ```
407
+
408
+ If an error in thrown within the chain, `router.run` will reject. You can also add a try-catch in the first middleware to catch the error before it rejects the `.run()` call:
409
+
410
+ ```js
411
+ router
412
+ .use(async (req, res, next) => {
413
+ return next().catch(errorHandler);
414
+ })
415
+ .use(thisMiddlewareMightThrow);
416
+
417
+ await router.run(req, res);
418
+ ```
419
+
420
+ ## Common errors
421
+
422
+ There are some pitfalls in using `@opensourceframework/next-connect`. Below are things to keep in mind to use it correctly.
423
+
424
+ 1. **Always** `await next()`
425
+
426
+ If `next()` is not awaited, errors will not be caught if they are thrown in async handlers, leading to `UnhandledPromiseRejection`.
427
+
428
+ ```javascript
429
+ // OK: we don't use async so no need to await
430
+ router
431
+ .use((req, res, next) => {
432
+ next();
433
+ })
434
+ .use((req, res, next) => {
435
+ next();
436
+ })
437
+ .use(() => {
438
+ throw new Error("💥");
439
+ });
440
+
441
+ // BAD: This will lead to UnhandledPromiseRejection
442
+ router
443
+ .use(async (req, res, next) => {
444
+ next();
445
+ })
446
+ .use(async (req, res, next) => {
447
+ next();
448
+ })
449
+ .use(async () => {
450
+ throw new Error("💥");
451
+ });
452
+
453
+ // GOOD
454
+ router
455
+ .use(async (req, res, next) => {
456
+ await next(); // next() is awaited, so errors are caught properly
457
+ })
458
+ .use((req, res, next) => {
459
+ return next(); // this works as well since we forward the rejected promise
460
+ })
461
+ .use(async () => {
462
+ throw new Error("💥");
463
+ // return new Promise.reject("💥");
464
+ });
465
+ ```
466
+
467
+ Another issue is that the handler would resolve before all the code in each layer runs.
468
+
469
+ ```javascript
470
+ const handler = router
471
+ .use(async (req, res, next) => {
472
+ next(); // this is not returned or await
473
+ })
474
+ .get(async () => {
475
+ // simulate a long task
476
+ await new Promise((resolve) => setTimeout(resolve, 1000));
477
+ res.send("ok");
478
+ console.log("request is completed");
479
+ })
480
+ .handler();
481
+
482
+ await handler(req, res);
483
+ console.log("finally"); // this will run before the get layer gets to finish
484
+
485
+ // This will result in:
486
+ // 1) "finally"
487
+ // 2) "request is completed"
488
+ ```
489
+
490
+ 2. **DO NOT** reuse the same instance of `router` like the below pattern:
491
+
492
+ ```javascript
493
+ // api-libs/base.js
494
+ export default createRouter().use(a).use(b);
495
+
496
+ // api/foo.js
497
+ import router from "api-libs/base";
498
+ export default router.get(x).handler();
499
+
500
+ // api/bar.js
501
+ import router from "api-libs/base";
502
+ export default router.get(y).handler();
503
+ ```
504
+
505
+ This is because, in each API Route, the same router instance is mutated, leading to undefined behaviors.
506
+ If you want to achieve something like that, you can use `router.clone` to return different instances with the same routes populated.
507
+
508
+ ```javascript
509
+ // api-libs/base.js
510
+ export default createRouter().use(a).use(b);
511
+
512
+ // api/foo.js
513
+ import router from "api-libs/base";
514
+ export default router.clone().get(x).handler();
515
+
516
+ // api/bar.js
517
+ import router from "api-libs/base";
518
+ export default router.clone().get(y).handler();
519
+ ```
520
+
521
+ 3. **DO NOT** use response function like `res.(s)end` or `res.redirect` inside `getServerSideProps`.
522
+
523
+ ```javascript
524
+ // page/index.js
525
+ const handler = createRouter()
526
+ .use((req, res) => {
527
+ // BAD: res.redirect is not a function (not defined in `getServerSideProps`)
528
+ // See https://github.com/hoangvvo/@opensourceframework/next-connect/issues/194#issuecomment-1172961741 for a solution
529
+ res.redirect("foo");
530
+ })
531
+ .use((req, res) => {
532
+ // BAD: `getServerSideProps` gives undefined behavior if we try to send a response
533
+ res.end("bar");
534
+ });
535
+
536
+ export async function getServerSideProps({ req, res }) {
537
+ await router.run(req, res);
538
+ return {
539
+ props: {},
540
+ };
541
+ }
542
+ ```
543
+
544
+ 3. **DO NOT** use `handler()` directly in `getServerSideProps`.
545
+
546
+ ```javascript
547
+ // page/index.js
548
+ const router = createRouter().use(foo).use(bar);
549
+ const handler = router.handler();
550
+
551
+ export async function getServerSideProps({ req, res }) {
552
+ await handler(req, res); // BAD: You should call router.run(req, res);
553
+ return {
554
+ props: {},
555
+ };
556
+ }
557
+ ```
558
+
559
+ ## Contributing
560
+
561
+ Please see my [contributing.md](CONTRIBUTING.md).
562
+
563
+ ## License
564
+
565
+ [MIT](LICENSE)
@@ -0,0 +1,25 @@
1
+ import { R as RouteShortcutMethod, V as ValueOrPromise, a as RouteMatch, b as Nextable, H as HandlerOptions } from './types-C6rPiWOS.cjs';
2
+
3
+ type RequestHandler<Req extends Request, Ctx> = (req: Req, ctx: Ctx) => ValueOrPromise<Response | void>;
4
+ declare class EdgeRouter<Req extends Request = Request, Ctx = unknown> {
5
+ private router;
6
+ private add;
7
+ all: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
8
+ get: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
9
+ head: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
10
+ post: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
11
+ put: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
12
+ patch: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
13
+ delete: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
14
+ use(base: RouteMatch | Nextable<RequestHandler<Req, Ctx>> | EdgeRouter<Req, Ctx>, ...fns: (Nextable<RequestHandler<Req, Ctx>> | EdgeRouter<Req, Ctx>)[]): this;
15
+ private prepareRequest;
16
+ clone(): EdgeRouter<Req, Ctx>;
17
+ run(req: Req, ctx: Ctx): Promise<unknown>;
18
+ handler(options?: HandlerOptions<RequestHandler<Req, Ctx>>): (req: Req, ctx: Ctx) => Promise<any>;
19
+ }
20
+ declare function getPathname(req: Request & {
21
+ nextUrl?: URL;
22
+ }): string;
23
+ declare function createEdgeRouter<Req extends Request, Ctx>(): EdgeRouter<Req, Ctx>;
24
+
25
+ export { EdgeRouter, type RequestHandler, createEdgeRouter, getPathname };
package/dist/edge.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { R as RouteShortcutMethod, V as ValueOrPromise, a as RouteMatch, b as Nextable, H as HandlerOptions } from './types-C6rPiWOS.js';
2
+
3
+ type RequestHandler<Req extends Request, Ctx> = (req: Req, ctx: Ctx) => ValueOrPromise<Response | void>;
4
+ declare class EdgeRouter<Req extends Request = Request, Ctx = unknown> {
5
+ private router;
6
+ private add;
7
+ all: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
8
+ get: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
9
+ head: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
10
+ post: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
11
+ put: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
12
+ patch: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
13
+ delete: RouteShortcutMethod<this, RequestHandler<Req, Ctx>>;
14
+ use(base: RouteMatch | Nextable<RequestHandler<Req, Ctx>> | EdgeRouter<Req, Ctx>, ...fns: (Nextable<RequestHandler<Req, Ctx>> | EdgeRouter<Req, Ctx>)[]): this;
15
+ private prepareRequest;
16
+ clone(): EdgeRouter<Req, Ctx>;
17
+ run(req: Req, ctx: Ctx): Promise<unknown>;
18
+ handler(options?: HandlerOptions<RequestHandler<Req, Ctx>>): (req: Req, ctx: Ctx) => Promise<any>;
19
+ }
20
+ declare function getPathname(req: Request & {
21
+ nextUrl?: URL;
22
+ }): string;
23
+ declare function createEdgeRouter<Req extends Request, Ctx>(): EdgeRouter<Req, Ctx>;
24
+
25
+ export { EdgeRouter, type RequestHandler, createEdgeRouter, getPathname };
@@ -0,0 +1,9 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+ import { RequestHandler } from './node.cjs';
3
+ import { b as Nextable } from './types-C6rPiWOS.cjs';
4
+
5
+ type NextFunction = (err?: any) => void;
6
+ type ExpressRequestHandler<Req, Res> = (req: Req, res: Res, next: NextFunction) => void;
7
+ declare function expressWrapper<Req extends IncomingMessage, Res extends ServerResponse>(fn: ExpressRequestHandler<Req, Res>): Nextable<RequestHandler<Req, Res>>;
8
+
9
+ export { expressWrapper };
@@ -0,0 +1,9 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+ import { RequestHandler } from './node.js';
3
+ import { b as Nextable } from './types-C6rPiWOS.js';
4
+
5
+ type NextFunction = (err?: any) => void;
6
+ type ExpressRequestHandler<Req, Res> = (req: Req, res: Res, next: NextFunction) => void;
7
+ declare function expressWrapper<Req extends IncomingMessage, Res extends ServerResponse>(fn: ExpressRequestHandler<Req, Res>): Nextable<RequestHandler<Req, Res>>;
8
+
9
+ export { expressWrapper };
@@ -0,0 +1,5 @@
1
+ export { createEdgeRouter } from './edge.cjs';
2
+ export { expressWrapper } from './express.cjs';
3
+ export { createRouter } from './node.cjs';
4
+ export { H as HandlerOptions, N as NextHandler } from './types-C6rPiWOS.cjs';
5
+ import 'http';
@@ -0,0 +1,5 @@
1
+ export { createEdgeRouter } from './edge.js';
2
+ export { expressWrapper } from './express.js';
3
+ export { createRouter } from './node.js';
4
+ export { H as HandlerOptions, N as NextHandler } from './types-C6rPiWOS.js';
5
+ import 'http';
@@ -0,0 +1,24 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+ import { V as ValueOrPromise, R as RouteShortcutMethod, a as RouteMatch, b as Nextable, H as HandlerOptions } from './types-C6rPiWOS.cjs';
3
+
4
+ type RequestHandler<Req extends IncomingMessage, Res extends ServerResponse> = (req: Req, res: Res) => ValueOrPromise<void>;
5
+ declare class NodeRouter<Req extends IncomingMessage = IncomingMessage, Res extends ServerResponse = ServerResponse> {
6
+ private router;
7
+ private add;
8
+ all: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
9
+ get: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
10
+ head: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
11
+ post: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
12
+ put: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
13
+ patch: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
14
+ delete: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
15
+ use(base: RouteMatch | Nextable<RequestHandler<Req, Res>> | NodeRouter<Req, Res>, ...fns: (Nextable<RequestHandler<Req, Res>> | NodeRouter<Req, Res>)[]): this;
16
+ private prepareRequest;
17
+ clone(): NodeRouter<Req, Res>;
18
+ run(req: Req, res: Res): Promise<unknown>;
19
+ handler(options?: HandlerOptions<RequestHandler<Req, Res>>): (req: Req, res: Res) => Promise<void>;
20
+ }
21
+ declare function getPathname(url: string): string;
22
+ declare function createRouter<Req extends IncomingMessage, Res extends ServerResponse>(): NodeRouter<Req, Res>;
23
+
24
+ export { NodeRouter, type RequestHandler, createRouter, getPathname };
package/dist/node.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+ import { V as ValueOrPromise, R as RouteShortcutMethod, a as RouteMatch, b as Nextable, H as HandlerOptions } from './types-C6rPiWOS.js';
3
+
4
+ type RequestHandler<Req extends IncomingMessage, Res extends ServerResponse> = (req: Req, res: Res) => ValueOrPromise<void>;
5
+ declare class NodeRouter<Req extends IncomingMessage = IncomingMessage, Res extends ServerResponse = ServerResponse> {
6
+ private router;
7
+ private add;
8
+ all: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
9
+ get: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
10
+ head: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
11
+ post: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
12
+ put: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
13
+ patch: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
14
+ delete: RouteShortcutMethod<this, RequestHandler<Req, Res>>;
15
+ use(base: RouteMatch | Nextable<RequestHandler<Req, Res>> | NodeRouter<Req, Res>, ...fns: (Nextable<RequestHandler<Req, Res>> | NodeRouter<Req, Res>)[]): this;
16
+ private prepareRequest;
17
+ clone(): NodeRouter<Req, Res>;
18
+ run(req: Req, res: Res): Promise<unknown>;
19
+ handler(options?: HandlerOptions<RequestHandler<Req, Res>>): (req: Req, res: Res) => Promise<void>;
20
+ }
21
+ declare function getPathname(url: string): string;
22
+ declare function createRouter<Req extends IncomingMessage, Res extends ServerResponse>(): NodeRouter<Req, Res>;
23
+
24
+ export { NodeRouter, type RequestHandler, createRouter, getPathname };
@@ -0,0 +1,12 @@
1
+ type FunctionLike = (...args: any[]) => unknown;
2
+ type RouteMatch = string | RegExp;
3
+ type NextHandler = () => ValueOrPromise<any>;
4
+ type Nextable<H extends FunctionLike> = (...args: [...Parameters<H>, NextHandler]) => ValueOrPromise<any>;
5
+ interface HandlerOptions<Handler extends FunctionLike> {
6
+ onNoMatch?: Handler;
7
+ onError?: (err: unknown, ...args: Parameters<Handler>) => ReturnType<Handler>;
8
+ }
9
+ type ValueOrPromise<T> = T | Promise<T>;
10
+ type RouteShortcutMethod<This, H extends FunctionLike> = (route: RouteMatch | Nextable<H>, ...fns: Nextable<H>[]) => This;
11
+
12
+ export type { HandlerOptions as H, NextHandler as N, RouteShortcutMethod as R, ValueOrPromise as V, RouteMatch as a, Nextable as b };
@@ -0,0 +1,12 @@
1
+ type FunctionLike = (...args: any[]) => unknown;
2
+ type RouteMatch = string | RegExp;
3
+ type NextHandler = () => ValueOrPromise<any>;
4
+ type Nextable<H extends FunctionLike> = (...args: [...Parameters<H>, NextHandler]) => ValueOrPromise<any>;
5
+ interface HandlerOptions<Handler extends FunctionLike> {
6
+ onNoMatch?: Handler;
7
+ onError?: (err: unknown, ...args: Parameters<Handler>) => ReturnType<Handler>;
8
+ }
9
+ type ValueOrPromise<T> = T | Promise<T>;
10
+ type RouteShortcutMethod<This, H extends FunctionLike> = (route: RouteMatch | Nextable<H>, ...fns: Nextable<H>[]) => This;
11
+
12
+ export type { HandlerOptions as H, NextHandler as N, RouteShortcutMethod as R, ValueOrPromise as V, RouteMatch as a, Nextable as b };
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@opensourceframework/next-connect",
4
+ "version": "1.0.0",
5
+ "description": "The method routing and middleware layer for Next.js (and many others)",
6
+ "keywords": [
7
+ "javascript",
8
+ "nextjs",
9
+ "middleware",
10
+ "router",
11
+ "connect"
12
+ ],
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "import": "./dist/index.js",
19
+ "require": "./dist/index.cjs"
20
+ },
21
+ "./node": {
22
+ "import": "./dist/node.js",
23
+ "require": "./dist/node.cjs"
24
+ },
25
+ "./edge": {
26
+ "import": "./dist/edge.js",
27
+ "require": "./dist/edge.cjs"
28
+ },
29
+ "./express": {
30
+ "import": "./dist/express.js",
31
+ "require": "./dist/express.cjs"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "sideEffects": false,
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "dev": "tsup --watch",
41
+ "lint": "eslint src --ext ts --ignore-path .gitignore",
42
+ "typecheck": "tsc --noEmit",
43
+ "test": "vitest run --passWithNoTests",
44
+ "test:watch": "vitest",
45
+ "test:coverage": "vitest run --coverage"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/riceharvest/opensourceframework.git",
50
+ "directory": "packages/next-connect"
51
+ },
52
+ "author": "OpenSource Framework Contributors (fork), Original: Hoang Vo",
53
+ "license": "MIT",
54
+ "bugs": {
55
+ "url": "https://github.com/riceharvest/opensourceframework/issues?q=is%3Aissue+is%3Aopen+next-connect"
56
+ },
57
+ "homepage": "https://github.com/riceharvest/opensourceframework/tree/main/packages/next-connect#readme",
58
+ "devDependencies": {
59
+ "@types/node": "^22.0.0",
60
+ "tsup": "^8.0.0",
61
+ "typescript": "^5.0.0",
62
+ "vitest": "^2.0.0"
63
+ },
64
+ "dependencies": {
65
+ "regexparam": "^2.0.1"
66
+ },
67
+ "engines": {
68
+ "node": ">=18.0.0"
69
+ },
70
+ "contributors": [
71
+ {
72
+ "name": "Hoang Vo",
73
+ "url": "https://github.com/hoangvvo/next-connect"
74
+ }
75
+ ],
76
+ "publishConfig": {
77
+ "access": "public"
78
+ },
79
+ "funding": {
80
+ "type": "GitHub",
81
+ "url": "https://github.com/sponsors/riceharvest"
82
+ }
83
+ }