mockaton 8.20.0 → 8.20.2
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 +73 -42
- package/index.d.ts +22 -24
- package/package.json +3 -3
- package/src/Api.js +29 -19
- package/src/Commander.js +52 -36
- package/src/Dashboard.js +199 -138
- package/src/Mockaton.js +1 -1
- package/src/logo.svg +17 -0
- package/src/mockBrokersCollection.js +3 -2
- package/src/staticCollection.js +2 -2
- package/src/utils/http-request.js +9 -3
- package/src/mockaton-logo.svg +0 -12
package/src/Dashboard.js
CHANGED
|
@@ -38,83 +38,117 @@ const Strings = {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const CSS = {
|
|
41
|
-
BulkSelector:
|
|
42
|
-
DelayToggler:
|
|
43
|
-
ErrorToast:
|
|
44
|
-
FallbackBackend:
|
|
45
|
-
Field:
|
|
46
|
-
GlobalDelayField:
|
|
47
|
-
Help:
|
|
48
|
-
InternalServerErrorToggler:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
status4xx:
|
|
41
|
+
BulkSelector: null,
|
|
42
|
+
DelayToggler: null,
|
|
43
|
+
ErrorToast: null,
|
|
44
|
+
FallbackBackend: null,
|
|
45
|
+
Field: null,
|
|
46
|
+
GlobalDelayField: null,
|
|
47
|
+
Help: null,
|
|
48
|
+
InternalServerErrorToggler: null,
|
|
49
|
+
MockList: null,
|
|
50
|
+
MockSelector: null,
|
|
51
|
+
NotFoundToggler: null,
|
|
52
|
+
PayloadViewer: null,
|
|
53
|
+
PreviewLink: null,
|
|
54
|
+
ProgressBar: null,
|
|
55
|
+
ProxyToggler: null,
|
|
56
|
+
ResetButton: null,
|
|
57
|
+
SaveProxiedCheckbox: null,
|
|
58
|
+
StaticFilesList: null,
|
|
59
|
+
|
|
60
|
+
chosen: null,
|
|
61
|
+
dittoDir: null,
|
|
62
|
+
empty: null,
|
|
63
|
+
leftSide: null,
|
|
64
|
+
nonDefault: null,
|
|
65
|
+
red: null,
|
|
66
|
+
rightSide: null,
|
|
67
|
+
status4xx: null
|
|
68
|
+
}
|
|
69
|
+
for (const k of Object.keys(CSS))
|
|
70
|
+
CSS[k] = k
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
const state = {
|
|
74
|
+
/** @type {ClientBrokersByMethod} */
|
|
75
|
+
brokersByMethod: {},
|
|
76
|
+
|
|
77
|
+
/** @type {ClientStaticBrokers} */
|
|
78
|
+
staticBrokers: {},
|
|
79
|
+
|
|
80
|
+
/** @type {[label:string, selected:boolean][]} */
|
|
81
|
+
cookies: [],
|
|
82
|
+
|
|
83
|
+
/** @type {string[]} */
|
|
84
|
+
comments: [],
|
|
85
|
+
|
|
86
|
+
delay: 0,
|
|
87
|
+
|
|
88
|
+
collectProxied: false,
|
|
89
|
+
|
|
90
|
+
fallbackAddress: '',
|
|
91
|
+
|
|
92
|
+
get canProxy() {
|
|
93
|
+
return Boolean(this.fallbackAddress)
|
|
94
|
+
}
|
|
68
95
|
}
|
|
69
96
|
|
|
70
|
-
const r = createElement
|
|
71
|
-
const s = createSvgElement
|
|
72
|
-
|
|
73
97
|
const mockaton = new Commander(window.location.origin)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
init()
|
|
98
|
+
updateState()
|
|
77
99
|
initLongPoll()
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return Promise.all([
|
|
100
|
+
function updateState() {
|
|
101
|
+
Promise.all([
|
|
81
102
|
mockaton.listMocks(),
|
|
103
|
+
mockaton.listStaticFiles(),
|
|
82
104
|
mockaton.listCookies(),
|
|
83
105
|
mockaton.listComments(),
|
|
84
106
|
mockaton.getGlobalDelay(),
|
|
85
107
|
mockaton.getCollectProxied(),
|
|
86
|
-
mockaton.getProxyFallback()
|
|
87
|
-
mockaton.listStaticFiles()
|
|
108
|
+
mockaton.getProxyFallback()
|
|
88
109
|
].map(api => api.then(response => response.ok && response.json())))
|
|
89
|
-
.then(data =>
|
|
110
|
+
.then(data => {
|
|
111
|
+
state.brokersByMethod = data[0]
|
|
112
|
+
state.staticBrokers = data[1]
|
|
113
|
+
state.cookies = data[2]
|
|
114
|
+
state.comments = data[3]
|
|
115
|
+
state.delay = data[4]
|
|
116
|
+
state.collectProxied = data[5]
|
|
117
|
+
state.fallbackAddress = data[6]
|
|
118
|
+
document.body.replaceChildren(...App())
|
|
119
|
+
})
|
|
90
120
|
.catch(onError)
|
|
91
121
|
}
|
|
92
122
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
const r = createElement
|
|
124
|
+
const s = createSvgElement
|
|
125
|
+
|
|
126
|
+
function App() {
|
|
96
127
|
return [
|
|
97
|
-
r(Header
|
|
128
|
+
r(Header),
|
|
98
129
|
r('main', null,
|
|
99
|
-
r('div',
|
|
100
|
-
r(MockList
|
|
101
|
-
r(StaticFilesList
|
|
102
|
-
r('div',
|
|
103
|
-
r(PayloadViewer)))
|
|
130
|
+
r('div', className(CSS.leftSide),
|
|
131
|
+
r(MockList),
|
|
132
|
+
r(StaticFilesList)),
|
|
133
|
+
r('div', className(CSS.rightSide),
|
|
134
|
+
r(PayloadViewer)))
|
|
135
|
+
]
|
|
104
136
|
}
|
|
105
137
|
|
|
106
138
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
function Header({ cookies, comments, delay, fallbackAddress, collectProxied }) {
|
|
139
|
+
function Header() {
|
|
110
140
|
return (
|
|
111
141
|
r('header', null,
|
|
112
|
-
r(
|
|
142
|
+
r('img', {
|
|
143
|
+
alt: Strings.title,
|
|
144
|
+
src: 'mockaton/logo.svg',
|
|
145
|
+
width: 160
|
|
146
|
+
}),
|
|
113
147
|
r('div', null,
|
|
114
|
-
r(CookieSelector
|
|
115
|
-
r(BulkSelector
|
|
116
|
-
r(GlobalDelayField
|
|
117
|
-
r(ProxyFallbackField
|
|
148
|
+
r(CookieSelector),
|
|
149
|
+
r(BulkSelector),
|
|
150
|
+
r(GlobalDelayField),
|
|
151
|
+
r(ProxyFallbackField),
|
|
118
152
|
r(ResetButton)),
|
|
119
153
|
r('a', {
|
|
120
154
|
className: CSS.Help,
|
|
@@ -124,22 +158,17 @@ function Header({ cookies, comments, delay, fallbackAddress, collectProxied }) {
|
|
|
124
158
|
}, r(HelpIcon))))
|
|
125
159
|
}
|
|
126
160
|
|
|
127
|
-
function Logo() {
|
|
128
|
-
return (
|
|
129
|
-
r('img', {
|
|
130
|
-
alt: Strings.title,
|
|
131
|
-
src: 'mockaton/mockaton-logo.svg',
|
|
132
|
-
width: 160
|
|
133
|
-
}))
|
|
134
|
-
}
|
|
135
161
|
|
|
136
|
-
function CookieSelector(
|
|
162
|
+
function CookieSelector() {
|
|
163
|
+
const { cookies } = state
|
|
137
164
|
function onChange() {
|
|
138
|
-
mockaton.selectCookie(this.value)
|
|
165
|
+
mockaton.selectCookie(this.value)
|
|
166
|
+
.then(parseError)
|
|
167
|
+
.catch(onError)
|
|
139
168
|
}
|
|
140
169
|
const disabled = cookies.length <= 1
|
|
141
170
|
return (
|
|
142
|
-
r('label',
|
|
171
|
+
r('label', className(CSS.Field),
|
|
143
172
|
r('span', null, Strings.cookie),
|
|
144
173
|
r('select', {
|
|
145
174
|
autocomplete: 'off',
|
|
@@ -150,7 +179,9 @@ function CookieSelector({ cookies }) {
|
|
|
150
179
|
r('option', { value, selected }, value)))))
|
|
151
180
|
}
|
|
152
181
|
|
|
153
|
-
|
|
182
|
+
|
|
183
|
+
function BulkSelector() {
|
|
184
|
+
const { comments } = state
|
|
154
185
|
// UX wise this should be a menu instead of this `select`.
|
|
155
186
|
// But this way is easier to implement, with a few hacks.
|
|
156
187
|
const firstOption = Strings.pick_comment
|
|
@@ -158,7 +189,8 @@ function BulkSelector({ comments }) {
|
|
|
158
189
|
const value = this.value
|
|
159
190
|
this.value = firstOption // Hack
|
|
160
191
|
mockaton.bulkSelectByComment(value)
|
|
161
|
-
.then(
|
|
192
|
+
.then(parseError)
|
|
193
|
+
.then(updateState)
|
|
162
194
|
.catch(onError)
|
|
163
195
|
}
|
|
164
196
|
const disabled = !comments.length
|
|
@@ -166,7 +198,7 @@ function BulkSelector({ comments }) {
|
|
|
166
198
|
? []
|
|
167
199
|
: [firstOption].concat(comments)
|
|
168
200
|
return (
|
|
169
|
-
r('label',
|
|
201
|
+
r('label', className(CSS.Field),
|
|
170
202
|
r('span', null, Strings.bulk_select),
|
|
171
203
|
r('select', {
|
|
172
204
|
className: CSS.BulkSelector,
|
|
@@ -179,13 +211,17 @@ function BulkSelector({ comments }) {
|
|
|
179
211
|
r('option', { value }, value)))))
|
|
180
212
|
}
|
|
181
213
|
|
|
182
|
-
|
|
214
|
+
|
|
215
|
+
function GlobalDelayField() {
|
|
216
|
+
const { delay } = state
|
|
183
217
|
function onChange() {
|
|
184
|
-
|
|
185
|
-
mockaton.setGlobalDelay(
|
|
218
|
+
state.delay = this.valueAsNumber
|
|
219
|
+
mockaton.setGlobalDelay(state.delay)
|
|
220
|
+
.then(parseError)
|
|
221
|
+
.catch(onError)
|
|
186
222
|
}
|
|
187
223
|
return (
|
|
188
|
-
r('label',
|
|
224
|
+
r('label', className(CSS.Field, CSS.GlobalDelayField),
|
|
189
225
|
r('span', null, r(TimerIcon), Strings.delay_ms),
|
|
190
226
|
r('input', {
|
|
191
227
|
type: 'number',
|
|
@@ -197,7 +233,9 @@ function GlobalDelayField({ delay }) {
|
|
|
197
233
|
})))
|
|
198
234
|
}
|
|
199
235
|
|
|
200
|
-
|
|
236
|
+
|
|
237
|
+
function ProxyFallbackField() {
|
|
238
|
+
const { fallbackAddress, collectProxied } = state
|
|
201
239
|
function onChange() {
|
|
202
240
|
const saveCheckbox = this.closest(`.${CSS.FallbackBackend}`).querySelector('[type=checkbox]')
|
|
203
241
|
saveCheckbox.disabled = !this.validity.valid || !this.value.trim()
|
|
@@ -206,11 +244,12 @@ function ProxyFallbackField({ fallbackAddress, collectProxied }) {
|
|
|
206
244
|
this.reportValidity()
|
|
207
245
|
else
|
|
208
246
|
mockaton.setProxyFallback(this.value.trim())
|
|
209
|
-
.then(
|
|
247
|
+
.then(parseError)
|
|
248
|
+
.then(updateState)
|
|
210
249
|
.catch(onError)
|
|
211
250
|
}
|
|
212
251
|
return (
|
|
213
|
-
r('div',
|
|
252
|
+
r('div', className(CSS.Field, CSS.FallbackBackend),
|
|
214
253
|
r('label', null,
|
|
215
254
|
r('span', null, r(CloudIcon), Strings.fallback_server),
|
|
216
255
|
r('input', {
|
|
@@ -226,12 +265,15 @@ function ProxyFallbackField({ fallbackAddress, collectProxied }) {
|
|
|
226
265
|
})))
|
|
227
266
|
}
|
|
228
267
|
|
|
229
|
-
function SaveProxiedCheckbox({ disabled
|
|
268
|
+
function SaveProxiedCheckbox({ disabled }) {
|
|
269
|
+
const { collectProxied } = state
|
|
230
270
|
function onChange() {
|
|
231
|
-
mockaton.setCollectProxied(this.checked)
|
|
271
|
+
mockaton.setCollectProxied(this.checked)
|
|
272
|
+
.then(parseError)
|
|
273
|
+
.catch(onError)
|
|
232
274
|
}
|
|
233
275
|
return (
|
|
234
|
-
r('label',
|
|
276
|
+
r('label', className(CSS.SaveProxiedCheckbox),
|
|
235
277
|
r('input', {
|
|
236
278
|
type: 'checkbox',
|
|
237
279
|
disabled,
|
|
@@ -241,34 +283,39 @@ function SaveProxiedCheckbox({ disabled, collectProxied }) {
|
|
|
241
283
|
r('span', null, Strings.save_proxied)))
|
|
242
284
|
}
|
|
243
285
|
|
|
286
|
+
|
|
244
287
|
function ResetButton() {
|
|
288
|
+
function onClick() {
|
|
289
|
+
mockaton.reset()
|
|
290
|
+
.then(parseError)
|
|
291
|
+
.then(updateState)
|
|
292
|
+
.catch(onError)
|
|
293
|
+
}
|
|
245
294
|
return (
|
|
246
295
|
r('button', {
|
|
247
296
|
className: CSS.ResetButton,
|
|
248
|
-
onClick
|
|
249
|
-
mockaton.reset()
|
|
250
|
-
.then(init)
|
|
251
|
-
.catch(onError)
|
|
252
|
-
}
|
|
297
|
+
onClick
|
|
253
298
|
}, Strings.reset))
|
|
254
299
|
}
|
|
255
300
|
|
|
256
301
|
|
|
302
|
+
|
|
257
303
|
/** # MockList */
|
|
258
304
|
|
|
259
|
-
function MockList(
|
|
260
|
-
const
|
|
261
|
-
if (!
|
|
305
|
+
function MockList() {
|
|
306
|
+
const { brokersByMethod } = state
|
|
307
|
+
if (!Object.keys(brokersByMethod).length)
|
|
262
308
|
return (
|
|
263
|
-
r('div',
|
|
309
|
+
r('div', className(CSS.empty),
|
|
264
310
|
Strings.no_mocks_found))
|
|
265
311
|
return (
|
|
266
312
|
r('div', null,
|
|
267
313
|
r('table', null, Object.entries(brokersByMethod).map(([method, brokers]) =>
|
|
268
|
-
r(SectionByMethod, { method, brokers
|
|
314
|
+
r(SectionByMethod, { method, brokers })))))
|
|
269
315
|
}
|
|
270
316
|
|
|
271
|
-
function SectionByMethod({ method, brokers
|
|
317
|
+
function SectionByMethod({ method, brokers }) {
|
|
318
|
+
const canProxy = state.canProxy
|
|
272
319
|
const brokersSorted = Object.entries(brokers)
|
|
273
320
|
.filter(([, broker]) => broker.mocks.length > 1) // >1 because of autogen500
|
|
274
321
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
@@ -309,16 +356,17 @@ function PreviewLink({ method, urlMask, urlMaskDittoed }) {
|
|
|
309
356
|
href: urlMask,
|
|
310
357
|
onClick
|
|
311
358
|
}, ditto
|
|
312
|
-
? [r('span',
|
|
359
|
+
? [r('span', className(CSS.dittoDir), ditto), tail]
|
|
313
360
|
: tail))
|
|
314
361
|
}
|
|
315
362
|
|
|
316
|
-
/** @param {{ broker:
|
|
363
|
+
/** @param {{ broker: ClientMockBroker }} props */
|
|
317
364
|
function MockSelector({ broker }) {
|
|
318
365
|
function onChange() {
|
|
319
366
|
const { urlMask, method } = parseFilename(this.value)
|
|
320
367
|
mockaton.select(this.value)
|
|
321
|
-
.then(
|
|
368
|
+
.then(parseError)
|
|
369
|
+
.then(updateState)
|
|
322
370
|
.then(() => linkFor(method, urlMask)?.click())
|
|
323
371
|
.catch(onError)
|
|
324
372
|
}
|
|
@@ -339,7 +387,7 @@ function MockSelector({ broker }) {
|
|
|
339
387
|
autocomplete: 'off',
|
|
340
388
|
'data-qaid': urlMask,
|
|
341
389
|
disabled: files.length <= 1,
|
|
342
|
-
className
|
|
390
|
+
...className(
|
|
343
391
|
CSS.MockSelector,
|
|
344
392
|
selected !== files[0] && CSS.nonDefault,
|
|
345
393
|
status >= 400 && status < 500 && CSS.status4xx)
|
|
@@ -350,11 +398,13 @@ function MockSelector({ broker }) {
|
|
|
350
398
|
}, file))))
|
|
351
399
|
}
|
|
352
400
|
|
|
353
|
-
/** @param {{ broker:
|
|
401
|
+
/** @param {{ broker: ClientMockBroker }} props */
|
|
354
402
|
function DelayRouteToggler({ broker }) {
|
|
355
403
|
function onChange() {
|
|
356
404
|
const { method, urlMask } = parseFilename(broker.mocks[0])
|
|
357
|
-
mockaton.setRouteIsDelayed(method, urlMask, this.checked)
|
|
405
|
+
mockaton.setRouteIsDelayed(method, urlMask, this.checked)
|
|
406
|
+
.then(parseError)
|
|
407
|
+
.catch(onError)
|
|
358
408
|
}
|
|
359
409
|
return (
|
|
360
410
|
r('label', {
|
|
@@ -369,7 +419,7 @@ function DelayRouteToggler({ broker }) {
|
|
|
369
419
|
TimerIcon()))
|
|
370
420
|
}
|
|
371
421
|
|
|
372
|
-
/** @param {{ broker:
|
|
422
|
+
/** @param {{ broker: ClientMockBroker }} props */
|
|
373
423
|
function InternalServerErrorToggler({ broker }) {
|
|
374
424
|
function onChange() {
|
|
375
425
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
@@ -377,7 +427,8 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
377
427
|
this.checked
|
|
378
428
|
? broker.mocks.find(f => parseFilename(f).status === 500)
|
|
379
429
|
: broker.mocks[0])
|
|
380
|
-
.then(
|
|
430
|
+
.then(parseError)
|
|
431
|
+
.then(updateState)
|
|
381
432
|
.then(() => linkFor(method, urlMask)?.click())
|
|
382
433
|
.catch(onError)
|
|
383
434
|
}
|
|
@@ -395,12 +446,13 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
395
446
|
r('span', null, '500')))
|
|
396
447
|
}
|
|
397
448
|
|
|
398
|
-
/** @param {{ broker:
|
|
449
|
+
/** @param {{ broker: ClientMockBroker }} props */
|
|
399
450
|
function ProxyToggler({ broker }) {
|
|
400
451
|
function onChange() {
|
|
401
452
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
402
453
|
mockaton.setRouteIsProxied(method, urlMask, this.checked)
|
|
403
|
-
.then(
|
|
454
|
+
.then(parseError)
|
|
455
|
+
.then(updateState)
|
|
404
456
|
.then(() => linkFor(method, urlMask)?.click())
|
|
405
457
|
.catch(onError)
|
|
406
458
|
}
|
|
@@ -418,24 +470,25 @@ function ProxyToggler({ broker }) {
|
|
|
418
470
|
}
|
|
419
471
|
|
|
420
472
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
473
|
+
|
|
474
|
+
/** # StaticFilesList */
|
|
475
|
+
|
|
476
|
+
function StaticFilesList() {
|
|
477
|
+
const { staticBrokers } = state
|
|
478
|
+
const canProxy = state.canProxy
|
|
479
|
+
if (!Object.keys(staticBrokers).length)
|
|
427
480
|
return null
|
|
428
|
-
const dp = dittoSplitPaths(Object.keys(
|
|
429
|
-
? [r('span',
|
|
481
|
+
const dp = dittoSplitPaths(Object.keys(staticBrokers)).map(([ditto, tail]) => ditto
|
|
482
|
+
? [r('span', className(CSS.dittoDir), ditto), tail]
|
|
430
483
|
: tail)
|
|
431
484
|
return (
|
|
432
|
-
r('table',
|
|
485
|
+
r('table', className(CSS.StaticFilesList),
|
|
433
486
|
r('thead', null,
|
|
434
487
|
r('tr', null,
|
|
435
488
|
r('th', { colspan: 2 + Number(canProxy) }),
|
|
436
489
|
r('th', null, Strings.static_get))),
|
|
437
490
|
r('tbody', null,
|
|
438
|
-
Object.values(
|
|
491
|
+
Object.values(staticBrokers).map((broker, i) =>
|
|
439
492
|
r('tr', null,
|
|
440
493
|
canProxy && r('td', null, r(ProxyStaticToggler, {})),
|
|
441
494
|
r('td', null, r(DelayStaticRouteToggler, { broker })),
|
|
@@ -444,11 +497,11 @@ function StaticFilesList({ brokers, canProxy }) {
|
|
|
444
497
|
)))))
|
|
445
498
|
}
|
|
446
499
|
|
|
447
|
-
|
|
448
|
-
/** @param {{ broker: StaticBroker }} props */
|
|
500
|
+
/** @param {{ broker: ClientStaticBroker }} props */
|
|
449
501
|
function DelayStaticRouteToggler({ broker }) {
|
|
450
502
|
function onChange() {
|
|
451
503
|
mockaton.setStaticRouteIsDelayed(broker.route, this.checked)
|
|
504
|
+
.then(parseError)
|
|
452
505
|
.catch(onError)
|
|
453
506
|
}
|
|
454
507
|
return (
|
|
@@ -464,10 +517,11 @@ function DelayStaticRouteToggler({ broker }) {
|
|
|
464
517
|
TimerIcon()))
|
|
465
518
|
}
|
|
466
519
|
|
|
467
|
-
/** @param {{ broker:
|
|
520
|
+
/** @param {{ broker: ClientStaticBroker }} props */
|
|
468
521
|
function NotFoundToggler({ broker }) {
|
|
469
522
|
function onChange() {
|
|
470
523
|
mockaton.setStaticRouteStatus(broker.route, this.checked ? 404 : 200)
|
|
524
|
+
.then(parseError)
|
|
471
525
|
.catch(onError)
|
|
472
526
|
}
|
|
473
527
|
return (
|
|
@@ -500,6 +554,8 @@ function ProxyStaticToggler({}) { // TODO
|
|
|
500
554
|
r(CloudIcon)))
|
|
501
555
|
}
|
|
502
556
|
|
|
557
|
+
|
|
558
|
+
|
|
503
559
|
/** # Payload Preview */
|
|
504
560
|
|
|
505
561
|
const payloadViewerTitleRef = useRef()
|
|
@@ -507,17 +563,16 @@ const payloadViewerRef = useRef()
|
|
|
507
563
|
|
|
508
564
|
function PayloadViewer() {
|
|
509
565
|
return (
|
|
510
|
-
r('div',
|
|
566
|
+
r('div', className(CSS.PayloadViewer),
|
|
511
567
|
r('h2', { ref: payloadViewerTitleRef }, Strings.preview),
|
|
512
568
|
r('pre', null,
|
|
513
569
|
r('code', { ref: payloadViewerRef }, Strings.click_link_to_preview))))
|
|
514
570
|
}
|
|
515
571
|
|
|
516
|
-
|
|
517
572
|
function PayloadViewerProgressBar() {
|
|
518
573
|
return (
|
|
519
|
-
r('div',
|
|
520
|
-
r('div', { style: { animationDuration:
|
|
574
|
+
r('div', className(CSS.ProgressBar),
|
|
575
|
+
r('div', { style: { animationDuration: state.delay + 'ms' } })))
|
|
521
576
|
}
|
|
522
577
|
|
|
523
578
|
function PayloadViewerTitle({ file, status, statusText }) {
|
|
@@ -532,7 +587,7 @@ function PayloadViewerTitleWhenProxied({ mime, status, statusText, gatewayIsBad
|
|
|
532
587
|
return (
|
|
533
588
|
r('span', null,
|
|
534
589
|
gatewayIsBad
|
|
535
|
-
? r('span',
|
|
590
|
+
? r('span', className(CSS.red), Strings.fallback_server_error + ' ')
|
|
536
591
|
: r('span', null, Strings.got + ' '),
|
|
537
592
|
r('abbr', { title: statusText }, status),
|
|
538
593
|
' ' + mime))
|
|
@@ -610,9 +665,19 @@ function mockSelectorFor(method, urlMask) {
|
|
|
610
665
|
|
|
611
666
|
/** # Misc */
|
|
612
667
|
|
|
668
|
+
async function parseError(response) {
|
|
669
|
+
if (response.ok)
|
|
670
|
+
return
|
|
671
|
+
if (response.status === 422)
|
|
672
|
+
throw await response.text()
|
|
673
|
+
throw response.statusText
|
|
674
|
+
}
|
|
675
|
+
|
|
613
676
|
function onError(error) {
|
|
614
677
|
if (error?.message === 'Failed to fetch')
|
|
615
678
|
showErrorToast('Looks like the Mockaton server is not running')
|
|
679
|
+
else
|
|
680
|
+
showErrorToast(error || 'Unexpected Error')
|
|
616
681
|
console.error(error)
|
|
617
682
|
}
|
|
618
683
|
|
|
@@ -673,7 +738,7 @@ async function poll() {
|
|
|
673
738
|
const syncVersion = await response.json()
|
|
674
739
|
if (poll.oldSyncVersion !== syncVersion) { // because it could be < or >
|
|
675
740
|
poll.oldSyncVersion = syncVersion
|
|
676
|
-
await
|
|
741
|
+
await updateState()
|
|
677
742
|
}
|
|
678
743
|
poll()
|
|
679
744
|
}
|
|
@@ -689,30 +754,27 @@ async function poll() {
|
|
|
689
754
|
|
|
690
755
|
/** # Utils */
|
|
691
756
|
|
|
692
|
-
function
|
|
693
|
-
return args.filter(Boolean).join(' ')
|
|
757
|
+
function className(...args) {
|
|
758
|
+
return { className: args.filter(Boolean).join(' ') }
|
|
694
759
|
}
|
|
695
760
|
|
|
696
761
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
function createElement(elem, props = null, ...children) {
|
|
762
|
+
function createElement(elem, props, ...children) {
|
|
700
763
|
if (typeof elem === 'function')
|
|
701
764
|
return elem(props)
|
|
702
765
|
|
|
703
766
|
const node = document.createElement(elem)
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
node.setAttribute(key, value)
|
|
767
|
+
for (const [key, value] of Object.entries(props || {}))
|
|
768
|
+
if (key === 'ref')
|
|
769
|
+
value.current = node
|
|
770
|
+
else if (key.startsWith('on'))
|
|
771
|
+
node.addEventListener(key.replace(/^on/, '').toLowerCase(), value)
|
|
772
|
+
else if (key === 'style')
|
|
773
|
+
Object.assign(node.style, value)
|
|
774
|
+
else if (key in node)
|
|
775
|
+
node[key] = value
|
|
776
|
+
else
|
|
777
|
+
node.setAttribute(key, value)
|
|
716
778
|
node.append(...children.flat().filter(Boolean))
|
|
717
779
|
return node
|
|
718
780
|
}
|
|
@@ -730,7 +792,6 @@ function useRef() {
|
|
|
730
792
|
}
|
|
731
793
|
|
|
732
794
|
|
|
733
|
-
|
|
734
795
|
/**
|
|
735
796
|
* Think of this as a way of printing a directory tree in which
|
|
736
797
|
* the repeated folder paths are kept but styled differently.
|
package/src/Mockaton.js
CHANGED
|
@@ -63,7 +63,7 @@ async function onRequest(req, response) {
|
|
|
63
63
|
}
|
|
64
64
|
catch (error) {
|
|
65
65
|
if (error instanceof BodyReaderError)
|
|
66
|
-
sendUnprocessableContent(response, error.name)
|
|
66
|
+
sendUnprocessableContent(response, `${error.name}: ${error.message}`)
|
|
67
67
|
else
|
|
68
68
|
sendInternalServerError(response, error)
|
|
69
69
|
}
|
package/src/logo.svg
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="556" height="100" version="1.1" viewBox="0 0 556 100" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<style>:root { --color: #000000; }
|
|
4
|
+
@media (prefers-color-scheme: light) { :root { --color: #444 } }
|
|
5
|
+
@media (prefers-color-scheme: dark) { :root { --color: #eee } }
|
|
6
|
+
path { fill: var(--color) }
|
|
7
|
+
</style>
|
|
8
|
+
<g stroke-opacity=".99608">
|
|
9
|
+
<path
|
|
10
|
+
d="m13.75 1.8789c-5.9487 0.19352-10.865 4.5652-11.082 11.686v81.445c-1e-7 2.216 1.784 4 4 4h4.793c2.216 0 4-1.784 4-4v-64.982c0.02794-3.4488 3.0988-3.5551 4.2031-1.1562l16.615 59.059c1.4393 5.3711 5.1083 7.9633 8.7656 7.9473 3.6573 0.01603 7.3263-2.5762 8.7656-7.9473l16.615-59.059c1.1043-2.3989 4.1752-2.2925 4.2031 1.1562v64.982c0 2.216 1.784 4 4 4h4.793c2.216 0 4-1.784 4-4v-81.445c-0.17732-7.0807-5.1334-11.492-11.082-11.686-5.9487-0.19352-12.652 3.8309-15.609 13.619l-15.686 57.334-15.686-57.334c-2.9569-9.7882-9.6607-13.813-15.609-13.619zm239.19 0.074219c-2.216 0-4 1.784-4 4v89.057c0 2.216 1.784 4 4 4h4.793c2.216 0 3.9868-1.784 4-4l0.10644-17.94c0.0734-0.07237 12.175-13.75 12.175-13.75 5.6772 11.091 11.404 22.158 17.113 33.232 1.0168 1.9689 3.4217 2.7356 5.3906 1.7188l4.2578-2.1992c1.9689-1.0168 2.7356-3.4217 1.7188-5.3906-6.4691-12.585-12.958-25.16-19.442-37.738l17.223-19.771c1.4555-1.671 1.2803-4.189-0.39062-5.6445l-3.6133-3.1465c-0.73105-0.63679-1.6224-0.96212-2.5176-0.98633-1.151-0.03113-2.3063 0.43508-3.125 1.375l-28.896 33.174v-51.99c0-2.216-1.784-4-4-4zm-58.255 23.316c-10.699 0-19.312 8.6137-19.312 19.312v34.535c0 10.699 8.6137 19.312 19.312 19.312h19.717c10.699 0 19.311-8.6137 19.311-19.312l-0.125-7.8457c0-2.216-1.784-4-4-4h-4.6524c-2.216 0-4 1.784-4 4l3e-3 6.7888c3e-3 3.8063-1.5601 9.3694-8.4716 9.3694h-15.846c-6.9115 0-8.4766-5.5631-8.4766-12.475v-26.209c0-6.9115 1.5651-12.477 8.4766-12.477h15.846c6.6937 0 8.3697 5.2207 8.4687 11.828v2.2207c0 2.216 1.784 4 4 4h4.6524c2.216 0 4-1.784 4-4l0.125-5.7363c0-10.699-8.6117-19.312-19.311-19.312zm-72.182 0c-10.699 0-19.312 8.6137-19.312 19.312v34.535c0 10.699 8.6137 19.312 19.312 19.312h19.717c10.699 0 19.311-8.6137 19.311-19.312v-34.535c0-10.699-8.6117-19.312-19.311-19.312zm1.9356 11h15.846c6.9115 0 8.4746 5.5651 8.4746 12.477v26.209c0 6.9115-1.5631 12.475-8.4746 12.475h-15.846c-6.9115 0-8.4766-5.5631-8.4766-12.475v-26.209c0-6.9115 1.5651-12.477 8.4766-12.477z"/>
|
|
11
|
+
<path
|
|
12
|
+
d="m331.9 25.27c-10.699 0-19.312 8.6137-19.312 19.312v4.3682c0 2.216 1.784 4 4 4h4.7715c2.216 0 4-1.784 4-4v-0.20414c0-6.9115 1.5651-12.477 8.4766-12.477h15.846c6.9115 0 8.4746 5.5651 8.4746 12.477v7.0148h-28.059c-10.699 0-19.312 8.6117-19.312 19.311v4.0477c0 10.699 8.6137 19.313 19.312 19.312h17.812c2.216-1e-6 4-1.784 4-4v-4.7715c0-2.216-1.784-4-4-4h-13.648c-6.9115-2e-5 -12.477-1.5651-12.477-8.5649 0-6.9998 5.5651-8.5629 12.477-8.5629h23.895v25.897c0 2.216 1.784 4 4 4h4.7715c2.216-1e-6 4-1.784 4-4v-49.848c0-10.699-8.6117-19.312-19.311-19.312z"
|
|
13
|
+
opacity="0.8"/>
|
|
14
|
+
<path
|
|
15
|
+
d="m392.75 1.373c-2.216 0-4 1.784-4 4v18.043h-5.3086c-2.216 0-4 1.784-4 4v4.793c0 2.216 1.784 4 4 4h5.3086v51.398c0 6.1465 3.7064 10.823 9.232 10.823h16.531c2.216 0 4-1.784 4-4v-4.793c0-2.216-1.784-4-4-4h-12.97v-49.428h9.8711c2.216 0 4-1.784 4-4v-4.793c0-2.216-1.784-4-4-4h-9.8711v-18.043c0-2.216-1.784-4-4-4zm122.96 23.896c-10.699 0-19.312 8.6137-19.312 19.312v49.812c0 2.216 1.784 4 4 4h4.7715c2.216 0 4-1.784 4-4v-45.648c0-6.9115 1.5651-12.477 8.4766-12.477h15.846c6.9115 0 8.4746 5.5651 8.4746 12.477v45.684c0 2.216 1.784 4 4 4h4.7715c2.216-1e-6 4-1.784 4-4v-49.848c0-10.699-8.6117-19.312-19.311-19.312zm-69.999 0c-10.699 0-19.312 8.6137-19.312 19.312v34.535c0 10.699 8.6137 19.312 19.312 19.312h19.717c10.699 0 19.311-8.6137 19.311-19.312v-34.535c0-10.699-8.6117-19.312-19.311-19.312zm1.9356 11h15.846c6.9115 0 8.4746 5.5651 8.4746 12.477v26.209c0 6.9115-1.5631 12.475-8.4746 12.475h-15.846c-6.9115 0-8.4766-5.5631-8.4766-12.475v-26.209c0-6.9115 1.5651-12.477 8.4766-12.477z"/>
|
|
16
|
+
</g>
|
|
17
|
+
</svg>
|
package/src/staticCollection.js
CHANGED
|
@@ -3,7 +3,7 @@ import { listFilesRecursively } from './utils/fs.js'
|
|
|
3
3
|
import { config, isFileAllowed } from './config.js'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
class StaticBroker {
|
|
6
|
+
export class StaticBroker {
|
|
7
7
|
constructor(route) {
|
|
8
8
|
this.route = route
|
|
9
9
|
this.delayed = false
|
|
@@ -14,7 +14,7 @@ class StaticBroker {
|
|
|
14
14
|
setStatus(value) { this.status = value }
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/** @type {{ [route:
|
|
17
|
+
/** @type {{ [route:string]: StaticBroker }} */
|
|
18
18
|
let collection = {}
|
|
19
19
|
|
|
20
20
|
export const all = () => collection
|