mockaton 8.16.1 → 8.16.3

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "mockaton",
3
3
  "description": "HTTP Mock Server",
4
4
  "type": "module",
5
- "version": "8.16.1",
5
+ "version": "8.16.3",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
package/src/Api.js CHANGED
@@ -25,7 +25,9 @@ const dashboardAssets = [
25
25
 
26
26
  export const apiGetRequests = new Map([
27
27
  [API.dashboard, serveDashboard],
28
- ...dashboardAssets.map(f => [API.dashboard + f, serveDashboardAsset(f)]),
28
+ ...dashboardAssets.map(f => [
29
+ API.dashboard + f, serveDashboardAsset(f)
30
+ ]),
29
31
  [API.cors, getIsCorsAllowed],
30
32
  [API.static, listStaticFiles],
31
33
  [API.mocks, listMockBrokers],
@@ -49,7 +51,7 @@ export const apiPatchRequests = new Map([
49
51
  [API.globalDelay, setGlobalDelay],
50
52
  [API.collectProxied, setCollectProxied],
51
53
  [API.delayStatic, setStaticRouteIsDelayed],
52
- [API.notFoundStatic, setStaticRouteIsNotFound]
54
+ [API.staticStatus, setStaticRouteStatusCode]
53
55
  ])
54
56
 
55
57
 
@@ -66,7 +68,7 @@ function serveDashboardAsset(f) {
66
68
 
67
69
  function listCookies(_, response) { sendJSON(response, cookie.list()) }
68
70
  function listComments(_, response) { sendJSON(response, mockBrokersCollection.extractAllComments()) }
69
- function listStaticFiles(req, response) { sendJSON(response, getStaticFilesCollection()) }
71
+ function listStaticFiles(_, response) { sendJSON(response, getStaticFilesCollection()) }
70
72
  function getGlobalDelay(_, response) { sendJSON(response, config.delay) }
71
73
  function listMockBrokers(_, response) { sendJSON(response, mockBrokersCollection.getAll()) }
72
74
  function getProxyFallback(_, response) { sendJSON(response, config.proxyFallback) }
@@ -86,12 +88,10 @@ function longPollClientSyncVersion(req, response) {
86
88
  }
87
89
 
88
90
  response.setTimeout(LONG_POLL_SERVER_TIMEOUT, onAddOrRemoveMock)
89
-
90
91
  req.on('error', () => {
91
92
  uiSyncVersion.unsubscribe(onAddOrRemoveMock)
92
93
  response.destroy()
93
94
  })
94
-
95
95
  uiSyncVersion.subscribe(onAddOrRemoveMock)
96
96
  }
97
97
 
@@ -136,7 +136,7 @@ async function setRouteIsDelayed(req, response) {
136
136
  else if (typeof delayed !== 'boolean')
137
137
  sendUnprocessableContent(response, `Expected a boolean for "delayed"`) // TESTME
138
138
  else {
139
- broker.updateDelayed(body[DF.delayed])
139
+ broker.setDelayed(delayed)
140
140
  sendOK(response)
141
141
  }
142
142
  }
@@ -155,7 +155,7 @@ async function setRouteIsProxied(req, response) { // TESTME
155
155
  else if (proxied && !config.proxyFallback)
156
156
  sendUnprocessableContent(response, `There’s no proxy fallback`)
157
157
  else {
158
- broker.updateProxied(proxied)
158
+ broker.setProxied(proxied)
159
159
  sendOK(response)
160
160
  }
161
161
  }
@@ -198,17 +198,17 @@ async function setGlobalDelay(req, response) { // TESTME
198
198
  }
199
199
 
200
200
 
201
- async function setStaticRouteIsNotFound(req, response) {
201
+ async function setStaticRouteStatusCode(req, response) {
202
202
  const body = await parseJSON(req)
203
- const shouldBeNotFound = body[DF.shouldBeNotFound]
203
+ const status = Number(body[DF.statusCode])
204
204
  const broker = findStaticBrokerByRoute(body[DF.routeUrlMask])
205
205
 
206
206
  if (!broker) // TESTME
207
207
  sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeUrlMask]}`)
208
- else if (typeof shouldBeNotFound !== 'boolean')
209
- sendUnprocessableContent(response, `Expected a boolean for "not found"`) // TESTME
208
+ else if (!(status === 200 || status === 404))
209
+ sendUnprocessableContent(response, `Expected a 200 or 404 status code. Received ${status}`) // TESTME
210
210
  else {
211
- broker.updateNotFound(body[DF.shouldBeNotFound])
211
+ broker.setStatus(status)
212
212
  sendOK(response)
213
213
  }
214
214
  }
@@ -216,15 +216,15 @@ async function setStaticRouteIsNotFound(req, response) {
216
216
 
217
217
  async function setStaticRouteIsDelayed(req, response) {
218
218
  const body = await parseJSON(req)
219
- const shouldBeNotFound = body[DF.delayed]
219
+ const delayed = body[DF.delayed]
220
220
  const broker = findStaticBrokerByRoute(body[DF.routeUrlMask])
221
221
 
222
222
  if (!broker) // TESTME
223
223
  sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeUrlMask]}`)
224
- else if (typeof shouldBeNotFound !== 'boolean')
225
- sendUnprocessableContent(response, `Expected a boolean for "delayed"`) // TESTME
224
+ else if (typeof delayed !== 'boolean')
225
+ sendUnprocessableContent(response, `Expected a boolean for "delayed". Received ${delayed}`) // TESTME
226
226
  else {
227
- broker.updateDelayed(body[DF.delayed])
227
+ broker.setDelayed(delayed)
228
228
  sendOK(response)
229
229
  }
230
230
  }
@@ -11,11 +11,11 @@ export const API = {
11
11
  fallback: MOUNT + '/fallback',
12
12
  globalDelay: MOUNT + '/global-delay',
13
13
  mocks: MOUNT + '/mocks',
14
- notFoundStatic: MOUNT + '/not-found-static',
15
14
  proxied: MOUNT + '/proxied',
16
15
  reset: MOUNT + '/reset',
17
16
  select: MOUNT + '/select',
18
17
  static: MOUNT + '/static',
18
+ staticStatus: MOUNT + '/static-status',
19
19
  syncVersion: MOUNT + '/sync_version'
20
20
  }
21
21
 
@@ -24,7 +24,7 @@ export const DF = { // Dashboard Fields (XHR)
24
24
  routeUrlMask: 'route_url_mask',
25
25
  delayed: 'delayed',
26
26
  proxied: 'proxied',
27
- shouldBeNotFound: 'should_be_not_found',
27
+ statusCode: 'status_code',
28
28
  syncVersion: 'last_received_sync_version'
29
29
  }
30
30
 
package/src/Commander.js CHANGED
@@ -44,10 +44,10 @@ export class Commander {
44
44
  })
45
45
  }
46
46
 
47
- setStaticRouteIs404(routeUrlMask, shouldBeNotFound) {
48
- return this.#patch(API.notFoundStatic, {
47
+ setStaticRouteStatus(routeUrlMask, status) {
48
+ return this.#patch(API.staticStatus, {
49
49
  [DF.routeUrlMask]: routeUrlMask,
50
- [DF.shouldBeNotFound]: shouldBeNotFound
50
+ [DF.statusCode]: status
51
51
  })
52
52
  }
53
53
 
package/src/Dashboard.css CHANGED
@@ -1,7 +1,6 @@
1
1
  :root {
2
- --boxShadow1: 0 2px 1px -1px rgba(0, 0, 0, 0.15), 0 1px 1px 0 rgba(0, 0, 0, 0.15), 0 1px 3px 0 rgba(0, 0, 0, 0.1);
2
+ --boxShadow1: 0 3px 1px -1px rgba(0, 0, 0, 0.04), 0 1px 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px 0 rgba(0, 0, 0, 0.1);
3
3
  --radius: 4px;
4
- --radiusSmall: 4px;
5
4
  }
6
5
 
7
6
  @media (prefers-color-scheme: light) {
@@ -12,9 +11,10 @@
12
11
  --colorBackground: #fff;
13
12
  --colorComboBoxHeaderBackground: #fff;
14
13
  --colorComboBoxBackground: #eee;
15
- --colorHeaderBackground: #eee;
16
- --colorSecondaryButtonBackground: #eee;
17
- --colorSecondaryAction: #555;
14
+ --colorHeaderBackground: #f0f0f0;
15
+ --colorSecondaryButtonBackground: #fcfcfc;
16
+ --colorSecondaryActionBorder: #ddd;
17
+ --colorSecondaryAction: #666;
18
18
  --colorDisabledMockSelector: #444;
19
19
  --colorHover: #dfefff;
20
20
  --colorLabel: #444;
@@ -31,6 +31,7 @@
31
31
  --colorHeaderBackground: #111;
32
32
  --colorComboBoxBackground: #2a2a2a;
33
33
  --colorSecondaryButtonBackground: #2a2a2a;
34
+ --colorSecondaryActionBorder: #333;
34
35
  --colorSecondaryAction: #999;
35
36
  --colorComboBoxHeaderBackground: #222;
36
37
  --colorDisabledMockSelector: #b9b9b9;
@@ -42,15 +43,22 @@
42
43
  }
43
44
  }
44
45
 
45
- html, body {
46
+ html,
47
+ body {
48
+ overflow: hidden;
49
+ height: 100%;
50
+ padding: 0;
46
51
  margin: 0;
52
+ background: var(--colorHeaderBackground);
47
53
  font-size: 12px;
48
54
  }
49
55
  body {
50
- padding: 16px;
56
+ display: grid;
57
+ grid-template-rows: auto 1fr;
51
58
  background: var(--colorBackground);
52
59
  color: var(--colorText);
53
60
  }
61
+
54
62
  * {
55
63
  box-sizing: border-box;
56
64
  padding: 0;
@@ -59,6 +67,7 @@ body {
59
67
  font-family: system-ui, sans-serif;
60
68
  font-size: 100%;
61
69
  outline: 0;
70
+ scrollbar-width: thin;
62
71
  }
63
72
 
64
73
  select, a, input, button, summary {
@@ -79,6 +88,7 @@ input[type=checkbox] ~ svg {
79
88
  }
80
89
 
81
90
  select {
91
+ border: 1px solid transparent;
82
92
  font-size: 100%;
83
93
  color: var(--colorText);
84
94
  cursor: pointer;
@@ -86,16 +96,17 @@ select {
86
96
  border-radius: var(--radius);
87
97
  appearance: none;
88
98
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23888888'><path d='M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z'/></svg>") no-repeat;
89
- background-color: var(--colorComboBoxBackground);
90
99
  background-size: 16px;
91
100
  background-position: 100% center;
92
101
 
93
102
  &:enabled {
94
- box-shadow: var(--boxShadow1);
95
- }
96
- &:enabled:hover {
97
- cursor: pointer;
98
- background-color: var(--colorHover);
103
+ border-color: var(--colorSecondaryActionBorder);
104
+ background-color: var(--colorSecondaryButtonBackground);
105
+ &:hover {
106
+ border-color: var(--colorHover);
107
+ cursor: pointer;
108
+ background-color: var(--colorHover);
109
+ }
99
110
  }
100
111
  &:disabled {
101
112
  cursor: not-allowed;
@@ -106,18 +117,19 @@ select {
106
117
  color: var(--colorRed);
107
118
  }
108
119
 
109
- .Header {
110
- position: fixed;
111
- z-index: 100;
112
- top: 0;
113
- left: 0;
120
+ header {
114
121
  display: flex;
115
122
  width: 100%;
116
- align-items: flex-end;
117
123
  padding: 16px;
118
124
  background: var(--colorHeaderBackground);
119
125
  box-shadow: var(--boxShadow1);
120
- gap: 10px;
126
+
127
+ > div {
128
+ display: flex;
129
+ flex-wrap: wrap;
130
+ align-items: flex-end;
131
+ gap: 10px;
132
+ }
121
133
 
122
134
  img {
123
135
  width: 130px;
@@ -151,11 +163,11 @@ select {
151
163
  width: 100%;
152
164
  height: 28px;
153
165
  padding: 4px 8px;
166
+ border: 0;
154
167
  border-right: 3px solid transparent;
155
168
  margin-top: 4px;
156
169
  color: var(--colorText);
157
170
  font-size: 11px;
158
- box-shadow: var(--boxShadow1);
159
171
  background-color: var(--colorComboBoxHeaderBackground);
160
172
  border-radius: var(--radius);
161
173
  }
@@ -219,56 +231,61 @@ select {
219
231
  &:hover {
220
232
  background: var(--colorRed);
221
233
  color: white;
222
- box-shadow: var(--boxShadow1);
223
234
  }
224
235
  }
225
236
  }
226
237
 
227
238
 
228
- .Main {
229
- display: flex;
239
+ main {
240
+ display: grid;
241
+ min-height: 0;
242
+ grid-template-columns: 50% 50%;
243
+
244
+ @media (max-width: 960px) {
245
+ grid-template-columns: 100%;
246
+ grid-template-rows: 50% 50%;
247
+ gap: 32px;
248
+ }
249
+
250
+ .leftSide {
251
+ padding: 16px;
252
+ padding-bottom: 0;
253
+ overflow-y: auto;
254
+ }
255
+
256
+ .rightSide {
257
+ padding-top: 16px;
258
+ padding-left: 16px;
259
+ overflow-y: auto;
260
+ }
230
261
  }
231
262
 
232
263
  table {
233
264
  border-collapse: collapse;
234
265
 
235
266
  th {
236
- padding-top: 20px;
237
267
  padding-bottom: 2px;
238
- padding-left: 99px;
268
+ padding-left: 110px;
239
269
  text-align: left;
240
270
  }
241
271
 
242
- tr {
243
- border-top: 2px solid transparent;
272
+ tbody {
273
+ border-bottom: 20px solid transparent;
244
274
  }
245
275
  }
246
276
 
247
- .MockList {
248
- display: flex;
249
- align-items: flex-start;
250
- margin-top: 64px;
251
-
252
- &.empty {
253
- margin-top: 80px;
254
- }
277
+ .empty {
278
+ margin-top: 80px;
255
279
  }
256
280
 
257
281
 
258
282
  .PayloadViewer {
259
- position: sticky;
260
- top: 62px;
261
- width: 50%;
262
- margin-top: 62px;
263
- margin-left: 20px;
264
-
265
- h2 {
266
- padding-top: 20px;
267
- }
268
-
283
+ /*h2 {*/
284
+ /* top: 0;*/
285
+ /* position: sticky;*/
286
+ /* background: var(--colorBackground);*/
287
+ /*}*/
269
288
  pre {
270
- overflow: auto;
271
- max-height: calc(100vh - 72px);
272
289
  padding-top: 12px;
273
290
 
274
291
  code {
@@ -285,20 +302,16 @@ table {
285
302
 
286
303
  .PreviewLink {
287
304
  position: relative;
288
- left: -6px;
305
+ left: -8px;
289
306
  display: inline-block;
290
- width: 272px;
291
- padding: 8px 6px;
307
+ width: 240px;
308
+ padding: 8px;
309
+ margin-left: 4px;
292
310
  border-radius: var(--radius);
293
311
  color: var(--colorAccent);
294
312
  text-decoration: none;
295
313
  word-break: break-word;
296
314
 
297
- span {
298
- opacity: 0.6;
299
- filter: saturate(0);
300
- }
301
-
302
315
  &:hover {
303
316
  background: var(--colorHover);
304
317
  }
@@ -309,11 +322,10 @@ table {
309
322
  }
310
323
 
311
324
  .MockSelector {
312
- width: 260px;
313
- height: 30px;
325
+ width: 100%;
326
+ height: 26px;
314
327
  padding-right: 5px;
315
328
  padding-left: 16px;
316
- border: 0;
317
329
  text-align: right;
318
330
  direction: rtl;
319
331
  text-overflow: ellipsis;
@@ -329,6 +341,7 @@ table {
329
341
  }
330
342
  &:disabled {
331
343
  padding-right: 4px;
344
+ border-color: transparent;
332
345
  appearance: none;
333
346
  background: transparent;
334
347
  cursor: default;
@@ -363,33 +376,32 @@ table {
363
376
  margin-left: 8px;
364
377
  > input {
365
378
  &:checked ~ svg {
379
+ border-color: var(--colorAccent);
366
380
  fill: var(--colorAccent);
367
381
  background: var(--colorAccent);
368
382
  stroke: var(--colorBackground);
369
383
  }
370
384
 
371
385
  &:enabled:hover:not(:checked) ~ svg {
372
- border-color: var(--colorText);
386
+ border-color: var(--colorHover);
373
387
  background: var(--colorHover);
374
388
  stroke: var(--colorText);
375
389
  }
376
390
  }
377
391
 
378
392
  > svg {
379
- width: 19px;
380
- height: 19px;
393
+ width: 22px;
394
+ height: 22px;
395
+ border: 1px solid var(--colorSecondaryActionBorder);
381
396
  stroke-width: 2.5px;
382
397
  border-radius: 50%;
383
- background: var(--colorSecondaryButtonBackground);
384
- box-shadow: var(--boxShadow1);
385
398
  }
386
399
  }
387
400
 
388
401
  .ProxyToggler {
389
402
  padding: 1px 3px;
390
- background: var(--colorSecondaryButtonBackground);
391
- border-radius: var(--radiusSmall);
392
- box-shadow: var(--boxShadow1);
403
+ border: 1px solid var(--colorSecondaryActionBorder);
404
+ border-radius: var(--radius);
393
405
 
394
406
  &:has(input:checked),
395
407
  &:has(input:disabled) {
@@ -414,7 +426,7 @@ table {
414
426
  }
415
427
 
416
428
  &:disabled ~ svg {
417
- stroke-opacity: 0.5;
429
+ stroke-opacity: 0.4;
418
430
  cursor: not-allowed;
419
431
  box-shadow: none;
420
432
  fill: transparent;
@@ -430,7 +442,7 @@ table {
430
442
  width: 18px;
431
443
  height: 18px;
432
444
  stroke-width: 2px;
433
- border-radius: var(--radiusSmall);
445
+ border-radius: var(--radius);
434
446
  }
435
447
  }
436
448
 
@@ -452,6 +464,7 @@ table {
452
464
  }
453
465
 
454
466
  &:checked ~ span {
467
+ border-color: var(--colorRed);
455
468
  color: white;
456
469
  background: var(--colorRed);
457
470
  }
@@ -459,14 +472,14 @@ table {
459
472
 
460
473
  > span {
461
474
  padding: 4px;
475
+ border: 1px solid var(--colorSecondaryActionBorder);
462
476
  font-size: 10px;
463
477
  font-weight: bold;
464
478
  color: var(--colorSecondaryAction);
465
- border-radius: var(--radiusSmall);
466
- background: var(--colorSecondaryButtonBackground);
467
- box-shadow: var(--boxShadow1);
479
+ border-radius: var(--radius);
468
480
 
469
481
  &:hover {
482
+ border-color: var(--colorLightRed);
470
483
  background: var(--colorLightRed);
471
484
  color: var(--colorRed);
472
485
  }
@@ -501,7 +514,6 @@ table {
501
514
 
502
515
 
503
516
  .StaticFilesList {
504
- margin-top: 8px;
505
517
 
506
518
  a {
507
519
  display: inline-block;
@@ -513,14 +525,14 @@ table {
513
525
  &:hover {
514
526
  text-decoration: underline;
515
527
  }
516
-
517
- span {
518
- opacity: 0.6;
519
- filter: saturate(0);
520
- }
521
528
  }
522
529
  }
523
530
 
531
+ .dittoDir {
532
+ opacity: 0.9;
533
+ filter: saturate(0);
534
+ }
535
+
524
536
 
525
537
  /*
526
538
  * Prism
package/src/Dashboard.js CHANGED
@@ -24,15 +24,16 @@ const Strings = {
24
24
  fallback_server_placeholder: 'Type Server Address',
25
25
  got: 'Got',
26
26
  internal_server_error: 'Internal Server Error',
27
- mock: 'Mock',
28
27
  no_mocks_found: 'No mocks found',
29
28
  not_found: 'Not Found',
30
29
  pick_comment: 'Pick Comment…',
30
+ preview: 'Preview',
31
31
  proxied: 'Proxied',
32
32
  proxy_toggler: 'Proxy Toggler',
33
33
  reset: 'Reset',
34
34
  save_proxied: 'Save Mocks',
35
- static_get: 'Static GET'
35
+ static_get: 'Static GET',
36
+ title: 'Mockaton'
36
37
  }
37
38
 
38
39
  const CSS = {
@@ -41,9 +42,9 @@ const CSS = {
41
42
  FallbackBackend: 'FallbackBackend',
42
43
  Field: 'Field',
43
44
  GlobalDelayField: 'GlobalDelayField',
44
- Header: 'Header',
45
45
  InternalServerErrorToggler: 'InternalServerErrorToggler',
46
- Main: 'Main',
46
+ MainLeftSide: 'leftSide',
47
+ MainRightSide: 'rightSide',
47
48
  MockList: 'MockList',
48
49
  MockSelector: 'MockSelector',
49
50
  NotFoundToggler: 'NotFoundToggler',
@@ -57,21 +58,20 @@ const CSS = {
57
58
  SpinnerClockMinuteHand: 'MinuteHand',
58
59
  StaticFilesList: 'StaticFilesList',
59
60
 
60
- red: 'red',
61
- empty: 'empty',
62
61
  chosen: 'chosen',
63
- status4xx: 'status4xx',
64
- nonDefault: 'nonDefault'
62
+ dittoDir: 'dittoDir',
63
+ empty: 'empty',
64
+ nonDefault: 'nonDefault',
65
+ red: 'red',
66
+ status4xx: 'status4xx'
65
67
  }
66
68
 
67
69
  const r = createElement
68
70
  const s = createSvgElement
69
- const mockaton = new Commander(window.location.origin)
70
71
 
71
- const SPINNER_DELAY = 180
72
+ const mockaton = new Commander(window.location.origin)
72
73
  let globalDelay = 1200
73
74
 
74
-
75
75
  init()
76
76
  initLongPoll()
77
77
 
@@ -85,20 +85,20 @@ function init() {
85
85
  mockaton.getProxyFallback(),
86
86
  mockaton.listStaticFiles()
87
87
  ].map(api => api.then(response => response.ok && response.json())))
88
- .then(data => document.body.replaceChildren(App(data)))
88
+ .then(data => document.body.replaceChildren(...App(data)))
89
89
  .catch(onError)
90
90
  }
91
91
 
92
- function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbackAddress, staticFiles]) {
92
+ function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbackAddress, staticBrokers]) {
93
93
  globalDelay = delay
94
- return (
95
- r('div', null,
96
- r(Header, { cookies, comments, delay, fallbackAddress, collectProxied }),
97
- r('main', { className: CSS.Main },
98
- r('div', null,
99
- r(MockList, { brokersByMethod, canProxy: Boolean(fallbackAddress) }),
100
- r(StaticFilesList, { staticFiles })),
101
- r(PayloadViewer))))
94
+ return [
95
+ r(Header, { cookies, comments, delay, fallbackAddress, collectProxied }),
96
+ r('main', null,
97
+ r('div', { className: CSS.MainLeftSide },
98
+ r(MockList, { brokersByMethod, canProxy: Boolean(fallbackAddress) }),
99
+ r(StaticFilesList, { brokers: staticBrokers })),
100
+ r('div', { className: CSS.MainRightSide },
101
+ r(PayloadViewer)))]
102
102
  }
103
103
 
104
104
 
@@ -106,13 +106,14 @@ function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbac
106
106
 
107
107
  function Header({ cookies, comments, delay, fallbackAddress, collectProxied }) {
108
108
  return (
109
- r('menu', { className: CSS.Header },
109
+ r('header', null,
110
110
  r(Logo),
111
- r(CookieSelector, { cookies }),
112
- r(BulkSelector, { comments }),
113
- r(GlobalDelayField, { delay }),
114
- r(ProxyFallbackField, { fallbackAddress, collectProxied }),
115
- r(ResetButton)))
111
+ r('div', null,
112
+ r(CookieSelector, { cookies }),
113
+ r(BulkSelector, { comments }),
114
+ r(GlobalDelayField, { delay }),
115
+ r(ProxyFallbackField, { fallbackAddress, collectProxied }),
116
+ r(ResetButton))))
116
117
  }
117
118
 
118
119
  function Logo() {
@@ -251,10 +252,10 @@ function MockList({ brokersByMethod, canProxy }) {
251
252
  const hasMocks = Object.keys(brokersByMethod).length
252
253
  if (!hasMocks)
253
254
  return (
254
- r('div', { className: cssClass(CSS.MockList, CSS.empty) },
255
+ r('div', { className: CSS.empty },
255
256
  Strings.no_mocks_found))
256
257
  return (
257
- r('div', { className: CSS.MockList },
258
+ r('div', null,
258
259
  r('table', null, Object.entries(brokersByMethod).map(([method, brokers]) =>
259
260
  r(SectionByMethod, { method, brokers, canProxy })))))
260
261
  }
@@ -298,10 +299,11 @@ function PreviewLink({ method, urlMask, urlMaskDittoed }) {
298
299
  href: urlMask,
299
300
  onClick
300
301
  }, ditto
301
- ? [r('span', null, ditto), tail]
302
+ ? [r('span', { className: CSS.dittoDir }, ditto), tail]
302
303
  : tail))
303
304
  }
304
305
 
306
+ /** @param {{ broker: MockBroker }} props */
305
307
  function MockSelector({ broker }) {
306
308
  function onChange() {
307
309
  const { urlMask, method } = parseFilename(this.value)
@@ -338,6 +340,7 @@ function MockSelector({ broker }) {
338
340
  }, file))))
339
341
  }
340
342
 
343
+ /** @param {{ broker: MockBroker }} props */
341
344
  function DelayRouteToggler({ broker }) {
342
345
  function onChange() {
343
346
  const { method, urlMask } = parseFilename(broker.mocks[0])
@@ -356,6 +359,7 @@ function DelayRouteToggler({ broker }) {
356
359
  TimerIcon()))
357
360
  }
358
361
 
362
+ /** @param {{ broker: MockBroker }} props */
359
363
  function InternalServerErrorToggler({ broker }) {
360
364
  function onChange() {
361
365
  const { urlMask, method } = parseFilename(broker.mocks[0])
@@ -381,6 +385,7 @@ function InternalServerErrorToggler({ broker }) {
381
385
  r('span', null, '500')))
382
386
  }
383
387
 
388
+ /** @param {{ broker: MockBroker, disabled: boolean }} props */
384
389
  function ProxyToggler({ broker, disabled }) {
385
390
  function onChange() {
386
391
  const { urlMask, method } = parseFilename(broker.mocks[0])
@@ -404,32 +409,34 @@ function ProxyToggler({ broker, disabled }) {
404
409
  }
405
410
 
406
411
 
407
-
408
- /** # StaticFilesList */
409
-
410
- function StaticFilesList({ staticFiles }) {
411
- if (!Object.keys(staticFiles).length)
412
+ /**
413
+ * # StaticFilesList
414
+ * @param {{ brokers: StaticBroker[] }} props
415
+ */
416
+ function StaticFilesList({ brokers }) {
417
+ if (!Object.keys(brokers).length)
412
418
  return null
413
- const paths = dittoSplitPaths(Object.keys(staticFiles)).map(([ditto, tail]) => ditto
414
- ? [r('span', null, ditto), tail]
419
+ const dp = dittoSplitPaths(Object.keys(brokers)).map(([ditto, tail]) => ditto
420
+ ? [r('span', { className: CSS.dittoDir }, ditto), tail]
415
421
  : tail)
416
422
  return (
417
423
  r('table', { className: CSS.StaticFilesList },
418
424
  r('tbody', null,
419
425
  r('th', { colspan: 4 }, Strings.static_get),
420
- Object.values(staticFiles).map((broker, i) =>
426
+ Object.values(brokers).map((broker, i) =>
421
427
  r('tr', null,
422
428
  r('td', null, r(ProxyStaticToggler, {})),
423
429
  r('td', null, r(DelayStaticRouteToggler, { broker })),
424
430
  r('td', null, r(NotFoundToggler, { broker })),
425
- r('td', null, r('a', { href: broker.file, target: '_blank' }, paths[i]))
431
+ r('td', null, r('a', { href: broker.route, target: '_blank' }, dp[i]))
426
432
  )))))
427
433
  }
428
434
 
429
435
 
436
+ /** @param {{ broker: StaticBroker }} props */
430
437
  function DelayStaticRouteToggler({ broker }) {
431
438
  function onChange() {
432
- mockaton.setStaticRouteIsDelayed(broker.file, this.checked)
439
+ mockaton.setStaticRouteIsDelayed(broker.route, this.checked)
433
440
  .catch(onError)
434
441
  }
435
442
  return (
@@ -445,9 +452,10 @@ function DelayStaticRouteToggler({ broker }) {
445
452
  TimerIcon()))
446
453
  }
447
454
 
455
+ /** @param {{ broker: StaticBroker }} props */
448
456
  function NotFoundToggler({ broker }) {
449
457
  function onChange() {
450
- mockaton.setStaticRouteIs404(broker.file, this.checked)
458
+ mockaton.setStaticRouteStatus(broker.route, this.checked ? 404 : 200)
451
459
  .catch(onError)
452
460
  }
453
461
  return (
@@ -457,15 +465,13 @@ function NotFoundToggler({ broker }) {
457
465
  },
458
466
  r('input', {
459
467
  type: 'checkbox',
460
- checked: broker.should404,
468
+ checked: broker.status === 404,
461
469
  onChange
462
470
  }),
463
471
  r('span', null, '404')))
464
472
  }
465
473
 
466
-
467
- // TODO
468
- function ProxyStaticToggler({}) {
474
+ function ProxyStaticToggler({}) { // TODO
469
475
  function onChange() {
470
476
  }
471
477
  return (
@@ -490,7 +496,7 @@ const payloadViewerRef = useRef()
490
496
  function PayloadViewer() {
491
497
  return (
492
498
  r('div', { className: CSS.PayloadViewer },
493
- r('h2', { ref: payloadViewerTitleRef }, Strings.mock),
499
+ r('h2', { ref: payloadViewerTitleRef }, Strings.preview),
494
500
  r('pre', null,
495
501
  r('code', { ref: payloadViewerRef }, Strings.click_link_to_preview))))
496
502
  }
@@ -523,7 +529,7 @@ function PayloadViewerTitleWhenProxied({ mime, status, statusText, gatewayIsBad
523
529
  }
524
530
 
525
531
  async function previewMock(method, urlMask, href) {
526
- const timer = setTimeout(renderSpinner, SPINNER_DELAY)
532
+ const timer = setTimeout(renderSpinner, 180)
527
533
  const response = await fetch(href, { method })
528
534
  clearTimeout(timer)
529
535
  await updatePayloadViewer(method, urlMask, response)
package/src/MockBroker.js CHANGED
@@ -83,11 +83,11 @@ export class MockBroker {
83
83
  this.currentMock.file = filename
84
84
  }
85
85
 
86
- updateDelayed(delayed) {
86
+ setDelayed(delayed) {
87
87
  this.currentMock.delayed = delayed
88
88
  }
89
89
 
90
- updateProxied(proxied) {
90
+ setProxied(proxied) {
91
91
  if (proxied)
92
92
  this.selectFile('')
93
93
  else
package/src/Mockaton.js CHANGED
@@ -54,7 +54,7 @@ async function onRequest(req, response) {
54
54
  else if (method === 'GET' && apiGetRequests.has(url))
55
55
  apiGetRequests.get(url)(req, response)
56
56
 
57
- else if (method === 'GET' && findStaticBrokerByRoute(req.url))
57
+ else if (method === 'GET' && findStaticBrokerByRoute(url))
58
58
  await dispatchStatic(req, response)
59
59
 
60
60
  else
@@ -1,37 +1,24 @@
1
- import { join, basename } from 'node:path'
2
1
  import { readFileSync } from 'node:fs'
2
+ import { join, basename } from 'node:path'
3
3
 
4
4
  import { mimeFor } from './utils/mime.js'
5
+ import { listFilesRecursively } from './utils/fs.js'
5
6
  import { config, isFileAllowed, calcDelay } from './config.js'
6
7
  import { sendPartialContent, sendNotFound } from './utils/http-response.js'
7
- import { isDirectory, isFile, listFilesRecursively } from './utils/fs.js'
8
8
 
9
9
 
10
10
  class StaticBroker {
11
- constructor(file) {
12
- this.file = file
11
+ constructor(route) {
12
+ this.route = route
13
13
  this.delayed = false
14
- this.should404 = false
15
- this.resolvedPath = this.#staticFilePath()
16
- }
17
-
18
- #staticFilePath() { // url is absolute e.g. /home/../.. => /
19
- let candidate = join(config.staticDir, this.file)
20
- if (isDirectory(candidate))
21
- candidate = join(candidate, 'index.html')
22
- if (isFile(candidate))
23
- return candidate
24
- }
25
-
26
- updateDelayed(value) {
27
- this.delayed = value
14
+ this.status = 200 // 200 or 404
28
15
  }
29
16
 
30
- updateNotFound(value) {
31
- this.should404 = value
32
- }
17
+ setDelayed(value) { this.delayed = value }
18
+ setStatus(value) { this.status = value }
33
19
  }
34
20
 
21
+ /** @type {{ [route: string]: StaticBroker }} */
35
22
  let collection = {}
36
23
 
37
24
  export function initStaticCollection() {
@@ -41,23 +28,27 @@ export function initStaticCollection() {
41
28
  .forEach(registerStaticMock)
42
29
  }
43
30
 
31
+
44
32
  /** @returns {boolean} registered */
45
- export function registerStaticMock(file) {
46
- if (!isFileAllowed(basename(file)))
33
+ export function registerStaticMock(relativeFile) {
34
+ if (!isFileAllowed(basename(relativeFile)))
47
35
  return false
48
36
 
49
- file = '/' + file
50
- if (findStaticBrokerByRoute(file))
37
+ const route = '/' + relativeFile
38
+ if (findStaticBrokerByRoute(route))
51
39
  return false
52
40
 
53
- collection[file] = new StaticBroker(file)
41
+ collection[route] = new StaticBroker(route)
54
42
  return true
55
43
  }
56
44
 
57
- export function unregisterStaticMock(file) {
58
- delete collection['/' + file]
45
+
46
+ export function unregisterStaticMock(relativeFile) {
47
+ delete collection['/' + relativeFile]
59
48
  }
60
49
 
50
+
51
+ /** @returns {StaticBroker | undefined} */
61
52
  export function findStaticBrokerByRoute(route) {
62
53
  return collection[route] || collection[join(route, 'index.html')]
63
54
  }
@@ -66,15 +57,16 @@ export function getStaticFilesCollection() {
66
57
  return collection
67
58
  }
68
59
 
60
+
69
61
  export async function dispatchStatic(req, response) {
70
62
  const broker = findStaticBrokerByRoute(req.url)
71
63
 
72
64
  setTimeout(async () => {
73
- if (!broker || broker.should404) { // TESTME
65
+ if (!broker || broker.status === 404) { // TESTME
74
66
  sendNotFound(response)
75
67
  return
76
68
  }
77
- const file = broker.resolvedPath
69
+ const file = join(config.staticDir, broker.route)
78
70
  if (req.headers.range)
79
71
  await sendPartialContent(response, req.headers.range, file)
80
72
  else {
@@ -1,3 +1,5 @@
1
+ import { basename } from 'node:path'
2
+
1
3
  import { cookie } from './cookie.js'
2
4
  import { MockBroker } from './MockBroker.js'
3
5
  import { listFilesRecursively } from './utils/fs.js'
@@ -38,7 +40,7 @@ export function init() {
38
40
  /** @returns {boolean} registered */
39
41
  export function registerMock(file, isFromWatcher = false) {
40
42
  if (findBrokerByFilename(file)?.hasMock(file)
41
- || !isFileAllowed(file)
43
+ || !isFileAllowed(basename(file)) // TESTME
42
44
  || !filenameIsValid(file))
43
45
  return false
44
46