@wooksjs/express-adapter 0.4.9 → 0.7.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 +130 -41
- package/dist/index.cjs +47 -79
- package/dist/index.d.cts +53 -0
- package/dist/index.d.mts +53 -0
- package/dist/index.d.ts +48 -14
- package/dist/index.mjs +49 -80
- package/package.json +53 -81
package/README.md
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
**!!! This is work-in-progress library, breaking changes are expected !!!**
|
|
1
|
+
# @wooksjs/express-adapter
|
|
4
2
|
|
|
5
3
|
<p align="center">
|
|
6
4
|
<img src="./docs/wooksjs-express.png" height="156px"><br>
|
|
@@ -9,76 +7,167 @@
|
|
|
9
7
|
</a>
|
|
10
8
|
</p>
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
Use [Wooks](https://wooks.moost.org) composables with [Express](https://expressjs.com). This adapter lets you register Wooks-style route handlers on top of an existing Express app — unmatched requests automatically fall through to Express middleware.
|
|
13
11
|
|
|
14
12
|
## Install
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
```bash
|
|
15
|
+
npm install @wooksjs/express-adapter @wooksjs/event-http wooks express
|
|
16
|
+
```
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
## Quick Start
|
|
21
19
|
|
|
22
|
-
### 1. Adapter for express API:
|
|
23
|
-
This one will modify express `get`, `post`, ..., methods. Take this one if you want to keep using express app API.
|
|
24
20
|
```ts
|
|
25
21
|
import express from 'express'
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { HttpError } from '@wooksjs/event-http'
|
|
29
|
-
import { useRouteParams } from '@wooksjs/event-core'
|
|
22
|
+
import { WooksExpress } from '@wooksjs/express-adapter'
|
|
23
|
+
import { useRouteParams, useRequest, HttpError } from '@wooksjs/event-http'
|
|
30
24
|
|
|
31
25
|
const app = express()
|
|
26
|
+
const wooks = new WooksExpress(app)
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
app.get('/test/:param', () => {
|
|
28
|
+
// Return values become the response body
|
|
29
|
+
wooks.get('/hello/:name', () => {
|
|
36
30
|
const { get } = useRouteParams()
|
|
37
|
-
return {
|
|
31
|
+
return { hello: get('name') }
|
|
38
32
|
})
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
// Async handlers work out of the box
|
|
35
|
+
wooks.post('/upload', async () => {
|
|
36
|
+
const { rawBody } = useRequest()
|
|
37
|
+
const body = await rawBody()
|
|
38
|
+
return { received: body.length }
|
|
43
39
|
})
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
// Throw HttpError for error responses
|
|
42
|
+
wooks.get('/protected', () => {
|
|
43
|
+
throw new HttpError(403, 'Forbidden')
|
|
47
44
|
})
|
|
48
45
|
|
|
49
|
-
app.listen(3000, () => console.log('listening 3000'))
|
|
46
|
+
app.listen(3000, () => console.log('listening on 3000'))
|
|
50
47
|
```
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
## How It Works
|
|
50
|
+
|
|
51
|
+
`WooksExpress` extends `WooksHttp` and registers itself as Express middleware. When a request comes in:
|
|
52
|
+
|
|
53
|
+
1. Wooks checks if a matching route is registered
|
|
54
|
+
2. If matched — the Wooks handler runs with full composable support
|
|
55
|
+
3. If not matched — the request falls through to the next Express middleware
|
|
56
|
+
|
|
57
|
+
This means you can **mix Wooks routes with regular Express routes and middleware**:
|
|
54
58
|
|
|
55
59
|
```ts
|
|
56
60
|
import express from 'express'
|
|
57
61
|
import { WooksExpress } from '@wooksjs/express-adapter'
|
|
58
|
-
import
|
|
59
|
-
import { HttpError } from '@wooksjs/event-http'
|
|
60
|
-
import { useRouteParams } from '@wooksjs/event-core'
|
|
62
|
+
import cors from 'cors'
|
|
61
63
|
|
|
62
|
-
const
|
|
64
|
+
const app = express()
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
// Express middleware works as usual
|
|
67
|
+
app.use(cors())
|
|
68
|
+
app.use(express.json())
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
// Wooks handles these routes
|
|
71
|
+
const wooks = new WooksExpress(app)
|
|
72
|
+
wooks.get('/api/users', () => {
|
|
73
|
+
return [{ id: 1, name: 'Alice' }]
|
|
69
74
|
})
|
|
70
75
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
// Express handles this route
|
|
77
|
+
app.get('/legacy', (req, res) => {
|
|
78
|
+
res.send('handled by express')
|
|
74
79
|
})
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
app.listen(3000)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## API
|
|
85
|
+
|
|
86
|
+
### `new WooksExpress(expressApp, options?)`
|
|
87
|
+
|
|
88
|
+
Creates a new adapter instance and registers Wooks middleware on the Express app.
|
|
89
|
+
|
|
90
|
+
| Option | Type | Default | Description |
|
|
91
|
+
| ---------------- | -------------------------- | ------- | -------------------------------------------------------------------------------- |
|
|
92
|
+
| `raise404` | `boolean` | `false` | Return 404 from Wooks for unmatched routes instead of falling through to Express |
|
|
93
|
+
| `onNotFound` | `() => unknown` | — | Custom handler for unmatched routes |
|
|
94
|
+
| `logger` | `TConsoleBase` | — | Custom logger instance |
|
|
95
|
+
| `router` | `object` | — | Router options (`ignoreTrailingSlash`, `ignoreCase`, `cacheLimit`) |
|
|
96
|
+
| `requestLimits` | `object` | — | Default request body size limits |
|
|
97
|
+
| `defaultHeaders` | `Record<string, string>` | — | Headers added to every response |
|
|
98
|
+
| `responseClass` | `typeof WooksHttpResponse` | — | Custom response class |
|
|
99
|
+
|
|
100
|
+
### Route Methods
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
wooks.get(path, handler)
|
|
104
|
+
wooks.post(path, handler)
|
|
105
|
+
wooks.put(path, handler)
|
|
106
|
+
wooks.patch(path, handler)
|
|
107
|
+
wooks.delete(path, handler)
|
|
108
|
+
wooks.head(path, handler)
|
|
109
|
+
wooks.options(path, handler)
|
|
110
|
+
wooks.all(path, handler)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Handlers take **no arguments** — use composables to access request data:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
wooks.get('/users/:id', () => {
|
|
117
|
+
const { get } = useRouteParams()
|
|
118
|
+
const { method, url, rawBody, getIp } = useRequest()
|
|
119
|
+
const headers = useHeaders()
|
|
120
|
+
const response = useResponse()
|
|
121
|
+
|
|
122
|
+
response.setHeader('x-custom', 'value')
|
|
123
|
+
return { id: get('id') }
|
|
78
124
|
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `wooks.listen(port)`
|
|
128
|
+
|
|
129
|
+
Starts the Express server and returns a promise that resolves when listening.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
await wooks.listen(3000)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `wooks.close()`
|
|
136
|
+
|
|
137
|
+
Stops the server.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
await wooks.close()
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Available Composables
|
|
144
|
+
|
|
145
|
+
These come from `@wooksjs/event-http` and work inside any Wooks handler:
|
|
146
|
+
|
|
147
|
+
| Composable | Purpose |
|
|
148
|
+
| -------------------- | ------------------------------------------- |
|
|
149
|
+
| `useRequest()` | Request method, URL, headers, body, IP |
|
|
150
|
+
| `useRouteParams()` | Route parameters (`:id`, etc.) |
|
|
151
|
+
| `useHeaders()` | Request headers |
|
|
152
|
+
| `useResponse()` | Set status, headers, cookies, cache control |
|
|
153
|
+
| `useCookies()` | Read request cookies |
|
|
154
|
+
| `useUrlParams()` | URL query parameters |
|
|
155
|
+
| `useAuthorization()` | Parse Authorization header |
|
|
156
|
+
| `useAccept()` | Check Accept header |
|
|
157
|
+
| `useLogger()` | Event-scoped logger |
|
|
158
|
+
|
|
159
|
+
See the [Wooks documentation](https://wooks.moost.org) for full composable reference.
|
|
160
|
+
|
|
161
|
+
## Development
|
|
79
162
|
|
|
80
|
-
|
|
163
|
+
```bash
|
|
164
|
+
npm install # install dependencies
|
|
165
|
+
npm test # run tests (vitest)
|
|
166
|
+
npm run build # build for distribution
|
|
167
|
+
npm run lint # lint with oxlint
|
|
168
|
+
npm run fmt # format with oxfmt
|
|
81
169
|
```
|
|
82
170
|
|
|
171
|
+
## License
|
|
83
172
|
|
|
84
|
-
|
|
173
|
+
[MIT](./LICENSE)
|
package/dist/index.cjs
CHANGED
|
@@ -1,101 +1,69 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const eventHttp = require('@wooksjs/event-http');
|
|
4
|
-
|
|
5
|
-
const methods = [
|
|
6
|
-
"get",
|
|
7
|
-
"post",
|
|
8
|
-
"delete",
|
|
9
|
-
"put",
|
|
10
|
-
"patch",
|
|
11
|
-
"options",
|
|
12
|
-
"head",
|
|
13
|
-
"all"
|
|
14
|
-
];
|
|
15
|
-
function applyExpressAdapter(app, eventOptions) {
|
|
16
|
-
const responder = eventHttp.createWooksResponder();
|
|
17
|
-
function useWooksDecorator(fn) {
|
|
18
|
-
return async () => {
|
|
19
|
-
const { restoreCtx, clearCtx } = eventHttp.useHttpContext();
|
|
20
|
-
try {
|
|
21
|
-
const result = await fn();
|
|
22
|
-
restoreCtx();
|
|
23
|
-
await responder.respond(result);
|
|
24
|
-
} catch (e) {
|
|
25
|
-
restoreCtx();
|
|
26
|
-
await responder.respond(e);
|
|
27
|
-
}
|
|
28
|
-
clearCtx();
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
app.use(wooksContext(eventOptions));
|
|
32
|
-
for (const m of methods) {
|
|
33
|
-
const defFn = app[m].bind(app);
|
|
34
|
-
const newFn = ((...args) => {
|
|
35
|
-
return defFn(...args.map((a) => typeof a === "function" ? useWooksDecorator(a) : a));
|
|
36
|
-
}).bind(app);
|
|
37
|
-
Object.defineProperty(app, m, { value: newFn });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function wooksContext(eventOptions) {
|
|
41
|
-
return (req, res, next) => {
|
|
42
|
-
const { store } = eventHttp.createHttpContext({ req, res }, eventOptions || {});
|
|
43
|
-
store("routeParams").value = new Proxy({}, {
|
|
44
|
-
get(target, prop, receiver) {
|
|
45
|
-
return req.params && req.params[prop];
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
next();
|
|
49
|
-
};
|
|
50
|
-
}
|
|
4
|
+
const eventCore = require('@wooksjs/event-core');
|
|
51
5
|
|
|
52
6
|
class WooksExpress extends eventHttp.WooksHttp {
|
|
7
|
+
expressApp;
|
|
8
|
+
expressOpts;
|
|
53
9
|
constructor(expressApp, opts) {
|
|
54
10
|
super(opts);
|
|
55
11
|
this.expressApp = expressApp;
|
|
56
|
-
this.
|
|
57
|
-
expressApp.use(this.
|
|
12
|
+
this.expressOpts = opts ?? {};
|
|
13
|
+
this.expressApp.use(this.getExpressMiddleware());
|
|
58
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Start the Express server and return a promise that resolves when listening.
|
|
17
|
+
*/
|
|
59
18
|
listen(...args) {
|
|
60
|
-
const server = this.server = this.expressApp.listen(
|
|
19
|
+
const server = this.server = this.expressApp.listen(
|
|
20
|
+
...args
|
|
21
|
+
);
|
|
61
22
|
return new Promise((resolve, reject) => {
|
|
62
|
-
server.once("listening", resolve);
|
|
23
|
+
server.once("listening", () => resolve());
|
|
63
24
|
server.once("error", reject);
|
|
64
25
|
});
|
|
65
26
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Returns Express middleware that routes requests through Wooks.
|
|
29
|
+
* Matched routes are handled by Wooks; unmatched requests call `next()`.
|
|
30
|
+
*/
|
|
31
|
+
getExpressMiddleware() {
|
|
32
|
+
const ctxOptions = this.eventContextOptions;
|
|
33
|
+
const requestLimits = this.expressOpts.requestLimits;
|
|
34
|
+
const defaultHeaders = this.expressOpts.defaultHeaders;
|
|
35
|
+
const notFoundHandler = this.expressOpts.onNotFound;
|
|
36
|
+
const raise404 = this.expressOpts.raise404;
|
|
37
|
+
return (req, res, next) => {
|
|
38
|
+
const response = new this.ResponseClass(res, req, ctxOptions.logger, defaultHeaders);
|
|
39
|
+
const method = req.method || "";
|
|
40
|
+
const url = req.url || "";
|
|
41
|
+
eventHttp.createHttpContext(ctxOptions, { req, response, requestLimits }, () => {
|
|
42
|
+
const ctx = eventCore.current();
|
|
43
|
+
const handlers = this.wooks.lookupHandlers(method, url, ctx);
|
|
44
|
+
if (handlers || notFoundHandler) {
|
|
45
|
+
const result = this.processHandlers(
|
|
46
|
+
handlers || [notFoundHandler],
|
|
47
|
+
ctx,
|
|
48
|
+
response
|
|
49
|
+
);
|
|
50
|
+
if (result !== null && result !== void 0 && typeof result.then === "function") {
|
|
51
|
+
result.catch((error) => {
|
|
52
|
+
this.logger.error("Internal error, please report", error);
|
|
53
|
+
this.respond(error, response, ctx);
|
|
54
|
+
});
|
|
80
55
|
}
|
|
81
|
-
|
|
82
|
-
this.respond(e);
|
|
83
|
-
clearCtx();
|
|
56
|
+
return result;
|
|
84
57
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (this.opts?.raise404) {
|
|
90
|
-
this.respond(new eventHttp.HttpError(404));
|
|
91
|
-
clearCtx();
|
|
92
|
-
} else if (next) {
|
|
93
|
-
next();
|
|
58
|
+
if (raise404) {
|
|
59
|
+
const error = new eventHttp.HttpError(404);
|
|
60
|
+
this.respond(error, response, ctx);
|
|
61
|
+
return error;
|
|
94
62
|
}
|
|
95
|
-
|
|
63
|
+
next();
|
|
64
|
+
});
|
|
96
65
|
};
|
|
97
66
|
}
|
|
98
67
|
}
|
|
99
68
|
|
|
100
69
|
exports.WooksExpress = WooksExpress;
|
|
101
|
-
exports.applyExpressAdapter = applyExpressAdapter;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TWooksHttpOptions, WooksHttp } from '@wooksjs/event-http';
|
|
2
|
+
import { Application, NextFunction } from 'express';
|
|
3
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
4
|
+
|
|
5
|
+
interface TWooksExpressOptions extends TWooksHttpOptions {
|
|
6
|
+
/**
|
|
7
|
+
* When true, respond with 404 for unmatched Wooks routes
|
|
8
|
+
* instead of passing to the next Express middleware.
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
raise404?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Express adapter for Wooks.
|
|
15
|
+
*
|
|
16
|
+
* Uses Wooks routing and composables on top of an Express application.
|
|
17
|
+
* Registers itself as Express middleware — requests matching Wooks routes
|
|
18
|
+
* are handled by Wooks; unmatched requests fall through to Express.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import express from 'express'
|
|
23
|
+
* import { WooksExpress } from '@wooksjs/express-adapter'
|
|
24
|
+
* import { useRouteParams } from '@wooksjs/event-http'
|
|
25
|
+
*
|
|
26
|
+
* const app = express()
|
|
27
|
+
* const wooks = new WooksExpress(app)
|
|
28
|
+
*
|
|
29
|
+
* wooks.get('/hello/:name', () => {
|
|
30
|
+
* const { get } = useRouteParams()
|
|
31
|
+
* return { hello: get('name') }
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* app.listen(3000)
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare class WooksExpress extends WooksHttp {
|
|
38
|
+
protected expressApp: Application;
|
|
39
|
+
protected expressOpts: TWooksExpressOptions;
|
|
40
|
+
constructor(expressApp: Application, opts?: TWooksExpressOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Start the Express server and return a promise that resolves when listening.
|
|
43
|
+
*/
|
|
44
|
+
listen(...args: unknown[]): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns Express middleware that routes requests through Wooks.
|
|
47
|
+
* Matched routes are handled by Wooks; unmatched requests call `next()`.
|
|
48
|
+
*/
|
|
49
|
+
getExpressMiddleware(): (req: IncomingMessage, res: ServerResponse, next: NextFunction) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { WooksExpress };
|
|
53
|
+
export type { TWooksExpressOptions };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TWooksHttpOptions, WooksHttp } from '@wooksjs/event-http';
|
|
2
|
+
import { Application, NextFunction } from 'express';
|
|
3
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
4
|
+
|
|
5
|
+
interface TWooksExpressOptions extends TWooksHttpOptions {
|
|
6
|
+
/**
|
|
7
|
+
* When true, respond with 404 for unmatched Wooks routes
|
|
8
|
+
* instead of passing to the next Express middleware.
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
raise404?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Express adapter for Wooks.
|
|
15
|
+
*
|
|
16
|
+
* Uses Wooks routing and composables on top of an Express application.
|
|
17
|
+
* Registers itself as Express middleware — requests matching Wooks routes
|
|
18
|
+
* are handled by Wooks; unmatched requests fall through to Express.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import express from 'express'
|
|
23
|
+
* import { WooksExpress } from '@wooksjs/express-adapter'
|
|
24
|
+
* import { useRouteParams } from '@wooksjs/event-http'
|
|
25
|
+
*
|
|
26
|
+
* const app = express()
|
|
27
|
+
* const wooks = new WooksExpress(app)
|
|
28
|
+
*
|
|
29
|
+
* wooks.get('/hello/:name', () => {
|
|
30
|
+
* const { get } = useRouteParams()
|
|
31
|
+
* return { hello: get('name') }
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* app.listen(3000)
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare class WooksExpress extends WooksHttp {
|
|
38
|
+
protected expressApp: Application;
|
|
39
|
+
protected expressOpts: TWooksExpressOptions;
|
|
40
|
+
constructor(expressApp: Application, opts?: TWooksExpressOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Start the Express server and return a promise that resolves when listening.
|
|
43
|
+
*/
|
|
44
|
+
listen(...args: unknown[]): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns Express middleware that routes requests through Wooks.
|
|
47
|
+
* Matched routes are handled by Wooks; unmatched requests call `next()`.
|
|
48
|
+
*/
|
|
49
|
+
getExpressMiddleware(): (req: IncomingMessage, res: ServerResponse, next: NextFunction) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { WooksExpress };
|
|
53
|
+
export type { TWooksExpressOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,53 @@
|
|
|
1
1
|
import { TWooksHttpOptions, WooksHttp } from '@wooksjs/event-http';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
declare function applyExpressAdapter(app: Express, eventOptions?: TWooksHttpOptions['eventOptions']): void;
|
|
2
|
+
import { Application, NextFunction } from 'express';
|
|
3
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
6
4
|
|
|
5
|
+
interface TWooksExpressOptions extends TWooksHttpOptions {
|
|
6
|
+
/**
|
|
7
|
+
* When true, respond with 404 for unmatched Wooks routes
|
|
8
|
+
* instead of passing to the next Express middleware.
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
raise404?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Express adapter for Wooks.
|
|
15
|
+
*
|
|
16
|
+
* Uses Wooks routing and composables on top of an Express application.
|
|
17
|
+
* Registers itself as Express middleware — requests matching Wooks routes
|
|
18
|
+
* are handled by Wooks; unmatched requests fall through to Express.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import express from 'express'
|
|
23
|
+
* import { WooksExpress } from '@wooksjs/express-adapter'
|
|
24
|
+
* import { useRouteParams } from '@wooksjs/event-http'
|
|
25
|
+
*
|
|
26
|
+
* const app = express()
|
|
27
|
+
* const wooks = new WooksExpress(app)
|
|
28
|
+
*
|
|
29
|
+
* wooks.get('/hello/:name', () => {
|
|
30
|
+
* const { get } = useRouteParams()
|
|
31
|
+
* return { hello: get('name') }
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* app.listen(3000)
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
7
37
|
declare class WooksExpress extends WooksHttp {
|
|
8
|
-
protected expressApp:
|
|
9
|
-
protected
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
38
|
+
protected expressApp: Application;
|
|
39
|
+
protected expressOpts: TWooksExpressOptions;
|
|
40
|
+
constructor(expressApp: Application, opts?: TWooksExpressOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Start the Express server and return a promise that resolves when listening.
|
|
43
|
+
*/
|
|
44
|
+
listen(...args: unknown[]): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns Express middleware that routes requests through Wooks.
|
|
47
|
+
* Matched routes are handled by Wooks; unmatched requests call `next()`.
|
|
48
|
+
*/
|
|
49
|
+
getExpressMiddleware(): (req: IncomingMessage, res: ServerResponse, next: NextFunction) => void;
|
|
17
50
|
}
|
|
18
51
|
|
|
19
|
-
export { WooksExpress
|
|
52
|
+
export { WooksExpress };
|
|
53
|
+
export type { TWooksExpressOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,98 +1,67 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const methods = [
|
|
4
|
-
"get",
|
|
5
|
-
"post",
|
|
6
|
-
"delete",
|
|
7
|
-
"put",
|
|
8
|
-
"patch",
|
|
9
|
-
"options",
|
|
10
|
-
"head",
|
|
11
|
-
"all"
|
|
12
|
-
];
|
|
13
|
-
function applyExpressAdapter(app, eventOptions) {
|
|
14
|
-
const responder = createWooksResponder();
|
|
15
|
-
function useWooksDecorator(fn) {
|
|
16
|
-
return async () => {
|
|
17
|
-
const { restoreCtx, clearCtx } = useHttpContext();
|
|
18
|
-
try {
|
|
19
|
-
const result = await fn();
|
|
20
|
-
restoreCtx();
|
|
21
|
-
await responder.respond(result);
|
|
22
|
-
} catch (e) {
|
|
23
|
-
restoreCtx();
|
|
24
|
-
await responder.respond(e);
|
|
25
|
-
}
|
|
26
|
-
clearCtx();
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
app.use(wooksContext(eventOptions));
|
|
30
|
-
for (const m of methods) {
|
|
31
|
-
const defFn = app[m].bind(app);
|
|
32
|
-
const newFn = ((...args) => {
|
|
33
|
-
return defFn(...args.map((a) => typeof a === "function" ? useWooksDecorator(a) : a));
|
|
34
|
-
}).bind(app);
|
|
35
|
-
Object.defineProperty(app, m, { value: newFn });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function wooksContext(eventOptions) {
|
|
39
|
-
return (req, res, next) => {
|
|
40
|
-
const { store } = createHttpContext({ req, res }, eventOptions || {});
|
|
41
|
-
store("routeParams").value = new Proxy({}, {
|
|
42
|
-
get(target, prop, receiver) {
|
|
43
|
-
return req.params && req.params[prop];
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
next();
|
|
47
|
-
};
|
|
48
|
-
}
|
|
1
|
+
import { WooksHttp, createHttpContext, HttpError } from '@wooksjs/event-http';
|
|
2
|
+
import { current } from '@wooksjs/event-core';
|
|
49
3
|
|
|
50
4
|
class WooksExpress extends WooksHttp {
|
|
5
|
+
expressApp;
|
|
6
|
+
expressOpts;
|
|
51
7
|
constructor(expressApp, opts) {
|
|
52
8
|
super(opts);
|
|
53
9
|
this.expressApp = expressApp;
|
|
54
|
-
this.
|
|
55
|
-
expressApp.use(this.
|
|
10
|
+
this.expressOpts = opts ?? {};
|
|
11
|
+
this.expressApp.use(this.getExpressMiddleware());
|
|
56
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Start the Express server and return a promise that resolves when listening.
|
|
15
|
+
*/
|
|
57
16
|
listen(...args) {
|
|
58
|
-
const server = this.server = this.expressApp.listen(
|
|
17
|
+
const server = this.server = this.expressApp.listen(
|
|
18
|
+
...args
|
|
19
|
+
);
|
|
59
20
|
return new Promise((resolve, reject) => {
|
|
60
|
-
server.once("listening", resolve);
|
|
21
|
+
server.once("listening", () => resolve());
|
|
61
22
|
server.once("error", reject);
|
|
62
23
|
});
|
|
63
24
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Returns Express middleware that routes requests through Wooks.
|
|
27
|
+
* Matched routes are handled by Wooks; unmatched requests call `next()`.
|
|
28
|
+
*/
|
|
29
|
+
getExpressMiddleware() {
|
|
30
|
+
const ctxOptions = this.eventContextOptions;
|
|
31
|
+
const requestLimits = this.expressOpts.requestLimits;
|
|
32
|
+
const defaultHeaders = this.expressOpts.defaultHeaders;
|
|
33
|
+
const notFoundHandler = this.expressOpts.onNotFound;
|
|
34
|
+
const raise404 = this.expressOpts.raise404;
|
|
35
|
+
return (req, res, next) => {
|
|
36
|
+
const response = new this.ResponseClass(res, req, ctxOptions.logger, defaultHeaders);
|
|
37
|
+
const method = req.method || "";
|
|
38
|
+
const url = req.url || "";
|
|
39
|
+
createHttpContext(ctxOptions, { req, response, requestLimits }, () => {
|
|
40
|
+
const ctx = current();
|
|
41
|
+
const handlers = this.wooks.lookupHandlers(method, url, ctx);
|
|
42
|
+
if (handlers || notFoundHandler) {
|
|
43
|
+
const result = this.processHandlers(
|
|
44
|
+
handlers || [notFoundHandler],
|
|
45
|
+
ctx,
|
|
46
|
+
response
|
|
47
|
+
);
|
|
48
|
+
if (result !== null && result !== void 0 && typeof result.then === "function") {
|
|
49
|
+
result.catch((error) => {
|
|
50
|
+
this.logger.error("Internal error, please report", error);
|
|
51
|
+
this.respond(error, response, ctx);
|
|
52
|
+
});
|
|
78
53
|
}
|
|
79
|
-
|
|
80
|
-
this.respond(e);
|
|
81
|
-
clearCtx();
|
|
54
|
+
return result;
|
|
82
55
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (this.opts?.raise404) {
|
|
88
|
-
this.respond(new HttpError(404));
|
|
89
|
-
clearCtx();
|
|
90
|
-
} else if (next) {
|
|
91
|
-
next();
|
|
56
|
+
if (raise404) {
|
|
57
|
+
const error = new HttpError(404);
|
|
58
|
+
this.respond(error, response, ctx);
|
|
59
|
+
return error;
|
|
92
60
|
}
|
|
93
|
-
|
|
61
|
+
next();
|
|
62
|
+
});
|
|
94
63
|
};
|
|
95
64
|
}
|
|
96
65
|
}
|
|
97
66
|
|
|
98
|
-
export { WooksExpress
|
|
67
|
+
export { WooksExpress };
|
package/package.json
CHANGED
|
@@ -1,98 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wooksjs/express-adapter",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Express Adapter for Wooks
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Express Adapter for Wooks — use Wooks composables with Express",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"adapter",
|
|
7
|
+
"api",
|
|
8
|
+
"app",
|
|
9
|
+
"composables",
|
|
10
|
+
"express",
|
|
11
|
+
"framework",
|
|
12
|
+
"http",
|
|
13
|
+
"rest",
|
|
14
|
+
"restful",
|
|
15
|
+
"web",
|
|
16
|
+
"wooks"
|
|
10
17
|
],
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"test": "jest --runInBand",
|
|
15
|
-
"test:cov": "jest --runInBand --coverage",
|
|
16
|
-
"lint": "eslint --ext .ts src/**/**.ts",
|
|
17
|
-
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
|
18
|
-
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
|
|
18
|
+
"homepage": "https://github.com/wooksjs/express-adapter#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/wooksjs/express-adapter/issues"
|
|
19
21
|
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Artem Maltsev",
|
|
20
24
|
"repository": {
|
|
21
25
|
"type": "git",
|
|
22
26
|
"url": "git+https://github.com/wooksjs/express-adapter.git"
|
|
23
27
|
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"main": "dist/index.cjs",
|
|
32
|
+
"module": "dist/index.mjs",
|
|
33
|
+
"types": "dist/index.d.ts",
|
|
24
34
|
"exports": {
|
|
25
35
|
".": {
|
|
26
|
-
"
|
|
27
|
-
"default": "./dist/index.mjs",
|
|
28
|
-
"require": "./dist/index.cjs",
|
|
29
|
-
"import": "./dist/index.mjs",
|
|
30
|
-
"types": "./dist/index.d.ts"
|
|
31
|
-
},
|
|
32
|
-
"default": "./dist/index.mjs",
|
|
33
|
-
"require": "./dist/index.cjs",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
34
37
|
"import": "./dist/index.mjs",
|
|
35
|
-
"
|
|
38
|
+
"require": "./dist/index.cjs"
|
|
36
39
|
}
|
|
37
40
|
},
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"prostojs"
|
|
51
|
-
],
|
|
52
|
-
"buildOptions": {
|
|
53
|
-
"formats": [
|
|
54
|
-
"esm-bundler",
|
|
55
|
-
"cjs"
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
"gitHooks": {
|
|
59
|
-
"commit-msg": "node scripts/verifyCommit.js"
|
|
60
|
-
},
|
|
61
|
-
"author": "Artem Maltsev",
|
|
62
|
-
"license": "MIT",
|
|
63
|
-
"bugs": {
|
|
64
|
-
"url": "https://github.com/wooksjs/express-adapter/issues"
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/express": "^5.0.0",
|
|
43
|
+
"@types/node": "^22.0.0",
|
|
44
|
+
"@wooksjs/event-core": "^0.7.4",
|
|
45
|
+
"@wooksjs/event-http": "^0.7.4",
|
|
46
|
+
"express": "^5.0.0",
|
|
47
|
+
"oxfmt": "^0.35.0",
|
|
48
|
+
"oxlint": "^1.50.0",
|
|
49
|
+
"typescript": "^5.7.0",
|
|
50
|
+
"unbuild": "^3.5.0",
|
|
51
|
+
"vitest": "^3.0.0",
|
|
52
|
+
"wooks": "^0.7.4"
|
|
65
53
|
},
|
|
66
|
-
"homepage": "https://github.com/wooksjs/express-adapter#readme",
|
|
67
54
|
"peerDependencies": {
|
|
68
|
-
"@wooksjs/event-
|
|
69
|
-
"
|
|
70
|
-
"express": "
|
|
55
|
+
"@wooksjs/event-core": ">=0.7.0",
|
|
56
|
+
"@wooksjs/event-http": ">=0.7.0",
|
|
57
|
+
"express": ">=4.0.0",
|
|
58
|
+
"wooks": ">=0.7.0"
|
|
71
59
|
},
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"conventional-changelog": "^3.1.24",
|
|
82
|
-
"conventional-changelog-cli": "^2.1.1",
|
|
83
|
-
"enquirer": "^2.3.6",
|
|
84
|
-
"eslint": "^7.32.0",
|
|
85
|
-
"eslint-config-prettier": "^8.3.0",
|
|
86
|
-
"eslint-plugin-import": "^2.24.2",
|
|
87
|
-
"execa": "^5.1.1",
|
|
88
|
-
"express": "^4.18.2",
|
|
89
|
-
"jest": "^29.2.2",
|
|
90
|
-
"minimist": "^1.2.6",
|
|
91
|
-
"semver": "^7.3.5",
|
|
92
|
-
"ts-jest": "^29.0.3",
|
|
93
|
-
"tslib": "^2.4.1",
|
|
94
|
-
"typescript": "^4.8.4",
|
|
95
|
-
"unbuild": "^0.9.4",
|
|
96
|
-
"yorkie": "^2.0.0"
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "unbuild",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"test:watch": "vitest",
|
|
64
|
+
"test:cov": "vitest run --coverage",
|
|
65
|
+
"lint": "oxlint",
|
|
66
|
+
"lint:fix": "oxlint --fix",
|
|
67
|
+
"fmt": "oxfmt",
|
|
68
|
+
"fmt:check": "oxfmt --check"
|
|
97
69
|
}
|
|
98
|
-
}
|
|
70
|
+
}
|