@autofleet/super-express 2.0.0 → 2.2.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/.nvmrc +1 -0
- package/README.md +119 -0
- package/config/default-options.json +10 -0
- package/package.json +8 -6
- package/src/index.d.ts +77 -0
- package/src/index.js +87 -0
- package/test/index.test.js +94 -0
- package/test/mock-package.json +1 -0
- package/default-options.json +0 -5
- package/index.js +0 -28
package/.nvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
20.15.0
|
package/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
|
2
|
+
# SuperExpress
|
3
|
+
|
4
|
+
SuperExpress is an enhanced Express.js server application with built-in middleware for logging, security, health checks, and more. This project aims to provide a ready-to-use Express.js server setup with configurable options.
|
5
|
+
|
6
|
+
## Features
|
7
|
+
|
8
|
+
- **Logging**: Uses Morgan for HTTP request logging.
|
9
|
+
- **Security**: Adds various security headers.
|
10
|
+
- **Body Parsing**: Configurable body parser with a high payload limit.
|
11
|
+
- **Health Checks**: An endpoint to check if the server is alive.
|
12
|
+
- **Stats Endpoint**: Provides server and application statistics.
|
13
|
+
- **Tracing**: Supports request tracing.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
To install the dependencies, run:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
npm install
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Importing and Initializing
|
26
|
+
|
27
|
+
To use SuperExpress, you need to import and initialize it in your application:
|
28
|
+
|
29
|
+
```javascript
|
30
|
+
import createSuperExpressApp from './index.js';
|
31
|
+
|
32
|
+
const options = {
|
33
|
+
bodyParser: true,
|
34
|
+
helmet: true,
|
35
|
+
morgan: true,
|
36
|
+
nitur: true,
|
37
|
+
stats: true,
|
38
|
+
packageJsonPath: './package.json',
|
39
|
+
tracing: true,
|
40
|
+
eagerLoadUserPermissions: true,
|
41
|
+
aliveEndpointOptions: { customOption: 'value' }
|
42
|
+
};
|
43
|
+
|
44
|
+
const app = await createSuperExpressApp(options);
|
45
|
+
|
46
|
+
app.listen(3000, () => {
|
47
|
+
console.log('Server is running on port 3000');
|
48
|
+
});
|
49
|
+
```
|
50
|
+
|
51
|
+
### Options
|
52
|
+
|
53
|
+
You can customize the behavior of SuperExpress by passing an options object:
|
54
|
+
|
55
|
+
- `bodyParser` (boolean): Enables or disables the body parser middleware.
|
56
|
+
- `helmet` (boolean): Enables or disables security headers.
|
57
|
+
- `morgan` (boolean): Enables or disables HTTP request logging.
|
58
|
+
- `nitur` (boolean): Enables or disables the alive endpoint.
|
59
|
+
- `stats` (boolean): Enables or disables the stats endpoint.
|
60
|
+
- `packageJsonPath` (string): Path to the `package.json` file for the stats endpoint.
|
61
|
+
- `tracing` (boolean): Enables or disables request tracing.
|
62
|
+
- `eagerLoadUserPermissions` (boolean): Enables or disables eager loading of user permissions for tracing middleware.
|
63
|
+
- `aliveEndpointOptions` (object): Options to customize the alive endpoint.
|
64
|
+
|
65
|
+
## Endpoints
|
66
|
+
|
67
|
+
### Alive Endpoint
|
68
|
+
|
69
|
+
- **Path**: `/alive`
|
70
|
+
- **Method**: `GET`
|
71
|
+
- **Description**: Checks if the server is alive.
|
72
|
+
|
73
|
+
### Stats Endpoint
|
74
|
+
|
75
|
+
- **Path**: `/`
|
76
|
+
- **Method**: `GET`
|
77
|
+
- **Description**: Provides server and application statistics.
|
78
|
+
|
79
|
+
## Tests
|
80
|
+
|
81
|
+
This project uses Node.js's built-in test runner for testing. To run the tests, execute:
|
82
|
+
|
83
|
+
```bash
|
84
|
+
node --test index.test.js
|
85
|
+
```
|
86
|
+
|
87
|
+
## Example
|
88
|
+
|
89
|
+
Here's an example of how to set up and run the server:
|
90
|
+
|
91
|
+
```javascript
|
92
|
+
import createSuperExpressApp from './index.js';
|
93
|
+
|
94
|
+
const options = {
|
95
|
+
bodyParser: true,
|
96
|
+
helmet: true,
|
97
|
+
morgan: true,
|
98
|
+
nitur: true,
|
99
|
+
stats: true,
|
100
|
+
packageJsonPath: './package.json',
|
101
|
+
tracing: true,
|
102
|
+
eagerLoadUserPermissions: true,
|
103
|
+
aliveEndpointOptions: { customOption: 'value' }
|
104
|
+
};
|
105
|
+
|
106
|
+
const app = await createSuperExpressApp(options);
|
107
|
+
|
108
|
+
app.listen(3000, () => {
|
109
|
+
console.log('Server is running on port 3000');
|
110
|
+
});
|
111
|
+
```
|
112
|
+
|
113
|
+
## Contributing
|
114
|
+
|
115
|
+
Feel free to submit issues or pull requests. Contributions are welcome!
|
116
|
+
|
117
|
+
## License
|
118
|
+
|
119
|
+
This project is licensed under the MIT License.
|
package/package.json
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
{
|
2
2
|
"name": "@autofleet/super-express",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.2.0",
|
4
4
|
"description": "AF Express with built in boilerplate",
|
5
|
-
"main": "index.js",
|
5
|
+
"main": "src/index.js",
|
6
6
|
"type": "module",
|
7
7
|
"author": "Autofleet",
|
8
8
|
"license": "MIT",
|
9
9
|
"dependencies": {
|
10
|
+
"@autofleet/nitur": "^1.3.4",
|
11
|
+
"@autofleet/zehut": "^3.0.10",
|
10
12
|
"express": "^4.19.2",
|
11
|
-
"
|
12
|
-
"morgan": "^1.9.1"
|
13
|
+
"morgan": "^1.10.0"
|
13
14
|
},
|
14
15
|
"scripts": {
|
15
|
-
"test": "
|
16
|
+
"test": "node --test test/index.test.js",
|
17
|
+
"publish": "npm publish"
|
16
18
|
},
|
17
19
|
"repository": {
|
18
20
|
"type": "git",
|
@@ -24,6 +26,6 @@
|
|
24
26
|
"homepage": "https://github.com/Autofleet/super-express#readme",
|
25
27
|
"devDependencies": {
|
26
28
|
"@types/express": "^4.17.21",
|
27
|
-
"
|
29
|
+
"supertest": "^7.0.0"
|
28
30
|
}
|
29
31
|
}
|
package/src/index.d.ts
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
import express from 'express';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Options to customize the behavior of the SuperExpress application.
|
5
|
+
*/
|
6
|
+
export type DefaultOptions = {
|
7
|
+
/**
|
8
|
+
* Enables or disables body parser middleware.
|
9
|
+
*/
|
10
|
+
bodyParser?: boolean;
|
11
|
+
/**
|
12
|
+
* Enables or disables security headers middleware.
|
13
|
+
*/
|
14
|
+
helmet?: boolean;
|
15
|
+
/**
|
16
|
+
* Enables or disables HTTP request logging middleware.
|
17
|
+
*/
|
18
|
+
morgan?: boolean;
|
19
|
+
/**
|
20
|
+
* Enables or disables the alive endpoint middleware.
|
21
|
+
*/
|
22
|
+
nitur?: boolean;
|
23
|
+
/**
|
24
|
+
* Enables or disables the stats endpoint middleware.
|
25
|
+
*/
|
26
|
+
stats?: boolean;
|
27
|
+
/**
|
28
|
+
* Path to the package.json file for the stats endpoint.
|
29
|
+
*/
|
30
|
+
packageJsonPath?: string;
|
31
|
+
/**
|
32
|
+
* Enables or disables request tracing middleware.
|
33
|
+
*/
|
34
|
+
tracing?: boolean;
|
35
|
+
/**
|
36
|
+
* Enables or disables eager loading of user permissions for tracing middleware.
|
37
|
+
*/
|
38
|
+
eagerLoadUserPermissions?: boolean;
|
39
|
+
/**
|
40
|
+
* Options to customize the alive endpoint middleware.
|
41
|
+
*/
|
42
|
+
aliveEndpointOptions?: Record<string, unknown>;
|
43
|
+
// Add other options from config/default-options.json as needed
|
44
|
+
};
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Type for the callback function used in the listen method.
|
48
|
+
*/
|
49
|
+
type ListenCallback = () => void;
|
50
|
+
|
51
|
+
type Listen = express.Application['listen'];
|
52
|
+
type Server = Listen extends (port: any, cb: any) => infer R ? R : never;
|
53
|
+
/**
|
54
|
+
* Extends the express.Application interface to include nativeListen and the overridden listen method.
|
55
|
+
*/
|
56
|
+
export interface SuperExpressApp extends express.Application {
|
57
|
+
/**
|
58
|
+
* Original express listen method.
|
59
|
+
*/
|
60
|
+
nativeListen: Listen;
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Overridden listen method to add custom behavior.
|
64
|
+
* @param port - The port number to listen on.
|
65
|
+
* @param cb - Optional callback function to execute after the server starts listening.
|
66
|
+
*/
|
67
|
+
listen(port: number, cb?: ListenCallback): Server;
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
* Creates a new SuperExpress application with the given options.
|
72
|
+
* @param options - Optional settings to customize the application.
|
73
|
+
* @returns A SuperExpress application instance.
|
74
|
+
*/
|
75
|
+
export default function createSuperExpressApp(
|
76
|
+
options?: Partial<DefaultOptions & express.ApplicationOptions>
|
77
|
+
): SuperExpressApp;
|
package/src/index.js
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
import express from 'express';
|
2
|
+
import morgan from 'morgan';
|
3
|
+
import { aliveEndpoint } from '@autofleet/nitur';
|
4
|
+
import zehut, { enableTracing } from '@autofleet/zehut';
|
5
|
+
|
6
|
+
import defaultOptions from '../config/default-options.json' assert { type: "json" };
|
7
|
+
|
8
|
+
export default function (options = {}) {
|
9
|
+
const app = express(options);
|
10
|
+
const mergedOptions = Object.assign({}, defaultOptions, options);
|
11
|
+
/** Formatting */
|
12
|
+
if (mergedOptions.morgan) {
|
13
|
+
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
14
|
+
}
|
15
|
+
/** Security */
|
16
|
+
if (mergedOptions.helmet) {
|
17
|
+
// this is what helmet does by default. https://helmetjs.github.io/faq/you-might-not-need-helmet/
|
18
|
+
const HEADERS = {
|
19
|
+
"Content-Security-Policy":
|
20
|
+
"default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests",
|
21
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
22
|
+
"Cross-Origin-Resource-Policy": "same-origin",
|
23
|
+
"Origin-Agent-Cluster": "?1",
|
24
|
+
"Referrer-Policy": "no-referrer",
|
25
|
+
"Strict-Transport-Security": "max-age=15552000; includeSubDomains",
|
26
|
+
"X-Content-Type-Options": "nosniff",
|
27
|
+
"X-DNS-Prefetch-Control": "off",
|
28
|
+
"X-Download-Options": "noopen",
|
29
|
+
"X-Frame-Options": "SAMEORIGIN",
|
30
|
+
"X-Permitted-Cross-Domain-Policies": "none",
|
31
|
+
"X-XSS-Protection": "0",
|
32
|
+
};
|
33
|
+
app.use((req, res, next) => {
|
34
|
+
res.set(HEADERS);
|
35
|
+
next();
|
36
|
+
});
|
37
|
+
app.disable('x-powered-by');
|
38
|
+
}
|
39
|
+
/** Body Parser */
|
40
|
+
if (mergedOptions.bodyParser) {
|
41
|
+
app.use(express.json({ limit: '1000mb' }))
|
42
|
+
} else {
|
43
|
+
console.log('[SuperExpress] Body parser is disabled');
|
44
|
+
}
|
45
|
+
/** Alive Endpoint */
|
46
|
+
if (mergedOptions.nitur) {
|
47
|
+
app.get('/alive', aliveEndpoint(mergedOptions.aliveEndpointOptions || {}));
|
48
|
+
}
|
49
|
+
/** Stats Endpoint */
|
50
|
+
if (mergedOptions.stats && mergedOptions.packageJsonPath) {
|
51
|
+
const serverRunningSince = new Date();
|
52
|
+
import(mergedOptions.packageJsonPath, { assert: { type: 'json' } })
|
53
|
+
.then(module => {
|
54
|
+
const packageJson = module.default;
|
55
|
+
app.get('/', (req, res) => {
|
56
|
+
res.json({
|
57
|
+
name: packageJson.name,
|
58
|
+
version: packageJson.version,
|
59
|
+
commit: packageJson.commit,
|
60
|
+
serverRunningSince,
|
61
|
+
});
|
62
|
+
});
|
63
|
+
});
|
64
|
+
}
|
65
|
+
/** Tracing */
|
66
|
+
if (mergedOptions.tracing) {
|
67
|
+
enableTracing({
|
68
|
+
outbreakOptions: {
|
69
|
+
headersPrefix: 'x-af',
|
70
|
+
},
|
71
|
+
});
|
72
|
+
|
73
|
+
app.use(zehut.middleware({
|
74
|
+
eagerLoadUserPermissions: mergedOptions.eagerLoadUserPermissions,
|
75
|
+
}));
|
76
|
+
}
|
77
|
+
|
78
|
+
app.nativeListen = app.listen
|
79
|
+
app.listen = function (port, cb) {
|
80
|
+
const isProd = process.env.NODE_ENV === 'production';
|
81
|
+
console.log(`[SuperExpress] will listen on port ${port}`);
|
82
|
+
console.log(`[SuperExpress] Production mode: ${isProd}`);
|
83
|
+
return app.nativeListen(port, cb)
|
84
|
+
}
|
85
|
+
|
86
|
+
return app
|
87
|
+
};
|
@@ -0,0 +1,94 @@
|
|
1
|
+
// index.test.js
|
2
|
+
import { strict as assert } from 'assert';
|
3
|
+
import { test } from 'node:test';
|
4
|
+
import supertest from 'supertest';
|
5
|
+
import createSuperExpressApp from '../src/index.js';
|
6
|
+
import fs from 'fs';
|
7
|
+
import path, { dirname } from 'path';
|
8
|
+
import { fileURLToPath } from 'url';
|
9
|
+
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
11
|
+
const __dirname = dirname(__filename);
|
12
|
+
|
13
|
+
// Mock the package.json file content
|
14
|
+
const mockPackageJson = {
|
15
|
+
name: 'test-package',
|
16
|
+
version: '1.0.0',
|
17
|
+
commit: 'abcdef123456'
|
18
|
+
};
|
19
|
+
|
20
|
+
// Mock the default-options.json file content
|
21
|
+
const defaultOptions = {
|
22
|
+
bodyParser: true,
|
23
|
+
helmet: true,
|
24
|
+
morgan: true,
|
25
|
+
nitur: true,
|
26
|
+
stats: true,
|
27
|
+
packageJsonPath: '../test/mock-package.json',
|
28
|
+
tracing: true,
|
29
|
+
eagerLoadUserPermissions: true,
|
30
|
+
aliveEndpointOptions: { customOption: 'value' }
|
31
|
+
};
|
32
|
+
|
33
|
+
// Write the mock package.json file to disk
|
34
|
+
fs.writeFileSync(path.resolve(__dirname, './mock-package.json'), JSON.stringify(mockPackageJson));
|
35
|
+
|
36
|
+
// Test for the existence of default middleware
|
37
|
+
test('should create an app with default middleware', async () => {
|
38
|
+
const app = createSuperExpressApp();
|
39
|
+
|
40
|
+
// Test for the existence of bodyParser middleware
|
41
|
+
await supertest(app)
|
42
|
+
.post('/')
|
43
|
+
.send({ key: 'value' })
|
44
|
+
.expect(404); // Expect 404 because no routes are defined
|
45
|
+
|
46
|
+
// Test for the overridden listen method
|
47
|
+
const originalConsoleLog = console.log;
|
48
|
+
let logOutput = [];
|
49
|
+
console.log = (...args) => logOutput.push(...args);
|
50
|
+
const server = app.listen(3000, () => {});
|
51
|
+
assert(logOutput.includes('[SuperExpress] will listen on port 3000'));
|
52
|
+
|
53
|
+
console.log = originalConsoleLog;
|
54
|
+
server.close();
|
55
|
+
});
|
56
|
+
|
57
|
+
// Test for custom options
|
58
|
+
test('should create an app with custom options', async () => {
|
59
|
+
const app = createSuperExpressApp(defaultOptions);
|
60
|
+
|
61
|
+
// Test for the alive endpoint
|
62
|
+
await supertest(app)
|
63
|
+
.get('/alive')
|
64
|
+
.expect(200);
|
65
|
+
|
66
|
+
// Test for the stats endpoint
|
67
|
+
await supertest(app)
|
68
|
+
.get('/')
|
69
|
+
.expect(200)
|
70
|
+
.expect((res) => {
|
71
|
+
assert(res.body.name);
|
72
|
+
assert(res.body.version);
|
73
|
+
assert(res.body.serverRunningSince);
|
74
|
+
});
|
75
|
+
});
|
76
|
+
|
77
|
+
// Test for disabled body parser
|
78
|
+
test('should disable body parser when option is set to false', async () => {
|
79
|
+
const originalConsoleLog = console.log;
|
80
|
+
let logOutput = [];
|
81
|
+
console.log = (...args) => logOutput.push(...args);
|
82
|
+
|
83
|
+
const app = createSuperExpressApp({ bodyParser: false });
|
84
|
+
|
85
|
+
|
86
|
+
await supertest(app)
|
87
|
+
.post('/')
|
88
|
+
.send({ key: 'value' })
|
89
|
+
.expect(404);
|
90
|
+
|
91
|
+
assert(logOutput.includes('[SuperExpress] Body parser is disabled'));
|
92
|
+
|
93
|
+
console.log = originalConsoleLog;
|
94
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
{"name":"test-package","version":"1.0.0","commit":"abcdef123456"}
|
package/default-options.json
DELETED
package/index.js
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
import express from 'express';
|
2
|
-
import helmet from 'helmet';
|
3
|
-
import morgan from 'morgan';
|
4
|
-
|
5
|
-
import defaultOptions from './default-options.json';
|
6
|
-
|
7
|
-
export default function(options = {}) {
|
8
|
-
const app = express(options);
|
9
|
-
const isProd = process.env.NODE_ENV === 'production';
|
10
|
-
const mergedOptions = Object.assign({}, defaultOptions, options);
|
11
|
-
|
12
|
-
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
13
|
-
app.use(helmet());
|
14
|
-
if (mergedOptions.bodyParser) {
|
15
|
-
app.use(express.json({ limit: '1000mb' }))
|
16
|
-
} else {
|
17
|
-
console.log('[SuperExpress] Body parser is disabled');
|
18
|
-
}
|
19
|
-
|
20
|
-
app.nativeListen = app.listen
|
21
|
-
app.listen = function (port, cb) {
|
22
|
-
console.log(`[SuperExpress] will listen on port ${port}`);
|
23
|
-
console.log(`[SuperExpress] Production mode: ${isProd}`);
|
24
|
-
app.nativeListen(port, cb)
|
25
|
-
}
|
26
|
-
|
27
|
-
return app
|
28
|
-
};
|