mockaton 9.4.2 → 9.5.1
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 +367 -343
- package/package.json +1 -1
- package/src/Dashboard.css +16 -15
- package/src/Dashboard.js +2 -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/package.json
CHANGED
package/src/Dashboard.css
CHANGED
|
@@ -102,18 +102,12 @@ select, a, input, button {
|
|
|
102
102
|
|
|
103
103
|
a {
|
|
104
104
|
text-decoration: none;
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
&:hover {
|
|
107
107
|
text-decoration: underline;
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
input[type="checkbox"] {
|
|
112
|
-
&:focus {
|
|
113
|
-
outline: 0;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
111
|
a,
|
|
118
112
|
button,
|
|
119
113
|
input[type=checkbox],
|
|
@@ -161,7 +155,7 @@ header {
|
|
|
161
155
|
display: flex;
|
|
162
156
|
flex-wrap: wrap;
|
|
163
157
|
align-items: flex-end;
|
|
164
|
-
gap: 16px
|
|
158
|
+
gap: 16px 10px;
|
|
165
159
|
|
|
166
160
|
@media (max-width: 800px) {
|
|
167
161
|
max-width: 400px;
|
|
@@ -202,8 +196,7 @@ header {
|
|
|
202
196
|
height: 28px;
|
|
203
197
|
padding: 4px 8px;
|
|
204
198
|
border: 1px solid var(--colorSecondaryActionBorder);
|
|
205
|
-
|
|
206
|
-
margin-top: 4px;
|
|
199
|
+
margin-top: 2px;
|
|
207
200
|
color: var(--colorText);
|
|
208
201
|
font-size: 11px;
|
|
209
202
|
background-color: var(--colorComboBoxHeaderBackground);
|
|
@@ -211,6 +204,7 @@ header {
|
|
|
211
204
|
}
|
|
212
205
|
|
|
213
206
|
select:enabled:hover {
|
|
207
|
+
border-color: var(--colorSecondaryActionBorder);
|
|
214
208
|
background: var(--colorHover);
|
|
215
209
|
}
|
|
216
210
|
|
|
@@ -218,7 +212,7 @@ header {
|
|
|
218
212
|
width: 84px;
|
|
219
213
|
|
|
220
214
|
input[type=number] {
|
|
221
|
-
padding-right:
|
|
215
|
+
padding-right: 4px;
|
|
222
216
|
}
|
|
223
217
|
|
|
224
218
|
svg {
|
|
@@ -230,6 +224,10 @@ header {
|
|
|
230
224
|
}
|
|
231
225
|
}
|
|
232
226
|
|
|
227
|
+
&.CookieSelector {
|
|
228
|
+
width: 100px;
|
|
229
|
+
}
|
|
230
|
+
|
|
233
231
|
&.FallbackBackend {
|
|
234
232
|
position: relative;
|
|
235
233
|
width: 160px;
|
|
@@ -272,11 +270,14 @@ header {
|
|
|
272
270
|
}
|
|
273
271
|
|
|
274
272
|
.MenuTrigger {
|
|
275
|
-
|
|
273
|
+
width: 24px;
|
|
274
|
+
height: 24px;
|
|
275
|
+
flex-shrink: 0;
|
|
276
276
|
align-self: end;
|
|
277
277
|
margin-left: auto;
|
|
278
278
|
fill: var(--colorSecondaryAction);
|
|
279
279
|
background: transparent;
|
|
280
|
+
border-radius: 50%;
|
|
280
281
|
|
|
281
282
|
&:hover {
|
|
282
283
|
fill: var(--colorAccent);
|
|
@@ -295,7 +296,7 @@ header {
|
|
|
295
296
|
border-radius: var(--radius);
|
|
296
297
|
box-shadow: var(--boxShadow1);
|
|
297
298
|
background: var(--colorHeaderBackground);
|
|
298
|
-
|
|
299
|
+
|
|
299
300
|
a {
|
|
300
301
|
color: var(--colorAccent);
|
|
301
302
|
}
|
|
@@ -438,7 +439,7 @@ table {
|
|
|
438
439
|
> input {
|
|
439
440
|
appearance: none;
|
|
440
441
|
|
|
441
|
-
&:focus
|
|
442
|
+
&:focus {
|
|
442
443
|
outline: 0;
|
|
443
444
|
& ~ svg {
|
|
444
445
|
outline: 2px solid var(--colorAccent)
|
|
@@ -530,7 +531,7 @@ table {
|
|
|
530
531
|
.InternalServerErrorToggler,
|
|
531
532
|
.NotFoundToggler {
|
|
532
533
|
display: flex;
|
|
533
|
-
margin-right:
|
|
534
|
+
margin-right: 10px;
|
|
534
535
|
margin-left: 8px;
|
|
535
536
|
cursor: pointer;
|
|
536
537
|
|
package/src/Dashboard.js
CHANGED
|
@@ -33,6 +33,7 @@ const Strings = {
|
|
|
33
33
|
|
|
34
34
|
const CSS = {
|
|
35
35
|
BulkSelector: null,
|
|
36
|
+
CookieSelector: null,
|
|
36
37
|
DelayToggler: null,
|
|
37
38
|
ErrorToast: null,
|
|
38
39
|
FallbackBackend: null,
|
|
@@ -195,7 +196,7 @@ function CookieSelector() {
|
|
|
195
196
|
}
|
|
196
197
|
const disabled = cookies.length <= 1
|
|
197
198
|
return (
|
|
198
|
-
r('label', className(CSS.Field),
|
|
199
|
+
r('label', className(CSS.Field, CSS.CookieSelector),
|
|
199
200
|
r('span', null, Strings.cookie),
|
|
200
201
|
r('select', {
|
|
201
202
|
autocomplete: 'off',
|
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
|
-
}
|