mockaton 13.3.4 → 13.4.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 +28 -16
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/src/client/ApiCommander.js +1 -1
- package/src/client/ApiConstants.js +2 -2
- package/src/client/IndexHtml.js +18 -18
- package/src/client/app-header.js +3 -2
- package/src/client/app-payload-viewer.js +4 -7
- package/src/client/app-store.js +20 -78
- package/src/client/app-store.test.js +1 -25
- package/src/client/app.css +9 -5
- package/src/client/app.js +7 -7
- package/src/client/dir/dittoSplitPaths.js +25 -0
- package/src/client/dir/dittoSplitPaths.test.js +28 -0
- package/src/client/{dirStructure.js → dir/groupByFolder.js} +17 -13
- package/src/client/dir/groupByFolder.test.js +82 -0
- package/src/client/graphics.js +2 -2
- package/src/client/utils/LocalStorage.js +69 -0
- package/src/client/utils/css.js +16 -0
- package/src/client/utils/css.test.js +74 -0
- package/src/client/{dom-utils.js → utils/dom.js} +2 -21
- package/src/client/utils/watcherDev.js +46 -0
- package/src/server/Api.js +16 -3
- package/src/server/MockDispatcher.js +11 -8
- package/src/server/Mockaton.js +18 -8
- package/src/server/Mockaton.test.js +14 -9
- package/src/server/ProxyRelay.js +9 -3
- package/src/server/{utils/UrlParsers.js → UrlParsers.js} +10 -4
- package/src/server/{utils/UrlParsers.test.js → UrlParsers.test.js} +14 -14
- package/src/server/config.js +2 -0
- package/src/server/utils/HttpServerResponse.js +18 -39
- package/src/server/{WatcherDevClient.js → utils/WatcherDevClient.js} +3 -17
- package/src/server/utils/fs.js +1 -1
- package/src/server/utils/logger.js +4 -4
- package/src/server/utils/mime.js +11 -11
- package/src/server/utils/mime.test.js +15 -11
- package/www/src/assets/openapi.json +147 -147
- package/src/client/dirStructure.test.js +0 -81
- package/src/client/dom-utils.test.js +0 -76
- package/src/client/watcherDev.js +0 -39
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import http from 'node:http'
|
|
2
|
-
import fs
|
|
2
|
+
import fs from 'node:fs'
|
|
3
3
|
|
|
4
|
-
import { logger } from './logger.js'
|
|
5
4
|
import { mimeFor } from './mime.js'
|
|
6
|
-
import { HEADER_502 } from '../../client/ApiConstants.js'
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
export class ServerResponse extends http.ServerResponse {
|
|
@@ -13,83 +11,64 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
ok() {
|
|
16
|
-
logger.access(this)
|
|
17
14
|
this.end()
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
html(html, csp) {
|
|
21
|
-
logger.access(this)
|
|
22
18
|
this.setHeader('Content-Type', mimeFor('.html'))
|
|
23
19
|
this.setHeader('Content-Security-Policy', csp)
|
|
24
20
|
this.end(html)
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
json(payload) {
|
|
28
|
-
logger.access(this)
|
|
29
24
|
this.setHeader('Content-Type', mimeFor('.json'))
|
|
30
25
|
this.end(JSON.stringify(payload))
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
file(file) {
|
|
34
|
-
logger.access(this)
|
|
28
|
+
async file(file) {
|
|
35
29
|
this.setHeader('Content-Type', mimeFor(file))
|
|
36
|
-
this.end(
|
|
30
|
+
this.end(await fs.promises.readFile(file, 'utf8'))
|
|
37
31
|
}
|
|
38
32
|
|
|
39
33
|
noContent() {
|
|
40
34
|
this.statusCode = 204
|
|
41
|
-
logger.access(this)
|
|
42
35
|
this.end()
|
|
43
36
|
}
|
|
44
37
|
|
|
45
38
|
|
|
46
39
|
badRequest() {
|
|
47
40
|
this.statusCode = 400
|
|
48
|
-
logger.access(this)
|
|
49
41
|
this.end()
|
|
50
42
|
}
|
|
51
43
|
|
|
52
44
|
forbidden() {
|
|
53
45
|
this.statusCode = 403
|
|
54
|
-
logger.access(this)
|
|
55
46
|
this.end()
|
|
56
47
|
}
|
|
57
48
|
|
|
58
49
|
notFound() {
|
|
59
50
|
this.statusCode = 404
|
|
60
|
-
logger.access(this)
|
|
61
|
-
this.end()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
mockNotFound() {
|
|
65
|
-
this.statusCode = 404
|
|
66
|
-
logger.accessMock(this.req.url, '404')
|
|
67
51
|
this.end()
|
|
68
52
|
}
|
|
69
53
|
|
|
70
54
|
uriTooLong() {
|
|
71
55
|
this.statusCode = 414
|
|
72
|
-
logger.access(this)
|
|
73
56
|
this.end()
|
|
74
57
|
}
|
|
75
58
|
|
|
76
59
|
unprocessable(error) {
|
|
77
|
-
logger.access(this, error)
|
|
78
60
|
this.statusCode = 422
|
|
79
61
|
this.end(error)
|
|
80
62
|
}
|
|
81
63
|
|
|
82
64
|
|
|
83
|
-
internalServerError(
|
|
84
|
-
logger.error(500, this.req.url, error?.message || error, error?.stack || '')
|
|
65
|
+
internalServerError() {
|
|
85
66
|
this.statusCode = 500
|
|
86
67
|
this.end()
|
|
87
68
|
}
|
|
88
69
|
|
|
89
|
-
badGateway(
|
|
90
|
-
logger.warn('Fallback Proxy Error:', error.cause.message)
|
|
70
|
+
badGateway() {
|
|
91
71
|
this.statusCode = 502
|
|
92
|
-
this.setHeader(HEADER_502, 1)
|
|
93
72
|
this.end()
|
|
94
73
|
}
|
|
95
74
|
|
|
@@ -104,20 +83,20 @@ export class ServerResponse extends http.ServerResponse {
|
|
|
104
83
|
this.statusCode = 416 // Range Not Satisfiable
|
|
105
84
|
this.setHeader('Content-Range', `bytes */${size}`)
|
|
106
85
|
this.end()
|
|
86
|
+
return
|
|
107
87
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
88
|
+
|
|
89
|
+
this.statusCode = 206 // Partial Content
|
|
90
|
+
this.setHeader('Accept-Ranges', 'bytes')
|
|
91
|
+
this.setHeader('Content-Range', `bytes ${start}-${end}/${size}`)
|
|
92
|
+
this.setHeader('Content-Type', mimeFor(file))
|
|
93
|
+
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
113
95
|
const reader = fs.createReadStream(file, { start, end })
|
|
114
|
-
|
|
115
|
-
reader.on('
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.internalServerError(error)
|
|
120
|
-
})
|
|
121
|
-
}
|
|
96
|
+
this.on('error', reject)
|
|
97
|
+
reader.on('error', reject)
|
|
98
|
+
reader.on('end', resolve)
|
|
99
|
+
reader.pipe(this)
|
|
100
|
+
})
|
|
122
101
|
}
|
|
123
102
|
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { watch } from 'node:fs'
|
|
2
2
|
import { EventEmitter } from 'node:events'
|
|
3
|
-
import { watch, readdirSync } from 'node:fs'
|
|
4
|
-
|
|
5
|
-
import { config } from './config.js'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const CLIENT_DIR = join(import.meta.dirname, '../client')
|
|
9
|
-
export const DASHBOARD_ASSETS = readdirSync(CLIENT_DIR)
|
|
10
3
|
|
|
11
4
|
|
|
12
5
|
const devClientWatcher = new class extends EventEmitter {
|
|
@@ -18,20 +11,14 @@ const devClientWatcher = new class extends EventEmitter {
|
|
|
18
11
|
|
|
19
12
|
// Although `client/IndexHtml.js` is watched, it returns a stale version.
|
|
20
13
|
// i.e., it would need to be a dynamic import + cache busting.
|
|
21
|
-
export function watchDevSPA() {
|
|
22
|
-
watch(
|
|
14
|
+
export function watchDevSPA(dir) {
|
|
15
|
+
watch(dir, (_, file) => {
|
|
23
16
|
devClientWatcher.emit(file)
|
|
24
17
|
})
|
|
25
18
|
}
|
|
26
19
|
|
|
27
|
-
|
|
28
20
|
/** Realtime notify Dev UI changes */
|
|
29
21
|
export function sseClientHotReload(req, response) {
|
|
30
|
-
if (!config.hotReload) {
|
|
31
|
-
response.notFound()
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
22
|
response.writeHead(200, {
|
|
36
23
|
'Content-Type': 'text/event-stream',
|
|
37
24
|
'Cache-Control': 'no-cache',
|
|
@@ -42,7 +29,6 @@ export function sseClientHotReload(req, response) {
|
|
|
42
29
|
function onDevChange(file = '') {
|
|
43
30
|
response.write(`data: ${file}\n\n`)
|
|
44
31
|
}
|
|
45
|
-
|
|
46
32
|
devClientWatcher.subscribe(onDevChange)
|
|
47
33
|
|
|
48
34
|
const keepAlive = setInterval(() => {
|
package/src/server/utils/fs.js
CHANGED
|
@@ -13,15 +13,15 @@ export const logger = new class {
|
|
|
13
13
|
console.info(this.#msg('INFO', ...msg))
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
normal(tag = 'NORMAL', url, ...msg) {
|
|
17
17
|
if (this.#level !== 'quiet')
|
|
18
|
-
console.log(this.#msg(
|
|
18
|
+
console.log(this.#msg(tag, url, ...msg))
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
verbose(tag = 'VERBOSE', response, error = '') {
|
|
22
22
|
if (this.#level === 'verbose')
|
|
23
23
|
console.log(this.#msg(
|
|
24
|
-
|
|
24
|
+
tag,
|
|
25
25
|
response.req.method,
|
|
26
26
|
response.statusCode,
|
|
27
27
|
response.req.url,
|
package/src/server/utils/mime.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { MIMEType } from 'node:util'
|
|
2
|
-
import { config } from '../config.js'
|
|
3
|
-
import { EXT_UNKNOWN_MIME, EXT_EMPTY } from '../../client/ApiConstants.js'
|
|
4
2
|
|
|
5
3
|
|
|
6
4
|
// Generated with:
|
|
@@ -119,7 +117,14 @@ const extToMime = {
|
|
|
119
117
|
zst: 'application/zstd'
|
|
120
118
|
}
|
|
121
119
|
|
|
122
|
-
const mimeToExt =
|
|
120
|
+
const mimeToExt = {
|
|
121
|
+
data: mapMimeToExt(extToMime)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function registerMimes(obj) {
|
|
125
|
+
Object.assign(extToMime, obj)
|
|
126
|
+
mimeToExt.data = mapMimeToExt(extToMime)
|
|
127
|
+
}
|
|
123
128
|
|
|
124
129
|
function mapMimeToExt(e2m) {
|
|
125
130
|
const m = {}
|
|
@@ -130,7 +135,7 @@ function mapMimeToExt(e2m) {
|
|
|
130
135
|
|
|
131
136
|
export function mimeFor(filename) {
|
|
132
137
|
const ext = extname(filename).toLowerCase()
|
|
133
|
-
return
|
|
138
|
+
return extToMime[ext] || ''
|
|
134
139
|
}
|
|
135
140
|
function extname(filename) {
|
|
136
141
|
const i = filename.lastIndexOf('.')
|
|
@@ -142,12 +147,7 @@ function extname(filename) {
|
|
|
142
147
|
|
|
143
148
|
export function extFor(mime) {
|
|
144
149
|
return mime
|
|
145
|
-
?
|
|
146
|
-
:
|
|
147
|
-
}
|
|
148
|
-
function findExt(rawMime) {
|
|
149
|
-
const m = new MIMEType(rawMime).essence
|
|
150
|
-
const extraMimeToExt = mapMimeToExt(config.extraMimes)
|
|
151
|
-
return extraMimeToExt[m] || mimeToExt[m] || EXT_UNKNOWN_MIME
|
|
150
|
+
? mimeToExt.data[new MIMEType(mime).essence]
|
|
151
|
+
: ''
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -3,15 +3,19 @@ import { equal } from 'node:assert/strict'
|
|
|
3
3
|
import { extFor, mimeFor } from './mime.js'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
test('extFor', () =>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
test('extFor', () => {
|
|
7
|
+
[
|
|
8
|
+
'text/html',
|
|
9
|
+
'Text/html',
|
|
10
|
+
'text/Html; charset=UTF-16'
|
|
11
|
+
].map(input =>
|
|
12
|
+
equal(extFor(input), 'html'))
|
|
13
|
+
})
|
|
12
14
|
|
|
13
|
-
test('mimeFor', () =>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
test('mimeFor', () => {
|
|
16
|
+
[
|
|
17
|
+
'file.html',
|
|
18
|
+
'file.HTmL'
|
|
19
|
+
].map(input =>
|
|
20
|
+
equal(mimeFor(input), 'text/html'))
|
|
21
|
+
})
|