barehttp 2.1.0 → 2.3.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 +18 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -1
- package/lib/logger/index.d.ts +18 -0
- package/lib/logger/index.js +134 -11
- package/lib/server.d.ts +10 -0
- package/lib/server.js +11 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Full-featured slim webserver for microservices with extremely low overhead and r
|
|
|
16
16
|
## Requirements
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
Node.js >=
|
|
19
|
+
Node.js >= 22
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Quick start
|
|
@@ -147,6 +147,23 @@ Default `false`
|
|
|
147
147
|
|
|
148
148
|
Enable request/response logging, format varies from `production` or `development` environments, though to change use e.g. `NODE_ENV=production`
|
|
149
149
|
|
|
150
|
+
#### `logger?` (Object)
|
|
151
|
+
|
|
152
|
+
Configure log outputs for app logs and http request logs.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const app = new BareHttp({
|
|
156
|
+
logging: true,
|
|
157
|
+
logger: {
|
|
158
|
+
app: { file: './logs/app.log' },
|
|
159
|
+
http: { file: './logs/http.log' },
|
|
160
|
+
level: 'debug',
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`app.file` and `http.file` accept a path string or `{ path, sync }`, and `http.file` is used only when `logging: true`. `console` (default `true`) keeps stdout logging, and `pretty` (default `true` outside production) controls colored console output. `level` sets the base level and can be overridden per target with `app.level` or `http.level`. Use `sourceMaps: true` to map log locations back to `.ts` files (requires source maps in your build).
|
|
166
|
+
|
|
150
167
|
#### `ws?` (Boolean)
|
|
151
168
|
|
|
152
169
|
Default `false`
|
package/lib/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { BareHttp } from './server.js';
|
|
|
2
2
|
export type { BareHttpType } from './server.js';
|
|
3
3
|
export type { BareRequest } from './request.js';
|
|
4
4
|
export { context } from './context/index.js';
|
|
5
|
-
export { logMe } from './logger/index.js';
|
|
5
|
+
export { configureLogger, logMe } from './logger/index.js';
|
|
6
|
+
export type { LoggerConfig } from './logger/index.js';
|
package/lib/index.js
CHANGED
package/lib/logger/index.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
import { type LevelWithSilent } from 'pino';
|
|
1
2
|
import { serializeHttp } from './serializers.js';
|
|
3
|
+
type LoggerFileConfig = {
|
|
4
|
+
path: string;
|
|
5
|
+
sync?: boolean;
|
|
6
|
+
};
|
|
7
|
+
type LoggerTargetConfig = {
|
|
8
|
+
file?: string | LoggerFileConfig;
|
|
9
|
+
level?: LevelWithSilent;
|
|
10
|
+
};
|
|
11
|
+
export type LoggerConfig = {
|
|
12
|
+
console?: boolean;
|
|
13
|
+
pretty?: boolean;
|
|
14
|
+
level?: LevelWithSilent;
|
|
15
|
+
app?: LoggerTargetConfig;
|
|
16
|
+
http?: LoggerTargetConfig;
|
|
17
|
+
sourceMaps?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare const configureLogger: (config?: LoggerConfig) => void;
|
|
2
20
|
interface LogMeFn {
|
|
3
21
|
(obj: unknown, ...args: []): void;
|
|
4
22
|
(msg: string, ...args: any[]): void;
|
package/lib/logger/index.js
CHANGED
|
@@ -1,26 +1,149 @@
|
|
|
1
|
-
import pino, { destination } from 'pino';
|
|
1
|
+
import pino, { destination, } from 'pino';
|
|
2
|
+
import { mkdirSync } from 'fs';
|
|
3
|
+
import { dirname } from 'path';
|
|
2
4
|
import { serializeLog, serializeHttp } from './serializers.js';
|
|
3
5
|
import { envs } from '../env.js';
|
|
4
|
-
const
|
|
6
|
+
const defaultPretty = !envs.isProd;
|
|
7
|
+
const defaultFileSync = envs.isProd ? false : true;
|
|
5
8
|
const pinoCommonOptions = {
|
|
6
9
|
timestamp: () => `,"time":"${new Date()[envs.isProd ? 'toISOString' : 'toLocaleTimeString']()}"`,
|
|
7
10
|
formatters: {
|
|
8
11
|
level: (label) => ({ level: label }),
|
|
9
12
|
},
|
|
10
13
|
messageKey: 'message',
|
|
11
|
-
transport: envs.isProd ? undefined : { target: 'pino-pretty', options: { colorize: true } },
|
|
12
14
|
};
|
|
13
|
-
const
|
|
15
|
+
const noop = () => { };
|
|
16
|
+
const noopLogger = {
|
|
17
|
+
trace: noop,
|
|
18
|
+
debug: noop,
|
|
19
|
+
info: noop,
|
|
20
|
+
warn: noop,
|
|
21
|
+
error: noop,
|
|
22
|
+
fatal: noop,
|
|
23
|
+
silent: noop,
|
|
24
|
+
};
|
|
25
|
+
let sourceMapsRegistered = false;
|
|
26
|
+
const enableSourceMaps = (enabled) => {
|
|
27
|
+
if (!enabled || sourceMapsRegistered)
|
|
28
|
+
return;
|
|
29
|
+
sourceMapsRegistered = true;
|
|
30
|
+
void import('source-map-support/register').catch(() => {
|
|
31
|
+
sourceMapsRegistered = false;
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const normalizeFileConfig = (file) => {
|
|
35
|
+
if (!file)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (typeof file === 'string')
|
|
38
|
+
return { path: file, sync: defaultFileSync };
|
|
39
|
+
return { path: file.path, sync: file.sync ?? defaultFileSync };
|
|
40
|
+
};
|
|
41
|
+
const ensureLogDir = (filePath) => {
|
|
42
|
+
const dir = dirname(filePath);
|
|
43
|
+
if (dir && dir !== '.') {
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const toLoggerLike = (logger) => {
|
|
48
|
+
const call = (method) => (...args) => logger[method](...args);
|
|
49
|
+
return {
|
|
50
|
+
trace: call('trace'),
|
|
51
|
+
debug: call('debug'),
|
|
52
|
+
info: call('info'),
|
|
53
|
+
warn: call('warn'),
|
|
54
|
+
error: call('error'),
|
|
55
|
+
fatal: call('fatal'),
|
|
56
|
+
silent: noop,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const buildConsoleLogger = (options, prettyEnabled) => {
|
|
60
|
+
if (prettyEnabled) {
|
|
61
|
+
try {
|
|
62
|
+
const transport = pino.transport({
|
|
63
|
+
targets: [{ target: 'pino-pretty', options: { colorize: true } }],
|
|
64
|
+
});
|
|
65
|
+
return toLoggerLike(pino(options, transport));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return toLoggerLike(pino(options, destination({ dest: 1, sync: false })));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return toLoggerLike(pino(options, destination({ dest: 1, sync: false })));
|
|
72
|
+
};
|
|
73
|
+
const buildFileLogger = (options, fileConfig) => {
|
|
74
|
+
ensureLogDir(fileConfig.path);
|
|
75
|
+
return toLoggerLike(pino(options, destination({ dest: fileConfig.path, sync: fileConfig.sync ?? defaultFileSync })));
|
|
76
|
+
};
|
|
77
|
+
const combineLoggers = (primary, secondary) => {
|
|
78
|
+
if (!primary && !secondary)
|
|
79
|
+
return noopLogger;
|
|
80
|
+
const first = primary ?? noopLogger;
|
|
81
|
+
const second = secondary ?? noopLogger;
|
|
82
|
+
return {
|
|
83
|
+
trace: (...args) => {
|
|
84
|
+
first.trace(...args);
|
|
85
|
+
second.trace(...args);
|
|
86
|
+
},
|
|
87
|
+
debug: (...args) => {
|
|
88
|
+
first.debug(...args);
|
|
89
|
+
second.debug(...args);
|
|
90
|
+
},
|
|
91
|
+
info: (...args) => {
|
|
92
|
+
first.info(...args);
|
|
93
|
+
second.info(...args);
|
|
94
|
+
},
|
|
95
|
+
warn: (...args) => {
|
|
96
|
+
first.warn(...args);
|
|
97
|
+
second.warn(...args);
|
|
98
|
+
},
|
|
99
|
+
error: (...args) => {
|
|
100
|
+
first.error(...args);
|
|
101
|
+
second.error(...args);
|
|
102
|
+
},
|
|
103
|
+
fatal: (...args) => {
|
|
104
|
+
first.fatal(...args);
|
|
105
|
+
second.fatal(...args);
|
|
106
|
+
},
|
|
107
|
+
silent: noop,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
const buildLogger = (config, target) => {
|
|
111
|
+
const consoleEnabled = config.console ?? true;
|
|
112
|
+
const prettyEnabled = config.pretty ?? defaultPretty;
|
|
113
|
+
const fileConfig = normalizeFileConfig(target?.file);
|
|
114
|
+
const level = target?.level ?? config.level;
|
|
115
|
+
const options = { ...pinoCommonOptions };
|
|
116
|
+
if (level)
|
|
117
|
+
options.level = level;
|
|
118
|
+
const consoleLogger = consoleEnabled ? buildConsoleLogger(options, prettyEnabled) : undefined;
|
|
119
|
+
const fileLogger = fileConfig ? buildFileLogger(options, fileConfig) : undefined;
|
|
120
|
+
return combineLoggers(consoleLogger, fileLogger);
|
|
121
|
+
};
|
|
122
|
+
let appLogger;
|
|
123
|
+
let httpLogger;
|
|
124
|
+
export const configureLogger = (config = {}) => {
|
|
125
|
+
enableSourceMaps(config.sourceMaps);
|
|
126
|
+
if (config.app || config.http) {
|
|
127
|
+
appLogger = buildLogger(config, config.app);
|
|
128
|
+
httpLogger = buildLogger(config, config.http);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const shared = buildLogger(config);
|
|
132
|
+
appLogger = shared;
|
|
133
|
+
httpLogger = shared;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
configureLogger();
|
|
14
137
|
export const logHttp = (...params) => {
|
|
15
138
|
const { level, logObject } = serializeHttp(...params);
|
|
16
|
-
|
|
139
|
+
httpLogger[level](logObject);
|
|
17
140
|
};
|
|
18
141
|
// TODO: remove the test condition
|
|
19
142
|
export const logMe = {
|
|
20
|
-
debug: (...args) => !envs.isTest &&
|
|
21
|
-
info: (...args) => !envs.isTest &&
|
|
22
|
-
warn: (...args) => !envs.isTest &&
|
|
23
|
-
error: (...args) => !envs.isTest &&
|
|
24
|
-
fatal: (...args) => !envs.isTest &&
|
|
25
|
-
trace: (...args) => !envs.isTest &&
|
|
143
|
+
debug: (...args) => !envs.isTest && appLogger.debug(serializeLog(...args)),
|
|
144
|
+
info: (...args) => !envs.isTest && appLogger.info(serializeLog(...args)),
|
|
145
|
+
warn: (...args) => !envs.isTest && appLogger.warn(serializeLog(...args)),
|
|
146
|
+
error: (...args) => !envs.isTest && appLogger.error(serializeLog(...args)),
|
|
147
|
+
fatal: (...args) => !envs.isTest && appLogger.fatal(serializeLog(...args)),
|
|
148
|
+
trace: (...args) => !envs.isTest && appLogger.trace(serializeLog(...args)),
|
|
26
149
|
};
|
package/lib/server.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ServerOptions } from 'ws';
|
|
2
2
|
import { Ajv } from 'ajv';
|
|
3
3
|
import { BareRequest, CacheOpts } from './request.js';
|
|
4
|
+
import type { LoggerConfig } from './logger/index.js';
|
|
4
5
|
import { CookiesManagerOptions } from './middlewares/cookies/cookie-manager.js';
|
|
5
6
|
import { HttpMethodsUnion, StatusCodesUnion } from './utils/index.js';
|
|
6
7
|
import { CorsOptions } from './middlewares/cors/cors.js';
|
|
7
8
|
import { WebSocketServer } from './websocket.js';
|
|
8
9
|
import { Server } from 'http';
|
|
10
|
+
import { ServerOptions as ServerHTTPSOptions } from 'https';
|
|
9
11
|
type Middleware = (flow: BareRequest) => Promise<void> | void;
|
|
10
12
|
type Handler<H extends {
|
|
11
13
|
[key: string]: string | undefined;
|
|
@@ -62,6 +64,10 @@ type BareOptions<A extends IP> = {
|
|
|
62
64
|
* Default `false`
|
|
63
65
|
*/
|
|
64
66
|
logging?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Logger configuration. Controls outputs for app and http logs.
|
|
69
|
+
*/
|
|
70
|
+
logger?: LoggerConfig;
|
|
65
71
|
errorHandlerMiddleware?: ErrorHandler;
|
|
66
72
|
/**
|
|
67
73
|
* Request time format in `seconds` or `milliseconds`
|
|
@@ -89,6 +95,10 @@ type BareOptions<A extends IP> = {
|
|
|
89
95
|
* Enable Cors
|
|
90
96
|
*/
|
|
91
97
|
cors?: boolean | CorsOptions;
|
|
98
|
+
/**
|
|
99
|
+
* Custom HTTPS options for the server
|
|
100
|
+
*/
|
|
101
|
+
httpsOptions?: ServerHTTPSOptions;
|
|
92
102
|
};
|
|
93
103
|
type ExtractRouteParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}` ? {
|
|
94
104
|
[K in Param | keyof ExtractRouteParams<Rest>]: string;
|
package/lib/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Router from 'find-my-way';
|
|
2
2
|
import { Ajv } from 'ajv';
|
|
3
3
|
import { BareRequest } from './request.js';
|
|
4
|
-
import { logMe } from './logger/index.js';
|
|
4
|
+
import { configureLogger, logMe } from './logger/index.js';
|
|
5
5
|
import { context, enableContext, newContext } from './context/index.js';
|
|
6
6
|
import { HttpMethods, } from './utils/index.js';
|
|
7
7
|
import { Cors } from './middlewares/cors/cors.js';
|
|
@@ -9,6 +9,7 @@ import { WebSocketServer } from './websocket.js';
|
|
|
9
9
|
import { generateRouteSchema } from './schemas/generator.js';
|
|
10
10
|
import dns from 'dns';
|
|
11
11
|
import { createServer } from 'http';
|
|
12
|
+
import { createServer as createServerHTTPS } from 'https';
|
|
12
13
|
export class BareServer {
|
|
13
14
|
bareOptions;
|
|
14
15
|
server;
|
|
@@ -29,7 +30,12 @@ export class BareServer {
|
|
|
29
30
|
constructor(bareOptions = {}) {
|
|
30
31
|
this.bareOptions = bareOptions;
|
|
31
32
|
// init
|
|
32
|
-
|
|
33
|
+
if (bareOptions.httpsOptions) {
|
|
34
|
+
this.server = createServerHTTPS(bareOptions.httpsOptions, this.#onRequest);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.server = createServer(this.#onRequest);
|
|
38
|
+
}
|
|
33
39
|
this.attachGracefulHandlers();
|
|
34
40
|
this.attachRoutesDeclarator();
|
|
35
41
|
this.applyLaunchOptions();
|
|
@@ -55,6 +61,7 @@ export class BareServer {
|
|
|
55
61
|
this.#router.lookup(flow._originalRequest, flow._originalResponse);
|
|
56
62
|
});
|
|
57
63
|
};
|
|
64
|
+
#onRequest = this.#listener.bind(this);
|
|
58
65
|
/**
|
|
59
66
|
* This function generates previously defined middlewares for the sequential execution
|
|
60
67
|
*/
|
|
@@ -75,6 +82,8 @@ export class BareServer {
|
|
|
75
82
|
};
|
|
76
83
|
applyLaunchOptions = () => {
|
|
77
84
|
const { bareOptions: bo } = this;
|
|
85
|
+
if (bo.logger)
|
|
86
|
+
configureLogger(bo.logger);
|
|
78
87
|
if (bo.setRandomPort) {
|
|
79
88
|
this.#port = undefined;
|
|
80
89
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "barehttp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Lightweight and fast Node.js web server",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"lodash": "^4.17.21",
|
|
54
54
|
"pino": "^10.1.0",
|
|
55
55
|
"pino-pretty": "^13.1.3",
|
|
56
|
+
"source-map-support": "^0.5.21",
|
|
56
57
|
"ts-morph": "^27.0.2",
|
|
57
58
|
"ws": "^8.18.3"
|
|
58
59
|
},
|