mockaton 10.4.2 → 10.5.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/package.json +1 -1
- package/src/Dashboard.js +115 -130
- package/src/Mockaton.js +7 -6
package/package.json
CHANGED
package/src/Dashboard.js
CHANGED
|
@@ -3,40 +3,6 @@ import { parseFilename, extractComments } from './Filename.js'
|
|
|
3
3
|
import { Commander } from './ApiCommander.js'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
const Strings = {
|
|
7
|
-
auto500: 'Auto500',
|
|
8
|
-
bulk_select: 'Bulk Select',
|
|
9
|
-
bulk_select_disabled_title: 'No mock files have comments, which are anything within parentheses on the filename.',
|
|
10
|
-
click_link_to_preview: 'Click a link to preview it',
|
|
11
|
-
cookie: 'Cookie',
|
|
12
|
-
cookie_disabled_title: 'No cookies specified in config.cookies',
|
|
13
|
-
delay: 'Delay',
|
|
14
|
-
delay_ms: 'Delay (ms)',
|
|
15
|
-
empty_response_body: '/* Empty Response Body */',
|
|
16
|
-
error_server_down: 'Looks like the Mockaton server is not running',
|
|
17
|
-
error_unexpected_error: 'Unexpected Error',
|
|
18
|
-
fallback_server: 'Fallback',
|
|
19
|
-
fallback_server_error: '⛔ Fallback Backend Error',
|
|
20
|
-
fallback_server_placeholder: 'Type backend address',
|
|
21
|
-
fetching: 'Fetching…',
|
|
22
|
-
got: 'Got',
|
|
23
|
-
group_by_method: 'Group by Method',
|
|
24
|
-
internal_server_error: 'Internal Server Error',
|
|
25
|
-
mock_selector: 'Mock Selector',
|
|
26
|
-
no_mocks_found: 'No mocks found',
|
|
27
|
-
none: 'None',
|
|
28
|
-
not_found: 'Not Found',
|
|
29
|
-
pick_comment: 'Pick Comment…',
|
|
30
|
-
preview: 'Preview',
|
|
31
|
-
proxied: 'Proxied',
|
|
32
|
-
proxy_toggler: 'Proxy Toggler',
|
|
33
|
-
reset: 'Reset',
|
|
34
|
-
save_proxied: 'Save Mocks',
|
|
35
|
-
settings: 'Settings',
|
|
36
|
-
static_get: 'Static GET',
|
|
37
|
-
title: 'Mockaton'
|
|
38
|
-
}
|
|
39
|
-
|
|
40
6
|
const CSS = {
|
|
41
7
|
BulkSelector: null,
|
|
42
8
|
CookieSelector: null,
|
|
@@ -72,26 +38,19 @@ const CSS = {
|
|
|
72
38
|
status4xx: null,
|
|
73
39
|
|
|
74
40
|
json: null,
|
|
75
|
-
syntaxKey: null,
|
|
76
|
-
syntaxStr: null,
|
|
77
|
-
syntaxVal: null,
|
|
78
|
-
|
|
79
41
|
syntaxAttr: null,
|
|
80
42
|
syntaxAttrVal: null,
|
|
43
|
+
syntaxKey: null,
|
|
44
|
+
syntaxPunc: null,
|
|
45
|
+
syntaxStr: null,
|
|
81
46
|
syntaxTag: null,
|
|
82
|
-
|
|
47
|
+
syntaxVal: null
|
|
83
48
|
}
|
|
84
49
|
for (const k of Object.keys(CSS))
|
|
85
50
|
CSS[k] = k
|
|
86
51
|
|
|
87
52
|
|
|
88
|
-
/** @type {State
|
|
89
|
-
* canProxy: boolean
|
|
90
|
-
* groupByMethod: boolean
|
|
91
|
-
* toggleGroupByMethod: () => void
|
|
92
|
-
* leftSideWidth?: number
|
|
93
|
-
* }} */
|
|
94
|
-
const state = {
|
|
53
|
+
const state = /** @type {State} */ {
|
|
95
54
|
brokersByMethod: {},
|
|
96
55
|
staticBrokers: {},
|
|
97
56
|
cookies: [],
|
|
@@ -101,18 +60,20 @@ const state = {
|
|
|
101
60
|
collectProxied: false,
|
|
102
61
|
proxyFallback: '',
|
|
103
62
|
get canProxy() {
|
|
104
|
-
return Boolean(
|
|
63
|
+
return Boolean(state.proxyFallback)
|
|
105
64
|
},
|
|
106
65
|
|
|
107
|
-
groupByMethod:
|
|
66
|
+
groupByMethod: initPreference('groupByMethod'),
|
|
108
67
|
toggleGroupByMethod() {
|
|
109
|
-
|
|
110
|
-
|
|
68
|
+
state.groupByMethod = !state.groupByMethod
|
|
69
|
+
togglePreference('groupByMethod', state.groupByMethod)
|
|
70
|
+
updateState()
|
|
111
71
|
},
|
|
112
72
|
|
|
113
73
|
leftSideWidth: undefined
|
|
114
74
|
}
|
|
115
75
|
|
|
76
|
+
|
|
116
77
|
const mockaton = new Commander(location.origin)
|
|
117
78
|
updateState()
|
|
118
79
|
initLongPoll()
|
|
@@ -133,6 +94,8 @@ async function updateState() {
|
|
|
133
94
|
const r = createElement
|
|
134
95
|
const s = createSvgElement
|
|
135
96
|
|
|
97
|
+
const t = translation => translation[0]
|
|
98
|
+
|
|
136
99
|
const leftSideRef = useRef()
|
|
137
100
|
|
|
138
101
|
function App() {
|
|
@@ -159,7 +122,7 @@ function Header() {
|
|
|
159
122
|
return (
|
|
160
123
|
r('header', null,
|
|
161
124
|
r('img', {
|
|
162
|
-
alt:
|
|
125
|
+
alt: t`Mockaton`,
|
|
163
126
|
src: 'mockaton/Logo.svg',
|
|
164
127
|
width: 160
|
|
165
128
|
}),
|
|
@@ -173,47 +136,38 @@ function Header() {
|
|
|
173
136
|
}
|
|
174
137
|
|
|
175
138
|
function SettingsMenu() {
|
|
176
|
-
const
|
|
139
|
+
const { groupByMethod, toggleGroupByMethod } = state
|
|
140
|
+
|
|
141
|
+
const id = '_settings_menu_'
|
|
142
|
+
return (
|
|
143
|
+
r('button', {
|
|
144
|
+
title: t`Settings`,
|
|
145
|
+
popovertarget: id,
|
|
146
|
+
className: CSS.MenuTrigger
|
|
147
|
+
},
|
|
148
|
+
SettingsIcon(),
|
|
177
149
|
|
|
178
|
-
function MenuContent() {
|
|
179
|
-
return (
|
|
180
150
|
r('menu', {
|
|
181
151
|
id,
|
|
152
|
+
deferred: true,
|
|
182
153
|
popover: '',
|
|
183
|
-
className: CSS.SettingsMenu
|
|
184
|
-
onToggle(event) {
|
|
185
|
-
if (event.newState === 'closed')
|
|
186
|
-
this.parentNode.removeChild(this)
|
|
187
|
-
}
|
|
154
|
+
className: CSS.SettingsMenu
|
|
188
155
|
},
|
|
156
|
+
|
|
189
157
|
r('label', className(CSS.GroupByMethod),
|
|
190
158
|
r('input', {
|
|
191
159
|
type: 'checkbox',
|
|
192
|
-
checked:
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
updateState()
|
|
196
|
-
}
|
|
160
|
+
checked: groupByMethod,
|
|
161
|
+
autofocus: true,
|
|
162
|
+
onChange: toggleGroupByMethod
|
|
197
163
|
}),
|
|
198
|
-
r('span', null,
|
|
164
|
+
r('span', null, t`Group by Method`)),
|
|
199
165
|
|
|
200
166
|
r('a', {
|
|
201
167
|
href: 'https://github.com/ericfortis/mockaton',
|
|
202
168
|
target: '_blank',
|
|
203
169
|
rel: 'noopener noreferrer'
|
|
204
|
-
},
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return (
|
|
208
|
-
r('button', {
|
|
209
|
-
title: Strings.settings,
|
|
210
|
-
onClick() {
|
|
211
|
-
if (!this.querySelector('menu'))
|
|
212
|
-
this.appendChild(MenuContent())
|
|
213
|
-
},
|
|
214
|
-
className: CSS.MenuTrigger,
|
|
215
|
-
popovertarget: id
|
|
216
|
-
}, SettingsIcon()))
|
|
170
|
+
}, t`Documentation`))))
|
|
217
171
|
}
|
|
218
172
|
|
|
219
173
|
function CookieSelector() {
|
|
@@ -224,14 +178,14 @@ function CookieSelector() {
|
|
|
224
178
|
.catch(onError)
|
|
225
179
|
}
|
|
226
180
|
const disabled = cookies.length <= 1
|
|
227
|
-
const list = cookies.length ? cookies : [[
|
|
181
|
+
const list = cookies.length ? cookies : [[t`None`, true]]
|
|
228
182
|
return (
|
|
229
183
|
r('label', className(CSS.Field, CSS.CookieSelector),
|
|
230
|
-
r('span', null,
|
|
184
|
+
r('span', null, t`Cookie`),
|
|
231
185
|
r('select', {
|
|
232
186
|
autocomplete: 'off',
|
|
233
187
|
disabled,
|
|
234
|
-
title: disabled ?
|
|
188
|
+
title: disabled ? t`No cookies specified in config.cookies` : '',
|
|
235
189
|
onChange
|
|
236
190
|
}, list.map(([value, selected]) =>
|
|
237
191
|
r('option', { value, selected }, value)))))
|
|
@@ -241,7 +195,7 @@ function BulkSelector() {
|
|
|
241
195
|
const { comments } = state
|
|
242
196
|
// UX wise this should be a menu instead of this `select`.
|
|
243
197
|
// But this way is easier to implement, with a few hacks.
|
|
244
|
-
const firstOption =
|
|
198
|
+
const firstOption = t`Pick Comment…`
|
|
245
199
|
function onChange() {
|
|
246
200
|
const value = this.value
|
|
247
201
|
this.value = firstOption // Hack
|
|
@@ -254,12 +208,12 @@ function BulkSelector() {
|
|
|
254
208
|
const disabled = !comments.length
|
|
255
209
|
return (
|
|
256
210
|
r('label', className(CSS.Field),
|
|
257
|
-
r('span', null,
|
|
211
|
+
r('span', null, t`Bulk Select`),
|
|
258
212
|
r('select', {
|
|
259
213
|
className: CSS.BulkSelector,
|
|
260
214
|
autocomplete: 'off',
|
|
261
215
|
disabled,
|
|
262
|
-
title: disabled ?
|
|
216
|
+
title: disabled ? t`No mock files have comments which are anything within parentheses on the filename.` : '',
|
|
263
217
|
onChange
|
|
264
218
|
},
|
|
265
219
|
r('option', { value: firstOption }, firstOption),
|
|
@@ -267,7 +221,7 @@ function BulkSelector() {
|
|
|
267
221
|
comments.map(value =>
|
|
268
222
|
r('option', { value }, value)),
|
|
269
223
|
r('hr'),
|
|
270
|
-
r('option', { value: AUTOGENERATED_500_COMMENT },
|
|
224
|
+
r('option', { value: AUTOGENERATED_500_COMMENT }, t`Auto500`)
|
|
271
225
|
)))
|
|
272
226
|
}
|
|
273
227
|
|
|
@@ -287,7 +241,7 @@ function GlobalDelayField() {
|
|
|
287
241
|
}
|
|
288
242
|
return (
|
|
289
243
|
r('label', className(CSS.Field, CSS.GlobalDelayField),
|
|
290
|
-
r('span', null,
|
|
244
|
+
r('span', null, t`Delay (ms)`),
|
|
291
245
|
r('input', {
|
|
292
246
|
type: 'number',
|
|
293
247
|
min: 0,
|
|
@@ -317,11 +271,11 @@ function ProxyFallbackField() {
|
|
|
317
271
|
return (
|
|
318
272
|
r('div', className(CSS.Field, CSS.FallbackBackend),
|
|
319
273
|
r('label', null,
|
|
320
|
-
r('span', null,
|
|
274
|
+
r('span', null, t`Fallback`),
|
|
321
275
|
r('input', {
|
|
322
276
|
type: 'url',
|
|
323
277
|
autocomplete: 'none',
|
|
324
|
-
placeholder:
|
|
278
|
+
placeholder: t`Type backend address`,
|
|
325
279
|
value: proxyFallback,
|
|
326
280
|
onChange
|
|
327
281
|
})),
|
|
@@ -343,7 +297,7 @@ function SaveProxiedCheckbox() {
|
|
|
343
297
|
checked: collectProxied,
|
|
344
298
|
onChange
|
|
345
299
|
}),
|
|
346
|
-
r('span', null,
|
|
300
|
+
r('span', null, t`Save Mocks`)))
|
|
347
301
|
}
|
|
348
302
|
|
|
349
303
|
function ResetButton() {
|
|
@@ -358,7 +312,7 @@ function ResetButton() {
|
|
|
358
312
|
r('button', {
|
|
359
313
|
className: CSS.ResetButton,
|
|
360
314
|
onClick
|
|
361
|
-
},
|
|
315
|
+
}, t`Reset`))
|
|
362
316
|
}
|
|
363
317
|
|
|
364
318
|
|
|
@@ -371,7 +325,7 @@ function MockList() {
|
|
|
371
325
|
if (!Object.keys(brokersByMethod).length)
|
|
372
326
|
return (
|
|
373
327
|
r('div', className(CSS.empty),
|
|
374
|
-
|
|
328
|
+
t`No mocks found`))
|
|
375
329
|
|
|
376
330
|
if (groupByMethod)
|
|
377
331
|
return Object.keys(brokersByMethod).map((method) => Fragment(
|
|
@@ -439,6 +393,8 @@ function PreviewLink(method, urlMask, urlMaskDittoed) {
|
|
|
439
393
|
: tail))
|
|
440
394
|
}
|
|
441
395
|
|
|
396
|
+
const STR_PROXIED = t`Proxied`
|
|
397
|
+
|
|
442
398
|
/** @param {ClientMockBroker} broker */
|
|
443
399
|
function MockSelector(broker) {
|
|
444
400
|
function onChange() {
|
|
@@ -456,20 +412,20 @@ function MockSelector(broker) {
|
|
|
456
412
|
status === 500 ||
|
|
457
413
|
!item.includes(AUTOGENERATED_500_COMMENT))
|
|
458
414
|
if (!selected) {
|
|
459
|
-
selected =
|
|
415
|
+
selected = STR_PROXIED
|
|
460
416
|
files.push(selected)
|
|
461
417
|
}
|
|
462
418
|
|
|
463
419
|
function nameFor(file) {
|
|
464
|
-
if (file ===
|
|
465
|
-
return
|
|
420
|
+
if (file === STR_PROXIED)
|
|
421
|
+
return STR_PROXIED
|
|
466
422
|
const { status, ext } = parseFilename(file)
|
|
467
423
|
const comments = extractComments(file)
|
|
468
424
|
const isAutogen500 = comments.includes(AUTOGENERATED_500_COMMENT)
|
|
469
425
|
return [
|
|
470
426
|
isAutogen500 ? '' : status,
|
|
471
427
|
ext === 'empty' || ext === 'unknown' ? '' : ext,
|
|
472
|
-
isAutogen500 ?
|
|
428
|
+
isAutogen500 ? t`Auto500` : comments.join(' ')
|
|
473
429
|
].filter(Boolean).join(' ')
|
|
474
430
|
}
|
|
475
431
|
|
|
@@ -477,7 +433,7 @@ function MockSelector(broker) {
|
|
|
477
433
|
r('select', {
|
|
478
434
|
onChange,
|
|
479
435
|
autocomplete: 'off',
|
|
480
|
-
'aria-label':
|
|
436
|
+
'aria-label': t`Mock Selector`,
|
|
481
437
|
disabled: files.length <= 1,
|
|
482
438
|
...className(
|
|
483
439
|
CSS.MockSelector,
|
|
@@ -518,7 +474,7 @@ function InternalServerErrorToggler(broker) {
|
|
|
518
474
|
return (
|
|
519
475
|
r('label', {
|
|
520
476
|
className: CSS.InternalServerErrorToggler,
|
|
521
|
-
title:
|
|
477
|
+
title: t`Internal Server Error`
|
|
522
478
|
},
|
|
523
479
|
r('input', {
|
|
524
480
|
type: 'checkbox',
|
|
@@ -526,7 +482,7 @@ function InternalServerErrorToggler(broker) {
|
|
|
526
482
|
checked: parseFilename(broker.currentMock.file).status === 500,
|
|
527
483
|
onChange
|
|
528
484
|
}),
|
|
529
|
-
r('span', null,
|
|
485
|
+
r('span', null, t`500`)))
|
|
530
486
|
}
|
|
531
487
|
|
|
532
488
|
/** @param {ClientMockBroker} broker */
|
|
@@ -542,7 +498,7 @@ function ProxyToggler(broker) {
|
|
|
542
498
|
return (
|
|
543
499
|
r('label', {
|
|
544
500
|
className: CSS.ProxyToggler,
|
|
545
|
-
title:
|
|
501
|
+
title: t`Proxy Toggler`
|
|
546
502
|
},
|
|
547
503
|
r('input', {
|
|
548
504
|
type: 'checkbox',
|
|
@@ -567,10 +523,10 @@ function StaticFilesList() {
|
|
|
567
523
|
Fragment(
|
|
568
524
|
r('tr', null,
|
|
569
525
|
r('th', { colspan: (2 + Number(!groupByMethod)) + Number(canProxy) }),
|
|
570
|
-
r('th', null,
|
|
526
|
+
r('th', null, t`Static GET`)),
|
|
571
527
|
Object.values(staticBrokers).map((broker, i) =>
|
|
572
528
|
r('tr', null,
|
|
573
|
-
canProxy && r('td'
|
|
529
|
+
canProxy && r('td'),
|
|
574
530
|
r('td', null, DelayStaticRouteToggler(broker)),
|
|
575
531
|
r('td', null, NotFoundToggler(broker)),
|
|
576
532
|
!groupByMethod && r('td', className(CSS.Method), 'GET'),
|
|
@@ -605,31 +561,14 @@ function NotFoundToggler(broker) {
|
|
|
605
561
|
return (
|
|
606
562
|
r('label', {
|
|
607
563
|
className: CSS.NotFoundToggler,
|
|
608
|
-
title:
|
|
564
|
+
title: t`Not Found`
|
|
609
565
|
},
|
|
610
566
|
r('input', {
|
|
611
567
|
type: 'checkbox',
|
|
612
568
|
checked: broker.status === 404,
|
|
613
569
|
onChange
|
|
614
570
|
}),
|
|
615
|
-
r('span', null,
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
function ProxyStaticToggler() { // TODO
|
|
619
|
-
function onChange() {
|
|
620
|
-
}
|
|
621
|
-
return (
|
|
622
|
-
r('label', {
|
|
623
|
-
style: { visibility: 'hidden' },
|
|
624
|
-
className: CSS.ProxyToggler,
|
|
625
|
-
title: Strings.proxy_toggler
|
|
626
|
-
},
|
|
627
|
-
r('input', {
|
|
628
|
-
type: 'checkbox',
|
|
629
|
-
disabled: true,
|
|
630
|
-
onChange
|
|
631
|
-
}),
|
|
632
|
-
CloudIcon()))
|
|
571
|
+
r('span', null, t`404`)))
|
|
633
572
|
}
|
|
634
573
|
|
|
635
574
|
|
|
@@ -652,7 +591,7 @@ function ClickDragToggler({ checked, commit }) {
|
|
|
652
591
|
return (
|
|
653
592
|
r('label', {
|
|
654
593
|
className: CSS.DelayToggler,
|
|
655
|
-
title:
|
|
594
|
+
title: t`Delay`
|
|
656
595
|
},
|
|
657
596
|
r('input', {
|
|
658
597
|
type: 'checkbox',
|
|
@@ -710,9 +649,9 @@ const payloadViewerRef = useRef()
|
|
|
710
649
|
function PayloadViewer() {
|
|
711
650
|
return (
|
|
712
651
|
r('div', className(CSS.PayloadViewer),
|
|
713
|
-
r('h2', { ref: payloadViewerTitleRef },
|
|
652
|
+
r('h2', { ref: payloadViewerTitleRef }, t`Preview`),
|
|
714
653
|
r('pre', null,
|
|
715
|
-
r('code', { ref: payloadViewerRef },
|
|
654
|
+
r('code', { ref: payloadViewerRef }, t`Click a link to preview it`))))
|
|
716
655
|
}
|
|
717
656
|
|
|
718
657
|
function PayloadViewerProgressBar() {
|
|
@@ -737,8 +676,8 @@ function PayloadViewerTitleWhenProxied({ mime, status, statusText, gatewayIsBad
|
|
|
737
676
|
return (
|
|
738
677
|
r('span', null,
|
|
739
678
|
gatewayIsBad
|
|
740
|
-
? r('span', className(CSS.red),
|
|
741
|
-
: r('span', null,
|
|
679
|
+
? r('span', className(CSS.red), t`⛔ Fallback Backend Error` + ' ')
|
|
680
|
+
: r('span', null, t`Got` + ' '),
|
|
742
681
|
r('abbr', { title: statusText }, status),
|
|
743
682
|
' ' + mime))
|
|
744
683
|
}
|
|
@@ -748,7 +687,7 @@ async function previewMock(method, urlMask, href) {
|
|
|
748
687
|
previewMock.controller = new AbortController
|
|
749
688
|
|
|
750
689
|
const spinnerTimer = setTimeout(() => {
|
|
751
|
-
payloadViewerTitleRef.current.replaceChildren(
|
|
690
|
+
payloadViewerTitleRef.current.replaceChildren(t`Fetching…`)
|
|
752
691
|
payloadViewerRef.current.replaceChildren(PayloadViewerProgressBar())
|
|
753
692
|
}, 80)
|
|
754
693
|
|
|
@@ -766,11 +705,12 @@ async function previewMock(method, urlMask, href) {
|
|
|
766
705
|
}
|
|
767
706
|
}
|
|
768
707
|
|
|
708
|
+
|
|
769
709
|
async function updatePayloadViewer(method, urlMask, response) {
|
|
770
710
|
const mime = response.headers.get('content-type') || ''
|
|
771
711
|
|
|
772
712
|
const file = mockSelectorFor(method, urlMask).value
|
|
773
|
-
if (file ===
|
|
713
|
+
if (file === STR_PROXIED)
|
|
774
714
|
payloadViewerTitleRef.current.replaceChildren(PayloadViewerTitleWhenProxied({
|
|
775
715
|
status: response.status,
|
|
776
716
|
statusText: response.statusText,
|
|
@@ -790,7 +730,7 @@ async function updatePayloadViewer(method, urlMask, response) {
|
|
|
790
730
|
}))
|
|
791
731
|
}
|
|
792
732
|
else {
|
|
793
|
-
const body = await response.text() ||
|
|
733
|
+
const body = await response.text() || t`/* Empty Response Body */`
|
|
794
734
|
if (mime === 'application/json')
|
|
795
735
|
payloadViewerRef.current.replaceChildren(r('span', className(CSS.json), syntaxJSON(body)))
|
|
796
736
|
else if (isXML(mime))
|
|
@@ -834,9 +774,9 @@ function onError(error) {
|
|
|
834
774
|
if (error?.name === 'AbortError')
|
|
835
775
|
return
|
|
836
776
|
if (error?.message === 'Failed to fetch')
|
|
837
|
-
showErrorToast(
|
|
777
|
+
showErrorToast(t`Looks like the Mockaton server is not running`)
|
|
838
778
|
else
|
|
839
|
-
showErrorToast(error ||
|
|
779
|
+
showErrorToast(error || t`Unexpected Error`)
|
|
840
780
|
console.error(error)
|
|
841
781
|
}
|
|
842
782
|
|
|
@@ -921,6 +861,14 @@ function className(...args) {
|
|
|
921
861
|
|
|
922
862
|
|
|
923
863
|
function createElement(tag, props, ...children) {
|
|
864
|
+
if (props?.deferred) {
|
|
865
|
+
delete props.deferred
|
|
866
|
+
const placeholder = document.createComment('')
|
|
867
|
+
deferred(() =>
|
|
868
|
+
placeholder.replaceWith(createElement(tag, props, ...children)))
|
|
869
|
+
return placeholder
|
|
870
|
+
}
|
|
871
|
+
|
|
924
872
|
const node = document.createElement(tag)
|
|
925
873
|
for (const [k, v] of Object.entries(props || {}))
|
|
926
874
|
if (k === 'ref') v.current = node
|
|
@@ -954,6 +902,43 @@ function Fragment(...args) {
|
|
|
954
902
|
return frag
|
|
955
903
|
}
|
|
956
904
|
|
|
905
|
+
function deferred(cb) {
|
|
906
|
+
return window.requestIdleCallback
|
|
907
|
+
? requestIdleCallback(cb)
|
|
908
|
+
: setTimeout(cb, 100) // Safari
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
// When false, the URL will be updated with param=false
|
|
913
|
+
function initPreference(param) {
|
|
914
|
+
const qs = new URLSearchParams(location.search)
|
|
915
|
+
if (!qs.has(param)) {
|
|
916
|
+
const group = localStorage.getItem(param) !== 'false'
|
|
917
|
+
if (!group) {
|
|
918
|
+
const url = new URL(location.href)
|
|
919
|
+
url.searchParams.set(param, false)
|
|
920
|
+
history.replaceState(null, '', url)
|
|
921
|
+
}
|
|
922
|
+
return group
|
|
923
|
+
}
|
|
924
|
+
return qs.get(param) !== 'false'
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// When false, the URL and localStorage will have param=false
|
|
928
|
+
function togglePreference(param, nextVal) {
|
|
929
|
+
if (nextVal)
|
|
930
|
+
localStorage.removeItem(param)
|
|
931
|
+
else
|
|
932
|
+
localStorage.setItem(param, nextVal)
|
|
933
|
+
|
|
934
|
+
const url = new URL(location.href)
|
|
935
|
+
if (nextVal)
|
|
936
|
+
url.searchParams.delete(param)
|
|
937
|
+
else
|
|
938
|
+
url.searchParams.set(param, false)
|
|
939
|
+
history.replaceState(null, '', url)
|
|
940
|
+
}
|
|
941
|
+
|
|
957
942
|
|
|
958
943
|
/**
|
|
959
944
|
* Think of this as a way of printing a directory tree in which
|
package/src/Mockaton.js
CHANGED
|
@@ -52,8 +52,9 @@ async function onRequest(req, response) {
|
|
|
52
52
|
sendBadRequest(response)
|
|
53
53
|
return
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
try {
|
|
57
|
+
const route = new URL(url, 'http://_').pathname
|
|
57
58
|
const { method } = req
|
|
58
59
|
|
|
59
60
|
if (config.corsAllowed)
|
|
@@ -61,11 +62,11 @@ async function onRequest(req, response) {
|
|
|
61
62
|
|
|
62
63
|
if (isPreflight(req))
|
|
63
64
|
sendNoContent(response)
|
|
64
|
-
else if (method === 'PATCH' && apiPatchRequests.has(
|
|
65
|
-
await apiPatchRequests.get(
|
|
66
|
-
else if (method === 'GET' && apiGetRequests.has(
|
|
67
|
-
apiGetRequests.get(
|
|
68
|
-
else if (method === 'GET' && staticCollection.brokerByRoute(
|
|
65
|
+
else if (method === 'PATCH' && apiPatchRequests.has(route))
|
|
66
|
+
await apiPatchRequests.get(route)(req, response)
|
|
67
|
+
else if (method === 'GET' && apiGetRequests.has(route))
|
|
68
|
+
apiGetRequests.get(route)(req, response)
|
|
69
|
+
else if (method === 'GET' && staticCollection.brokerByRoute(route))
|
|
69
70
|
await dispatchStatic(req, response)
|
|
70
71
|
else
|
|
71
72
|
await dispatchMock(req, response)
|