mockaton 11.2.5 → 11.3.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 +15 -841
- package/index.js +1 -1
- package/package.json +7 -1
- package/src/client/ApiCommander.js +54 -39
- package/src/client/ApiConstants.js +1 -1
- package/src/client/Filename.js +3 -3
- package/src/client/app-store.test.js +81 -0
- package/src/client/app.js +83 -68
- package/src/client/dom-utils.js +5 -3
- package/src/client/styles.css +83 -48
- package/src/server/Api.js +112 -152
- package/src/server/Filename.js +8 -6
- package/src/server/MockBroker.js +1 -1
- package/src/server/MockDispatcher.js +5 -5
- package/src/server/Mockaton.js +23 -20
- package/src/server/Mockaton.test.js +1190 -0
- package/src/server/ProxyRelay.js +5 -5
- package/src/server/StaticDispatcher.js +4 -4
- package/src/server/Watcher.js +30 -3
- package/src/server/WatcherDevClient.js +37 -5
- package/src/server/cli.js +1 -0
- package/src/server/config.js +3 -2
- package/src/server/mockBrokersCollection.js +2 -1
- package/src/server/utils/{http-request.js → HttpIncomingMessage.js} +7 -1
- package/src/server/utils/HttpServerResponse.js +117 -0
- package/src/server/utils/fs.js +1 -0
- package/src/server/utils/http-cors.js +1 -1
- package/src/server/utils/http-cors.test.js +225 -0
- package/src/server/utils/logger.js +1 -1
- package/src/server/utils/mime.test.js +24 -0
- package/src/server/utils/validate.test.js +47 -0
- package/src/server/utils/http-response.js +0 -107
package/src/client/styles.css
CHANGED
|
@@ -76,14 +76,12 @@ body {
|
|
|
76
76
|
scrollbar-width: thin;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
a,
|
|
80
|
-
input,
|
|
81
|
-
select,
|
|
82
|
-
button {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
outline: 2px solid var(--colorAccent);
|
|
86
|
-
}
|
|
79
|
+
a:focus-visible,
|
|
80
|
+
input:focus-visible,
|
|
81
|
+
select:focus-visible,
|
|
82
|
+
button:focus-visible {
|
|
83
|
+
outline-offset: -1px;
|
|
84
|
+
outline: 2px solid var(--colorAccent);
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
a,
|
|
@@ -91,10 +89,13 @@ select,
|
|
|
91
89
|
button,
|
|
92
90
|
input[type=checkbox] {
|
|
93
91
|
cursor: pointer;
|
|
92
|
+
}
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
a:active,
|
|
95
|
+
select:active,
|
|
96
|
+
button:active,
|
|
97
|
+
input[type=checkbox]:active {
|
|
98
|
+
cursor: grabbing;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
a {
|
|
@@ -132,10 +133,14 @@ header {
|
|
|
132
133
|
border-bottom: 1px solid var(--colorSecondaryActionBorder);
|
|
133
134
|
background: var(--colorHeaderBackground);
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
.Logo {
|
|
136
137
|
align-self: end;
|
|
137
138
|
margin-right: 22px;
|
|
138
|
-
margin-bottom:
|
|
139
|
+
margin-bottom: 3px;
|
|
140
|
+
|
|
141
|
+
object {
|
|
142
|
+
pointer-events: none;
|
|
143
|
+
}
|
|
139
144
|
}
|
|
140
145
|
|
|
141
146
|
> div {
|
|
@@ -231,14 +236,14 @@ header {
|
|
|
231
236
|
font-size: 11px;
|
|
232
237
|
gap: 4px;
|
|
233
238
|
|
|
234
|
-
input +
|
|
239
|
+
input + .checkboxBody {
|
|
235
240
|
margin: 0;
|
|
236
241
|
margin-right: 4px;
|
|
237
242
|
}
|
|
238
|
-
input:enabled +
|
|
243
|
+
input:enabled + .checkboxBody {
|
|
239
244
|
cursor: pointer;
|
|
240
245
|
}
|
|
241
|
-
input:disabled +
|
|
246
|
+
input:disabled + .checkboxBody {
|
|
242
247
|
opacity: 0.8;
|
|
243
248
|
}
|
|
244
249
|
}
|
|
@@ -369,24 +374,40 @@ main {
|
|
|
369
374
|
}
|
|
370
375
|
}
|
|
371
376
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
th {
|
|
377
|
+
.Table {
|
|
378
|
+
.TableHeading {
|
|
376
379
|
padding-bottom: 4px;
|
|
377
380
|
padding-left: 4px;
|
|
378
381
|
border-top: 24px solid transparent;
|
|
382
|
+
margin-left: 74px;
|
|
383
|
+
font-weight: bold;
|
|
379
384
|
text-align: left;
|
|
380
|
-
}
|
|
381
385
|
|
|
382
|
-
|
|
383
|
-
|
|
386
|
+
&:first-of-type {
|
|
387
|
+
border-top: 0
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
&.canProxy {
|
|
391
|
+
margin-left: 110px;
|
|
392
|
+
}
|
|
393
|
+
&.nonGroupedByMethod {
|
|
394
|
+
margin-left: 122px;
|
|
395
|
+
|
|
396
|
+
&.canProxy {
|
|
397
|
+
margin-left: 160px;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
384
400
|
}
|
|
385
401
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
402
|
+
.TableRow {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
|
|
406
|
+
&.animIn {
|
|
407
|
+
opacity: 0;
|
|
408
|
+
transform: scaleY(0);
|
|
409
|
+
animation: _kfRowIn 180ms ease-in-out forwards;
|
|
410
|
+
}
|
|
390
411
|
}
|
|
391
412
|
}
|
|
392
413
|
|
|
@@ -398,10 +419,15 @@ table {
|
|
|
398
419
|
}
|
|
399
420
|
|
|
400
421
|
.Method {
|
|
401
|
-
|
|
422
|
+
overflow: hidden;
|
|
423
|
+
min-width: 38px;
|
|
424
|
+
padding: 4px 0;
|
|
425
|
+
margin-right: 8px;
|
|
402
426
|
color: var(--colorSecondaryAction);
|
|
403
|
-
text-align: center;
|
|
404
427
|
font-size: 11px;
|
|
428
|
+
text-align: center;
|
|
429
|
+
white-space: nowrap;
|
|
430
|
+
text-overflow: ellipsis;
|
|
405
431
|
}
|
|
406
432
|
|
|
407
433
|
.PreviewLink {
|
|
@@ -416,6 +442,7 @@ table {
|
|
|
416
442
|
color: var(--colorAccent);
|
|
417
443
|
word-break: break-word;
|
|
418
444
|
|
|
445
|
+
|
|
419
446
|
&:hover {
|
|
420
447
|
background: var(--colorHover);
|
|
421
448
|
}
|
|
@@ -503,6 +530,10 @@ table {
|
|
|
503
530
|
stroke-width: 2.5px;
|
|
504
531
|
border-radius: 50%;
|
|
505
532
|
}
|
|
533
|
+
|
|
534
|
+
&.canProxy {
|
|
535
|
+
margin-left: 36px;
|
|
536
|
+
}
|
|
506
537
|
}
|
|
507
538
|
|
|
508
539
|
.ProxyToggler {
|
|
@@ -565,30 +596,30 @@ table {
|
|
|
565
596
|
|
|
566
597
|
&:focus-visible {
|
|
567
598
|
outline: 0;
|
|
568
|
-
& +
|
|
599
|
+
& + .checkboxBody {
|
|
569
600
|
outline: 2px solid var(--colorAccent)
|
|
570
601
|
}
|
|
571
602
|
}
|
|
572
603
|
|
|
573
|
-
&:disabled +
|
|
604
|
+
&:disabled + .checkboxBody {
|
|
574
605
|
cursor: not-allowed;
|
|
575
606
|
opacity: 0.7;
|
|
576
607
|
}
|
|
577
|
-
&:checked +
|
|
608
|
+
&:checked + .checkboxBody {
|
|
578
609
|
border-color: var(--colorRed);
|
|
579
610
|
color: white;
|
|
580
611
|
background: var(--colorRed);
|
|
581
612
|
}
|
|
582
|
-
&:not(:checked):enabled:hover +
|
|
613
|
+
&:not(:checked):enabled:hover + .checkboxBody {
|
|
583
614
|
border-color: var(--colorRed);
|
|
584
615
|
color: var(--colorRed);
|
|
585
616
|
}
|
|
586
|
-
&:enabled:active +
|
|
617
|
+
&:enabled:active + .checkboxBody {
|
|
587
618
|
cursor: grabbing;
|
|
588
619
|
}
|
|
589
620
|
}
|
|
590
621
|
|
|
591
|
-
>
|
|
622
|
+
> .checkboxBody {
|
|
592
623
|
padding: 4px;
|
|
593
624
|
border: 1px solid var(--colorSecondaryActionBorder);
|
|
594
625
|
font-size: 10px;
|
|
@@ -621,23 +652,27 @@ table {
|
|
|
621
652
|
white-space: pre;
|
|
622
653
|
tab-size: 2;
|
|
623
654
|
color: var(--colorSecondaryAction);
|
|
624
|
-
|
|
625
|
-
.syntaxPunc {
|
|
626
|
-
color: var(--colorSecondaryAction);
|
|
627
|
-
}
|
|
628
|
-
.syntaxKey, .syntaxTag {
|
|
629
|
-
color: var(--colorPink);
|
|
630
|
-
}
|
|
631
|
-
.syntaxVal, .syntaxAttr {
|
|
632
|
-
color: var(--colorPurple);
|
|
633
|
-
}
|
|
634
|
-
.syntaxStr, .syntaxAttrVal {
|
|
635
|
-
color: var(--colorGreen);
|
|
636
|
-
}
|
|
637
655
|
}
|
|
638
656
|
}
|
|
639
657
|
}
|
|
640
658
|
|
|
659
|
+
.syntaxPunc {
|
|
660
|
+
color: var(--colorSecondaryAction);
|
|
661
|
+
}
|
|
662
|
+
.syntaxKey,
|
|
663
|
+
.syntaxTag {
|
|
664
|
+
color: var(--colorPink);
|
|
665
|
+
}
|
|
666
|
+
.syntaxVal,
|
|
667
|
+
.syntaxAttr {
|
|
668
|
+
color: var(--colorPurple);
|
|
669
|
+
}
|
|
670
|
+
.syntaxStr,
|
|
671
|
+
.syntaxAttrVal {
|
|
672
|
+
color: var(--colorGreen);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
|
|
641
676
|
.ProgressBar {
|
|
642
677
|
position: relative;
|
|
643
678
|
top: -4px;
|
package/src/server/Api.js
CHANGED
|
@@ -4,54 +4,52 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { join } from 'node:path'
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
longPollDevClientHotReload,
|
|
10
|
+
DASHBOARD_ASSETS,
|
|
11
|
+
CLIENT_DIR
|
|
12
|
+
} from './WatcherDevClient.js'
|
|
13
|
+
import { longPollClientSyncVersion } from './Watcher.js'
|
|
8
14
|
|
|
9
15
|
import { IndexHtml, CSP } from '../client/indexHtml.js'
|
|
10
16
|
|
|
17
|
+
import { API } from './ApiConstants.js'
|
|
11
18
|
import { cookie } from './cookie.js'
|
|
12
19
|
import { config, ConfigValidator } from './config.js'
|
|
13
20
|
|
|
14
|
-
import { uiSyncVersion } from './Watcher.js'
|
|
15
|
-
import { devClientWatcher } from './WatcherDevClient.js'
|
|
16
|
-
|
|
17
21
|
import * as staticCollection from './staticCollection.js'
|
|
18
22
|
import * as mockBrokersCollection from './mockBrokersCollection.js'
|
|
19
23
|
|
|
20
|
-
import { parseJSON } from './utils/http-request.js'
|
|
21
|
-
import { sendOK, sendJSON, sendUnprocessable, sendFile, sendHTML } from './utils/http-response.js'
|
|
22
|
-
import { API, LONG_POLL_SERVER_TIMEOUT, HEADER_SYNC_VERSION } from './ApiConstants.js'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const DEV = process.env.NODE_ENV === 'development'
|
|
26
|
-
const CLIENT_DIR = join(import.meta.dirname, '../client')
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
export const apiGetRequests = new Map([
|
|
25
|
+
export const apiGetReqs = new Map([
|
|
30
26
|
[API.dashboard, serveDashboard],
|
|
31
|
-
...
|
|
32
|
-
.map(f => [API.dashboard + '/' + f, serveStatic(f)]),
|
|
27
|
+
...DASHBOARD_ASSETS.map(f => [API.dashboard + '/' + f, serveStatic(f)]),
|
|
33
28
|
|
|
34
29
|
[API.state, getState],
|
|
35
30
|
[API.syncVersion, longPollClientSyncVersion],
|
|
31
|
+
|
|
32
|
+
[API.watchHotReload, longPollDevClientHotReload],
|
|
33
|
+
[API.throws, () => { throw new Error('Test500') }]
|
|
36
34
|
])
|
|
37
|
-
if (DEV) {
|
|
38
|
-
apiGetRequests.set(API.throws, () => { throw new Error('Test500') })
|
|
39
|
-
apiGetRequests.set(API.watchHotReload, longPollDevHotReload)
|
|
40
|
-
}
|
|
41
35
|
|
|
42
36
|
|
|
43
|
-
export const
|
|
37
|
+
export const apiPatchReqs = new Map([
|
|
44
38
|
[API.cors, setCorsAllowed],
|
|
45
|
-
[API.delay, setRouteIsDelayed],
|
|
46
39
|
[API.reset, reinitialize],
|
|
47
|
-
[API.select, selectMock],
|
|
48
|
-
[API.proxied, setRouteIsProxied],
|
|
49
40
|
[API.cookies, selectCookie],
|
|
50
|
-
[API.fallback, updateProxyFallback],
|
|
51
|
-
[API.toggle500, toggle500],
|
|
52
|
-
[API.bulkSelect, bulkUpdateBrokersByCommentTag],
|
|
53
41
|
[API.globalDelay, setGlobalDelay],
|
|
42
|
+
|
|
43
|
+
[API.fallback, setProxyFallback],
|
|
54
44
|
[API.collectProxied, setCollectProxied],
|
|
45
|
+
|
|
46
|
+
[API.bulkSelect, bulkUpdateBrokersByCommentTag],
|
|
47
|
+
|
|
48
|
+
[API.delay, setRouteIsDelayed],
|
|
49
|
+
[API.select, selectMock],
|
|
50
|
+
[API.proxied, setRouteIsProxied],
|
|
51
|
+
[API.toggle500, toggleRoute500],
|
|
52
|
+
|
|
55
53
|
[API.delayStatic, setStaticRouteIsDelayed],
|
|
56
54
|
[API.staticStatus, setStaticRouteStatusCode]
|
|
57
55
|
])
|
|
@@ -60,16 +58,17 @@ export const apiPatchRequests = new Map([
|
|
|
60
58
|
/** # GET */
|
|
61
59
|
|
|
62
60
|
function serveDashboard(_, response) {
|
|
63
|
-
|
|
61
|
+
response.html(IndexHtml(config.hotReload), CSP)
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
function serveStatic(f) {
|
|
67
|
-
return (_, response) =>
|
|
65
|
+
return (_, response) => {
|
|
66
|
+
response.file(join(CLIENT_DIR, f))
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
71
70
|
function getState(_, response) {
|
|
72
|
-
|
|
71
|
+
response.json({
|
|
73
72
|
cookies: cookie.list(),
|
|
74
73
|
comments: mockBrokersCollection.extractAllComments(),
|
|
75
74
|
|
|
@@ -86,205 +85,166 @@ function getState(_, response) {
|
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
function onAddOrRemoveMock() {
|
|
97
|
-
uiSyncVersion.unsubscribe(onAddOrRemoveMock)
|
|
98
|
-
sendJSON(response, uiSyncVersion.version)
|
|
99
|
-
}
|
|
100
|
-
response.setTimeout(LONG_POLL_SERVER_TIMEOUT, onAddOrRemoveMock)
|
|
101
|
-
req.on('error', () => {
|
|
102
|
-
uiSyncVersion.unsubscribe(onAddOrRemoveMock)
|
|
103
|
-
response.destroy()
|
|
104
|
-
})
|
|
105
|
-
uiSyncVersion.subscribe(onAddOrRemoveMock)
|
|
88
|
+
/** # PATCH */
|
|
89
|
+
|
|
90
|
+
function reinitialize(_, response) {
|
|
91
|
+
mockBrokersCollection.init()
|
|
92
|
+
staticCollection.init()
|
|
93
|
+
response.ok()
|
|
106
94
|
}
|
|
107
95
|
|
|
108
96
|
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
async function setCorsAllowed(req, response) {
|
|
98
|
+
const corsAllowed = await req.json()
|
|
99
|
+
|
|
100
|
+
if (!ConfigValidator.corsAllowed(corsAllowed))
|
|
101
|
+
response.unprocessable(`Expected boolean for "corsAllowed"`)
|
|
102
|
+
else {
|
|
103
|
+
config.corsAllowed = corsAllowed
|
|
104
|
+
response.ok()
|
|
113
105
|
}
|
|
114
|
-
response.setTimeout(LONG_POLL_SERVER_TIMEOUT, () => {
|
|
115
|
-
devClientWatcher.unsubscribe(onDevChange)
|
|
116
|
-
sendJSON(response, '')
|
|
117
|
-
})
|
|
118
|
-
req.on('error', () => {
|
|
119
|
-
devClientWatcher.unsubscribe(onDevChange)
|
|
120
|
-
response.destroy()
|
|
121
|
-
})
|
|
122
|
-
devClientWatcher.subscribe(onDevChange)
|
|
123
106
|
}
|
|
124
107
|
|
|
125
108
|
|
|
109
|
+
async function setGlobalDelay(req, response) {
|
|
110
|
+
const delay = await req.json()
|
|
126
111
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
112
|
+
if (!ConfigValidator.delay(delay))
|
|
113
|
+
response.unprocessable(`Expected non-negative integer for "delay"`)
|
|
114
|
+
else {
|
|
115
|
+
config.delay = delay
|
|
116
|
+
response.ok()
|
|
117
|
+
}
|
|
133
118
|
}
|
|
134
119
|
|
|
135
120
|
|
|
136
121
|
async function selectCookie(req, response) {
|
|
137
|
-
const cookieKey = await
|
|
122
|
+
const cookieKey = await req.json()
|
|
138
123
|
|
|
139
124
|
const error = cookie.setCurrent(cookieKey)
|
|
140
125
|
if (error)
|
|
141
|
-
|
|
126
|
+
response.unprocessable(error?.message || error)
|
|
142
127
|
else
|
|
143
|
-
|
|
128
|
+
response.json(cookie.list())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
async function setProxyFallback(req, response) {
|
|
133
|
+
const fallback = await req.json()
|
|
134
|
+
|
|
135
|
+
if (!ConfigValidator.proxyFallback(fallback))
|
|
136
|
+
response.unprocessable(`Invalid Proxy Fallback URL`)
|
|
137
|
+
else {
|
|
138
|
+
config.proxyFallback = fallback
|
|
139
|
+
response.ok()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function setCollectProxied(req, response) {
|
|
144
|
+
const collectProxied = await req.json()
|
|
145
|
+
|
|
146
|
+
if (!ConfigValidator.collectProxied(collectProxied))
|
|
147
|
+
response.unprocessable(`Expected a boolean for "collectProxied"`)
|
|
148
|
+
else {
|
|
149
|
+
config.collectProxied = collectProxied
|
|
150
|
+
response.ok()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
async function bulkUpdateBrokersByCommentTag(req, response) {
|
|
157
|
+
const comment = await req.json()
|
|
158
|
+
|
|
159
|
+
mockBrokersCollection.setMocksMatchingComment(comment)
|
|
160
|
+
response.ok()
|
|
144
161
|
}
|
|
145
162
|
|
|
146
163
|
|
|
147
164
|
async function selectMock(req, response) {
|
|
148
|
-
const file = await
|
|
165
|
+
const file = await req.json()
|
|
149
166
|
|
|
150
167
|
const broker = mockBrokersCollection.brokerByFilename(file)
|
|
151
168
|
if (!broker || !broker.hasMock(file))
|
|
152
|
-
|
|
169
|
+
response.unprocessable(`Missing Mock: ${file}`)
|
|
153
170
|
else {
|
|
154
171
|
broker.selectFile(file)
|
|
155
|
-
|
|
172
|
+
response.json(broker)
|
|
156
173
|
}
|
|
157
174
|
}
|
|
158
175
|
|
|
159
176
|
|
|
160
|
-
async function
|
|
161
|
-
const [method, urlMask] = await
|
|
177
|
+
async function toggleRoute500(req, response) {
|
|
178
|
+
const [method, urlMask] = await req.json()
|
|
162
179
|
|
|
163
180
|
const broker = mockBrokersCollection.brokerByRoute(method, urlMask)
|
|
164
181
|
if (!broker)
|
|
165
|
-
|
|
182
|
+
response.unprocessable(`Route does not exist: ${method} ${urlMask}`)
|
|
166
183
|
else {
|
|
167
184
|
broker.toggle500()
|
|
168
|
-
|
|
185
|
+
response.json(broker)
|
|
169
186
|
}
|
|
170
187
|
}
|
|
171
188
|
|
|
172
189
|
|
|
173
190
|
async function setRouteIsDelayed(req, response) {
|
|
174
|
-
const [method, urlMask, delayed] = await
|
|
191
|
+
const [method, urlMask, delayed] = await req.json()
|
|
175
192
|
|
|
176
193
|
const broker = mockBrokersCollection.brokerByRoute(method, urlMask)
|
|
177
194
|
if (!broker)
|
|
178
|
-
|
|
195
|
+
response.unprocessable(`Route does not exist: ${method} ${urlMask}`)
|
|
179
196
|
else if (typeof delayed !== 'boolean')
|
|
180
|
-
|
|
197
|
+
response.unprocessable(`Expected boolean for "delayed"`)
|
|
181
198
|
else {
|
|
182
199
|
broker.setDelayed(delayed)
|
|
183
|
-
|
|
200
|
+
response.json(broker)
|
|
184
201
|
}
|
|
185
202
|
}
|
|
186
203
|
|
|
187
204
|
|
|
188
205
|
async function setRouteIsProxied(req, response) {
|
|
189
|
-
const [method, urlMask, proxied] = await
|
|
206
|
+
const [method, urlMask, proxied] = await req.json()
|
|
190
207
|
|
|
191
208
|
const broker = mockBrokersCollection.brokerByRoute(method, urlMask)
|
|
192
209
|
if (!broker)
|
|
193
|
-
|
|
210
|
+
response.unprocessable( `Route does not exist: ${method} ${urlMask}`)
|
|
194
211
|
else if (typeof proxied !== 'boolean')
|
|
195
|
-
|
|
212
|
+
response.unprocessable(`Expected boolean for "proxied"`)
|
|
196
213
|
else if (proxied && !config.proxyFallback)
|
|
197
|
-
|
|
214
|
+
response.unprocessable(`There’s no proxy fallback`)
|
|
198
215
|
else {
|
|
199
216
|
broker.setProxied(proxied)
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
async function updateProxyFallback(req, response) {
|
|
206
|
-
const fallback = await parseJSON(req)
|
|
207
|
-
|
|
208
|
-
if (!ConfigValidator.proxyFallback(fallback))
|
|
209
|
-
sendUnprocessable(response, `Invalid Proxy Fallback URL`)
|
|
210
|
-
else {
|
|
211
|
-
config.proxyFallback = fallback
|
|
212
|
-
sendOK(response)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
async function setCollectProxied(req, response) {
|
|
218
|
-
const collectProxied = await parseJSON(req)
|
|
219
|
-
|
|
220
|
-
if (!ConfigValidator.collectProxied(collectProxied))
|
|
221
|
-
sendUnprocessable(response, `Expected a boolean for "collectProxied"`)
|
|
222
|
-
else {
|
|
223
|
-
config.collectProxied = collectProxied
|
|
224
|
-
sendOK(response)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
async function bulkUpdateBrokersByCommentTag(req, response) {
|
|
230
|
-
const comment = await parseJSON(req)
|
|
231
|
-
|
|
232
|
-
mockBrokersCollection.setMocksMatchingComment(comment)
|
|
233
|
-
sendOK(response)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
async function setCorsAllowed(req, response) {
|
|
238
|
-
const corsAllowed = await parseJSON(req)
|
|
239
|
-
|
|
240
|
-
if (!ConfigValidator.corsAllowed(corsAllowed))
|
|
241
|
-
sendUnprocessable(response, `Expected boolean for "corsAllowed"`)
|
|
242
|
-
else {
|
|
243
|
-
config.corsAllowed = corsAllowed
|
|
244
|
-
sendOK(response)
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
async function setGlobalDelay(req, response) {
|
|
250
|
-
const delay = await parseJSON(req)
|
|
251
|
-
|
|
252
|
-
if (!ConfigValidator.delay(delay))
|
|
253
|
-
sendUnprocessable(response, `Expected non-negative integer for "delay"`)
|
|
254
|
-
else {
|
|
255
|
-
config.delay = delay
|
|
256
|
-
sendOK(response)
|
|
217
|
+
response.json(broker)
|
|
257
218
|
}
|
|
258
219
|
}
|
|
259
220
|
|
|
260
221
|
|
|
261
222
|
|
|
262
223
|
async function setStaticRouteStatusCode(req, response) {
|
|
263
|
-
const [
|
|
224
|
+
const [route, status] = await req.json()
|
|
264
225
|
|
|
265
|
-
const broker = staticCollection.brokerByRoute(
|
|
226
|
+
const broker = staticCollection.brokerByRoute(route)
|
|
266
227
|
if (!broker)
|
|
267
|
-
|
|
228
|
+
response.unprocessable(`Static route does not exist: ${route}`)
|
|
268
229
|
else if (!(status === 200 || status === 404))
|
|
269
|
-
|
|
230
|
+
response.unprocessable(`Expected 200 or 404 status code`)
|
|
270
231
|
else {
|
|
271
232
|
broker.setStatus(status)
|
|
272
|
-
|
|
233
|
+
response.ok()
|
|
273
234
|
}
|
|
274
235
|
}
|
|
275
236
|
|
|
276
237
|
|
|
277
238
|
async function setStaticRouteIsDelayed(req, response) {
|
|
278
|
-
const [
|
|
239
|
+
const [route, delayed] = await req.json()
|
|
279
240
|
|
|
280
|
-
const broker = staticCollection.brokerByRoute(
|
|
241
|
+
const broker = staticCollection.brokerByRoute(route)
|
|
281
242
|
if (!broker)
|
|
282
|
-
|
|
243
|
+
response.unprocessable(`Static route does not exist: ${route}`)
|
|
283
244
|
else if (typeof delayed !== 'boolean')
|
|
284
|
-
|
|
245
|
+
response.unprocessable(`Expected boolean for "delayed"`)
|
|
285
246
|
else {
|
|
286
247
|
broker.setDelayed(delayed)
|
|
287
|
-
|
|
248
|
+
response.ok()
|
|
288
249
|
}
|
|
289
250
|
}
|
|
290
|
-
|
package/src/server/Filename.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/** @KeepSync src/client/Filename.js */
|
|
2
2
|
|
|
3
3
|
import { METHODS } from 'node:http'
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
const reComments = /\(.*?\)/g // Anything within parentheses
|
|
7
7
|
|
|
8
|
-
export
|
|
9
|
-
Array.from(file.matchAll(reComments), ([c]) => c)
|
|
8
|
+
export function extractComments(file) {
|
|
9
|
+
return Array.from(file.matchAll(reComments), ([c]) => c)
|
|
10
|
+
}
|
|
10
11
|
|
|
11
|
-
export
|
|
12
|
-
extractComments(file).some(c => c.includes(search))
|
|
12
|
+
export function includesComment(file, search) {
|
|
13
|
+
return extractComments(file).some(c => c.includes(search))
|
|
14
|
+
}
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
export function validateFilename(file) {
|
|
@@ -23,7 +25,7 @@ export function validateFilename(file) {
|
|
|
23
25
|
|
|
24
26
|
if (!METHODS.includes(method))
|
|
25
27
|
return `Unrecognized HTTP Method: "${method}"`
|
|
26
|
-
}
|
|
28
|
+
}
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
export function parseFilename(file) {
|
package/src/server/MockBroker.js
CHANGED
|
@@ -3,12 +3,12 @@ import { readFileSync } from 'node:fs'
|
|
|
3
3
|
import { pathToFileURL } from 'node:url'
|
|
4
4
|
|
|
5
5
|
import { logger } from './utils/logger.js'
|
|
6
|
+
import { mimeFor } from './utils/mime.js'
|
|
7
|
+
|
|
6
8
|
import { proxy } from './ProxyRelay.js'
|
|
7
9
|
import { cookie } from './cookie.js'
|
|
8
|
-
import { mimeFor } from './utils/mime.js'
|
|
9
10
|
import { brokerByRoute } from './mockBrokersCollection.js'
|
|
10
11
|
import { config, calcDelay } from './config.js'
|
|
11
|
-
import { sendInternalServerError, sendMockNotFound } from './utils/http-response.js'
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
export async function dispatchMock(req, response) {
|
|
@@ -24,7 +24,7 @@ export async function dispatchMock(req, response) {
|
|
|
24
24
|
return
|
|
25
25
|
}
|
|
26
26
|
if (!broker) {
|
|
27
|
-
|
|
27
|
+
response.mockNotFound()
|
|
28
28
|
return
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -51,9 +51,9 @@ export async function dispatchMock(req, response) {
|
|
|
51
51
|
}
|
|
52
52
|
catch (error) { // TESTME
|
|
53
53
|
if (error?.code === 'ENOENT') // mock-file has been deleted
|
|
54
|
-
|
|
54
|
+
response.mockNotFound()
|
|
55
55
|
else
|
|
56
|
-
|
|
56
|
+
response.internalServerError(error)
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|