mockaton 12.3.1 → 12.3.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 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.2",
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,44 @@ 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.disabled = newEl.disabled
337
+ oldEl.value = newEl.value
338
+ break
339
+ }
340
+ }
341
+ }
318
342
  }
319
343
 
320
344
 
345
+
321
346
  function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
322
347
  function onClick(event) {
323
348
  event.preventDefault()
@@ -327,12 +352,12 @@ function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
327
352
  const [ditto, tail] = urlMaskDittoed
328
353
  return (
329
354
  r('a', {
330
- ...className(CSS.PreviewLink, isChosen && CSS.chosen),
355
+ ...classNames(CSS.PreviewLink, isChosen && CSS.chosen),
331
356
  href: urlMask,
332
357
  autofocus,
333
358
  onClick
334
359
  }, ditto
335
- ? [r('span', className(CSS.dittoDir), ditto), tail]
360
+ ? [r('span', classNames(CSS.dittoDir), ditto), tail]
336
361
  : tail))
337
362
  }
338
363
 
@@ -352,7 +377,7 @@ function MockSelector(row) {
352
377
  },
353
378
  'aria-label': t`Mock Selector`,
354
379
  disabled: row.opts.length < 2,
355
- ...className(
380
+ ...classNames(
356
381
  CSS.MockSelector,
357
382
  row.selectedIdx > 0 && CSS.nonDefault,
358
383
  row.selectedFileIs4xx && CSS.status4xx)
@@ -362,17 +387,15 @@ function MockSelector(row) {
362
387
 
363
388
 
364
389
  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()))
390
+ return ClickDragToggler({
391
+ className: CSS.ProxyToggler,
392
+ title: t`Proxy Toggler`,
393
+ checked,
394
+ commit(checked) {
395
+ store.setProxied(method, urlMask, checked)
396
+ },
397
+ body: CloudIcon()
398
+ })
376
399
  }
377
400
 
378
401
 
@@ -385,7 +408,7 @@ function StaticFilesList() {
385
408
  ? null
386
409
  : Fragment(
387
410
  r('div',
388
- className(CSS.TableHeading,
411
+ classNames(CSS.TableHeading,
389
412
  store.canProxy && CSS.canProxy,
390
413
  !store.groupByMethod && CSS.nonGroupedByMethod),
391
414
  store.groupByMethod
@@ -401,7 +424,7 @@ function StaticRow(row) {
401
424
  return (
402
425
  r('div', {
403
426
  key: row.key,
404
- ...className(CSS.TableRow,
427
+ ...classNames(CSS.TableRow,
405
428
  mounted && row.isNew && CSS.animIn)
406
429
  },
407
430
 
@@ -415,42 +438,49 @@ function StaticRow(row) {
415
438
 
416
439
  StatusCodeToggler({
417
440
  title: t`Not Found`,
418
- label: t`404`,
441
+ body: t`404`,
419
442
  checked: row.status === 404,
420
- onChange() {
421
- store.setStaticRouteStatus(row.urlMask, this.checked
443
+ commit(checked) {
444
+ store.setStaticRouteStatus(row.urlMask, checked
422
445
  ? 404
423
446
  : 200)
424
447
  }
425
448
  }),
426
449
 
427
- !groupByMethod && r('span', className(CSS.Method), 'GET'),
450
+ !groupByMethod && r('span', classNames(CSS.Method), 'GET'),
428
451
 
429
452
  r('a', {
430
453
  href: row.urlMask,
431
454
  target: '_blank',
432
455
  className: CSS.PreviewLink,
433
456
  }, ditto
434
- ? [r('span', className(CSS.dittoDir), ditto), tail]
457
+ ? [r('span', classNames(CSS.dittoDir), ditto), tail]
435
458
  : tail)))
436
459
  }
437
460
 
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)))
461
+ function StatusCodeToggler({ title, body, commit, checked, disabled }) {
462
+ return ClickDragToggler({
463
+ title,
464
+ disabled,
465
+ className: CSS.StatusCodeToggler,
466
+ commit,
467
+ checked,
468
+ body
469
+ })
450
470
  }
451
471
 
452
-
453
472
  function DelayToggler({ checked, commit, optClassName }) {
473
+ return ClickDragToggler({
474
+ canClickDrag: true,
475
+ checked,
476
+ commit,
477
+ ...classNames(CSS.DelayToggler, optClassName),
478
+ title: t`Delay`,
479
+ body: TimerIcon()
480
+ })
481
+ }
482
+
483
+ function ClickDragToggler({ checked, commit, className, title, body }) {
454
484
  function onPointerEnter(event) {
455
485
  if (event.buttons === 1)
456
486
  onPointerDown.call(this)
@@ -468,10 +498,7 @@ function DelayToggler({ checked, commit, optClassName }) {
468
498
  commit(this.checked)
469
499
  }
470
500
  return (
471
- r('label', {
472
- ...className(CSS.DelayToggler, optClassName),
473
- title: t`Delay`
474
- },
501
+ r('label', { ...classNames(CSS.Toggler, className), title },
475
502
  r('input', {
476
503
  type: 'checkbox',
477
504
  checked,
@@ -480,7 +507,7 @@ function DelayToggler({ checked, commit, optClassName }) {
480
507
  onClick,
481
508
  onChange
482
509
  }),
483
- TimerIcon()))
510
+ r('span', classNames(CSS.checkboxBody), body)))
484
511
  }
485
512
 
486
513
  function Resizer(ref) {
@@ -535,7 +562,7 @@ const payloadViewerCodeRef = {}
535
562
 
536
563
  function PayloadViewer() {
537
564
  return (
538
- r('div', className(CSS.PayloadViewer),
565
+ r('div', classNames(CSS.PayloadViewer),
539
566
  RightToolbar(),
540
567
  r('pre', null,
541
568
  r('code', { ref: payloadViewerCodeRef },
@@ -544,7 +571,7 @@ function PayloadViewer() {
544
571
 
545
572
  function RightToolbar() {
546
573
  return (
547
- r('div', className(CSS.SubToolbar),
574
+ r('div', classNames(CSS.SubToolbar),
548
575
  r('h2', { ref: payloadViewerTitleRef },
549
576
  !store.hasChosenLink && t`Preview`)))
550
577
  }
@@ -575,7 +602,7 @@ function PayloadViewerTitleWhenProxied(response) {
575
602
  const SPINNER_DELAY = 80
576
603
  function PayloadViewerProgressBar() {
577
604
  return (
578
- r('div', className(CSS.ProgressBar),
605
+ r('div', classNames(CSS.ProgressBar),
579
606
  r('div', {
580
607
  style: {
581
608
  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