mockaton 12.3.1 → 12.3.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": "12.3.1",
5
+ "version": "12.3.3",
6
6
  "exports": {
7
7
  ".": {
8
8
  "import": "./index.js",
package/src/client/app.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createElement as r,
3
3
  createSvgElement as s,
4
- className, restoreFocus, Fragment, adoptCSS
4
+ classNames, restoreFocus, Fragment, adoptCSS
5
5
  } from './dom-utils.js'
6
6
 
7
7
  import { store } from './app-store.js'
@@ -23,7 +23,9 @@ initKeyboardNavigation()
23
23
 
24
24
  let mounted = false
25
25
  function render() {
26
- restoreFocus(() => document.body.replaceChildren(...App()))
26
+ restoreFocus(() => {
27
+ document.body.replaceChildren(...App())
28
+ })
27
29
  if (store.hasChosenLink)
28
30
  previewMock()
29
31
  mounted = true
@@ -41,10 +43,10 @@ function App() {
41
43
  style: { width: leftSideRef.width },
42
44
  className: CSS.leftSide
43
45
  },
44
- r('div', className(CSS.SubToolbar),
46
+ r('div', classNames(CSS.SubToolbar),
45
47
  GroupByMethod(),
46
48
  BulkSelector()),
47
- r('div', className(CSS.Table),
49
+ r('div', classNames(CSS.Table),
48
50
  MockList(),
49
51
  StaticFilesList())),
50
52
  r('div', { className: CSS.rightSide },
@@ -63,7 +65,7 @@ function Header() {
63
65
  },
64
66
  Logo()),
65
67
  r('div', null,
66
- r('div', className(CSS.GlobalDelayWrap),
68
+ r('div', classNames(CSS.GlobalDelayWrap),
67
69
  GlobalDelayField(),
68
70
  GlobalDelayJitterField()),
69
71
  CookieSelector(),
@@ -87,7 +89,7 @@ function GlobalDelayField() {
87
89
  onWheel.timer = setTimeout(onChange.bind(this), 300)
88
90
  }
89
91
  return (
90
- r('label', className(CSS.Field, CSS.GlobalDelayField),
92
+ r('label', classNames(CSS.Field, CSS.GlobalDelayField),
91
93
  r('span', null, t`Delay (ms)`),
92
94
  r('input', {
93
95
  type: 'number',
@@ -116,7 +118,7 @@ function GlobalDelayJitterField() {
116
118
  onWheel.timer = setTimeout(onChange.bind(this), 300)
117
119
  }
118
120
  return (
119
- r('label', className(CSS.Field, CSS.GlobalDelayJitterField),
121
+ r('label', classNames(CSS.Field, CSS.GlobalDelayJitterField),
120
122
  r('span', null, t`Max Jitter %`),
121
123
  r('input', {
122
124
  type: 'number',
@@ -136,7 +138,7 @@ function CookieSelector() {
136
138
  const disabled = cookies.length <= 1
137
139
  const list = cookies.length ? cookies : [[t`None`, true]]
138
140
  return (
139
- r('label', className(CSS.Field, CSS.CookieSelector),
141
+ r('label', classNames(CSS.Field, CSS.CookieSelector),
140
142
  r('span', null, t`Cookie`),
141
143
  r('select', {
142
144
  autocomplete: 'off',
@@ -160,7 +162,7 @@ function ProxyFallbackField() {
160
162
  store.setProxyFallback(this.value.trim())
161
163
  }
162
164
  return (
163
- r('div', className(CSS.Field, CSS.FallbackBackend),
165
+ r('div', classNames(CSS.Field, CSS.FallbackBackend),
164
166
  r('label', null,
165
167
  r('span', null, t`Fallback`),
166
168
  r('input', {
@@ -175,7 +177,7 @@ function ProxyFallbackField() {
175
177
 
176
178
  function SaveProxiedCheckbox(ref) {
177
179
  return (
178
- r('label', className(CSS.SaveProxiedCheckbox),
180
+ r('label', classNames(CSS.SaveProxiedCheckbox),
179
181
  r('input', {
180
182
  ref,
181
183
  type: 'checkbox',
@@ -183,7 +185,7 @@ function SaveProxiedCheckbox(ref) {
183
185
  checked: store.collectProxied,
184
186
  onChange() { store.setCollectProxied(this.checked) }
185
187
  }),
186
- r('span', className(CSS.checkboxBody), t`Save Mocks`)))
188
+ r('span', classNames(CSS.checkboxBody), t`Save Mocks`)))
187
189
  }
188
190
 
189
191
 
@@ -220,7 +222,7 @@ function BulkSelector() {
220
222
  }
221
223
  const disabled = !comments.length
222
224
  return (
223
- r('label', className(CSS.BulkSelector),
225
+ r('label', classNames(CSS.BulkSelector),
224
226
  r('span', null, t`Bulk Select`),
225
227
  r('select', {
226
228
  autocomplete: 'off',
@@ -239,13 +241,13 @@ function BulkSelector() {
239
241
 
240
242
  function GroupByMethod() {
241
243
  return (
242
- r('label', className(CSS.GroupByMethod),
244
+ r('label', classNames(CSS.GroupByMethod),
243
245
  r('input', {
244
246
  type: 'checkbox',
245
247
  checked: store.groupByMethod,
246
248
  onChange: store.toggleGroupByMethod
247
249
  }),
248
- r('span', className(CSS.checkboxBody), t`Group by Method`)))
250
+ r('span', classNames(CSS.checkboxBody), t`Group by Method`)))
249
251
  }
250
252
 
251
253
 
@@ -256,10 +258,10 @@ function MockList() {
256
258
  return r('div', null, t`No mocks found`)
257
259
 
258
260
  if (store.groupByMethod)
259
- return Object.keys(store.brokersByMethod).map(method => Fragment(
260
- r('div', className(CSS.TableHeading, store.canProxy && CSS.canProxy),
261
- method),
262
- store.brokersAsRowsByMethod(method).map(Row)))
261
+ return Object.keys(store.brokersByMethod).map(method =>
262
+ Fragment(
263
+ r('div', classNames(CSS.TableHeading, store.canProxy && CSS.canProxy), method),
264
+ store.brokersAsRowsByMethod(method).map(Row)))
263
265
 
264
266
  return store.brokersAsRowsByMethod('*').map(Row)
265
267
  }
@@ -273,27 +275,29 @@ function Row(row, i) {
273
275
  return (
274
276
  r('div', {
275
277
  key: row.key,
276
- ...className(CSS.TableRow,
278
+ ...classNames(CSS.TableRow,
277
279
  mounted && row.isNew && CSS.animIn)
278
280
  },
279
281
  store.canProxy && ProxyToggler(method, urlMask, row.proxied),
280
282
 
281
283
  DelayToggler({
282
284
  checked: row.delayed,
283
- commit(checked) { store.setDelayed(method, urlMask, checked) },
285
+ commit(checked) {
286
+ store.setDelayed(method, urlMask, checked)
287
+ },
284
288
  }),
285
289
 
286
290
  StatusCodeToggler({
287
291
  title: t`Internal Server Error`,
288
- label: t`500`,
292
+ body: t`500`,
289
293
  disabled: row.opts.length === 1 && row.status === 500,
290
294
  checked: !row.proxied && row.status === 500,
291
- onChange() {
295
+ commit() {
292
296
  store.toggle500(method, urlMask)
293
297
  }
294
298
  }),
295
299
 
296
- !store.groupByMethod && r('span', className(CSS.Method), method),
300
+ !store.groupByMethod && r('span', classNames(CSS.Method), method),
297
301
 
298
302
  PreviewLink(method, urlMask, row.urlMaskDittoed, i === 0),
299
303
 
@@ -301,23 +305,45 @@ function Row(row, i) {
301
305
  }
302
306
 
303
307
  function renderRow(method, urlMask) {
304
- restoreFocus(() => {
305
- unChooseOld()
306
- const row = store.brokerAsRow(method, urlMask)
307
- trFor(row.key).replaceWith(Row(row))
308
- previewMock()
309
- })
308
+ unChooseOld()
309
+ const row = store.brokerAsRow(method, urlMask)
310
+ const tr = leftSideRef.elem.querySelector(`.${CSS.TableRow}[key="${row.key}"]`)
311
+ mergeTableRow(tr, Row(row))
312
+ previewMock()
310
313
 
311
- function trFor(key) {
312
- return leftSideRef.elem.querySelector(`.${CSS.TableRow}[key="${key}"]`)
313
- }
314
314
  function unChooseOld() {
315
315
  return leftSideRef.elem.querySelector(`a.${CSS.chosen}`)
316
316
  ?.classList.remove(CSS.chosen)
317
317
  }
318
+
319
+ function mergeTableRow(oldRow, newRow) {
320
+ for (let i = 0; i < newRow.children.length; i++) {
321
+ const oldEl = oldRow.children[i]
322
+ const newEl = newRow.children[i]
323
+ switch (newEl.tagName) {
324
+ case 'LABEL': {
325
+ const oldInput = oldEl.querySelector('[type="checkbox"]')
326
+ const newInput = newEl.querySelector('[type="checkbox"]')
327
+ oldInput.checked = newInput.checked
328
+ oldInput.disabled = newInput.disabled
329
+ break
330
+ }
331
+ case 'A':
332
+ oldEl.className = newEl.className
333
+ break
334
+ case 'SELECT':
335
+ oldEl.replaceChildren(...newEl.cloneNode(true).children)
336
+ oldEl.className = newEl.className
337
+ oldEl.disabled = newEl.disabled
338
+ oldEl.value = newEl.value
339
+ break
340
+ }
341
+ }
342
+ }
318
343
  }
319
344
 
320
345
 
346
+
321
347
  function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
322
348
  function onClick(event) {
323
349
  event.preventDefault()
@@ -327,12 +353,12 @@ function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
327
353
  const [ditto, tail] = urlMaskDittoed
328
354
  return (
329
355
  r('a', {
330
- ...className(CSS.PreviewLink, isChosen && CSS.chosen),
356
+ ...classNames(CSS.PreviewLink, isChosen && CSS.chosen),
331
357
  href: urlMask,
332
358
  autofocus,
333
359
  onClick
334
360
  }, ditto
335
- ? [r('span', className(CSS.dittoDir), ditto), tail]
361
+ ? [r('span', classNames(CSS.dittoDir), ditto), tail]
336
362
  : tail))
337
363
  }
338
364
 
@@ -352,7 +378,7 @@ function MockSelector(row) {
352
378
  },
353
379
  'aria-label': t`Mock Selector`,
354
380
  disabled: row.opts.length < 2,
355
- ...className(
381
+ ...classNames(
356
382
  CSS.MockSelector,
357
383
  row.selectedIdx > 0 && CSS.nonDefault,
358
384
  row.selectedFileIs4xx && CSS.status4xx)
@@ -362,17 +388,15 @@ function MockSelector(row) {
362
388
 
363
389
 
364
390
  function ProxyToggler(method, urlMask, checked) {
365
- return (
366
- r('label', {
367
- className: CSS.ProxyToggler,
368
- title: t`Proxy Toggler`
369
- },
370
- r('input', {
371
- type: 'checkbox',
372
- checked,
373
- onChange() { store.setProxied(method, urlMask, this.checked) },
374
- }),
375
- CloudIcon()))
391
+ return ClickDragToggler({
392
+ className: CSS.ProxyToggler,
393
+ title: t`Proxy Toggler`,
394
+ checked,
395
+ commit(checked) {
396
+ store.setProxied(method, urlMask, checked)
397
+ },
398
+ body: CloudIcon()
399
+ })
376
400
  }
377
401
 
378
402
 
@@ -385,7 +409,7 @@ function StaticFilesList() {
385
409
  ? null
386
410
  : Fragment(
387
411
  r('div',
388
- className(CSS.TableHeading,
412
+ classNames(CSS.TableHeading,
389
413
  store.canProxy && CSS.canProxy,
390
414
  !store.groupByMethod && CSS.nonGroupedByMethod),
391
415
  store.groupByMethod
@@ -401,7 +425,7 @@ function StaticRow(row) {
401
425
  return (
402
426
  r('div', {
403
427
  key: row.key,
404
- ...className(CSS.TableRow,
428
+ ...classNames(CSS.TableRow,
405
429
  mounted && row.isNew && CSS.animIn)
406
430
  },
407
431
 
@@ -415,42 +439,49 @@ function StaticRow(row) {
415
439
 
416
440
  StatusCodeToggler({
417
441
  title: t`Not Found`,
418
- label: t`404`,
442
+ body: t`404`,
419
443
  checked: row.status === 404,
420
- onChange() {
421
- store.setStaticRouteStatus(row.urlMask, this.checked
444
+ commit(checked) {
445
+ store.setStaticRouteStatus(row.urlMask, checked
422
446
  ? 404
423
447
  : 200)
424
448
  }
425
449
  }),
426
450
 
427
- !groupByMethod && r('span', className(CSS.Method), 'GET'),
451
+ !groupByMethod && r('span', classNames(CSS.Method), 'GET'),
428
452
 
429
453
  r('a', {
430
454
  href: row.urlMask,
431
455
  target: '_blank',
432
456
  className: CSS.PreviewLink,
433
457
  }, ditto
434
- ? [r('span', className(CSS.dittoDir), ditto), tail]
458
+ ? [r('span', classNames(CSS.dittoDir), ditto), tail]
435
459
  : tail)))
436
460
  }
437
461
 
438
- function StatusCodeToggler({ title, label, onChange, checked }) {
439
- return (
440
- r('label', {
441
- title,
442
- className: CSS.StatusCodeToggler
443
- },
444
- r('input', {
445
- type: 'checkbox',
446
- checked,
447
- onChange
448
- }),
449
- r('span', className(CSS.checkboxBody), label)))
462
+ function StatusCodeToggler({ title, body, commit, checked, disabled }) {
463
+ return ClickDragToggler({
464
+ title,
465
+ disabled,
466
+ className: CSS.StatusCodeToggler,
467
+ commit,
468
+ checked,
469
+ body
470
+ })
450
471
  }
451
472
 
452
-
453
473
  function DelayToggler({ checked, commit, optClassName }) {
474
+ return ClickDragToggler({
475
+ canClickDrag: true,
476
+ checked,
477
+ commit,
478
+ ...classNames(CSS.DelayToggler, optClassName),
479
+ title: t`Delay`,
480
+ body: TimerIcon()
481
+ })
482
+ }
483
+
484
+ function ClickDragToggler({ checked, commit, className, title, body }) {
454
485
  function onPointerEnter(event) {
455
486
  if (event.buttons === 1)
456
487
  onPointerDown.call(this)
@@ -468,10 +499,7 @@ function DelayToggler({ checked, commit, optClassName }) {
468
499
  commit(this.checked)
469
500
  }
470
501
  return (
471
- r('label', {
472
- ...className(CSS.DelayToggler, optClassName),
473
- title: t`Delay`
474
- },
502
+ r('label', { ...classNames(CSS.Toggler, className), title },
475
503
  r('input', {
476
504
  type: 'checkbox',
477
505
  checked,
@@ -480,7 +508,7 @@ function DelayToggler({ checked, commit, optClassName }) {
480
508
  onClick,
481
509
  onChange
482
510
  }),
483
- TimerIcon()))
511
+ r('span', classNames(CSS.checkboxBody), body)))
484
512
  }
485
513
 
486
514
  function Resizer(ref) {
@@ -535,7 +563,7 @@ const payloadViewerCodeRef = {}
535
563
 
536
564
  function PayloadViewer() {
537
565
  return (
538
- r('div', className(CSS.PayloadViewer),
566
+ r('div', classNames(CSS.PayloadViewer),
539
567
  RightToolbar(),
540
568
  r('pre', null,
541
569
  r('code', { ref: payloadViewerCodeRef },
@@ -544,7 +572,7 @@ function PayloadViewer() {
544
572
 
545
573
  function RightToolbar() {
546
574
  return (
547
- r('div', className(CSS.SubToolbar),
575
+ r('div', classNames(CSS.SubToolbar),
548
576
  r('h2', { ref: payloadViewerTitleRef },
549
577
  !store.hasChosenLink && t`Preview`)))
550
578
  }
@@ -575,7 +603,7 @@ function PayloadViewerTitleWhenProxied(response) {
575
603
  const SPINNER_DELAY = 80
576
604
  function PayloadViewerProgressBar() {
577
605
  return (
578
- r('div', className(CSS.ProgressBar),
606
+ r('div', classNames(CSS.ProgressBar),
579
607
  r('div', {
580
608
  style: {
581
609
  animationDuration: store.delay - SPINNER_DELAY + 'ms'
@@ -1,4 +1,4 @@
1
- export function className(...args) {
1
+ export function classNames(...args) {
2
2
  return {
3
3
  className: args.filter(Boolean).join(' ')
4
4
  }
@@ -400,7 +400,7 @@ main {
400
400
  padding-bottom: 4px;
401
401
  padding-left: 1px;
402
402
  border-top: 24px solid transparent;
403
- margin-left: 74px;
403
+ margin-left: 71px;
404
404
  font-weight: bold;
405
405
  text-align: left;
406
406
 
@@ -409,7 +409,7 @@ main {
409
409
  }
410
410
 
411
411
  &.canProxy {
412
- margin-left: 110px;
412
+ margin-left: 100px;
413
413
  }
414
414
  &.nonGroupedByMethod {
415
415
  margin-left: 122px;
@@ -505,11 +505,11 @@ main {
505
505
  }
506
506
  }
507
507
 
508
- .DelayToggler,
509
- .ProxyToggler {
508
+
509
+ .Toggler {
510
510
  display: flex;
511
511
 
512
- > input {
512
+ input {
513
513
  /* For click drag target */
514
514
  position: absolute;
515
515
  width: 22px;
@@ -518,136 +518,85 @@ main {
518
518
 
519
519
  &:focus-visible {
520
520
  outline: 0;
521
- & + svg {
521
+ & + .checkboxBody {
522
522
  outline: 2px solid var(--colorAccent)
523
523
  }
524
524
  }
525
- }
526
525
 
527
- > svg {
528
- fill: none;
529
- stroke: var(--colorSecondaryAction);
530
- }
531
- }
526
+ &:disabled + .checkboxBody {
527
+ cursor: not-allowed;
528
+ opacity: 0.7;
529
+ }
532
530
 
533
- .DelayToggler {
534
- > input {
535
- &:checked + svg {
531
+ &:checked + .checkboxBody {
536
532
  border-color: var(--colorAccent);
537
533
  fill: var(--colorAccent);
538
534
  background: var(--colorAccent);
539
535
  stroke: var(--colorBackground);
540
536
  }
541
537
 
542
- &:enabled:hover:not(:checked) + svg {
538
+ &:enabled:hover:not(:checked) + .checkboxBody {
543
539
  border-color: var(--colorHover);
544
540
  background: var(--colorHover);
545
541
  stroke: var(--colorText);
546
542
  }
547
543
  }
548
544
 
549
- > svg {
545
+ .checkboxBody {
546
+ display: flex;
550
547
  width: 22px;
551
548
  height: 22px;
549
+ align-items: center;
550
+ justify-content: center;
552
551
  border: 1px solid var(--colorSecondaryActionBorder);
552
+ fill: none;
553
+ stroke: var(--colorSecondaryAction);
553
554
  stroke-width: 2.5px;
554
555
  border-radius: 50%;
555
556
  }
556
557
 
557
558
  &.canProxy {
558
- margin-left: 36px;
559
+ margin-left: 30px;
559
560
  }
560
561
  }
561
562
 
563
+ .DelayToggler {
564
+ }
565
+
562
566
  .ProxyToggler {
563
- border: 1px solid var(--colorSecondaryActionBorder);
564
567
  margin-right: 8px;
565
- border-radius: var(--radius);
566
-
567
- &:has(input:checked),
568
- &:has(input:disabled) {
569
- background: transparent;
570
- box-shadow: none;
571
- }
572
-
573
- > input {
574
- &:checked + svg {
575
- fill: var(--colorAccent);
576
- stroke: var(--colorAccent);
577
-
578
- path:last-of-type { /* inner cloud curve */
579
- stroke: var(--colorBackground);
580
- }
581
- transform: scale(1.1);
582
- }
583
-
584
- &:enabled:hover:not(:checked) + svg {
585
- fill: var(--colorHover);
586
- stroke: var(--colorText);
587
- }
588
568
 
589
- &:disabled + svg {
590
- stroke-opacity: 0.4;
591
- cursor: not-allowed;
592
- box-shadow: none;
593
- fill: transparent;
594
-
595
- path:last-of-type {
596
- stroke-opacity: 0;
597
- stroke: transparent;
598
- }
569
+ .checkboxBody {
570
+ svg {
571
+ width: 16px;
599
572
  }
600
573
  }
601
-
602
- > svg {
603
- width: 26px;
604
- padding: 1px 4px;
605
- stroke-width: 2px;
606
- border-radius: var(--radius);
607
- }
608
574
  }
609
575
 
610
576
  .StatusCodeToggler {
611
- display: flex;
612
577
  margin-right: 10px;
613
578
  margin-left: 8px;
614
- cursor: pointer;
615
-
616
- > input {
617
- appearance: none;
618
579
 
619
- &:focus-visible {
620
- outline: 0;
621
- & + .checkboxBody {
622
- outline: 2px solid var(--colorAccent)
623
- }
624
- }
580
+ input {
581
+ width: 26px;
625
582
 
626
- &:disabled + .checkboxBody {
627
- cursor: not-allowed;
628
- opacity: 0.7;
583
+ &:not(:checked):enabled:hover + .checkboxBody {
584
+ border-color: var(--colorRed);
585
+ color: var(--colorRed);
629
586
  }
630
587
  &:checked + .checkboxBody {
631
588
  border-color: var(--colorRed);
632
589
  color: white;
633
590
  background: var(--colorRed);
634
591
  }
635
- &:not(:checked):enabled:hover + .checkboxBody {
636
- border-color: var(--colorRed);
637
- color: var(--colorRed);
638
- }
639
- &:enabled:active + .checkboxBody {
640
- cursor: grabbing;
641
- }
642
592
  }
643
593
 
644
- > .checkboxBody {
594
+ .checkboxBody {
595
+ width: 27px;
645
596
  padding: 4px;
646
- border: 1px solid var(--colorSecondaryActionBorder);
647
597
  font-size: 10px;
648
598
  font-weight: bold;
649
599
  color: var(--colorSecondaryAction);
650
- border-radius: var(--radius);
651
600
  }
652
601
  }
653
602