@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 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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "bodyParser": true,
3
+ "compression": true,
4
+ "helmet": true,
5
+ "morgan": true,
6
+ "nitur": true,
7
+ "stats": true,
8
+ "tracing": true,
9
+ "eagerLoadUserPermissions": true
10
+ }
package/package.json CHANGED
@@ -1,18 +1,20 @@
1
1
  {
2
2
  "name": "@autofleet/super-express",
3
- "version": "2.0.0",
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
- "helmet": "^3.21.2",
12
- "morgan": "^1.9.1"
13
+ "morgan": "^1.10.0"
13
14
  },
14
15
  "scripts": {
15
- "test": "echo \"Error: no test specified\" && exit 1"
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
- "@types/helmet": "^4.0.0"
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"}
@@ -1,5 +0,0 @@
1
- {
2
- "bodyParser": true,
3
- "compression": true,
4
- "helmet": true
5
- }
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
- };