mockaton 9.4.1 → 9.5.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 +3 -2
- package/index.d.ts +1 -1
- package/lcov.info +1 -1
- package/package.json +1 -1
- package/src/Dashboard.css +1 -1
- package/src/MockDispatcher.js +2 -7
- package/src/Mockaton.js +4 -4
- package/src/StaticDispatcher.js +3 -3
- package/src/config.js +3 -3
- package/src/mockBrokersCollection.js +2 -2
- package/src/utils/fs.js +2 -2
- package/src/utils/http-response.js +16 -9
- package/src/utils/logger.js +42 -0
- package/src/utils/log.js +0 -34
package/README.md
CHANGED
|
@@ -485,11 +485,12 @@ At any rate, you can trigger any command besides opening a browser.
|
|
|
485
485
|
|
|
486
486
|
<br/>
|
|
487
487
|
|
|
488
|
-
### `logLevel?: 'quiet' | 'normal'`
|
|
488
|
+
### `logLevel?: 'quiet' | 'normal' | 'verbose'`
|
|
489
489
|
Defaults to `'normal'`.
|
|
490
490
|
|
|
491
491
|
- `quiet`: only errors (stderr)
|
|
492
|
-
- `normal`: info, access, warnings, and errors
|
|
492
|
+
- `normal`: info, mock access, warnings, and errors
|
|
493
|
+
- `verbose`: normal + API access
|
|
493
494
|
|
|
494
495
|
</details>
|
|
495
496
|
|
package/index.d.ts
CHANGED
package/lcov.info
CHANGED
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
package/src/MockDispatcher.js
CHANGED
|
@@ -2,7 +2,7 @@ import { join } from 'node:path'
|
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
3
|
import { pathToFileURL } from 'node:url'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { logger } from './utils/logger.js'
|
|
6
6
|
import { proxy } from './ProxyRelay.js'
|
|
7
7
|
import { cookie } from './cookie.js'
|
|
8
8
|
import { mimeFor } from './utils/mime.js'
|
|
@@ -25,7 +25,7 @@ export async function dispatchMock(req, response) {
|
|
|
25
25
|
return
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
logger.accessMock(req.url, broker.file)
|
|
29
29
|
response.statusCode = broker.status
|
|
30
30
|
|
|
31
31
|
if (cookie.getCurrent())
|
|
@@ -46,11 +46,6 @@ export async function dispatchMock(req, response) {
|
|
|
46
46
|
catch (error) {
|
|
47
47
|
if (error?.code === 'ENOENT') // mock-file has been deleted
|
|
48
48
|
sendNotFound(response)
|
|
49
|
-
else if (error?.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
|
|
50
|
-
if (error.toString().includes('Unknown file extension ".ts'))
|
|
51
|
-
log.warn('\nLooks like you need a TypeScript compiler\n')
|
|
52
|
-
sendInternalServerError(response, error)
|
|
53
|
-
}
|
|
54
49
|
else
|
|
55
50
|
sendInternalServerError(response, error)
|
|
56
51
|
}
|
package/src/Mockaton.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createServer } from 'node:http'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from './utils/logger.js'
|
|
4
4
|
import { API } from './ApiConstants.js'
|
|
5
5
|
import { config, setup } from './config.js'
|
|
6
6
|
import { dispatchMock } from './MockDispatcher.js'
|
|
@@ -27,8 +27,8 @@ export function Mockaton(options) {
|
|
|
27
27
|
server.listen(config.port, config.host, function () {
|
|
28
28
|
const { address, port } = this.address()
|
|
29
29
|
const url = `http://${address}:${port}`
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
logger.info('Listening', url)
|
|
31
|
+
logger.info('Dashboard', url + API.dashboard)
|
|
32
32
|
config.onReady(url + API.dashboard)
|
|
33
33
|
})
|
|
34
34
|
|
|
@@ -37,7 +37,7 @@ export function Mockaton(options) {
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
async function onRequest(req, response) {
|
|
40
|
-
response.on('error',
|
|
40
|
+
response.on('error', logger.warn)
|
|
41
41
|
|
|
42
42
|
try {
|
|
43
43
|
response.setHeader('Server', 'Mockaton')
|
package/src/StaticDispatcher.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { logger } from './utils/logger.js'
|
|
5
5
|
import { mimeFor } from './utils/mime.js'
|
|
6
6
|
import { brokerByRoute } from './staticCollection.js'
|
|
7
7
|
import { config, calcDelay } from './config.js'
|
|
@@ -13,12 +13,12 @@ export async function dispatchStatic(req, response) {
|
|
|
13
13
|
|
|
14
14
|
setTimeout(async () => {
|
|
15
15
|
if (!broker || broker.status === 404) { // TESTME
|
|
16
|
-
|
|
16
|
+
logger.accessMock(req.url, 'static404')
|
|
17
17
|
sendNotFound(response)
|
|
18
18
|
return
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
logger.accessMock(req.url, 'static200')
|
|
22
22
|
|
|
23
23
|
const file = join(config.staticDir, broker.route)
|
|
24
24
|
if (req.headers.range)
|
package/src/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join, isAbsolute } from 'node:path'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from './utils/logger.js'
|
|
4
4
|
import { isDirectory } from './utils/fs.js'
|
|
5
5
|
import { openInBrowser } from './utils/openInBrowser.js'
|
|
6
6
|
import { jsToJsonPlugin } from './MockDispatcher.js'
|
|
@@ -23,7 +23,7 @@ const schema = {
|
|
|
23
23
|
host: ['127.0.0.1', is(String)],
|
|
24
24
|
port: [0, port => Number.isInteger(port) && port >= 0 && port < 2 ** 16], // 0 means auto-assigned
|
|
25
25
|
|
|
26
|
-
logLevel: ['normal', val => ['normal', 'quiet'].includes(val)],
|
|
26
|
+
logLevel: ['normal', val => ['normal', 'quiet', 'verbose'].includes(val)],
|
|
27
27
|
|
|
28
28
|
delay: [1200, ms => Number.isInteger(ms) && ms >= 0],
|
|
29
29
|
delayJitter: [0, percent => percent >= 0 && percent <= 3],
|
|
@@ -80,7 +80,7 @@ export function setup(options) {
|
|
|
80
80
|
|
|
81
81
|
Object.assign(config, options)
|
|
82
82
|
validate(config, ConfigValidator)
|
|
83
|
-
|
|
83
|
+
logger.setLevel(config.logLevel)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { basename } from 'node:path'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from './utils/logger.js'
|
|
4
4
|
import { cookie } from './cookie.js'
|
|
5
5
|
import { MockBroker } from './MockBroker.js'
|
|
6
6
|
import { listFilesRecursively } from './utils/fs.js'
|
|
@@ -68,7 +68,7 @@ export function registerMock(file, isFromWatcher = false) {
|
|
|
68
68
|
function filenameIsValid(file) {
|
|
69
69
|
const error = validateFilename(file)
|
|
70
70
|
if (error)
|
|
71
|
-
|
|
71
|
+
logger.warn(error, file)
|
|
72
72
|
return !error
|
|
73
73
|
}
|
|
74
74
|
|
package/src/utils/fs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join, dirname, sep, posix } from 'node:path'
|
|
2
2
|
import { lstatSync, readdirSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from './logger.js'
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
export const isFile = path => lstatSync(path, { throwIfNoEntry: false })?.isFile()
|
|
@@ -25,6 +25,6 @@ export const write = (path, body) => {
|
|
|
25
25
|
writeFileSync(path, body)
|
|
26
26
|
}
|
|
27
27
|
catch (err) {
|
|
28
|
-
|
|
28
|
+
logger.warn('Write access denied', err)
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,47 +1,54 @@
|
|
|
1
1
|
import fs, { readFileSync } from 'node:fs'
|
|
2
|
-
import {
|
|
2
|
+
import { logger } from './logger.js'
|
|
3
3
|
import { mimeFor } from './mime.js'
|
|
4
4
|
import { HEADER_FOR_502 } from '../ApiConstants.js'
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
export function sendOK(response) {
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function sendNoContent(response) {
|
|
12
|
-
response.statusCode = 204
|
|
8
|
+
logger.access(response)
|
|
13
9
|
response.end()
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
export function sendJSON(response, payload) {
|
|
13
|
+
logger.access(response)
|
|
17
14
|
response.setHeader('Content-Type', 'application/json')
|
|
18
15
|
response.end(JSON.stringify(payload))
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
export function sendFile(response, file) {
|
|
19
|
+
logger.access(response)
|
|
22
20
|
response.setHeader('Content-Type', mimeFor(file))
|
|
23
21
|
response.end(readFileSync(file, 'utf8'))
|
|
24
22
|
}
|
|
25
23
|
|
|
24
|
+
export function sendNoContent(response) {
|
|
25
|
+
response.statusCode = 204
|
|
26
|
+
logger.access(response)
|
|
27
|
+
response.end()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
26
31
|
export function sendNotFound(response) {
|
|
27
32
|
response.statusCode = 404
|
|
33
|
+
logger.access(response)
|
|
28
34
|
response.end()
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
export function sendUnprocessableContent(response, error) {
|
|
32
|
-
|
|
38
|
+
logger.warn(error)
|
|
33
39
|
response.statusCode = 422
|
|
34
40
|
response.end(error)
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
|
|
37
44
|
export function sendInternalServerError(response, error) {
|
|
38
|
-
|
|
45
|
+
logger.error(error?.message || error, error?.stack || undefined)
|
|
39
46
|
response.statusCode = 500
|
|
40
47
|
response.end()
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
export function sendBadGateway(response, error) {
|
|
44
|
-
|
|
51
|
+
logger.warn('Fallback Proxy Error:', error.cause.message)
|
|
45
52
|
response.statusCode = 502
|
|
46
53
|
response.setHeader(HEADER_FOR_502, 1)
|
|
47
54
|
response.end()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const logger = new class {
|
|
2
|
+
#level = 'normal'
|
|
3
|
+
|
|
4
|
+
setLevel(level) {
|
|
5
|
+
this.#level = level
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
info(...msg) {
|
|
9
|
+
if (this.#level !== 'quiet')
|
|
10
|
+
console.info(this.#msg('INFO', ...msg))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
accessMock(url, ...msg) {
|
|
14
|
+
if (this.#level !== 'quiet')
|
|
15
|
+
console.log(this.#msg('MOCK', this.#sanitizeURL(url), ...msg))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
access(response) {
|
|
19
|
+
if (this.#level === 'verbose')
|
|
20
|
+
console.log(this.#msg(
|
|
21
|
+
'ACCESS',
|
|
22
|
+
response.req.method,
|
|
23
|
+
response.statusCode,
|
|
24
|
+
this.#sanitizeURL(response.req.url)))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
warn(...msg) {
|
|
28
|
+
console.warn(this.#msg('WARN', ...msg))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
error(...msg) {
|
|
32
|
+
console.error(this.#msg('ERROR', ...msg))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#msg(...msg) {
|
|
36
|
+
return [new Date().toISOString(), ...msg].join('::')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#sanitizeURL(url) {
|
|
40
|
+
return decodeURIComponent(url).replace(/[\x00-\x1F\x7F\x9B]/g, '')
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/utils/log.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export const log = new class {
|
|
2
|
-
#level = 'normal'
|
|
3
|
-
|
|
4
|
-
setLevel(level) {
|
|
5
|
-
this.#level = level
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
info(...msg) {
|
|
9
|
-
if (this.#level !== 'quiet')
|
|
10
|
-
console.info([this.#date, 'INFO', ...msg].join('::'))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
access(url, ...msg) {
|
|
14
|
-
if (this.#level !== 'quiet')
|
|
15
|
-
console.log([this.#date, 'ACCESS', this.#sanitizeURL(url), ...msg].join('::'))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
warn(...msg) {
|
|
19
|
-
console.warn([this.#date, 'WARN', ...msg].join('::'))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
error(...msg) {
|
|
23
|
-
console.error([this.#date, 'ERROR', ...msg].join('::'))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
get #date() {
|
|
28
|
-
return new Date().toISOString()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
#sanitizeURL(url) {
|
|
32
|
-
return decodeURIComponent(url).replace(/[\x00-\x1F\x7F\x9B]/g, '')
|
|
33
|
-
}
|
|
34
|
-
}
|