mockaton 10.6.1 → 10.6.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/package.json +2 -2
- package/src/Dashboard.js +108 -75
- package/src/DashboardHtml.js +11 -11
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "mockaton",
|
|
3
3
|
"description": "HTTP Mock Server",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "10.6.
|
|
5
|
+
"version": "10.6.2",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
8
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"start": "node src/cli.js",
|
|
24
24
|
"watch": "node --watch src/cli.js",
|
|
25
25
|
"pixaton": "node --test --import=./pixaton-tests/_setup.js --experimental-test-isolation=none 'pixaton-tests/**/*.test.js'",
|
|
26
|
-
"outdated": "npm outdated --parseable | awk -F: '{ printf \"npm i
|
|
26
|
+
"outdated": "npm outdated --parseable | awk -F: '{ printf \"npm i %s ;# %s\\n\", $4, $2 }'"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"pixaton": "1.1.3",
|
package/src/Dashboard.js
CHANGED
|
@@ -69,6 +69,8 @@ const state = /** @type {State} */ {
|
|
|
69
69
|
return Boolean(state.proxyFallback)
|
|
70
70
|
},
|
|
71
71
|
|
|
72
|
+
leftSideWidth: window.innerWidth / 2,
|
|
73
|
+
|
|
72
74
|
groupByMethod: initPreference('groupByMethod'),
|
|
73
75
|
toggleGroupByMethod() {
|
|
74
76
|
state.groupByMethod = !state.groupByMethod
|
|
@@ -76,10 +78,7 @@ const state = /** @type {State} */ {
|
|
|
76
78
|
updateState()
|
|
77
79
|
},
|
|
78
80
|
|
|
79
|
-
leftSideWidth: window.innerWidth / 2,
|
|
80
|
-
|
|
81
81
|
chosenLink: { method: '', urlMask: '' },
|
|
82
|
-
clearChosenLink() { state.setChosenLink('', '') },
|
|
83
82
|
setChosenLink(method, urlMask) {
|
|
84
83
|
state.chosenLink = { method, urlMask }
|
|
85
84
|
}
|
|
@@ -88,7 +87,7 @@ const state = /** @type {State} */ {
|
|
|
88
87
|
|
|
89
88
|
const mockaton = new Commander(location.origin)
|
|
90
89
|
updateState()
|
|
91
|
-
|
|
90
|
+
initRealTimeUpdates()
|
|
92
91
|
initKeyboardNavigation()
|
|
93
92
|
|
|
94
93
|
async function updateState() {
|
|
@@ -96,13 +95,18 @@ async function updateState() {
|
|
|
96
95
|
const response = await mockaton.getState()
|
|
97
96
|
if (!response.ok)
|
|
98
97
|
throw response.status
|
|
98
|
+
|
|
99
99
|
Object.assign(state, await response.json())
|
|
100
|
+
|
|
101
|
+
const focusedElem = selectorFor(document.activeElement)
|
|
100
102
|
document.body.replaceChildren(...App())
|
|
103
|
+
if (focusedElem)
|
|
104
|
+
document.querySelector(focusedElem)?.focus()
|
|
101
105
|
|
|
102
|
-
findChosenLink()?.focus()
|
|
103
106
|
const { method, urlMask } = state.chosenLink
|
|
104
107
|
if (method && urlMask)
|
|
105
108
|
await previewMock(method, urlMask)
|
|
109
|
+
|
|
106
110
|
}
|
|
107
111
|
catch (error) {
|
|
108
112
|
onError(error)
|
|
@@ -167,7 +171,7 @@ function SettingsMenuTrigger() {
|
|
|
167
171
|
|
|
168
172
|
function SettingsMenu(id) {
|
|
169
173
|
const { groupByMethod, toggleGroupByMethod } = state
|
|
170
|
-
|
|
174
|
+
|
|
171
175
|
const firstInputRef = useRef()
|
|
172
176
|
function onToggle(event) {
|
|
173
177
|
if (event.newState === 'open')
|
|
@@ -225,13 +229,11 @@ function BulkSelector() {
|
|
|
225
229
|
// But this way is easier to implement, with a few hacks.
|
|
226
230
|
const firstOption = t`Pick Comment…`
|
|
227
231
|
function onChange() {
|
|
228
|
-
state.clearChosenLink()
|
|
229
232
|
const value = this.value
|
|
230
233
|
this.value = firstOption // Hack
|
|
231
234
|
mockaton.bulkSelectByComment(value)
|
|
232
235
|
.then(parseError)
|
|
233
236
|
.then(updateState)
|
|
234
|
-
.then(() => focus(`.${CSS.BulkSelector}`))
|
|
235
237
|
.catch(onError)
|
|
236
238
|
}
|
|
237
239
|
const disabled = !comments.length
|
|
@@ -296,7 +298,6 @@ function ProxyFallbackField() {
|
|
|
296
298
|
mockaton.setProxyFallback(this.value.trim())
|
|
297
299
|
.then(parseError)
|
|
298
300
|
.then(updateState)
|
|
299
|
-
.then(() => focus(`.${CSS.FallbackBackend} input`))
|
|
300
301
|
.catch(onError)
|
|
301
302
|
}
|
|
302
303
|
return (
|
|
@@ -334,11 +335,9 @@ function SaveProxiedCheckbox(ref) {
|
|
|
334
335
|
|
|
335
336
|
function ResetButton() {
|
|
336
337
|
function onClick() {
|
|
337
|
-
state.clearChosenLink()
|
|
338
338
|
mockaton.reset()
|
|
339
339
|
.then(parseError)
|
|
340
340
|
.then(updateState)
|
|
341
|
-
.then(() => focus(`.${CSS.ResetButton}`))
|
|
342
341
|
.catch(onError)
|
|
343
342
|
}
|
|
344
343
|
return (
|
|
@@ -374,7 +373,7 @@ function MockList() {
|
|
|
374
373
|
function Row({ method, urlMask, urlMaskDittoed, broker }, i) {
|
|
375
374
|
const { canProxy, groupByMethod } = state
|
|
376
375
|
return (
|
|
377
|
-
r('tr', {
|
|
376
|
+
r('tr', { key: method + '::' + urlMask },
|
|
378
377
|
canProxy && r('td', null, ProxyToggler(broker)),
|
|
379
378
|
r('td', null, DelayRouteToggler(broker)),
|
|
380
379
|
r('td', null, InternalServerErrorToggler(broker)),
|
|
@@ -627,6 +626,7 @@ function ClickDragToggler({ checked, commit, focusGroup }) {
|
|
|
627
626
|
}
|
|
628
627
|
function onPointerDown() {
|
|
629
628
|
this.checked = !this.checked
|
|
629
|
+
this.focus()
|
|
630
630
|
commit(this.checked)
|
|
631
631
|
}
|
|
632
632
|
function onClick(event) {
|
|
@@ -655,38 +655,48 @@ function ClickDragToggler({ checked, commit, focusGroup }) {
|
|
|
655
655
|
|
|
656
656
|
|
|
657
657
|
function Resizer() {
|
|
658
|
+
let raf = 0
|
|
659
|
+
let initialX = 0
|
|
660
|
+
let panelWidth = 0
|
|
661
|
+
|
|
662
|
+
function onPointerDown(event) {
|
|
663
|
+
initialX = event.clientX
|
|
664
|
+
panelWidth = leftSideRef.current.clientWidth
|
|
665
|
+
addEventListener('pointerup', onUp, { once: true })
|
|
666
|
+
addEventListener('pointermove', onMove)
|
|
667
|
+
Object.assign(document.body.style, {
|
|
668
|
+
cursor: 'col-resize',
|
|
669
|
+
userSelect: 'none',
|
|
670
|
+
pointerEvents: 'none'
|
|
671
|
+
})
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function onMove(event) {
|
|
675
|
+
const MIN_LEFT_WIDTH = 380
|
|
676
|
+
raf = raf || requestAnimationFrame(() => {
|
|
677
|
+
state.leftSideWidth = Math.max(panelWidth - (initialX - event.clientX), MIN_LEFT_WIDTH)
|
|
678
|
+
leftSideRef.current.style.width = state.leftSideWidth + 'px'
|
|
679
|
+
raf = 0
|
|
680
|
+
})
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function onUp() {
|
|
684
|
+
removeEventListener('pointermove', onMove)
|
|
685
|
+
cancelAnimationFrame(raf)
|
|
686
|
+
raf = 0
|
|
687
|
+
Object.assign(document.body.style, {
|
|
688
|
+
cursor: 'auto',
|
|
689
|
+
userSelect: 'auto',
|
|
690
|
+
pointerEvents: 'auto'
|
|
691
|
+
})
|
|
692
|
+
}
|
|
693
|
+
|
|
658
694
|
return (
|
|
659
695
|
r('div', {
|
|
660
696
|
className: CSS.Resizer,
|
|
661
|
-
onPointerDown
|
|
697
|
+
onPointerDown
|
|
662
698
|
}))
|
|
663
699
|
}
|
|
664
|
-
Resizer.raf = 0
|
|
665
|
-
Resizer.initialX = 0
|
|
666
|
-
Resizer.panelWidth = 0
|
|
667
|
-
Resizer.onPointerDown = function (event) {
|
|
668
|
-
Resizer.initialX = event.clientX
|
|
669
|
-
Resizer.panelWidth = leftSideRef.current.clientWidth
|
|
670
|
-
addEventListener('pointerup', Resizer.onUp, { once: true })
|
|
671
|
-
addEventListener('pointermove', Resizer.onMove)
|
|
672
|
-
document.body.style.userSelect = 'none'
|
|
673
|
-
document.body.style.cursor = 'col-resize'
|
|
674
|
-
}
|
|
675
|
-
Resizer.onMove = function (event) {
|
|
676
|
-
const MIN_LEFT_WIDTH = 380
|
|
677
|
-
Resizer.raf = Resizer.raf || requestAnimationFrame(() => {
|
|
678
|
-
state.leftSideWidth = Math.max(Resizer.panelWidth - (Resizer.initialX - event.clientX), MIN_LEFT_WIDTH)
|
|
679
|
-
leftSideRef.current.style.width = state.leftSideWidth + 'px'
|
|
680
|
-
Resizer.raf = 0
|
|
681
|
-
})
|
|
682
|
-
}
|
|
683
|
-
Resizer.onUp = function () {
|
|
684
|
-
removeEventListener('pointermove', Resizer.onMove)
|
|
685
|
-
cancelAnimationFrame(Resizer.raf)
|
|
686
|
-
Resizer.raf = 0
|
|
687
|
-
document.body.style.userSelect = 'auto'
|
|
688
|
-
document.body.style.cursor = 'auto'
|
|
689
|
-
}
|
|
690
700
|
|
|
691
701
|
|
|
692
702
|
/** # Payload Preview */
|
|
@@ -758,7 +768,9 @@ async function previewMock(method, urlMask) {
|
|
|
758
768
|
async function updatePayloadViewer(method, urlMask, response) {
|
|
759
769
|
const mime = response.headers.get('content-type') || ''
|
|
760
770
|
|
|
761
|
-
const file = mockSelectorFor(method, urlMask)
|
|
771
|
+
const file = mockSelectorFor(method, urlMask)?.value
|
|
772
|
+
if (!file)
|
|
773
|
+
return // e.g. selected was deleted
|
|
762
774
|
if (file === STR_PROXIED)
|
|
763
775
|
payloadViewerTitleRef.current.replaceChildren(PayloadViewerTitleWhenProxied({
|
|
764
776
|
status: response.status,
|
|
@@ -796,15 +808,10 @@ function isXML(mime) {
|
|
|
796
808
|
|
|
797
809
|
|
|
798
810
|
function mockSelectorFor(method, urlMask) {
|
|
799
|
-
|
|
800
|
-
}
|
|
801
|
-
function trFor(method, urlMask) {
|
|
802
|
-
return document.querySelector(`tr[data-method="${method}"][data-urlMask="${urlMask}"]`)
|
|
811
|
+
const tr = document.querySelector(`tr[key="${method}::${urlMask}"]`)
|
|
812
|
+
return tr?.querySelector(`.${CSS.MockSelector}`)
|
|
803
813
|
}
|
|
804
814
|
|
|
805
|
-
function focus(selector) {
|
|
806
|
-
document.querySelector(selector)?.focus()
|
|
807
|
-
}
|
|
808
815
|
|
|
809
816
|
function initKeyboardNavigation() {
|
|
810
817
|
addEventListener('keydown', onKeyDown)
|
|
@@ -825,7 +832,7 @@ function initKeyboardNavigation() {
|
|
|
825
832
|
case 'ArrowLeft': {
|
|
826
833
|
if (pivot.hasAttribute('data-focus-group') || pivot.classList.contains(CSS.MockSelector)) {
|
|
827
834
|
const offset = event.key === 'ArrowRight' ? +1 : -1
|
|
828
|
-
rowFocusable(pivot, offset)
|
|
835
|
+
rowFocusable(pivot, offset).focus()
|
|
829
836
|
}
|
|
830
837
|
break
|
|
831
838
|
}
|
|
@@ -850,7 +857,7 @@ function initKeyboardNavigation() {
|
|
|
850
857
|
}
|
|
851
858
|
}
|
|
852
859
|
|
|
853
|
-
/** #
|
|
860
|
+
/** # Error */
|
|
854
861
|
|
|
855
862
|
async function parseError(response) {
|
|
856
863
|
if (response.ok)
|
|
@@ -882,6 +889,9 @@ function showErrorToast(msg) {
|
|
|
882
889
|
}, msg))
|
|
883
890
|
}
|
|
884
891
|
|
|
892
|
+
|
|
893
|
+
/** # Icons */
|
|
894
|
+
|
|
885
895
|
function TimerIcon() {
|
|
886
896
|
return (
|
|
887
897
|
s('svg', { viewBox: '0 0 24 24' },
|
|
@@ -902,43 +912,43 @@ function SettingsIcon() {
|
|
|
902
912
|
}
|
|
903
913
|
|
|
904
914
|
/**
|
|
905
|
-
* #
|
|
906
|
-
* The version increments when a mock file is added or
|
|
915
|
+
* # Long polls UI sync version
|
|
916
|
+
* The version increments when a mock file is added, removed, or renamed.
|
|
907
917
|
*/
|
|
918
|
+
function initRealTimeUpdates() {
|
|
919
|
+
let oldSyncVersion = -1
|
|
920
|
+
let controller = new AbortController()
|
|
908
921
|
|
|
909
|
-
function initLongPoll() {
|
|
910
|
-
poll.oldSyncVersion = -1
|
|
911
|
-
poll.controller = new AbortController()
|
|
912
922
|
poll()
|
|
913
923
|
document.addEventListener('visibilitychange', () => {
|
|
914
924
|
if (document.hidden) {
|
|
915
|
-
|
|
916
|
-
|
|
925
|
+
controller.abort('_hidden_tab_')
|
|
926
|
+
controller = new AbortController()
|
|
917
927
|
}
|
|
918
928
|
else
|
|
919
929
|
poll()
|
|
920
930
|
})
|
|
921
|
-
}
|
|
922
931
|
|
|
923
|
-
async function poll() {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
932
|
+
async function poll() {
|
|
933
|
+
try {
|
|
934
|
+
const response = await mockaton.getSyncVersion(oldSyncVersion, controller.signal)
|
|
935
|
+
if (response.ok) {
|
|
936
|
+
const syncVersion = await response.json()
|
|
937
|
+
const skipUpdate = oldSyncVersion === -1
|
|
938
|
+
if (oldSyncVersion !== syncVersion) { // because it could be < or >
|
|
939
|
+
oldSyncVersion = syncVersion
|
|
940
|
+
if (!skipUpdate)
|
|
941
|
+
await updateState()
|
|
942
|
+
}
|
|
943
|
+
poll()
|
|
933
944
|
}
|
|
934
|
-
|
|
945
|
+
else
|
|
946
|
+
throw response.status
|
|
947
|
+
}
|
|
948
|
+
catch (error) {
|
|
949
|
+
if (error !== '_hidden_tab_')
|
|
950
|
+
setTimeout(poll, 3000)
|
|
935
951
|
}
|
|
936
|
-
else
|
|
937
|
-
throw response.status
|
|
938
|
-
}
|
|
939
|
-
catch (error) {
|
|
940
|
-
if (error !== '_hidden_tab_')
|
|
941
|
-
setTimeout(poll, 3000)
|
|
942
952
|
}
|
|
943
953
|
}
|
|
944
954
|
|
|
@@ -951,7 +961,6 @@ function className(...args) {
|
|
|
951
961
|
}
|
|
952
962
|
}
|
|
953
963
|
|
|
954
|
-
|
|
955
964
|
function createElement(tag, props, ...children) {
|
|
956
965
|
const node = document.createElement(tag)
|
|
957
966
|
for (const [k, v] of Object.entries(props || {}))
|
|
@@ -998,6 +1007,30 @@ function deferred(cb) {
|
|
|
998
1007
|
: setTimeout(cb, 100) // Safari
|
|
999
1008
|
}
|
|
1000
1009
|
|
|
1010
|
+
function selectorFor(elem) {
|
|
1011
|
+
if (!(elem instanceof Element))
|
|
1012
|
+
return
|
|
1013
|
+
|
|
1014
|
+
const path = []
|
|
1015
|
+
while (elem) {
|
|
1016
|
+
let mod = ''
|
|
1017
|
+
if (elem.hasAttribute('key'))
|
|
1018
|
+
mod = `[key="${elem.getAttribute('key')}"]`
|
|
1019
|
+
else {
|
|
1020
|
+
let i = 0
|
|
1021
|
+
let sib = elem
|
|
1022
|
+
while ((sib = sib.previousElementSibling))
|
|
1023
|
+
if (sib.nodeName === elem.nodeName)
|
|
1024
|
+
i++
|
|
1025
|
+
if (i)
|
|
1026
|
+
mod = `:nth-of-type(${i + 1})`
|
|
1027
|
+
}
|
|
1028
|
+
path.push(elem.nodeName + mod)
|
|
1029
|
+
elem = elem.parentElement
|
|
1030
|
+
}
|
|
1031
|
+
return path.reverse().join('>')
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1001
1034
|
|
|
1002
1035
|
// When false, the URL will be updated with param=false
|
|
1003
1036
|
function initPreference(param) {
|
package/src/DashboardHtml.js
CHANGED
|
@@ -9,23 +9,23 @@ export const CSP = [
|
|
|
9
9
|
export const DashboardHtml = `<!DOCTYPE html>
|
|
10
10
|
<html lang="en-US">
|
|
11
11
|
<head>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
<meta charset="UTF-8">
|
|
13
|
+
<base href="${API.dashboard}/">
|
|
14
|
+
|
|
15
|
+
<link rel="stylesheet" href="Dashboard.css">
|
|
16
|
+
<script type="module" src="Dashboard.js"></script>
|
|
17
|
+
|
|
18
18
|
<link rel="preload" href="${API.state}" as="fetch" crossorigin>
|
|
19
19
|
|
|
20
20
|
<link rel="modulepreload" href="ApiConstants.js">
|
|
21
21
|
<link rel="modulepreload" href="ApiCommander.js">
|
|
22
22
|
<link rel="modulepreload" href="Filename.js">
|
|
23
23
|
<link rel="preload" href="Logo.svg" as="image">
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
|
|
25
|
+
<link rel="icon" href="data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m235 33.7v202c0 9.19-5.81 14-17.4 14-11.6 0-17.4-4.83-17.4-14v-151c-0.115-4.49-6.72-5.88-8.46-0.87l-48.3 155c-2.22 7.01-7.72 10.1-16 9.9-3.63-0.191-7.01-1.14-9.66-2.89-2.89-1.72-4.83-4.34-5.57-7.72-11.1-37-22.6-74.3-34.1-111-4.34-14-8.95-31.4-14-48.3-1.82-4.83-8.16-5.32-8.46 1.16v156c0 9.19-5.81 14-17.4 14-11.6 0-17.4-4.83-17.4-14v-207c0-5.74 2.62-13.2 9.39-16.3 7.5-3.14 15-4.05 21.8-3.8 3.14 0 6.03 0.686 8.95 1.46 3.14 0.797 6.03 1.98 8.7 3.63 2.65 1.38 5.32 3.14 7.5 5.57 2.22 2.22 3.87 4.83 5.07 7.72l45.8 157c4.63-15.9 32.4-117 33.3-121 4.12-13.8 7.72-26.5 10.9-38.7 1.16-2.65 2.89-5.32 5.07-7.5 2.15-2.15 4.58-4.12 7.5-5.32 2.65-1.57 5.57-2.89 8.46-3.63 3.14-0.797 9.44-0.988 12.1-0.988 11.6 1.07 29.4 9.14 29.4 27z' fill='%23808080'/%3E%3C/svg%3E">
|
|
26
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
27
|
+
<meta name="description" content="HTTP Mock Server">
|
|
28
|
+
<title>Mockaton</title>
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
</body>
|