mockaton 12.7.0 → 12.7.1
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/client/ApiCommander.js +8 -31
- package/src/client/ApiConstants.js +0 -2
- package/src/client/IndexHtml.js +4 -6
- package/src/client/app-header.js +161 -0
- package/src/client/app-icons.js +29 -0
- package/src/client/{payload-viewer.js → app-payload-viewer.js} +12 -15
- package/src/client/app-store.js +0 -2
- package/src/client/app.css +11 -3
- package/src/client/app.js +77 -246
- package/src/client/dom-utils.js +1 -3
- package/src/client/{css-modules.test.js → dom-utils.test.js} +7 -2
- package/src/client/watcherDev.js +26 -17
- package/src/server/Api.js +6 -6
- package/src/server/Mockaton.test.js +45 -34
- package/src/server/Watcher.js +25 -21
- package/src/server/WatcherDevClient.js +23 -14
- package/src/server/cli.js +1 -1
- package/src/server/cli.test.js +13 -4
- package/src/server/utils/validate.test.js +16 -11
package/src/client/app.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createElement as r,
|
|
3
|
-
createSvgElement as s,
|
|
4
3
|
t, classNames, restoreFocus, Fragment, defineClassNames
|
|
5
4
|
} from './dom-utils.js'
|
|
5
|
+
|
|
6
6
|
import { store } from './app-store.js'
|
|
7
|
-
import {
|
|
7
|
+
import { API } from './ApiConstants.js'
|
|
8
|
+
import { Header } from './app-header.js'
|
|
9
|
+
import { TimerIcon, CloudIcon } from './app-icons.js'
|
|
10
|
+
import { PayloadViewer, previewMock } from './app-payload-viewer.js'
|
|
8
11
|
|
|
9
12
|
import CSS from './app.css' with { type: 'css' }
|
|
10
13
|
document.adoptedStyleSheets.push(CSS)
|
|
@@ -15,14 +18,12 @@ store.onError = onError
|
|
|
15
18
|
store.render = render
|
|
16
19
|
store.renderRow = renderRow
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
onRealTimeUpdate(store.fetchState)
|
|
19
22
|
initKeyboardNavigation()
|
|
20
23
|
|
|
21
24
|
let mounted = false
|
|
22
25
|
function render() {
|
|
23
|
-
restoreFocus(() =>
|
|
24
|
-
document.body.replaceChildren(...App())
|
|
25
|
-
})
|
|
26
|
+
restoreFocus(() => document.body.replaceChildren(App()))
|
|
26
27
|
if (store.hasChosenLink)
|
|
27
28
|
previewMock()
|
|
28
29
|
mounted = true
|
|
@@ -32,183 +33,42 @@ function render() {
|
|
|
32
33
|
const leftSideRef = {}
|
|
33
34
|
|
|
34
35
|
function App() {
|
|
35
|
-
return
|
|
36
|
-
|
|
36
|
+
return Fragment(Header(), Main())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
function Main() {
|
|
41
|
+
return (
|
|
37
42
|
r('main', null,
|
|
38
43
|
r('div', {
|
|
39
44
|
ref: leftSideRef,
|
|
40
45
|
style: { width: leftSideRef.width },
|
|
41
46
|
className: CSS.leftSide
|
|
42
47
|
},
|
|
43
|
-
r('div',
|
|
48
|
+
r('div', { className: CSS.SubToolbar },
|
|
44
49
|
GroupByMethod(),
|
|
45
50
|
BulkSelector()),
|
|
46
|
-
r('div',
|
|
51
|
+
r('div', { className: CSS.Table },
|
|
47
52
|
MockList(),
|
|
48
53
|
StaticFilesList())),
|
|
49
54
|
r('div', { className: CSS.rightSide },
|
|
50
55
|
Resizer(leftSideRef),
|
|
51
|
-
PayloadViewer()))
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function Header() {
|
|
56
|
-
return (
|
|
57
|
-
r('header', null,
|
|
58
|
-
r('a', {
|
|
59
|
-
className: CSS.Logo,
|
|
60
|
-
href: 'https://mockaton.com',
|
|
61
|
-
alt: t`Documentation`
|
|
62
|
-
},
|
|
63
|
-
Logo()),
|
|
64
|
-
r('div', null,
|
|
65
|
-
r('div', classNames(CSS.GlobalDelayWrap),
|
|
66
|
-
GlobalDelayField(),
|
|
67
|
-
GlobalDelayJitterField()),
|
|
68
|
-
CookieSelector(),
|
|
69
|
-
store.showProxyField && ProxyFallbackField(),
|
|
70
|
-
ResetButton(),
|
|
71
|
-
HelpLink()
|
|
72
|
-
)))
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
function GlobalDelayField() {
|
|
77
|
-
function onChange() {
|
|
78
|
-
store.setGlobalDelay(this.valueAsNumber)
|
|
79
|
-
}
|
|
80
|
-
function onWheel(event) {
|
|
81
|
-
if (event.deltaY > 0)
|
|
82
|
-
this.stepUp()
|
|
83
|
-
else
|
|
84
|
-
this.stepDown()
|
|
85
|
-
clearTimeout(onWheel.timer)
|
|
86
|
-
onWheel.timer = setTimeout(onChange.bind(this), 300)
|
|
87
|
-
}
|
|
88
|
-
return (
|
|
89
|
-
r('label', classNames(CSS.Field, CSS.GlobalDelayField),
|
|
90
|
-
r('span', null, t`Delay (ms)`),
|
|
91
|
-
r('input', {
|
|
92
|
-
type: 'number',
|
|
93
|
-
min: 0,
|
|
94
|
-
step: 100,
|
|
95
|
-
autocomplete: 'none',
|
|
96
|
-
value: store.delay,
|
|
97
|
-
onChange,
|
|
98
|
-
onWheel: [onWheel, { passive: true }]
|
|
99
|
-
})))
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function GlobalDelayJitterField() {
|
|
103
|
-
function onChange() {
|
|
104
|
-
this.value = this.valueAsNumber.toFixed(0)
|
|
105
|
-
this.value = Math.max(0, this.valueAsNumber)
|
|
106
|
-
this.value = Math.min(300, this.valueAsNumber)
|
|
107
|
-
store.setGlobalDelayJitter(this.valueAsNumber / 100)
|
|
108
|
-
}
|
|
109
|
-
function onWheel(event) {
|
|
110
|
-
if (event.deltaY > 0)
|
|
111
|
-
this.stepUp()
|
|
112
|
-
else
|
|
113
|
-
this.stepDown()
|
|
114
|
-
clearTimeout(onWheel.timer)
|
|
115
|
-
onWheel.timer = setTimeout(onChange.bind(this), 300)
|
|
116
|
-
}
|
|
117
|
-
return (
|
|
118
|
-
r('label', classNames(CSS.Field, CSS.GlobalDelayJitterField),
|
|
119
|
-
r('span', null, t`Max Jitter %`),
|
|
120
|
-
r('input', {
|
|
121
|
-
type: 'number',
|
|
122
|
-
min: 0,
|
|
123
|
-
max: 300,
|
|
124
|
-
step: 10,
|
|
125
|
-
autocomplete: 'none',
|
|
126
|
-
value: (store.delayJitter * 100).toFixed(0),
|
|
127
|
-
onChange,
|
|
128
|
-
onWheel: [onWheel, { passive: true }]
|
|
129
|
-
})))
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
function CookieSelector() {
|
|
134
|
-
const { cookies } = store
|
|
135
|
-
const disabled = cookies.length <= 1
|
|
136
|
-
const list = cookies.length ? cookies : [[t`None`, true]]
|
|
137
|
-
return (
|
|
138
|
-
r('label', classNames(CSS.Field, CSS.CookieSelector),
|
|
139
|
-
r('span', null, t`Cookie`),
|
|
140
|
-
r('select', {
|
|
141
|
-
autocomplete: 'off',
|
|
142
|
-
disabled,
|
|
143
|
-
title: disabled
|
|
144
|
-
? t`No cookies specified in config.cookies`
|
|
145
|
-
: undefined,
|
|
146
|
-
onChange() { store.selectCookie(this.value) }
|
|
147
|
-
}, list.map(([value, selected]) =>
|
|
148
|
-
r('option', { value, selected }, value)))))
|
|
56
|
+
PayloadViewer())))
|
|
149
57
|
}
|
|
150
58
|
|
|
151
59
|
|
|
152
|
-
function
|
|
153
|
-
const checkboxRef = {}
|
|
154
|
-
function onChange() {
|
|
155
|
-
checkboxRef.elem.disabled = !this.validity.valid || !this.value.trim()
|
|
156
|
-
if (!this.validity.valid)
|
|
157
|
-
this.reportValidity()
|
|
158
|
-
else
|
|
159
|
-
store.setProxyFallback(this.value.trim())
|
|
160
|
-
}
|
|
161
|
-
return (
|
|
162
|
-
r('div', classNames(CSS.Field, CSS.FallbackBackend),
|
|
163
|
-
r('label', null,
|
|
164
|
-
r('span', null, t`Fallback`),
|
|
165
|
-
r('input', {
|
|
166
|
-
type: 'url',
|
|
167
|
-
name: 'fallback',
|
|
168
|
-
placeholder: t`Type backend address`,
|
|
169
|
-
value: store.proxyFallback,
|
|
170
|
-
onChange
|
|
171
|
-
})),
|
|
172
|
-
SaveProxiedCheckbox(checkboxRef)))
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function SaveProxiedCheckbox(ref) {
|
|
60
|
+
function GroupByMethod() {
|
|
176
61
|
return (
|
|
177
|
-
r('label',
|
|
62
|
+
r('label', { className: CSS.GroupByMethod },
|
|
178
63
|
r('input', {
|
|
179
|
-
ref,
|
|
180
64
|
type: 'checkbox',
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
onChange() { store.setCollectProxied(this.checked) }
|
|
65
|
+
checked: store.groupByMethod,
|
|
66
|
+
onChange: store.toggleGroupByMethod
|
|
184
67
|
}),
|
|
185
|
-
r('span',
|
|
68
|
+
r('span', { className: CSS.checkboxBody }, t`Group by Method`)))
|
|
186
69
|
}
|
|
187
70
|
|
|
188
71
|
|
|
189
|
-
function ResetButton() {
|
|
190
|
-
return (
|
|
191
|
-
r('button', {
|
|
192
|
-
className: CSS.ResetButton,
|
|
193
|
-
onClick: store.reset
|
|
194
|
-
}, t`Reset`))
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
function HelpLink() {
|
|
199
|
-
return (
|
|
200
|
-
r('a', {
|
|
201
|
-
target: '_blank',
|
|
202
|
-
href: 'https://mockaton.com',
|
|
203
|
-
title: t`Documentation`,
|
|
204
|
-
className: CSS.HelpLink
|
|
205
|
-
}, HelpIcon()))
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
/** # Left Side */
|
|
210
|
-
|
|
211
|
-
|
|
212
72
|
function BulkSelector() {
|
|
213
73
|
const { comments } = store
|
|
214
74
|
const firstOption = t`Pick Comment…`
|
|
@@ -219,7 +79,7 @@ function BulkSelector() {
|
|
|
219
79
|
}
|
|
220
80
|
const disabled = !comments.length
|
|
221
81
|
return (
|
|
222
|
-
r('label',
|
|
82
|
+
r('label', { className: CSS.BulkSelector },
|
|
223
83
|
r('span', null, t`Bulk Select`),
|
|
224
84
|
r('select', {
|
|
225
85
|
autocomplete: 'off',
|
|
@@ -232,23 +92,9 @@ function BulkSelector() {
|
|
|
232
92
|
r('option', { value: firstOption }, firstOption),
|
|
233
93
|
r('hr'),
|
|
234
94
|
comments.map(value => r('option', { value }, value)))))
|
|
235
|
-
// TODO For a11y, use `menu` instead of `select`
|
|
236
95
|
}
|
|
237
96
|
|
|
238
97
|
|
|
239
|
-
function GroupByMethod() {
|
|
240
|
-
return (
|
|
241
|
-
r('label', classNames(CSS.GroupByMethod),
|
|
242
|
-
r('input', {
|
|
243
|
-
type: 'checkbox',
|
|
244
|
-
checked: store.groupByMethod,
|
|
245
|
-
onChange: store.toggleGroupByMethod
|
|
246
|
-
}),
|
|
247
|
-
r('span', classNames(CSS.checkboxBody), t`Group by Method`)))
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
/** # MockList */
|
|
252
98
|
|
|
253
99
|
function MockList() {
|
|
254
100
|
if (!Object.keys(store.brokersByMethod).length)
|
|
@@ -257,7 +103,11 @@ function MockList() {
|
|
|
257
103
|
if (store.groupByMethod)
|
|
258
104
|
return Object.keys(store.brokersByMethod).map(method =>
|
|
259
105
|
Fragment(
|
|
260
|
-
r('div',
|
|
106
|
+
r('div', {
|
|
107
|
+
className: classNames(
|
|
108
|
+
CSS.TableHeading,
|
|
109
|
+
store.canProxy && CSS.canProxy)
|
|
110
|
+
}, method),
|
|
261
111
|
store.brokersAsRowsByMethod(method).map(Row)))
|
|
262
112
|
|
|
263
113
|
return store.brokersAsRowsByMethod('*').map(Row)
|
|
@@ -272,7 +122,8 @@ function Row(row, i) {
|
|
|
272
122
|
return (
|
|
273
123
|
r('div', {
|
|
274
124
|
key: row.key,
|
|
275
|
-
|
|
125
|
+
className: classNames(
|
|
126
|
+
CSS.TableRow,
|
|
276
127
|
mounted && row.isNew && CSS.animIn)
|
|
277
128
|
},
|
|
278
129
|
store.canProxy && ProxyToggler(method, urlMask, row.proxied),
|
|
@@ -294,7 +145,7 @@ function Row(row, i) {
|
|
|
294
145
|
}
|
|
295
146
|
}),
|
|
296
147
|
|
|
297
|
-
!store.groupByMethod && r('span',
|
|
148
|
+
!store.groupByMethod && r('span', { className: CSS.Method }, method),
|
|
298
149
|
|
|
299
150
|
PreviewLink(method, urlMask, row.urlMaskDittoed, i === 0),
|
|
300
151
|
|
|
@@ -350,12 +201,14 @@ function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
|
|
|
350
201
|
const [ditto, tail] = urlMaskDittoed
|
|
351
202
|
return (
|
|
352
203
|
r('a', {
|
|
353
|
-
|
|
204
|
+
className: classNames(
|
|
205
|
+
CSS.PreviewLink,
|
|
206
|
+
isChosen && CSS.chosen),
|
|
354
207
|
href: urlMask,
|
|
355
208
|
autofocus,
|
|
356
209
|
onClick
|
|
357
210
|
}, ditto
|
|
358
|
-
? [r('span',
|
|
211
|
+
? [r('span', { className: CSS.dittoDir }, ditto), tail]
|
|
359
212
|
: tail))
|
|
360
213
|
}
|
|
361
214
|
|
|
@@ -375,7 +228,7 @@ function MockSelector(row) {
|
|
|
375
228
|
},
|
|
376
229
|
'aria-label': t`Mock Selector`,
|
|
377
230
|
disabled: row.opts.length < 2,
|
|
378
|
-
|
|
231
|
+
className: classNames(
|
|
379
232
|
CSS.MockSelector,
|
|
380
233
|
row.selectedIdx > 0 && CSS.nonDefault,
|
|
381
234
|
row.selectedFileIs4xx && CSS.status4xx)
|
|
@@ -405,10 +258,11 @@ function StaticFilesList() {
|
|
|
405
258
|
return !rows.length
|
|
406
259
|
? null
|
|
407
260
|
: Fragment(
|
|
408
|
-
r('div',
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
261
|
+
r('div', {
|
|
262
|
+
className: classNames(CSS.TableHeading,
|
|
263
|
+
store.canProxy && CSS.canProxy,
|
|
264
|
+
!store.groupByMethod && CSS.nonGroupedByMethod),
|
|
265
|
+
},
|
|
412
266
|
store.groupByMethod
|
|
413
267
|
? t`Static GET`
|
|
414
268
|
: t`Static`),
|
|
@@ -422,7 +276,8 @@ function StaticRow(row) {
|
|
|
422
276
|
return (
|
|
423
277
|
r('div', {
|
|
424
278
|
key: row.key,
|
|
425
|
-
|
|
279
|
+
className: classNames(
|
|
280
|
+
CSS.TableRow,
|
|
426
281
|
mounted && row.isNew && CSS.animIn)
|
|
427
282
|
},
|
|
428
283
|
|
|
@@ -445,14 +300,14 @@ function StaticRow(row) {
|
|
|
445
300
|
}
|
|
446
301
|
}),
|
|
447
302
|
|
|
448
|
-
!groupByMethod && r('span',
|
|
303
|
+
!groupByMethod && r('span', { className: CSS.Method }, 'GET'),
|
|
449
304
|
|
|
450
305
|
r('a', {
|
|
451
306
|
href: row.urlMask,
|
|
452
307
|
target: '_blank',
|
|
453
308
|
className: CSS.PreviewLink,
|
|
454
309
|
}, ditto
|
|
455
|
-
? [r('span',
|
|
310
|
+
? [r('span', { className: CSS.dittoDir }, ditto), tail]
|
|
456
311
|
: tail)))
|
|
457
312
|
}
|
|
458
313
|
|
|
@@ -472,7 +327,7 @@ function DelayToggler({ checked, commit, optClassName }) {
|
|
|
472
327
|
canClickDrag: true,
|
|
473
328
|
checked,
|
|
474
329
|
commit,
|
|
475
|
-
|
|
330
|
+
className: classNames(CSS.DelayToggler, optClassName),
|
|
476
331
|
title: t`Delay`,
|
|
477
332
|
body: TimerIcon()
|
|
478
333
|
})
|
|
@@ -521,7 +376,7 @@ function ClickDragToggler({ checked, commit, className, title, body }) {
|
|
|
521
376
|
commit(this.checked)
|
|
522
377
|
}
|
|
523
378
|
return (
|
|
524
|
-
r('label', {
|
|
379
|
+
r('label', { className: classNames(CSS.Toggler, className), title },
|
|
525
380
|
r('input', {
|
|
526
381
|
type: 'checkbox',
|
|
527
382
|
checked,
|
|
@@ -530,7 +385,7 @@ function ClickDragToggler({ checked, commit, className, title, body }) {
|
|
|
530
385
|
onClick,
|
|
531
386
|
onChange
|
|
532
387
|
}),
|
|
533
|
-
r('span',
|
|
388
|
+
r('span', { className: CSS.checkboxBody }, body)))
|
|
534
389
|
}
|
|
535
390
|
|
|
536
391
|
function Resizer(ref) {
|
|
@@ -621,77 +476,54 @@ ErrorToast.close = () => {
|
|
|
621
476
|
}
|
|
622
477
|
|
|
623
478
|
|
|
624
|
-
/** # Graphics */
|
|
625
479
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
s('svg', { viewBox: '0 0 556 100' },
|
|
629
|
-
s('path', { 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' }),
|
|
630
|
-
s('path', { opacity: 0.7, 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' }),
|
|
631
|
-
s('path', { 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' })))
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
function TimerIcon() {
|
|
635
|
-
return (
|
|
636
|
-
s('svg', { viewBox: '0 0 24 24' },
|
|
637
|
-
s('path', { d: 'm11 5.6 0.14 7.2 6 3.7' })))
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function CloudIcon() {
|
|
641
|
-
return (
|
|
642
|
-
s('svg', { viewBox: '0 0 24 24' },
|
|
643
|
-
s('path', { d: 'm6.1 8.9c0.98-2.3 3.3-3.9 6-3.9 3.3-2e-7 6 2.5 6.4 5.7 0.018 0.15 0.024 0.18 0.026 0.23 0.0016 0.037 8.2e-4 0.084 0.098 0.14 0.097 0.054 0.29 0.05 0.48 0.05 2.2 0 4 1.8 4 4s-1.8 4-4 4c-4-0.038-9-0.038-13-0.018-2.8 0-5-2.2-5-5-2.2e-7 -2.8 2.2-5 5-5 2.8 2e-7 5 2.2 5 5' }),
|
|
644
|
-
s('path', { d: 'm6.1 9.1c2.8 0 5 2.3 5 5' })))
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function HelpIcon() {
|
|
648
|
-
return (
|
|
649
|
-
s('svg', { viewBox: '0 0 24 24' },
|
|
650
|
-
s('path', { d: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 17h-2v-2h2zm2.07-7.75-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25' })))
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* # Long polls UI sync version
|
|
656
|
-
* The version increments when a mock file is added, removed, or renamed.
|
|
657
|
-
*/
|
|
658
|
-
function initRealTimeUpdates() {
|
|
480
|
+
/** The version increments when a mock file is added, removed, or renamed. */
|
|
481
|
+
function onRealTimeUpdate(onUpdate) {
|
|
659
482
|
let oldVersion = -1
|
|
660
|
-
let
|
|
483
|
+
let es = null
|
|
484
|
+
let timer = null
|
|
661
485
|
|
|
662
|
-
|
|
486
|
+
connect()
|
|
663
487
|
document.addEventListener('visibilitychange', () => {
|
|
664
|
-
if (document.hidden)
|
|
665
|
-
|
|
666
|
-
controller = new AbortController()
|
|
667
|
-
}
|
|
488
|
+
if (document.hidden)
|
|
489
|
+
teardown()
|
|
668
490
|
else
|
|
669
|
-
|
|
491
|
+
connect()
|
|
670
492
|
})
|
|
493
|
+
window.addEventListener('beforeunload', teardown)
|
|
494
|
+
|
|
495
|
+
function connect() {
|
|
496
|
+
if (es) return
|
|
671
497
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
const response = await store.getSyncVersion(oldVersion, controller.signal)
|
|
675
|
-
if (!response.ok)
|
|
676
|
-
throw response.status
|
|
498
|
+
clearTimeout(timer)
|
|
499
|
+
es = new EventSource(API.syncVersion)
|
|
677
500
|
|
|
501
|
+
es.onmessage = function (event) {
|
|
678
502
|
if (ErrorToast.isOffline)
|
|
679
503
|
ErrorToast.close()
|
|
680
504
|
|
|
681
|
-
const version =
|
|
682
|
-
if (oldVersion !== version) {
|
|
505
|
+
const version = Number(event.data)
|
|
506
|
+
if (oldVersion !== version) {
|
|
683
507
|
oldVersion = version
|
|
684
|
-
|
|
508
|
+
onUpdate()
|
|
685
509
|
}
|
|
686
|
-
longPoll()
|
|
687
510
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
511
|
+
|
|
512
|
+
es.onerror = function () {
|
|
513
|
+
teardown()
|
|
514
|
+
timer = setTimeout(connect, 3000)
|
|
691
515
|
}
|
|
692
516
|
}
|
|
517
|
+
|
|
518
|
+
function teardown() {
|
|
519
|
+
clearTimeout(timer)
|
|
520
|
+
es?.close()
|
|
521
|
+
es = null
|
|
522
|
+
}
|
|
693
523
|
}
|
|
694
524
|
|
|
525
|
+
|
|
526
|
+
|
|
695
527
|
function selectorForColumnOf(elem) {
|
|
696
528
|
return columnSelectors().find(s => elem?.matches(s))
|
|
697
529
|
}
|
|
@@ -745,4 +577,3 @@ function initKeyboardNavigation() {
|
|
|
745
577
|
return arr[(arr.indexOf(pivot) + step + arr.length) % arr.length]
|
|
746
578
|
}
|
|
747
579
|
}
|
|
748
|
-
|
package/src/client/dom-utils.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { test } from 'node:test'
|
|
2
|
-
import { deepEqual } from 'node:assert/strict'
|
|
3
|
-
import { extractClassNames } from './dom-utils.js'
|
|
2
|
+
import { deepEqual, equal } from 'node:assert/strict'
|
|
3
|
+
import { extractClassNames, classNames } from './dom-utils.js'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
test('classNames', () => {
|
|
7
|
+
equal(classNames('a', false && 'b'), 'a')
|
|
8
|
+
})
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
const cssRules = [
|
package/src/client/watcherDev.js
CHANGED
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
import { API } from './ApiConstants.js'
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
4
|
+
let es = null
|
|
5
|
+
let timer = null
|
|
6
|
+
|
|
7
|
+
window.addEventListener('beforeunload', teardown)
|
|
8
|
+
connect()
|
|
9
|
+
function connect() {
|
|
10
|
+
if (es) return
|
|
11
|
+
|
|
12
|
+
clearTimeout(timer)
|
|
13
|
+
es = new EventSource(API.watchHotReload)
|
|
14
|
+
|
|
15
|
+
es.onmessage = function (event) {
|
|
16
|
+
const file = event.data
|
|
17
|
+
if (file.endsWith('.css'))
|
|
18
|
+
hotReloadCSS(file)
|
|
16
19
|
else if (file)
|
|
17
20
|
location.reload()
|
|
18
|
-
else // server timeout
|
|
19
|
-
longPollDevChanges()
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
|
|
23
|
+
es.onerror = function () {
|
|
24
|
+
console.error('hot reload')
|
|
25
|
+
teardown()
|
|
26
|
+
timer = setTimeout(connect, 3000)
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
function teardown() {
|
|
31
|
+
clearTimeout(timer)
|
|
32
|
+
es?.close()
|
|
33
|
+
es = null
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
async function hotReloadCSS(file) {
|
|
28
37
|
const mod = await import(`./${file}?${Date.now()}`, { with: { type: 'css' } })
|
|
29
38
|
document.adoptedStyleSheets = [mod.default]
|
package/src/server/Api.js
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
import { join } from 'node:path'
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
sseClientHotReload,
|
|
10
10
|
DASHBOARD_ASSETS,
|
|
11
11
|
CLIENT_DIR
|
|
12
12
|
} from './WatcherDevClient.js'
|
|
13
|
-
import {
|
|
13
|
+
import { startWatchers, stopWatchers, sseClientSyncVersion } from './Watcher.js'
|
|
14
14
|
|
|
15
15
|
import pkgJSON from '../../package.json' with { type: 'json' }
|
|
16
16
|
|
|
@@ -29,16 +29,16 @@ export const apiGetReqs = new Map([
|
|
|
29
29
|
...DASHBOARD_ASSETS.map(f => [API.dashboard + '/' + f, serveStatic(f)]),
|
|
30
30
|
|
|
31
31
|
[API.state, getState],
|
|
32
|
-
[API.syncVersion,
|
|
32
|
+
[API.syncVersion, sseClientSyncVersion],
|
|
33
33
|
|
|
34
|
-
[API.watchHotReload,
|
|
34
|
+
[API.watchHotReload, sseClientHotReload],
|
|
35
35
|
[API.throws, () => { throw new Error('Test500') }]
|
|
36
36
|
])
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
export const apiPatchReqs = new Map([
|
|
40
40
|
[API.cors, setCorsAllowed],
|
|
41
|
-
[API.reset,
|
|
41
|
+
[API.reset, reset],
|
|
42
42
|
[API.cookies, selectCookie],
|
|
43
43
|
[API.globalDelay, setGlobalDelay],
|
|
44
44
|
[API.globalDelayJitter, setGlobalDelayJitter],
|
|
@@ -92,7 +92,7 @@ function getState(_, response) {
|
|
|
92
92
|
|
|
93
93
|
/** # PATCH */
|
|
94
94
|
|
|
95
|
-
function
|
|
95
|
+
function reset(_, response) {
|
|
96
96
|
mockBrokersCollection.init()
|
|
97
97
|
staticCollection.init()
|
|
98
98
|
response.ok()
|