mockaton 13.3.2 → 13.3.4

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": "13.3.2",
5
+ "version": "13.3.4",
6
6
  "exports": {
7
7
  ".": {
8
8
  "import": "./index.js",
@@ -93,8 +93,6 @@ export async function previewMock() {
93
93
 
94
94
 
95
95
  async function updatePayloadViewer(proxied, file, response) {
96
- const mime = response.headers.get('content-type') || ''
97
-
98
96
  titleRef.elem.replaceChildren(proxied
99
97
  ? PayloadViewerTitleWhenProxied(response)
100
98
  : PayloadViewerTitle(file, response.statusText))
@@ -104,6 +102,12 @@ async function updatePayloadViewer(proxied, file, response) {
104
102
  return
105
103
  }
106
104
 
105
+ async function bodyAsText() {
106
+ return (await response.text()) || t`/* Empty Response Body */`
107
+ }
108
+
109
+ const mime = response.headers.get('content-type') || ''
110
+
107
111
  if (mime.startsWith('image/'))
108
112
  codeRef.elem.replaceChildren(r('img', {
109
113
  src: URL.createObjectURL(await response.blob())
@@ -140,11 +144,6 @@ async function updatePayloadViewer(proxied, file, response) {
140
144
  href: URL.createObjectURL(await response.blob()),
141
145
  download: store.chosenLink.urlMask
142
146
  }, t`Download`))
143
-
144
-
145
- async function bodyAsText() {
146
- return (await response.text()) || t`/* Empty Response Body */`
147
- }
148
147
  }
149
148
 
150
149
 
@@ -140,21 +140,7 @@ export const store = {
140
140
  store.brokersByMethod[method][urlMask] = broker
141
141
  },
142
142
 
143
- folderGroupsByMethod(method) {
144
- const groups = []
145
- let g = null
146
- for (const row of store._brokersAsRowsByMethod(method)) {
147
- const folder = row.urlMask.substring(0, row.urlMask.lastIndexOf('/') + 1)
148
- if (!g || g.folder !== folder) {
149
- g = { folder, children: [] }
150
- groups.push(g)
151
- }
152
- g.children.push(row)
153
- }
154
- return groups
155
- },
156
-
157
- _brokersAsRowsByMethod(method) {
143
+ brokersAsRowsByMethod(method) {
158
144
  const rows = store._brokersAsArray(method)
159
145
  .map(b => new BrokerRowModel(b, store.canProxy))
160
146
  .sort((a, b) => a.urlMask.localeCompare(b.urlMask))
@@ -223,18 +209,18 @@ export const store = {
223
209
  function initPreference(param) {
224
210
  const qs = new URLSearchParams(globalThis.location?.search)
225
211
  if (!qs.has(param)) {
226
- const group = globalThis.localStorage?.getItem(param) !== 'false'
212
+ const group = globalThis.localStorage?.getItem(param) !== '0'
227
213
  if (!group) {
228
214
  const url = new URL(globalThis.location?.href)
229
- url.searchParams.set(param, false)
215
+ url.searchParams.set(param, '0')
230
216
  history.replaceState(null, '', url)
231
217
  }
232
218
  return group
233
219
  }
234
- return qs.get(param) !== 'false'
220
+ return qs.get(param) !== '0'
235
221
  }
236
222
 
237
- // When false, the URL and localStorage will have param=false
223
+ // When false, the URL and localStorage will have param='0'
238
224
  function togglePreference(param, nextVal) {
239
225
  if (nextVal)
240
226
  globalThis.localStorage?.removeItem(param)
@@ -245,7 +231,7 @@ function togglePreference(param, nextVal) {
245
231
  if (nextVal)
246
232
  url.searchParams.delete(param)
247
233
  else
248
- url.searchParams.set(param, false)
234
+ url.searchParams.set(param, '0')
249
235
  history.replaceState(null, '', url)
250
236
  }
251
237
 
@@ -285,6 +271,7 @@ export class BrokerRowModel {
285
271
  method = ''
286
272
  urlMask = ''
287
273
  urlMaskDittoed = ['', '']
274
+ children = []
288
275
  #broker = /** @type ClientMockBroker */ {}
289
276
  #canProxy = false
290
277
 
@@ -6,17 +6,17 @@
6
6
  --colorBgHeader: light-dark(#f2f2f3, #141414);
7
7
  --colorBgHeaderField: light-dark(#fff, #222);
8
8
 
9
- --colorBorder: light-dark(#e0e0e0, #323232);
9
+ --colorBorder: light-dark(#e0e0e0, #2c2c2c);
10
10
  --colorBorderActive: light-dark(#c8c8c8, #3c3c3c);
11
11
 
12
12
  --colorLabel: light-dark(#555, #aaa);
13
13
  --colorText: light-dark(#000, #fff);
14
14
 
15
- --colorAccent: light-dark(#0059dd, #2495ff);
16
- --colorHover: light-dark(#c1e2ff, #062d59);
15
+ --colorAccent: light-dark(#0081ff, #2495ff);
16
+ --colorHover: light-dark(#dbedff, #062d59);
17
17
 
18
18
  --colorRed: light-dark(#da0f00, #f41606);
19
- --colorPink: light-dark(#ed206a, #f92672);
19
+ --colorPink: light-dark(#ed206a, #ff2e7a);
20
20
  --colorPurple: light-dark(#9b71e8, #ae81ff);
21
21
  --colorGreen: light-dark(#388e3c, #a6e22e);
22
22
  --colorBg4xx: light-dark(#ffedd1, #68554a);
@@ -44,6 +44,8 @@ body {
44
44
  padding: 0;
45
45
  border: 0;
46
46
  margin: 0;
47
+ letter-spacing: -0.374px;
48
+ line-height: 1.2;
47
49
  font-family: inherit;
48
50
  font-size: 100%;
49
51
  scrollbar-width: thin;
@@ -119,13 +121,8 @@ header {
119
121
  align-self: end;
120
122
  margin-right: 22px;
121
123
  margin-bottom: 3px;
122
- opacity: 94%;
123
124
  transition: opacity 240ms ease-in-out;
124
125
 
125
- &:hover {
126
- opacity: 1;
127
- }
128
-
129
126
  svg {
130
127
  width: 120px;
131
128
  pointer-events: none;
@@ -192,12 +189,14 @@ header {
192
189
  height: 24px;
193
190
  flex-shrink: 0;
194
191
  align-self: end;
192
+ margin-bottom: 2px;
195
193
  margin-left: auto;
196
194
  border-radius: 50%;
197
- fill: var(--colorLabel);
195
+ fill: white;
196
+ background: var(--colorAccent);
198
197
 
199
- &:hover {
200
- fill: var(--colorAccent);
198
+ svg {
199
+ transform: scale(.7);
201
200
  }
202
201
  }
203
202
  }
@@ -420,6 +419,7 @@ main {
420
419
  .Table {
421
420
  height: 100%;
422
421
  padding: 16px;
422
+ padding-bottom: 64px;
423
423
  padding-left: 12px;
424
424
  user-select: none;
425
425
  overflow-y: auto;
@@ -462,6 +462,7 @@ main {
462
462
  overflow: hidden;
463
463
  align-items: center;
464
464
  padding: 5px 0;
465
+ font-weight: 500;
465
466
  font-size: 12px;
466
467
  list-style: none;
467
468
  cursor: pointer;
@@ -516,7 +517,7 @@ main {
516
517
  }
517
518
 
518
519
  &[open] {
519
- &:has(summary:hover) {
520
+ &:has(summary:hover):not(:has(details:hover)) {
520
521
  background: linear-gradient(90deg, var(--colorHover), var(--colorBg));
521
522
  }
522
523
 
@@ -567,6 +568,7 @@ main {
567
568
  padding: 6px 8px;
568
569
  margin-right: -2px;
569
570
  margin-left: 4px;
571
+ font-weight: 500;
570
572
  border-radius: var(--radius);
571
573
  word-break: break-word;
572
574
 
@@ -578,7 +580,7 @@ main {
578
580
  background: var(--colorAccent);
579
581
  }
580
582
  .dittoDir {
581
- opacity: 0.9;
583
+ opacity: 0.8;
582
584
  filter: saturate(0.1);
583
585
  }
584
586
  }
package/src/client/app.js CHANGED
@@ -3,6 +3,7 @@ import { createElement as r, t, classNames, restoreFocus, Fragment, defineClassN
3
3
  import { store } from './app-store.js'
4
4
  import { API } from './ApiConstants.js'
5
5
  import { Header } from './app-header.js'
6
+ import { dirStructure } from './dirStructure.js'
6
7
  import { PayloadViewer, previewMock } from './app-payload-viewer.js'
7
8
  import { TimerIcon, CloudIcon, ChevronDownIcon } from './graphics.js'
8
9
 
@@ -21,36 +22,36 @@ initKeyboardNavigation()
21
22
  let mounted = false
22
23
  function render() {
23
24
  restoreFocus(() => document.body.replaceChildren(App()))
24
- if (store.hasChosenLink)
25
- previewMock()
25
+ if (store.hasChosenLink) previewMock()
26
+ if (!mounted) LeftSide.$('a')?.focus()
26
27
  mounted = true
27
28
  }
28
29
 
29
-
30
- const leftSideRef = {}
31
-
32
30
  function App() {
33
- return Fragment(Header(), Main())
34
- }
35
-
36
-
37
- function Main() {
38
- return (
31
+ return Fragment(
32
+ Header(),
39
33
  r('main', null,
40
- r('div', {
41
- ref: leftSideRef,
42
- style: { width: leftSideRef.width },
43
- className: CSS.leftSide
44
- },
45
- r('div', { className: CSS.SubToolbar },
46
- GroupByMethod(),
47
- BulkSelector()),
48
- r('div', { className: CSS.Table }, MockList())),
34
+ LeftSide(),
49
35
  r('div', { className: CSS.rightSide },
50
- Resizer(leftSideRef),
36
+ Resizer(LeftSide.ref),
51
37
  PayloadViewer())))
52
38
  }
53
39
 
40
+ function LeftSide() {
41
+ return r('div', {
42
+ ref: LeftSide.ref,
43
+ style: { width: LeftSide.ref.width },
44
+ className: CSS.leftSide
45
+ },
46
+ r('div', { className: CSS.SubToolbar },
47
+ GroupByMethod(),
48
+ BulkSelector()),
49
+ r('div', { className: CSS.Table }, MockList()))
50
+ }
51
+ LeftSide.ref = { width: undefined }
52
+ LeftSide.$ = selector => LeftSide.ref.elem.querySelector(selector)
53
+ LeftSide.$$ = selector => LeftSide.ref.elem.querySelectorAll(selector)
54
+
54
55
 
55
56
  function GroupByMethod() {
56
57
  return (
@@ -90,7 +91,6 @@ function BulkSelector() {
90
91
  }
91
92
 
92
93
 
93
-
94
94
  function MockList() {
95
95
  if (!Object.keys(store.brokersByMethod).length)
96
96
  return r('div', null, t`No mocks found`)
@@ -100,15 +100,27 @@ function MockList() {
100
100
  r('div', {
101
101
  className: classNames(CSS.TableHeading, store.canProxy && CSS.canProxy)
102
102
  }, method),
103
- FolderGroups(store.folderGroupsByMethod(method))))
103
+ FolderGroups(store.brokersAsRowsByMethod(method))))
104
+
105
+ return FolderGroups(store.brokersAsRowsByMethod('*'))
106
+ }
104
107
 
105
- return FolderGroups(store.folderGroupsByMethod('*'))
108
+ function FolderGroups(bRows) {
109
+ const res = []
110
+ for (const b of dirStructure(bRows)) {
111
+ if (!b.children.length)
112
+ res.push(Row(b))
113
+ else
114
+ res.push(FolderGroup(b))
115
+ }
116
+ return res
106
117
  }
107
118
 
108
- function FolderGroups(groups) {
109
- return groups.map(({ folder, children }) => children.length === 1
110
- ? Row(children[0], 0)
111
- : r('details', {
119
+ function FolderGroup(broker) {
120
+ const folder = broker.urlMask
121
+ const children = broker.children
122
+ return (
123
+ r('details', {
112
124
  className: CSS.FolderGroup,
113
125
  open: !store.collapsedFolders.has(folder),
114
126
  onToggle() {
@@ -124,55 +136,77 @@ function FolderGroups(groups) {
124
136
  store.canProxy && CSS.canProxy)
125
137
  },
126
138
  folder + '…')),
127
- children.map(Row)))
139
+ Row(broker),
140
+ children.map(c => c.children.length
141
+ ? FolderGroup(c)
142
+ : Row(c))))
128
143
  }
129
144
 
130
- /**
131
- * @param {BrokerRowModel} row
132
- * @param {number} i
133
- */
134
- function Row(row, i) {
145
+ /** @param {BrokerRowModel} row */
146
+ function Row(row) {
135
147
  const { method, urlMask } = row
136
148
  return (
137
149
  r('div', {
138
150
  key: row.key,
139
151
  className: classNames(CSS.TableRow, mounted && row.isNew && CSS.animIn)
140
152
  },
141
- store.canProxy && ProxyToggler(method, urlMask, row.proxied),
142
153
 
143
- DelayToggler({
144
- checked: row.delayed,
154
+ store.canProxy && ClickDragToggler({
155
+ className: CSS.ProxyToggler,
156
+ title: t`Proxy Toggler`,
157
+ body: CloudIcon(),
158
+ checked: row.proxied,
145
159
  commit(checked) {
146
- store.setDelayed(method, urlMask, checked)
147
- },
160
+ store.setProxied(method, urlMask, checked)
161
+ }
148
162
  }),
149
163
 
150
- StatusCodeToggler({
151
- title: row.isStatic ? t`Not Found` : t`Internal Server Error`,
152
- body: row.isStatic ? t`404` : t`500`,
153
- disabled: row.opts.length === 1 && (row.isStatic ? row.status === 404 : row.status === 500),
154
- checked: !row.proxied && (row.isStatic ? row.status === 404 : row.status === 500),
155
- commit() {
156
- store.toggleStatus(method, urlMask, row.isStatic ? 404 : 500)
164
+ ClickDragToggler({
165
+ className: CSS.DelayToggler,
166
+ title: t`Delay`,
167
+ body: TimerIcon(),
168
+ checked: row.delayed,
169
+ commit(checked) {
170
+ store.setDelayed(method, urlMask, checked)
157
171
  }
158
172
  }),
159
173
 
174
+ ClickDragToggler(
175
+ row.isStatic
176
+ ? {
177
+ className: CSS.StatusCodeToggler,
178
+ title: t`Not Found`,
179
+ body: t`404`,
180
+ disabled: row.opts.length === 1 && row.status === 404,
181
+ checked: !row.proxied && row.status === 404,
182
+ commit() { store.toggleStatus(method, urlMask, 404) }
183
+ }
184
+ : {
185
+ className: CSS.StatusCodeToggler,
186
+ title: t`Internal Server Error`,
187
+ body: t`500`,
188
+ disabled: row.opts.length === 1 && row.status === 500,
189
+ checked: !row.proxied && row.status === 500,
190
+ commit() { store.toggleStatus(method, urlMask, 500) }
191
+ }),
192
+
160
193
  !store.groupByMethod && r('span', { className: CSS.Method }, method),
161
194
 
162
- PreviewLink(method, urlMask, row.urlMaskDittoed, i === 0),
195
+ PreviewLink(method, urlMask, row.urlMaskDittoed),
163
196
 
164
197
  MockSelector(row)))
165
198
  }
166
199
 
200
+
167
201
  function renderRow(method, urlMask) {
168
202
  unChooseOld()
169
203
  const row = store.brokerAsRow(method, urlMask)
170
- const tr = leftSideRef.elem.querySelector(`.${CSS.TableRow}[key="${row.key}"]`)
204
+ const tr = LeftSide.$(`.${CSS.TableRow}[key="${row.key}"]`)
171
205
  mergeTableRow(tr, Row(row))
172
206
  previewMock()
173
207
 
174
208
  function unChooseOld() {
175
- return leftSideRef.elem.querySelector(`a.${CSS.chosen}`)
209
+ return LeftSide.$(`a.${CSS.chosen}`)
176
210
  ?.classList.remove(CSS.chosen)
177
211
  }
178
212
 
@@ -203,8 +237,7 @@ function renderRow(method, urlMask) {
203
237
  }
204
238
 
205
239
 
206
-
207
- function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
240
+ function PreviewLink(method, urlMask, urlMaskDittoed) {
208
241
  function onClick(event) {
209
242
  event.preventDefault()
210
243
  store.previewLink(method, urlMask)
@@ -215,7 +248,6 @@ function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
215
248
  r('a', {
216
249
  className: classNames(CSS.PreviewLink, isChosen && CSS.chosen),
217
250
  href: urlMask,
218
- autofocus,
219
251
  onClick
220
252
  }, ditto
221
253
  ? [r('span', { className: CSS.dittoDir }, ditto), tail]
@@ -247,42 +279,6 @@ function MockSelector(row) {
247
279
  }
248
280
 
249
281
 
250
- function ProxyToggler(method, urlMask, checked) {
251
- return ClickDragToggler({
252
- className: CSS.ProxyToggler,
253
- title: t`Proxy Toggler`,
254
- body: CloudIcon(),
255
- checked,
256
- commit(checked) {
257
- store.setProxied(method, urlMask, checked)
258
- }
259
- })
260
- }
261
-
262
-
263
-
264
- function StatusCodeToggler({ title, body, commit, checked, disabled }) {
265
- return ClickDragToggler({
266
- title,
267
- disabled,
268
- className: CSS.StatusCodeToggler,
269
- commit,
270
- checked,
271
- body
272
- })
273
- }
274
-
275
- function DelayToggler({ checked, commit, className }) {
276
- return ClickDragToggler({
277
- checked,
278
- commit,
279
- className: classNames(CSS.DelayToggler, className),
280
- canClickDrag: true,
281
- title: t`Delay`,
282
- body: TimerIcon()
283
- })
284
- }
285
-
286
282
  function ClickDragToggler({ checked, commit, className, title, body }) {
287
283
  function onPointerEnter(event) {
288
284
  if (event.buttons === 1)
@@ -305,7 +301,7 @@ function ClickDragToggler({ checked, commit, className, title, body }) {
305
301
  return
306
302
 
307
303
  // Uncheck all other in the column.
308
- for (const elem of leftSideRef.elem.querySelectorAll(selector))
304
+ for (const elem of LeftSide.$$(selector))
309
305
  if (elem !== this && elem.checked && !elem.disabled) {
310
306
  elem.checked = false
311
307
  elem.dispatchEvent(new Event('change'))
@@ -488,7 +484,6 @@ function columnSelectors() {
488
484
  ]
489
485
  }
490
486
 
491
-
492
487
  function initKeyboardNavigation() {
493
488
  const rowSelectors = [
494
489
  ...columnSelectors(),
@@ -503,7 +498,7 @@ function initKeyboardNavigation() {
503
498
  const sel = selectorForColumnOf(pivot)
504
499
  if (sel) {
505
500
  const offset = key === 'ArrowDown' ? +1 : -1
506
- const siblings = leftSideRef.elem.querySelectorAll(sel)
501
+ const siblings = LeftSide.$$(sel)
507
502
  circularAdjacent(offset, siblings, pivot).focus()
508
503
  }
509
504
  break
@@ -0,0 +1,47 @@
1
+ function TrieNode() {
2
+ this.brokers = []
3
+ this.tnChildren = new Map()
4
+ }
5
+
6
+ /**
7
+ * @param {Partial<BrokerRowModel>[]} brokers
8
+ * @returns {Partial<BrokerRowModel>[]}
9
+ */
10
+ export function dirStructure(brokers) {
11
+ return dfs(trie(brokers))
12
+ }
13
+
14
+ function trie(brokers) {
15
+ const root = new TrieNode()
16
+ for (const b of brokers) {
17
+ let node = root
18
+ for (const seg of b.urlMask.split('/')) { // TODO it should ignore query string
19
+ const segNode = node.tnChildren.get(seg) || new TrieNode()
20
+ node.tnChildren.set(seg, segNode)
21
+ node = segNode
22
+ }
23
+ node.brokers.push(b)
24
+ }
25
+ return root
26
+ }
27
+
28
+ /** @param {TrieNode} node */
29
+ function dfs(node) {
30
+ const childBrokers = []
31
+ for (const tnc of node.tnChildren.values())
32
+ childBrokers.push(...dfs(tnc))
33
+
34
+ const brokers = node.brokers.length
35
+ ? [node.brokers[0], ...childBrokers, ...node.brokers.slice(1)]
36
+ : childBrokers
37
+
38
+ if (!brokers.length)
39
+ return []
40
+
41
+ const [head, ...rest] = brokers
42
+ if (node.brokers.length || !head.children.length) {
43
+ head.children.push(...rest)
44
+ return [head]
45
+ }
46
+ return brokers
47
+ }
@@ -0,0 +1,81 @@
1
+ import { test } from 'node:test'
2
+ import { deepEqual } from 'node:assert/strict'
3
+ import { dirStructure } from './dirStructure.js'
4
+
5
+
6
+ const input = [
7
+ { children: [], method: 'GET', urlMask: '/api/user' },
8
+ { children: [], method: 'GET', urlMask: '/api/user/avatar' },
9
+ { children: [], method: 'GET', urlMask: '/api/video/[id]' },
10
+ { children: [], method: 'GET', urlMask: '/index.html' },
11
+ { children: [], method: 'GET', urlMask: '/media/file-a.txt' },
12
+ { children: [], method: 'GET', urlMask: '/media/file-b.txt' },
13
+ { children: [], method: 'GET', urlMask: '/media/sub/file-aa.txt' },
14
+ { children: [], method: 'GET', urlMask: '/media/sub/file-bb.txt' },
15
+ { children: [], method: 'POST', urlMask: '/api/user' },
16
+ { children: [], method: 'POST', urlMask: '/api/user/avatar/foo' },
17
+ { children: [], method: 'PATCH', urlMask: '/api/user' }
18
+ ]
19
+
20
+
21
+ const expected = [
22
+ {
23
+ urlMask: '/api/user',
24
+ method: 'GET',
25
+ children: [
26
+ {
27
+ urlMask: '/api/user/avatar',
28
+ method: 'GET',
29
+ children: [
30
+ {
31
+ urlMask: '/api/user/avatar/foo',
32
+ method: 'POST',
33
+ children: []
34
+ }
35
+ ]
36
+ }, {
37
+ urlMask: '/api/user',
38
+ method: 'POST',
39
+ children: []
40
+ }, {
41
+ urlMask: '/api/user',
42
+ method: 'PATCH',
43
+ children: []
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ urlMask: '/api/video/[id]',
49
+ method: 'GET',
50
+ children: []
51
+ },
52
+ {
53
+ urlMask: '/index.html',
54
+ method: 'GET',
55
+ children: []
56
+ },
57
+ {
58
+ urlMask: '/media/file-a.txt',
59
+ method: 'GET',
60
+ children: [
61
+ {
62
+ urlMask: '/media/file-b.txt',
63
+ method: 'GET',
64
+ children: []
65
+ }, {
66
+ urlMask: '/media/sub/file-aa.txt',
67
+ method: 'GET',
68
+ children: [
69
+ {
70
+ urlMask: '/media/sub/file-bb.txt',
71
+ method: 'GET',
72
+ children: []
73
+ }
74
+ ]
75
+ }
76
+ ]
77
+ }
78
+ ]
79
+
80
+ test('acceptance', () => deepEqual(dirStructure(input), expected))
81
+
@@ -1,35 +1,25 @@
1
1
  import { createSvgElement as s } from './dom-utils.js'
2
2
 
3
3
 
4
- export function Logo() {
5
- return (
6
- s('svg', { viewBox: '0 0 556 100' },
7
- 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' }),
8
- s('path', { opacity: 0.6, 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' }),
9
- 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' })))
10
- }
4
+ export const Logo = () =>
5
+ s('svg', { viewBox: '0 0 556 100' },
6
+ 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' }),
7
+ s('path', { opacity: 1, fill: 'currentColor', 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' }),
8
+ 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' }))
11
9
 
12
- export function TimerIcon() {
13
- return (
14
- s('svg', { viewBox: '0 0 24 24' },
15
- s('path', { d: 'm11 5.6 0.14 7.2 6 3.7' })))
16
- }
10
+ export const TimerIcon = () =>
11
+ s('svg', { viewBox: '0 0 24 24' },
12
+ s('path', { d: 'm11 5.6 0.14 7.2 6 3.7' }))
17
13
 
18
- export function CloudIcon() {
19
- return (
20
- s('svg', { viewBox: '0 0 24 24' },
21
- 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' }),
22
- s('path', { d: 'm6.1 9.1c2.8 0 5 2.3 5 5' })))
23
- }
14
+ export const CloudIcon = () =>
15
+ s('svg', { viewBox: '0 0 24 24' },
16
+ 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' }),
17
+ s('path', { d: 'm6.1 9.1c2.8 0 5 2.3 5 5' }))
24
18
 
25
- export function HelpIcon() {
26
- return (
27
- s('svg', { viewBox: '0 0 24 24' },
28
- 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' })))
29
- }
19
+ export const HelpIcon = () =>
20
+ s('svg', { viewBox: '0 0 24 24' },
21
+ s('path', { d: 'M11.07 12.85c.77-1.39 2.25-2.21 3.11-3.44.91-1.29.4-3.7-2.18-3.7-1.69 0-2.52 1.28-2.87 2.34L6.54 6.96C7.25 4.83 9.18 3 11.99 3c2.35 0 3.96 1.07 4.78 2.41.7 1.15 1.11 3.3.03 4.9-1.2 1.77-2.35 2.31-2.97 3.45-.25.46-.35.76-.35 2.24h-2.89c-.01-.78-.13-2.05.48-3.15M14 20c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2' }))
30
22
 
31
- export function ChevronDownIcon() {
32
- return (
33
- s('svg', { viewBox: '0 0 24 24', stroke: 'currentColor', fill: 'none', 'stroke-width': '2' },
34
- s('path', { d: 'M6 9l6 6 6-6' })))
35
- }
23
+ export const ChevronDownIcon = () =>
24
+ s('svg', { viewBox: '0 0 24 24', stroke: 'currentColor', fill: 'none', 'stroke-width': '2' },
25
+ s('path', { d: 'M6 9l6 6 6-6' }))
@@ -5,8 +5,8 @@ import pkgJSON from '../../package.json' with { type: 'json' }
5
5
 
6
6
  import { logger } from './utils/logger.js'
7
7
  import { ServerResponse } from './utils/HttpServerResponse.js'
8
- import { IncomingMessage, BodyReaderError, hasControlChars } from './utils/HttpIncomingMessage.js'
9
8
  import { setCorsHeaders, isPreflight } from './utils/http-cors.js'
9
+ import { IncomingMessage, BodyReaderError, hasControlChars } from './utils/HttpIncomingMessage.js'
10
10
 
11
11
  import { API } from '../client/ApiConstants.js'
12
12
 
@@ -4,7 +4,6 @@ import { EventEmitter } from 'node:events'
4
4
 
5
5
  import { config } from './config.js'
6
6
  import { isFile, isDirectory } from './utils/fs.js'
7
-
8
7
  import * as mockBrokerCollection from './mockBrokersCollection.js'
9
8
 
10
9
 
@@ -35,7 +35,7 @@ export function init() {
35
35
 
36
36
  /** @returns {boolean} registered */
37
37
  export function registerMock(file, isFromWatcher = false) {
38
- if (brokerByFilename(file)?.hasMock(file) ||
38
+ if (brokerByFilename(file)?.hasMock(file) ||
39
39
  !isFileAllowed(basename(file)))
40
40
  return false
41
41