noxt-server 0.1.14 → 0.1.16
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 +3 -116
- package/noxt-server.js +37 -13
- package/package.json +8 -10
- package/units/bundler.js +56 -0
- package/units/bust-cache.js +30 -0
- package/units/compression.js +30 -0
- package/units/config.js +2 -2
- package/units/env.js +3 -2
- package/units/express.js +87 -54
- package/units/fetch-cache-fs.js +172 -45
- package/units/fetch-cache.js +1 -1
- package/units/fetch.js +31 -15
- package/units/hooks.js +22 -9
- package/units/image-resizer.js +263 -0
- package/units/logger.js +59 -8
- package/units/noxt-dev.js +4 -2
- package/units/noxt-plugin.js +98 -98
- package/units/noxt-router.js +25 -8
- package/units/noxt.js +3 -1
- package/units/plugin.js +1 -1
- package/units/reload.js +101 -12
- package/units/services.js +16 -12
- package/units/static.js +43 -15
- package/units/utils.js +18 -1
- package/units.txt +86 -119
- package/units/noxt-router-dev.js +0 -33
package/README.md
CHANGED
|
@@ -1,118 +1,5 @@
|
|
|
1
|
-
# noxt-
|
|
1
|
+
# noxt-server
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Run it with `npx noxt-js` and drop `.jsx` files into your `views/` folder — routes are created automatically.
|
|
3
|
+
Server stack for [noxt-js-middleware](https://npmjs.com/package/noxt-js-middleware)
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## Installation
|
|
11
|
-
|
|
12
|
-
```sh
|
|
13
|
-
npm install noxt-js
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
or run it directly without installing:
|
|
17
|
-
|
|
18
|
-
```sh
|
|
19
|
-
npx noxt-js
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Quick Start
|
|
25
|
-
|
|
26
|
-
```sh
|
|
27
|
-
npx noxt-js
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
By default, this will:
|
|
31
|
-
|
|
32
|
-
- Serve pages from `./views/`
|
|
33
|
-
- Start an HTTP server on port `3000`
|
|
34
|
-
- Look for configuration in `noxt.config.yaml` (optional)
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Configuration
|
|
39
|
-
|
|
40
|
-
`noxt-js` reads options from `noxt.config.yaml` in your project root, or from CLI flags.
|
|
41
|
-
CLI flags override config file values.
|
|
42
|
-
|
|
43
|
-
Example `noxt.config.yaml`:
|
|
44
|
-
|
|
45
|
-
```yaml
|
|
46
|
-
port: 4000
|
|
47
|
-
host: localhost
|
|
48
|
-
views: views
|
|
49
|
-
logLevel: info
|
|
50
|
-
ssl: false
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Run with CLI overrides:
|
|
54
|
-
|
|
55
|
-
```sh
|
|
56
|
-
npx noxt-js --port 8080 --views src/pages
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Available options
|
|
60
|
-
|
|
61
|
-
- **`port`**: HTTP port number (default: `3000`)
|
|
62
|
-
- **`host`**: Hostname or IP (default: `0.0.0.0`)
|
|
63
|
-
- **`views`**: Directory containing `.jsx` files (default: `views`)
|
|
64
|
-
- **`logLevel`**: One of `error`, `warn`, `info`, `debug`
|
|
65
|
-
- **`ssl`**:
|
|
66
|
-
- `false` (disable SSL)
|
|
67
|
-
- or object with `cert` and `key` paths for HTTPS
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## Context
|
|
72
|
-
|
|
73
|
-
You can provide shared helpers/utilities to all components via a `context.js` file (or any path you specify in config). For example:
|
|
74
|
-
|
|
75
|
-
```js
|
|
76
|
-
// context.js
|
|
77
|
-
export async function fetchUser(id) {
|
|
78
|
-
return db.users.findById(id);
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Then in a page:
|
|
83
|
-
|
|
84
|
-
```jsx
|
|
85
|
-
export const route = '/user/:id';
|
|
86
|
-
|
|
87
|
-
export default async function UserPage({ id }, { fetchUser }) {
|
|
88
|
-
const user = await fetchUser(id);
|
|
89
|
-
return <h1>{user.name}</h1>;
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Pages & Routing
|
|
96
|
-
|
|
97
|
-
- Any `.jsx` file in `views/` is loaded as a component.
|
|
98
|
-
- If it exports `route`, it becomes a page at that route.
|
|
99
|
-
- Props come from route params, query string, and optional `params` export.
|
|
100
|
-
|
|
101
|
-
Example:
|
|
102
|
-
|
|
103
|
-
```jsx
|
|
104
|
-
export const route = '/hello/:name';
|
|
105
|
-
|
|
106
|
-
export default function HelloPage({ name }) {
|
|
107
|
-
return <h1>Hello, {name}!</h1>;
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## License
|
|
117
|
-
|
|
118
|
-
LGPL-3.0-or-later
|
|
5
|
+
Run with [noxt-cli](https://npmjs.com/package/noxt-cli)
|
package/noxt-server.js
CHANGED
|
@@ -5,32 +5,56 @@ import MLM from 'mlm-core';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = dirname(__filename);
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function resolveModule(name) {
|
|
9
|
+
if (!name.includes('/')) {
|
|
10
|
+
return `${__dirname}/units/${name}.js`;
|
|
11
|
+
}
|
|
12
|
+
if (name.startsWith('app/')) {
|
|
13
|
+
return `${process.cwd()}/units/${name.slice(4)}.js`;
|
|
14
|
+
}
|
|
15
|
+
if (name.startsWith('contrib/')) {
|
|
16
|
+
const m = name.match(/^contrib\/([^/]+)(?:\/(.*))?$/);
|
|
17
|
+
if (!m) throw new Error(`Invalid contrib module name: ${name}`);
|
|
18
|
+
if (!m[2]) return `noxt-contrib-${m[1]}`;
|
|
19
|
+
return `noxt-contrib-${m[1]}/${m[2]}`;
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Invalid module name: ${name}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function startServer({ config, recipe, import: _import}) {
|
|
9
25
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
resolveModule
|
|
14
|
-
name => name.startsWith('app/')
|
|
15
|
-
? `${process.cwd()}/units/${name.slice(4)}.js`
|
|
16
|
-
: `${__dirname}/units/${name}.js`
|
|
26
|
+
const beforeBoot = process.hrtime.bigint();
|
|
27
|
+
const mlmInstance = await MLM({
|
|
28
|
+
import: _import ?? (p => import(p)),
|
|
29
|
+
resolveModule
|
|
17
30
|
});
|
|
18
31
|
try {
|
|
19
|
-
|
|
32
|
+
const afterBoot = process.hrtime.bigint();
|
|
20
33
|
const report = await mlmInstance.analyze(recipe ?? 'noxt-dev');
|
|
21
34
|
if (!report.success) {
|
|
22
35
|
console.log('Bad recipe: ' + recipe + '\n' + report.order.join(', '));
|
|
23
36
|
console.log(report.errors.join('\n'));
|
|
24
37
|
process.exit(1);
|
|
25
38
|
}
|
|
26
|
-
|
|
39
|
+
const afterAnalyze = process.hrtime.bigint();
|
|
27
40
|
await mlmInstance.install(recipe ?? 'noxt-dev');
|
|
41
|
+
const afterInstall = process.hrtime.bigint();
|
|
28
42
|
const mlm = mlmInstance.context;
|
|
29
43
|
await mlm.services.config.merge(config);
|
|
30
|
-
|
|
31
|
-
mlmInstance.start();
|
|
44
|
+
const afterConfig = process.hrtime.bigint();
|
|
45
|
+
await mlmInstance.start();
|
|
46
|
+
const afterStart = process.hrtime.bigint();
|
|
47
|
+
|
|
48
|
+
console.log(
|
|
49
|
+
'[noxt-server] Total time', Number((afterStart - beforeBoot) / 1_000_000n), 'ms',
|
|
50
|
+
'| Boot', Number((afterBoot - beforeBoot) / 1_000_000n), 'ms',
|
|
51
|
+
'| Load', Number((afterAnalyze - afterBoot) / 1_000_000n), 'ms',
|
|
52
|
+
'| Install', Number((afterInstall - afterAnalyze) / 1_000_000n), 'ms',
|
|
53
|
+
'| Config', Number((afterConfig - afterInstall) / 1_000_000n), 'ms',
|
|
54
|
+
'| Start', Number((afterStart - afterConfig) / 1_000_000n), 'ms',
|
|
55
|
+
);
|
|
32
56
|
|
|
33
|
-
|
|
57
|
+
mlm.DEV && mlmInstance.repl();
|
|
34
58
|
} catch (e) {
|
|
35
59
|
console.log(await mlmInstance.analyze(recipe ?? 'noxt-dev'));
|
|
36
60
|
console.error(e);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noxt-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Server for noxt-js-middleware with CLI and config support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "noxt-server.js",
|
|
@@ -23,17 +23,15 @@
|
|
|
23
23
|
"author": "Zoran Obradović [https://github.com/zocky]",
|
|
24
24
|
"license": "GPL-3.0-or-later",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"noxt-js-middleware": "^1.0.
|
|
27
|
-
"mlm-core": "^1.0.
|
|
28
|
-
"express": "^5.
|
|
26
|
+
"noxt-js-middleware": "^1.0.9",
|
|
27
|
+
"mlm-core": "^1.0.8",
|
|
28
|
+
"express": "^5.1.0",
|
|
29
29
|
"cookie-parser": "^1.4.6",
|
|
30
|
-
"js-yaml": "^4.1.0"
|
|
31
|
-
"minimist": "^1.2.8",
|
|
32
|
-
"node-fetch-cache": "^5.0.2"
|
|
30
|
+
"js-yaml": "^4.1.0"
|
|
33
31
|
},
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"compression": "^1.8.1",
|
|
34
|
+
"sharp": "^0.34.5"
|
|
37
35
|
},
|
|
38
36
|
"engines": {
|
|
39
37
|
"node": ">=18"
|
package/units/bundler.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const info = {
|
|
2
|
+
name: 'bundler',
|
|
3
|
+
version: '1.0.0',
|
|
4
|
+
description: 'Bundle js and css from component exports',
|
|
5
|
+
requires: ['noxt-plugin'],
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default mlm => {
|
|
9
|
+
const styles = {};
|
|
10
|
+
const scripts = {};
|
|
11
|
+
return {
|
|
12
|
+
'config.bundler': {
|
|
13
|
+
is: {
|
|
14
|
+
style: 'string|false',
|
|
15
|
+
script: 'string|false',
|
|
16
|
+
},
|
|
17
|
+
default: {
|
|
18
|
+
style: '_style',
|
|
19
|
+
script: '_script',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
'services.bundler': () => ({ styles, scripts }),
|
|
23
|
+
'registerComponent.bundler': async ({ name, component, module }) => {
|
|
24
|
+
styles[component.name] = module.style ? `/* ${name} */ \n ${module.style}\n` : '';
|
|
25
|
+
|
|
26
|
+
if (!module.script) return;
|
|
27
|
+
|
|
28
|
+
if (typeof module.script == 'string') {
|
|
29
|
+
scripts[component.name] = `/* ${name} */ \n (()=>{\n${module.script}\n})();\n\n`;
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
if (typeof module.script == 'function') {
|
|
33
|
+
scripts[component.name] = `/* ${name} */ \n (${module.script.toString()})(window);\n\n`;
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
'middleware.bundler': () => {
|
|
38
|
+
const router = mlm.services.express.Router();
|
|
39
|
+
if (mlm.config.bundler.style) {
|
|
40
|
+
router.get(`/${mlm.config.bundler.style}`, (req, res) => {
|
|
41
|
+
res.logGroup = 'bundler';
|
|
42
|
+
res.set('Content-Type', 'text/css');
|
|
43
|
+
res.send( Object.values(styles).join('') );
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (mlm.config.bundler.script) {
|
|
47
|
+
router.get(`/${mlm.config.bundler.script}`, (req, res) => {
|
|
48
|
+
res.logGroup = 'bundler';
|
|
49
|
+
res.set('Content-Type', 'text/javascript');
|
|
50
|
+
res.send( Object.values(scripts).join('') );
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return router
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const info = {
|
|
2
|
+
name: 'bust-cache',
|
|
3
|
+
version: '1.0.0',
|
|
4
|
+
description: 'Bust cache on GET requests',
|
|
5
|
+
requires: ['express'],
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default mlm => ({
|
|
9
|
+
'hook.bustCache': async (fn, req) => {
|
|
10
|
+
req.bustCache ||= fn(req);
|
|
11
|
+
},
|
|
12
|
+
'prod.middleware.bustCache': async () => {
|
|
13
|
+
return (req, res, next) => {
|
|
14
|
+
if (req.method === 'GET') {
|
|
15
|
+
mlm.hook.bustCache(req);
|
|
16
|
+
}
|
|
17
|
+
next();
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
'dev.middleware.bustCache': () => {
|
|
21
|
+
return (req, res, next) => {
|
|
22
|
+
if (req.method === 'GET') {
|
|
23
|
+
if (req.headers['cache-control']?.includes('no-cache')) {
|
|
24
|
+
req.bustCache = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
next();
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// units/compression.js
|
|
2
|
+
export const info = {
|
|
3
|
+
name: 'compression',
|
|
4
|
+
version: '1.0.0',
|
|
5
|
+
description: 'Response compression',
|
|
6
|
+
requires: ['express'],
|
|
7
|
+
packages: ['compression'],
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default mlm => ({
|
|
11
|
+
'config.compression': {
|
|
12
|
+
is: {
|
|
13
|
+
enabled: 'boolean',
|
|
14
|
+
level: 'number', // 0-9, default 6
|
|
15
|
+
threshold: 'number', // bytes, default 1024
|
|
16
|
+
},
|
|
17
|
+
default: {
|
|
18
|
+
enabled: true,
|
|
19
|
+
level: 6,
|
|
20
|
+
threshold: 1024, // only compress responses > 1kb
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
'middleware.compression': async () => {
|
|
25
|
+
const { enabled, level, threshold } = mlm.config.compression;
|
|
26
|
+
if (!enabled) return null;
|
|
27
|
+
const compression = mlm.packages.compression.default;
|
|
28
|
+
return compression({ level, threshold });
|
|
29
|
+
},
|
|
30
|
+
});
|
package/units/config.js
CHANGED
|
@@ -100,9 +100,9 @@ export default mlm => ({
|
|
|
100
100
|
return ret;
|
|
101
101
|
}
|
|
102
102
|
merge(userConfig) {
|
|
103
|
-
console.log('initial',config_defaults)
|
|
103
|
+
// console.log('initial',config_defaults,userConfig)
|
|
104
104
|
Object.assign(config, this.process(userConfig));
|
|
105
|
-
console.log(userConfig,'final',config)
|
|
105
|
+
// console.log(userConfig,'final',config)
|
|
106
106
|
}
|
|
107
107
|
get_defs() {
|
|
108
108
|
return {
|
package/units/env.js
CHANGED
|
@@ -9,6 +9,7 @@ export default mlm => ({
|
|
|
9
9
|
'define.PROD': () => process.env.NODE_ENV === 'production',
|
|
10
10
|
'onBeforeLoad': () => {
|
|
11
11
|
process.env.NODE_ENV ||= 'development'
|
|
12
|
-
}
|
|
12
|
+
},
|
|
13
|
+
'inject.dev': (confs, unit) => mlm.DEV ? confs : null,
|
|
14
|
+
'inject.prod': (confs, unit) => !mlm.DEV ? confs : null,
|
|
13
15
|
})
|
|
14
|
-
|
package/units/express.js
CHANGED
|
@@ -2,62 +2,95 @@ export const info = {
|
|
|
2
2
|
name: 'express',
|
|
3
3
|
description: 'Sets up an Express server',
|
|
4
4
|
requires: ['plugin'],
|
|
5
|
-
|
|
6
|
-
'express': '^5.0.0',
|
|
7
|
-
'cookie-parser': '^1.4.6'
|
|
8
|
-
},
|
|
5
|
+
packages: ['express'],
|
|
9
6
|
}
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let c = conf[key];
|
|
21
|
-
if (mlm.is.function(c)) {
|
|
22
|
-
c = { create: c };
|
|
8
|
+
export default async mlm => {
|
|
9
|
+
let app;
|
|
10
|
+
const middlewareNames = new Set();
|
|
11
|
+
const middlewares = [];
|
|
12
|
+
const { default: express } = mlm.packages.express;
|
|
13
|
+
return {
|
|
14
|
+
'services.express': () => new class {
|
|
15
|
+
get app () {
|
|
16
|
+
return app;
|
|
23
17
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const { default: express } = await mlm.import('express');
|
|
46
|
-
return express.urlencoded({ extended: true });
|
|
47
|
-
},
|
|
48
|
-
async onStart() {
|
|
49
|
-
const { default: express } = await mlm.import('express');
|
|
50
|
-
app = express();
|
|
51
|
-
for (const middleware of middlewares) {
|
|
52
|
-
const mw = await middleware.create(app);
|
|
53
|
-
if (middleware.path) {
|
|
54
|
-
app.use(middleware.path, mw);
|
|
55
|
-
} else {
|
|
56
|
-
app.use(mw);
|
|
18
|
+
get Router() {
|
|
19
|
+
return express.Router;
|
|
20
|
+
}
|
|
21
|
+
get express () {
|
|
22
|
+
return express;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
'register.middleware': (conf, unit) => {
|
|
26
|
+
for (const key in conf) {
|
|
27
|
+
mlm.assert.not(key in middlewareNames, 'Duplicate middleware ' + key);
|
|
28
|
+
middlewareNames.add(key);
|
|
29
|
+
let c = conf[key];
|
|
30
|
+
if (mlm.is.function(c)) {
|
|
31
|
+
c = { create: c };
|
|
32
|
+
}
|
|
33
|
+
mlm.assert.is({
|
|
34
|
+
path: 'string|none',
|
|
35
|
+
create: 'function'
|
|
36
|
+
}, c, 'middleware');
|
|
37
|
+
mlm.assert.is.function(c.create, 'middleware');
|
|
38
|
+
middlewares.push({ ...c, unit: unit.name });
|
|
57
39
|
}
|
|
40
|
+
},
|
|
41
|
+
'config.port': {
|
|
42
|
+
is: v => Number.isInteger(v) && v > 0 && v < 65536,
|
|
43
|
+
default: 3000
|
|
44
|
+
},
|
|
45
|
+
'config.host': {
|
|
46
|
+
is: 'string',
|
|
47
|
+
default: 'localhost'
|
|
48
|
+
},
|
|
49
|
+
'middleware.json': async (app) => {
|
|
50
|
+
return express.json();
|
|
51
|
+
},
|
|
52
|
+
'middleware.urlencoded': async (app) => {
|
|
53
|
+
return express.urlencoded({ extended: true });
|
|
54
|
+
},
|
|
55
|
+
async onStart() {
|
|
56
|
+
app = express();
|
|
57
|
+
for (const middleware of middlewares) {
|
|
58
|
+
const mw = await middleware.create(app);
|
|
59
|
+
if (!mw) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (middleware.path) {
|
|
63
|
+
app.use(middleware.path, mw);
|
|
64
|
+
} else {
|
|
65
|
+
app.use(mw);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
app.use('/.well-known', (req, res, next) => {
|
|
69
|
+
res.logGroup = 'well-known';
|
|
70
|
+
res.status(404).send('Not found');
|
|
71
|
+
res.end();
|
|
72
|
+
});
|
|
73
|
+
app.use((req, res, next) => {
|
|
74
|
+
res.logGroup = '404';
|
|
75
|
+
res.status(404).send('Not found');
|
|
76
|
+
})
|
|
77
|
+
app.use((error, req, res, next,) => {
|
|
78
|
+
mlm.log('error', error);
|
|
79
|
+
res.status(500).send(error.stack);
|
|
80
|
+
})
|
|
81
|
+
const server = app.listen(mlm.config.port, mlm.config.host, (error) => {
|
|
82
|
+
if (error) {
|
|
83
|
+
mlm.throw(error);
|
|
84
|
+
}
|
|
85
|
+
mlm.log(`Listening on ${mlm.config.host}:${mlm.config.port}`);
|
|
86
|
+
});
|
|
87
|
+
server.on('close', () => {
|
|
88
|
+
mlm.log('Server closed');
|
|
89
|
+
});
|
|
90
|
+
app.on('error', (error) => {
|
|
91
|
+
mlm.error(error);
|
|
92
|
+
app.throw(error);
|
|
93
|
+
});
|
|
58
94
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
})
|
|
95
|
+
}
|
|
96
|
+
}
|