mockaton 12.5.0 → 12.5.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.5.0",
5
+ "version": "12.5.2",
6
6
  "exports": {
7
7
  ".": {
8
8
  "import": "./index.js",
@@ -9,7 +9,7 @@ const METHODS = [ // @KeepSync node:http.METHODS
9
9
  'TRACE', 'UNBIND', 'UNLINK', 'UNLOCK', 'UNSUBSCRIBE'
10
10
  ]
11
11
 
12
- const reComments = /\(.*?\)/g // Anything within parentheses
12
+ const reComments = /\([^()]*\)/g // Anything within parentheses
13
13
 
14
14
  export function extractComments(file) {
15
15
  return Array.from(file.matchAll(reComments), ([c]) => c)
@@ -44,13 +44,17 @@ export function parseFilename(file) {
44
44
  }
45
45
  }
46
46
 
47
- function removeTrailingSlash(url = '') {
47
+ export function removeTrailingSlash(url = '') {
48
48
  return url
49
49
  .replace(/\/$/, '')
50
50
  .replace('/?', '?')
51
51
  .replace('/#', '#')
52
52
  }
53
53
 
54
+ export function removeQueryStringAndFragment(url = '') {
55
+ return new URL(url, 'http://_').pathname
56
+ }
57
+
54
58
  function responseStatusIsValid(status) {
55
59
  return Number.isInteger(status)
56
60
  && status >= 100
package/src/client/app.js CHANGED
@@ -679,7 +679,7 @@ async function onError(error) {
679
679
  msg = error.statusText
680
680
  }
681
681
  else if (error?.message === 'Failed to fetch') {
682
- msg = t`Looks like the Mockaton server is not running`
682
+ msg = t`Looks like Mockaton server is not running`
683
683
  isOffline = true
684
684
  }
685
685
  else
@@ -714,7 +714,7 @@ function Logo() {
714
714
  return (
715
715
  s('svg', { viewBox: '0 0 556 100' },
716
716
  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' }),
717
- s('path', { opacity: 0.75, 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' }),
717
+ 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' }),
718
718
  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' })))
719
719
  }
720
720
 
@@ -734,7 +734,7 @@ function CloudIcon() {
734
734
  function HelpIcon() {
735
735
  return (
736
736
  s('svg', { viewBox: '0 0 24 24' },
737
- s('path', { d: 'M11 18h2v-2h-2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8m0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4' })))
737
+ 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' })))
738
738
  }
739
739
 
740
740
 
@@ -1,51 +1,25 @@
1
1
  :root {
2
+ color-scheme: light dark;
3
+ --colorBackground: light-dark(#fff, #181818);
4
+ --colorComboBoxHeaderBackground: light-dark(#fff, #222);
5
+ --colorSecondaryButtonBackground: light-dark(#fcfcfc, #2c2c2c);
6
+ --colorHeaderBackground: light-dark(#f2f2f3, #141414);
7
+ --colorComboBoxBackground: light-dark(#eee, #2a2a2a);
8
+ --colorBorder: light-dark(#e0e0e0, #333);
9
+ --colorSecondaryAction: light-dark(#666, #aaa);
10
+ --colorLabel: light-dark(#555, #aaa);
11
+ --colorDisabledMockSelector: light-dark(#444, #a9b9b9);
12
+ --colorText: light-dark(#000, #fff);
13
+ --colorAccent: light-dark(#0059dd, #2495ff);
14
+ --colorHover: light-dark(rgba(119, 193, 255, 0.4), rgba(0, 63, 115, 0.5));
15
+ --colorRed: light-dark(#da0f00, #f41606);
16
+ --colorPink: light-dark(#ed206a, #f92672);
17
+ --colorPurple: light-dark(#9b71e8, #ae81ff);
18
+ --colorGreen: light-dark(#388e3c, #a6e22e);
19
+ --color4xxBackground: light-dark(#ffedd1, #68554a);
20
+
21
+ accent-color: var(--colorAccent);
2
22
  --radius: 16px;
3
- --boxShadow1: rgba(0, 0, 0, 0.18) 0 2px 1px -1px, rgba(0, 0, 0, 0.12) 0 1px 1px 0, rgba(0, 0, 0, 0.1) 0 1px 3px 0px;
4
- }
5
-
6
- @media (prefers-color-scheme: light) {
7
- :root {
8
- color-scheme: light;
9
- --colorBackground: #fff;
10
- --colorComboBoxHeaderBackground: #fff;
11
- --colorSecondaryButtonBackground: #fcfcfc;
12
- --colorHeaderBackground: #f2f2f3;
13
- --colorComboBoxBackground: #eee;
14
- --colorSecondaryActionBorder: #e0e0e0;
15
- --colorSecondaryAction: #666;
16
- --colorLabel: #555;
17
- --colorDisabledMockSelector: #444;
18
- --colorText: #000;
19
- --colorAccent: #0059dd;
20
- --colorHover: rgba(119, 193, 255, 0.4);
21
- --colorRed: #da0f00;
22
- --colorPink: #ed206a;
23
- --colorPurple: #9b71e8;
24
- --colorGreen: #388e3c;
25
- --color4xxBackground: #ffedd1;
26
- }
27
- }
28
- @media (prefers-color-scheme: dark) {
29
- :root {
30
- color-scheme: dark;
31
- --colorBackground: #181818;
32
- --colorComboBoxHeaderBackground: #222;
33
- --colorSecondaryButtonBackground: #2c2c2c;
34
- --colorHeaderBackground: #141414;
35
- --colorComboBoxBackground: #2a2a2a;
36
- --colorSecondaryActionBorder: #333;
37
- --colorSecondaryAction: #aaa;
38
- --colorLabel: #aaa;
39
- --colorDisabledMockSelector: #a9b9b9;
40
- --colorText: #fff;
41
- --colorAccent: #2495ff;
42
- --colorHover: rgba(0, 63, 115, 0.5);
43
- --colorRed: #f41606;
44
- --colorPink: #f92672;
45
- --colorPurple: #ae81ff;
46
- --colorGreen: #a6e22e;
47
- --color4xxBackground: #68554a;
48
- }
49
23
  }
50
24
 
51
25
  html,
@@ -112,7 +86,7 @@ select {
112
86
  transition: all 240ms ease-out;
113
87
 
114
88
  &:enabled {
115
- border-color: var(--colorSecondaryActionBorder);
89
+ border-color: var(--colorBorder);
116
90
  background-color: var(--colorSecondaryButtonBackground);
117
91
  &:hover {
118
92
  border-color: var(--colorHover);
@@ -129,7 +103,6 @@ header {
129
103
  display: flex;
130
104
  flex: 0 0 100%;
131
105
  padding: 16px;
132
- border-bottom: 1px solid var(--colorSecondaryActionBorder);
133
106
  background: var(--colorHeaderBackground);
134
107
 
135
108
  .Logo {
@@ -232,7 +205,7 @@ header {
232
205
  width: 100%;
233
206
  height: 28px;
234
207
  padding: 4px 8px;
235
- border: 1px solid var(--colorSecondaryActionBorder);
208
+ border: 1px solid var(--colorBorder);
236
209
  margin-top: 2px;
237
210
  color: var(--colorText);
238
211
  font-size: 11px;
@@ -337,8 +310,8 @@ main {
337
310
 
338
311
  .leftSide {
339
312
  width: 50%; /* resizable in js */
340
- border-right: 1px solid var(--colorSecondaryActionBorder);
341
- box-shadow: var(--boxShadow1);
313
+ border-top: 1px solid var(--colorBorder);
314
+ border-right: 1px solid var(--colorBorder);
342
315
  }
343
316
 
344
317
  .rightSide {
@@ -346,6 +319,7 @@ main {
346
319
  min-width: 100px;
347
320
  min-height: 0;
348
321
  flex: 1;
322
+ border-top: 1px solid var(--colorBorder);
349
323
 
350
324
  .Resizer {
351
325
  position: absolute;
@@ -357,7 +331,7 @@ main {
357
331
  cursor: col-resize;
358
332
 
359
333
  &:active {
360
- border-color: var(--colorSecondaryActionBorder);
334
+ border-color: var(--colorBorder);
361
335
  }
362
336
  }
363
337
  }
@@ -370,7 +344,7 @@ main {
370
344
  justify-content: space-between;
371
345
  padding-right: 14px;
372
346
  padding-left: 16px;
373
- border-bottom: 1px solid var(--colorSecondaryActionBorder);
347
+ border-bottom: 1px solid var(--colorBorder);
374
348
  background: var(--colorHeaderBackground);
375
349
  }
376
350
 
@@ -385,7 +359,7 @@ main {
385
359
  select {
386
360
  width: 110px;
387
361
  padding: 6px 8px;
388
- border: 1px solid var(--colorSecondaryActionBorder);
362
+ border: 1px solid var(--colorBorder);
389
363
  margin-left: 4px;
390
364
  background-image: none;
391
365
  text-align-last: center;
@@ -555,7 +529,7 @@ main {
555
529
  height: 22px;
556
530
  align-items: center;
557
531
  justify-content: center;
558
- border: 1px solid var(--colorSecondaryActionBorder);
532
+ border: 1px solid var(--colorBorder);
559
533
  fill: none;
560
534
  stroke: var(--colorSecondaryAction);
561
535
  stroke-width: 2.5px;
@@ -683,7 +657,6 @@ main {
683
657
  background: var(--colorRed);
684
658
  color: white;
685
659
  border-radius: var(--radius);
686
- box-shadow: var(--boxShadow1);
687
660
  opacity: 0;
688
661
  transform: translateY(20px);
689
662
  animation: _kfToastIn 240ms forwards;
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_MOCK_COMMENT } from '../client/ApiConstants.js'
2
- import { parseFilename, includesComment, extractComments } from '../client/Filename.js'
2
+ import { parseFilename, includesComment, extractComments, removeQueryStringAndFragment } from '../client/Filename.js'
3
3
 
4
4
 
5
5
  /**
@@ -105,15 +105,11 @@ class UrlMatcher {
105
105
 
106
106
  #buildUrlRegex(file) {
107
107
  let { urlMask } = parseFilename(file)
108
- urlMask = this.#removeQueryStringAndFragment(urlMask)
108
+ urlMask = removeQueryStringAndFragment(urlMask)
109
109
  urlMask = this.#disregardVariables(urlMask)
110
110
  return new RegExp('^' + urlMask + '/*$')
111
111
  }
112
112
 
113
- #removeQueryStringAndFragment(str) {
114
- return str.replace(/[?#].*/, '')
115
- }
116
-
117
113
  #disregardVariables(str) { // Stars out all parts that are in square brackets
118
114
  return str.replace(/\[.*?]/g, '[^/]+')
119
115
  }
@@ -127,7 +123,7 @@ class UrlMatcher {
127
123
  // slashes. For instance, for routing api/foo/[id]?qs…
128
124
  urlMaskMatches = (url) => {
129
125
  let u = decodeURIComponent(url)
130
- u = this.#removeQueryStringAndFragment(u)
126
+ u = removeQueryStringAndFragment(u)
131
127
  u += '/'
132
128
  return this.#urlRegex.test(u)
133
129
  }
@@ -1,6 +1,7 @@
1
1
  import { relative } from 'node:path'
2
2
  import { config } from '../config.js'
3
- import { parseFilename } from '../../client/Filename.js'
3
+ import { decode } from './HttpIncomingMessage.js'
4
+ import { parseFilename, removeTrailingSlash, removeQueryStringAndFragment } from '../../client/Filename.js'
4
5
 
5
6
 
6
7
  export function parseQueryParams(url) {
@@ -11,14 +12,16 @@ export function parseSplats(url, filename) {
11
12
  const { urlMask } = parseFilename(relative(config.mocksDir, filename))
12
13
 
13
14
  const splats = []
14
- const pattern = urlMask
15
+ const pattern = removeQueryStringAndFragment(decode(urlMask))
15
16
  .replace(/\[(.+?)]/g, (_, name) => {
16
17
  splats.push(name)
17
18
  return '([^/]+)'
18
19
  })
19
- .replace(/\//g, '\\/')
20
20
 
21
- const match = url.match(new RegExp(`^${pattern}$`))
21
+ let u = removeQueryStringAndFragment(url)
22
+ u = removeTrailingSlash(u)
23
+
24
+ const match = u.match(new RegExp(`^${pattern}$`))
22
25
  if (!match)
23
26
  return {}
24
27
 
@@ -1,4 +1,4 @@
1
- import { test } from 'node:test'
1
+ import { test, describe } from 'node:test'
2
2
  import { deepEqual, equal } from 'node:assert/strict'
3
3
  import { parseSplats, parseQueryParams } from './UrlParsers.js'
4
4
  import { config } from '../config.js'
@@ -9,14 +9,46 @@ test('parseQueryParams', () => {
9
9
  })
10
10
 
11
11
 
12
- test('parseSplats', () => {
13
- const p = parseSplats(
14
- '/api/company/123/user/456',
15
- `${config.mocksDir}/api/company/[companyId]/user/[userId](comments).GET.200.js`
16
- )
17
- deepEqual(p, {
18
- companyId: '123',
19
- userId: '456',
12
+ describe('parseSplats', () => {
13
+ test('one splat', () => {
14
+ const splats = parseSplats(
15
+ '/api/company/123',
16
+ `${config.mocksDir}/api/company/[companyId].GET.200.js`
17
+ )
18
+ deepEqual(splats, {
19
+ companyId: '123'
20
+ })
21
+ })
22
+
23
+ test('one splat with trailing slash', () => {
24
+ const splats = parseSplats(
25
+ '/api/company/123/',
26
+ `${config.mocksDir}/api/company/[companyId].GET.200.js`
27
+ )
28
+ deepEqual(splats, {
29
+ companyId: '123'
30
+ })
31
+ })
32
+
33
+ test('two splats and comment', () => {
34
+ const splats = parseSplats(
35
+ '/api/company/123/user/456',
36
+ `${config.mocksDir}/api/company/[companyId]/user/[userId](comments).GET.200.js`
37
+ )
38
+ deepEqual(splats, {
39
+ companyId: '123',
40
+ userId: '456',
41
+ })
42
+ })
43
+
44
+ test('ignores query string', () => {
45
+ const splats = parseSplats(
46
+ '/api/company/123?foo=456',
47
+ `${config.mocksDir}/api/company/[companyId]?foo=[fooId].GET.200.js`
48
+ )
49
+ deepEqual(splats, {
50
+ companyId: '123'
51
+ })
20
52
  })
21
53
  })
22
54