barehttp 1.0.0 → 2.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/README.md +185 -28
- package/lib/context/execution.d.ts +7 -0
- package/lib/context/execution.js +14 -0
- package/lib/context/index.d.ts +10 -0
- package/lib/context/index.js +46 -0
- package/lib/env.d.ts +5 -0
- package/lib/env.js +5 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +3 -0
- package/lib/logger/index.d.ts +16 -0
- package/lib/logger/index.js +26 -0
- package/lib/logger/serializers.d.ts +28 -0
- package/lib/logger/serializers.js +78 -0
- package/lib/middlewares/cookies/cookie-manager.d.ts +25 -0
- package/lib/middlewares/cookies/cookie-manager.js +68 -0
- package/lib/middlewares/cookies/signer.d.ts +8 -0
- package/lib/middlewares/cookies/signer.js +25 -0
- package/lib/middlewares/cors/cors.d.ts +38 -0
- package/lib/middlewares/cors/cors.js +164 -0
- package/lib/request.d.ts +84 -0
- package/lib/request.js +260 -0
- package/lib/schemas/custom-schema.d.ts +32 -0
- package/lib/schemas/custom-schema.js +62 -0
- package/lib/schemas/dirty-tsm.d.ts +1 -0
- package/lib/schemas/dirty-tsm.js +199 -0
- package/lib/schemas/generator.d.ts +7 -0
- package/lib/schemas/generator.js +179 -0
- package/lib/schemas/helpers.d.ts +27 -0
- package/lib/schemas/helpers.js +40 -0
- package/lib/schemas/json-schema.d.ts +2 -0
- package/lib/schemas/json-schema.js +48 -0
- package/lib/schemas/openami-schema.d.ts +2 -0
- package/lib/schemas/openami-schema.js +59 -0
- package/lib/schemas/project.d.ts +1 -0
- package/lib/schemas/project.js +1 -0
- package/lib/server.d.ts +154 -0
- package/lib/server.js +396 -0
- package/lib/utils/content-type.d.ts +54 -0
- package/lib/utils/content-type.js +54 -0
- package/lib/utils/http-methods.d.ts +11 -0
- package/lib/utils/http-methods.js +9 -0
- package/lib/utils/index.d.ts +4 -0
- package/lib/utils/index.js +4 -0
- package/lib/utils/safe-json.d.ts +2 -0
- package/lib/utils/safe-json.js +18 -0
- package/lib/utils/status-codes.d.ts +339 -0
- package/lib/utils/status-codes.js +339 -0
- package/lib/utils/status-phrases.d.ts +338 -0
- package/lib/utils/status-phrases.js +339 -0
- package/lib/websocket.d.ts +36 -0
- package/lib/websocket.js +176 -0
- package/package.json +64 -32
- package/.eslintrc.js +0 -47
- package/.github/workflows/release.yml +0 -27
- package/.jest-setup.js +0 -1
- package/jest.config.js +0 -8
- package/prettier.config.js +0 -6
- package/src/context/context.test.ts +0 -30
- package/src/context/execution.ts +0 -17
- package/src/context/index.ts +0 -61
- package/src/env.ts +0 -5
- package/src/examples/bare-http.ts +0 -36
- package/src/examples/express.ts +0 -11
- package/src/examples/fastify.ts +0 -18
- package/src/index.ts +0 -4
- package/src/logger/index.ts +0 -67
- package/src/logger/serializers.test.ts +0 -186
- package/src/logger/serializers.ts +0 -109
- package/src/middlewares/cookies/cookie-manager.ts +0 -86
- package/src/middlewares/cookies/signer.ts +0 -30
- package/src/report.ts +0 -25
- package/src/request.test.ts +0 -143
- package/src/request.ts +0 -277
- package/src/server.integration.test.ts +0 -296
- package/src/server.middlewares.test.ts +0 -93
- package/src/server.routes.test.ts +0 -71
- package/src/server.ts +0 -450
- package/src/utils/content-type.ts +0 -59
- package/src/utils/index.ts +0 -2
- package/src/utils/safe-json.ts +0 -17
- package/src/utils/status-codes.ts +0 -339
- package/src/utils/status-phrases.ts +0 -339
- package/tsconfig.json +0 -24
package/README.md
CHANGED
|
@@ -49,22 +49,29 @@ yarn add barehttp
|
|
|
49
49
|
```typescript
|
|
50
50
|
import { BareHttp, logMe } from 'barehttp';
|
|
51
51
|
|
|
52
|
-
const app = new BareHttp(
|
|
52
|
+
const app = new BareHttp();
|
|
53
53
|
|
|
54
54
|
app.route.get({
|
|
55
55
|
route: '/route',
|
|
56
|
-
handler: function
|
|
57
|
-
flow.json({
|
|
56
|
+
handler: function routeGet(flow) {
|
|
57
|
+
flow.json({ everything: 'OK' });
|
|
58
58
|
})
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
// you can chain the routes
|
|
63
|
+
app.route
|
|
64
|
+
.post({
|
|
65
|
+
route: '/route',
|
|
66
|
+
handler: async function routePost(flow) {
|
|
67
|
+
return 'RESPONSE POST';
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
.route.patch({
|
|
71
|
+
route: '/route',
|
|
72
|
+
handler: async function routePatch(flow) {
|
|
73
|
+
return 'RESPONSE PATCH';
|
|
74
|
+
})
|
|
68
75
|
|
|
69
76
|
// Define a middleware
|
|
70
77
|
app.use((flow) => {
|
|
@@ -81,16 +88,19 @@ app.start((address) => {
|
|
|
81
88
|
```typescript
|
|
82
89
|
import { BareHttp, logMe } from 'barehttp';
|
|
83
90
|
|
|
84
|
-
const app = new BareHttp({ logging:
|
|
91
|
+
const app = new BareHttp({ logging: true });
|
|
85
92
|
|
|
86
|
-
app.route.get(
|
|
87
|
-
|
|
93
|
+
app.route.get({
|
|
94
|
+
route:'/route',
|
|
95
|
+
handler: async function routeV1() {
|
|
96
|
+
return { promised: 'data' };
|
|
97
|
+
})
|
|
88
98
|
});
|
|
89
99
|
|
|
90
100
|
// Define a middleware
|
|
91
101
|
app.use(async (flow) => {
|
|
92
102
|
logMe.info('my middleware');
|
|
93
|
-
await
|
|
103
|
+
await someAuthenticationFlow();
|
|
94
104
|
});
|
|
95
105
|
|
|
96
106
|
app.start((address) => {
|
|
@@ -108,7 +118,7 @@ An instance of the application
|
|
|
108
118
|
|
|
109
119
|
Options submitted to the server at initialization
|
|
110
120
|
|
|
111
|
-
#### `middlewares?` (Array<(flow: BareRequest) => Promise<void> | void>)
|
|
121
|
+
#### `middlewares?` (Array<(flow: BareRequest) => Promise<void> | void>)
|
|
112
122
|
|
|
113
123
|
If provided, this will apply an array of middlewares to each incoming request.
|
|
114
124
|
The order of the array is the order of middlewares to apply
|
|
@@ -137,11 +147,21 @@ Default `false`
|
|
|
137
147
|
|
|
138
148
|
Enable request/response logging, format varies from `production` or `development` environments, though to change use e.g. `NODE_ENV=production`
|
|
139
149
|
|
|
150
|
+
#### `ws?` (Boolean)
|
|
151
|
+
|
|
152
|
+
Default `false`
|
|
153
|
+
|
|
154
|
+
Enable WebSocket support through `ws` package.
|
|
155
|
+
|
|
156
|
+
#### `wsOptions?` (Object)
|
|
157
|
+
|
|
158
|
+
Refer to [ws](https://github.com/websockets/ws) documentation.
|
|
159
|
+
|
|
140
160
|
#### `errorHandlerMiddleware?` ((err: any, flow: BareRequest) => void)
|
|
141
161
|
|
|
142
162
|
If provided, will set a custom error handler to catch the bubbled errors from the handlers
|
|
143
163
|
|
|
144
|
-
#### `
|
|
164
|
+
#### `requestTimeFormat?` (String: 's' | 'ms')
|
|
145
165
|
|
|
146
166
|
Default `'s'` - seconds
|
|
147
167
|
|
|
@@ -160,26 +180,30 @@ To set options for the cookies decoding/encoding
|
|
|
160
180
|
If enabled, and also with `logging: true`, will try to log the resolved reverse DNS of the first hop for remote ip of the client (first proxy).
|
|
161
181
|
Logs follow an [Apache Common Log Format](https://httpd.apache.org/docs/2.4/logs.html)
|
|
162
182
|
|
|
163
|
-
#### `
|
|
183
|
+
#### `statisticsReport?` (Boolean)
|
|
164
184
|
|
|
165
185
|
Default `false`
|
|
166
186
|
|
|
167
187
|
Exposes a basic report with the routes usage under `GET /_report` route
|
|
168
188
|
|
|
169
|
-
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## `BareServer.use` ((flow: BareRequest) => Promise<void> | void)
|
|
170
192
|
|
|
171
193
|
Attach a middleware `after` the middlewares optional array.
|
|
172
194
|
The order of the middlewares is followed by code declarations order.
|
|
173
195
|
|
|
174
|
-
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## `BareServer.route.get | post | patch | put | delete | options | head | declare` (Function)
|
|
175
199
|
|
|
176
200
|
To set a route for `get | post | patch | put | delete | options | head` with following parameters:
|
|
177
201
|
|
|
178
202
|
- `route` (String) - should follow a format of `/your_route`, including params as `/your_route/:param`
|
|
179
203
|
- `options` (Object) - `RouteOptions`
|
|
180
204
|
- `handler` (Function) - A function with the signature `(flow: BareRequest) => Promise<any> | any`
|
|
181
|
-
|
|
182
|
-
Example
|
|
205
|
+
- `method` (Array) - if the method is `declare` you have to indicate an array of methods to declare the route e.g. `['get', 'post']`
|
|
206
|
+
Example
|
|
183
207
|
|
|
184
208
|
```ts
|
|
185
209
|
app.route.get({
|
|
@@ -189,6 +213,36 @@ app.route.get({
|
|
|
189
213
|
return 'My route response';
|
|
190
214
|
},
|
|
191
215
|
});
|
|
216
|
+
|
|
217
|
+
app.route.declare({
|
|
218
|
+
route: '/declared_route',
|
|
219
|
+
handler: () => {
|
|
220
|
+
return 'My declared route response';
|
|
221
|
+
},
|
|
222
|
+
methods: ['post', 'patch'],
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## `BareServer.runtimeRoute.get | post | patch | put | delete | options | head | declare` (Function)
|
|
227
|
+
|
|
228
|
+
Same as the above routes API, but you can only declare them when the server is `listening`
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
app.runtimeRoute
|
|
232
|
+
.get({
|
|
233
|
+
route: '/route',
|
|
234
|
+
options: { timeout: 2000 },
|
|
235
|
+
handler: async (flow) => {
|
|
236
|
+
return 'My route response';
|
|
237
|
+
},
|
|
238
|
+
})
|
|
239
|
+
.declare({
|
|
240
|
+
route: '/declared_runtime_route',
|
|
241
|
+
handler: () => {
|
|
242
|
+
return 'My declared runtime route response';
|
|
243
|
+
},
|
|
244
|
+
methods: ['post', 'patch'],
|
|
245
|
+
});
|
|
192
246
|
```
|
|
193
247
|
|
|
194
248
|
#### `RouteOptions` (Object)
|
|
@@ -201,16 +255,76 @@ Disables all cache headers for the response. This overrides any other cache sett
|
|
|
201
255
|
|
|
202
256
|
##### `cache?` (CacheOptions)
|
|
203
257
|
|
|
204
|
-
If set, provides a granular cache handling per route.
|
|
258
|
+
If set, provides a granular cache headers handling per route.
|
|
205
259
|
|
|
206
260
|
##### `timeout?` (Number)
|
|
207
261
|
|
|
208
262
|
Request timeout value in `ms`. This will cancel the request _only_ for this route if time expired
|
|
209
263
|
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## `BareServer.ws?` (WebSocketServer)
|
|
267
|
+
|
|
268
|
+
Based on `ws` package, for internals please refer to [external WebSocketServer](https://github.com/websockets/ws#external-https-server) for documentation.
|
|
269
|
+
|
|
270
|
+
This particular implementation works out easily for WebSockets interaction for pushing data to server from the clients and waiting for some answer in async.
|
|
271
|
+
|
|
272
|
+
Also exposes an way to keep pushing messages to the Client from the Server on server handle through internal clients list. (WIP optimizing this)
|
|
273
|
+
|
|
274
|
+
### `WebSocketServer.declareReceiver` ((Data, UserClient, WSClient, MessageEvent) => Promise\<M> | M)
|
|
275
|
+
|
|
276
|
+
This is the main 'handler' function for any kind of Client request. If there's a response to that push from the client the return should contain it, otherwise if the response is `void` there will be no answer to the client side.
|
|
277
|
+
|
|
278
|
+
- `Data`: is the data received from the client for this exact `Type`
|
|
279
|
+
- `UserClient`: is an optional client defined on the stage of `Upgrade` to provide some closured client data to be able to know what Client is exactly making the request to the Server
|
|
280
|
+
- `WSClient`: raw instance of `ws.Client & { userClient: UC }`
|
|
281
|
+
- `MessageEvent`: raw instance of `ws.MessageClient`
|
|
282
|
+
|
|
283
|
+
Code Example:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
app.ws?.declareReceiver<{ ok: string }>({
|
|
287
|
+
type: 'BASE_TYPE',
|
|
288
|
+
handler: async (data, client) => {
|
|
289
|
+
// do your async or sync operations here
|
|
290
|
+
// return the response if you need to send an answer
|
|
291
|
+
return { cool: 'some answer', client };
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### `WebSocketServer.defineUpgrade` ((IncomingRequest) => Promise\<M> | M)
|
|
297
|
+
|
|
298
|
+
To de able to handle authorization or any other previous operation before opening and upgrading an incoming client's request.
|
|
299
|
+
**If this function is not initialized with the callback, all incoming connections will be accepted by default**
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
app.ws?.defineUpgrade(async (req) => {
|
|
303
|
+
// you can do some async or sync operation here
|
|
304
|
+
// the returning of this function will be
|
|
305
|
+
// defined as the `UserClient` and attached to the `ws.Client` instance
|
|
306
|
+
return { access: true, client: {...properties of the client} };
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
210
312
|
## `BareRequest` (Class)
|
|
211
313
|
|
|
212
314
|
An instance of the request passed through to middlewares and handlers
|
|
213
315
|
|
|
316
|
+
### `cm?` (CookiesManager)
|
|
317
|
+
|
|
318
|
+
Access to the CookiesManager instance attached to the request.
|
|
319
|
+
|
|
320
|
+
#### `CookiesManager` (Class)
|
|
321
|
+
|
|
322
|
+
Internal methods to work with cookies
|
|
323
|
+
|
|
324
|
+
##### `setCookie` (Function)
|
|
325
|
+
|
|
326
|
+
##### `clearCookie` (Function)
|
|
327
|
+
|
|
214
328
|
### `getHeader` (Function)
|
|
215
329
|
|
|
216
330
|
Get an exact header stored to return with this request to the client
|
|
@@ -231,21 +345,29 @@ Imperatively disables cache, does the same as `disableCache: true` in `RouteOpti
|
|
|
231
345
|
|
|
232
346
|
Imperatively sets the cache, does the same as `cache: CacheOptions` in `RouteOptions`
|
|
233
347
|
|
|
348
|
+
### `addHeader` (Function)
|
|
349
|
+
|
|
350
|
+
Adds a header outgoing header string as a (key, value) `addHeader(header, value)`. Can **not** overwrite.
|
|
351
|
+
|
|
352
|
+
### `addHeaders` (Function)
|
|
353
|
+
|
|
354
|
+
Adds outgoing headers in a "batch", merges provided headers object `{ [header: string]: value }` to already existing headers. Can **not** overwrite.
|
|
355
|
+
|
|
234
356
|
### `setHeader` (Function)
|
|
235
357
|
|
|
236
|
-
|
|
358
|
+
Does the same as `addHeader` but overrides the value.
|
|
237
359
|
|
|
238
360
|
### `setHeaders` (Function)
|
|
239
361
|
|
|
240
|
-
|
|
362
|
+
Does the same as `addHeaders` but overrides the value.
|
|
241
363
|
|
|
242
364
|
### `status` (Function)
|
|
243
365
|
|
|
244
|
-
Set a status for the
|
|
366
|
+
Set a status for the response
|
|
245
367
|
|
|
246
368
|
### `sendStatus` (Function)
|
|
247
369
|
|
|
248
|
-
Set a status for the
|
|
370
|
+
Set a status for the response and send it (end the request)
|
|
249
371
|
|
|
250
372
|
### `stream` (Function)
|
|
251
373
|
|
|
@@ -265,19 +387,54 @@ Some of the features are in progress.
|
|
|
265
387
|
|
|
266
388
|
- [x] Request wide context storage and incorporated tracing (ready for cloud)
|
|
267
389
|
- [x] UID (adopted or generated)
|
|
390
|
+
- [x] WebSocket server exposure
|
|
391
|
+
- [x] handy WebSocket interaction tools, for authorization, etc.
|
|
268
392
|
- [x] Request-Processing-Time header and value
|
|
269
393
|
- [x] Promised or conventional middlewares
|
|
270
394
|
- [x] Logging and serialized with `pino`
|
|
271
395
|
- [x] Routes usage report and endpoint
|
|
272
396
|
- [x] Cache headers handy handling, per route
|
|
273
397
|
- [x] Cookies creation/parsing
|
|
398
|
+
- [x] CORS middleware options
|
|
274
399
|
- [x] Request execution cancellation by timeout
|
|
275
|
-
- [
|
|
276
|
-
- [
|
|
400
|
+
- [x] Bulk/chaining routes declaration
|
|
401
|
+
- [x] Runtime routes hot swapping
|
|
402
|
+
- [x] runtime validation schema generation per route response types (on project compile/on launch)
|
|
403
|
+
- [ ] middlewares per route
|
|
404
|
+
- [ ] swagger OpenAPI 3.0 on `/docs` endpoint
|
|
405
|
+
- [ ] swagger OpenAPI 3.0 scheme on `/docs_raw` endpoint
|
|
406
|
+
- [ ] optional export of generated schema to a location (yaml, json)
|
|
407
|
+
- [ ] streaming/receiving of chunked multipart
|
|
408
|
+
- [ ] runtime route params or query validation upon declared types (on project compile/on launch)
|
|
409
|
+
|
|
410
|
+
## Runtime schema validation on response (EXPERIMENTAL)
|
|
411
|
+
|
|
412
|
+
This feature enables a runtime check for the returned value for a route,
|
|
413
|
+
for now it only works for `return` statements of the routes declared in handlers.
|
|
414
|
+
|
|
415
|
+
Please write your `return` statements with plain response objects within the `handler` or `controller` function.
|
|
416
|
+
|
|
417
|
+
To enable this feature you need to set up the following:
|
|
418
|
+
|
|
419
|
+
- On `BareHttp` settings set: `enableSchemaValidation: true`
|
|
420
|
+
- On `BareHttp` settings set: `declaredRoutesPaths: [...array of paths to routes]`,
|
|
421
|
+
- On `route.<method>` declaration set: `options: { builtInRuntime: { output: true } }`
|
|
277
422
|
|
|
278
423
|
## Benchmarks
|
|
279
424
|
|
|
280
|
-
|
|
425
|
+
Done on MacBook Pro with M1 Pro processor. No logs enabled. `NODE_ENV=production` is set. All settings set to default.
|
|
426
|
+
|
|
427
|
+
### BareHttp
|
|
428
|
+
|
|
429
|
+

|
|
430
|
+
|
|
431
|
+
### Express
|
|
432
|
+
|
|
433
|
+

|
|
434
|
+
|
|
435
|
+
### Fastify
|
|
436
|
+
|
|
437
|
+

|
|
281
438
|
|
|
282
439
|
## Support
|
|
283
440
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import hyperid from 'hyperid';
|
|
2
|
+
const generateId = hyperid();
|
|
3
|
+
export class Execution {
|
|
4
|
+
id;
|
|
5
|
+
type;
|
|
6
|
+
store;
|
|
7
|
+
headers;
|
|
8
|
+
constructor(type) {
|
|
9
|
+
this.id = generateId();
|
|
10
|
+
this.type = type;
|
|
11
|
+
this.store = new Map();
|
|
12
|
+
this.headers = new Map();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Execution } from './execution.js';
|
|
2
|
+
import asyncHooks from 'async_hooks';
|
|
3
|
+
type Context = {
|
|
4
|
+
current?: Execution;
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare const newContext: (type: string) => void;
|
|
8
|
+
declare const context: Context;
|
|
9
|
+
declare const enableContext: () => asyncHooks.AsyncHook;
|
|
10
|
+
export { context, newContext, enableContext };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Execution } from './execution.js';
|
|
2
|
+
import asyncHooks from 'async_hooks';
|
|
3
|
+
const running = new Map();
|
|
4
|
+
const previous = new Map();
|
|
5
|
+
const newContext = (type) => {
|
|
6
|
+
if (!context.enabled)
|
|
7
|
+
return;
|
|
8
|
+
context.current = new Execution(type);
|
|
9
|
+
running.set(asyncHooks.executionAsyncId(), context.current);
|
|
10
|
+
};
|
|
11
|
+
const context = {
|
|
12
|
+
enabled: false,
|
|
13
|
+
};
|
|
14
|
+
function init(asyncId, _type, triggerAsyncId) {
|
|
15
|
+
if (running.get(triggerAsyncId)) {
|
|
16
|
+
running.set(asyncId, running.get(triggerAsyncId));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function before(asyncId) {
|
|
20
|
+
if (!running.get(asyncId))
|
|
21
|
+
return;
|
|
22
|
+
previous.set(asyncId, context.current);
|
|
23
|
+
context.current = running.get(asyncId);
|
|
24
|
+
}
|
|
25
|
+
function after(asyncId) {
|
|
26
|
+
if (!running.get(asyncId))
|
|
27
|
+
return;
|
|
28
|
+
context.current = previous.get(asyncId);
|
|
29
|
+
}
|
|
30
|
+
function destroy(asyncId) {
|
|
31
|
+
if (running.get(asyncId)) {
|
|
32
|
+
running.delete(asyncId);
|
|
33
|
+
previous.delete(asyncId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const hook = asyncHooks.createHook({
|
|
37
|
+
init,
|
|
38
|
+
before,
|
|
39
|
+
after,
|
|
40
|
+
destroy,
|
|
41
|
+
});
|
|
42
|
+
const enableContext = () => {
|
|
43
|
+
context.enabled = true;
|
|
44
|
+
return hook.enable();
|
|
45
|
+
};
|
|
46
|
+
export { context, newContext, enableContext };
|
package/lib/env.d.ts
ADDED
package/lib/env.js
ADDED
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { serializeHttp } from './serializers.js';
|
|
2
|
+
interface LogMeFn {
|
|
3
|
+
(obj: unknown, ...args: []): void;
|
|
4
|
+
(msg: string, ...args: any[]): void;
|
|
5
|
+
}
|
|
6
|
+
type LogMe = {
|
|
7
|
+
info: LogMeFn;
|
|
8
|
+
warn: LogMeFn;
|
|
9
|
+
error: LogMeFn;
|
|
10
|
+
fatal: LogMeFn;
|
|
11
|
+
debug: LogMeFn;
|
|
12
|
+
trace: LogMeFn;
|
|
13
|
+
};
|
|
14
|
+
export declare const logHttp: (...params: Parameters<typeof serializeHttp>) => void;
|
|
15
|
+
export declare const logMe: LogMe;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pino, { destination } from 'pino';
|
|
2
|
+
import { serializeLog, serializeHttp } from './serializers.js';
|
|
3
|
+
import { envs } from '../env.js';
|
|
4
|
+
const asyncDest = envs.isProd ? [destination({ sync: false })] : [];
|
|
5
|
+
const pinoCommonOptions = {
|
|
6
|
+
timestamp: () => `,"time":"${new Date()[envs.isProd ? 'toISOString' : 'toLocaleTimeString']()}"`,
|
|
7
|
+
formatters: {
|
|
8
|
+
level: (label) => ({ level: label }),
|
|
9
|
+
},
|
|
10
|
+
messageKey: 'message',
|
|
11
|
+
transport: envs.isProd ? undefined : { target: 'pino-pretty', options: { colorize: true } },
|
|
12
|
+
};
|
|
13
|
+
const logger = pino(pinoCommonOptions, asyncDest[0]);
|
|
14
|
+
export const logHttp = (...params) => {
|
|
15
|
+
const { level, logObject } = serializeHttp(...params);
|
|
16
|
+
logger[level](logObject);
|
|
17
|
+
};
|
|
18
|
+
// TODO: remove the test condition
|
|
19
|
+
export const logMe = {
|
|
20
|
+
debug: (...args) => !envs.isTest && logger.debug(serializeLog(...args)),
|
|
21
|
+
info: (...args) => !envs.isTest && logger.info(serializeLog(...args)),
|
|
22
|
+
warn: (...args) => !envs.isTest && logger.warn(serializeLog(...args)),
|
|
23
|
+
error: (...args) => !envs.isTest && logger.error(serializeLog(...args)),
|
|
24
|
+
fatal: (...args) => !envs.isTest && logger.fatal(serializeLog(...args)),
|
|
25
|
+
trace: (...args) => !envs.isTest && logger.trace(serializeLog(...args)),
|
|
26
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
+
export declare function parseError(e: any, meta: any): any;
|
|
3
|
+
export declare function serializeLog(...args: any[]): any;
|
|
4
|
+
export declare function getStatusLevel(statusCode: number): "error" | "info" | "warn";
|
|
5
|
+
export declare function serializeHttp(headers: {
|
|
6
|
+
[k: string]: any;
|
|
7
|
+
}, startDate: Date, remoteClient: string, req: IncomingMessage, res: ServerResponse): {
|
|
8
|
+
level: string;
|
|
9
|
+
logObject: {
|
|
10
|
+
message: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
trace: string | number | undefined;
|
|
13
|
+
request: {
|
|
14
|
+
headers: import("node:http").IncomingHttpHeaders;
|
|
15
|
+
http_version: string;
|
|
16
|
+
id: any;
|
|
17
|
+
method: string | undefined;
|
|
18
|
+
url: string | undefined;
|
|
19
|
+
};
|
|
20
|
+
response: {
|
|
21
|
+
status_code: number;
|
|
22
|
+
headers: {
|
|
23
|
+
[k: string]: any;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
duration: any;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import callsites from 'callsites';
|
|
2
|
+
import { context } from '../context/index.js';
|
|
3
|
+
import util from 'util';
|
|
4
|
+
export function parseError(e, meta) {
|
|
5
|
+
const toSend = { error: { ...e }, ...meta };
|
|
6
|
+
toSend.message = e.message;
|
|
7
|
+
toSend.error.stack = e.stack;
|
|
8
|
+
toSend.error.kind = e.constructor.name;
|
|
9
|
+
return toSend;
|
|
10
|
+
}
|
|
11
|
+
const makeLoggerMetadata = (method) => ({
|
|
12
|
+
name: 'pino',
|
|
13
|
+
version: 'v1.0.0',
|
|
14
|
+
method_name: method,
|
|
15
|
+
});
|
|
16
|
+
const parseArgs = (argSlice) => argSlice.map((arg) => {
|
|
17
|
+
if (util.types.isNativeError(arg))
|
|
18
|
+
return parseError(arg, {});
|
|
19
|
+
return arg;
|
|
20
|
+
});
|
|
21
|
+
export function serializeLog(...args) {
|
|
22
|
+
const site = callsites()[2];
|
|
23
|
+
const meta = {
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
location: `${site.getFileName()}:${site.getLineNumber()}:${site.getColumnNumber()}`,
|
|
26
|
+
logger: makeLoggerMetadata(site.getFunctionName()),
|
|
27
|
+
trace: context.current?.store.get('id'),
|
|
28
|
+
};
|
|
29
|
+
if (!args.length)
|
|
30
|
+
return { message: 'EMPTY_LOG', ...meta };
|
|
31
|
+
if (args.length === 1) {
|
|
32
|
+
if (typeof args[0] === 'string')
|
|
33
|
+
return { message: args[0], ...meta };
|
|
34
|
+
if (util.types.isNativeError(args[0]))
|
|
35
|
+
return parseError(args[0], meta);
|
|
36
|
+
if (args[0].message)
|
|
37
|
+
return { message: args[0].message, ...args };
|
|
38
|
+
return { message: 'EMPTY_MESSAGE', args: args[0], ...meta };
|
|
39
|
+
}
|
|
40
|
+
if (typeof args[0] === 'string')
|
|
41
|
+
return { message: args.shift(), args: parseArgs(args), ...meta };
|
|
42
|
+
return { message: 'EMPTY_MESSAGE', args: parseArgs(args), ...meta };
|
|
43
|
+
}
|
|
44
|
+
function apacheLogFormat(startDate, remoteClient, content, req, statusCode) {
|
|
45
|
+
return `${req.headers['x-forwarded-for'] || req.socket.remoteAddress} ${remoteClient || '-'} ${startDate.toISOString()} "${req.method} ${req.url} HTTP/${req.httpVersionMajor}.${req.httpVersionMinor}" ${statusCode} ${content || '-'}`;
|
|
46
|
+
}
|
|
47
|
+
export function getStatusLevel(statusCode) {
|
|
48
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
49
|
+
return 'warn';
|
|
50
|
+
}
|
|
51
|
+
else if (statusCode >= 500) {
|
|
52
|
+
return 'error';
|
|
53
|
+
}
|
|
54
|
+
return 'info';
|
|
55
|
+
}
|
|
56
|
+
export function serializeHttp(headers, startDate, remoteClient, req, res) {
|
|
57
|
+
const executionId = context.current?.store.get('id');
|
|
58
|
+
return {
|
|
59
|
+
level: getStatusLevel(res.statusCode),
|
|
60
|
+
logObject: {
|
|
61
|
+
message: apacheLogFormat(startDate, remoteClient, headers['Content-Length'], req, res.statusCode),
|
|
62
|
+
timestamp: Date.now(),
|
|
63
|
+
trace: executionId,
|
|
64
|
+
request: {
|
|
65
|
+
headers: req.headers,
|
|
66
|
+
http_version: req.httpVersion,
|
|
67
|
+
id: req.headers['x-request-id'] || req.id || 'unknown',
|
|
68
|
+
method: req.method,
|
|
69
|
+
url: req.url,
|
|
70
|
+
},
|
|
71
|
+
response: {
|
|
72
|
+
status_code: res.statusCode,
|
|
73
|
+
headers: headers,
|
|
74
|
+
},
|
|
75
|
+
duration: headers['X-Processing-Time'] || 'unknown',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ParseOptions, type SerializeOptions } from 'cookie';
|
|
2
|
+
import { secretsOperator } from './signer.js';
|
|
3
|
+
import type { BareRequest } from '../../request.js';
|
|
4
|
+
export type CookiesManagerOptions = Omit<SerializeOptions, 'expires'> & {
|
|
5
|
+
expires?: Date | number;
|
|
6
|
+
signed?: boolean;
|
|
7
|
+
parseOptions?: ParseOptions;
|
|
8
|
+
secret?: string | string[];
|
|
9
|
+
};
|
|
10
|
+
export declare class CookiesManager {
|
|
11
|
+
private options;
|
|
12
|
+
private flow;
|
|
13
|
+
signer: null | ReturnType<typeof secretsOperator>;
|
|
14
|
+
constructor(options: CookiesManagerOptions | undefined, flow: BareRequest);
|
|
15
|
+
setCookie(name: string, value: string, options?: CookiesManagerOptions, signer?: ReturnType<typeof secretsOperator>): void;
|
|
16
|
+
clearCookie(name: string, options?: CookiesManagerOptions): void;
|
|
17
|
+
parseCookie(rawCookie?: string): {
|
|
18
|
+
[k: string]: string;
|
|
19
|
+
};
|
|
20
|
+
unsignCookie(value: any): {
|
|
21
|
+
valid: boolean;
|
|
22
|
+
renew: boolean;
|
|
23
|
+
value: string | null;
|
|
24
|
+
} | undefined;
|
|
25
|
+
}
|