mockaton 13.11.0 → 13.11.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/README.md +11 -8
- package/package.json +1 -1
- package/skills/mockaton/SKILL.md +8 -5
- package/src/client/ApiConstants.js +1 -1
- package/src/client/IndexHtml.js +1 -1
- package/src/client/app-header.css +231 -0
- package/src/client/app-header.js +30 -19
- package/src/client/app-mock-list.css +340 -0
- package/src/client/app-mock-list.js +370 -0
- package/src/client/app-payload-viewer.css +90 -0
- package/src/client/app-payload-viewer.js +4 -4
- package/src/client/app-store.js +1 -0
- package/src/client/app.css +0 -654
- package/src/client/app.js +18 -360
- package/src/client/utils/css.js +7 -0
- package/src/client/utils/watcherDev.js +4 -1
- package/src/server/Api.js +18 -18
- package/src/server/Mockaton.js +1 -1
- package/src/server/Mockaton.test.js +5 -4
- package/src/server/cli.js +37 -33
- package/src/server/cli.test.js +2 -4
- package/src/server/utils/HttpServerResponse.test.js +7 -9
- package/src/server/utils/WatcherDevClient.js +1 -1
- package/src/server/utils/fs.js +1 -1
- package/src/server/utils/openInBrowser.js +15 -10
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
## [Docs ↗](https://mockaton.com) | [Changelog ↗](https://mockaton.com/changelog) | [Skills](skills/mockaton/SKILL.md)
|
|
9
9
|
|
|
10
|
-
Mockaton is an HTTP mock server for simulating APIs,
|
|
11
|
-
|
|
10
|
+
Mockaton is an HTTP mock server for simulating APIs. With it, you can
|
|
11
|
+
test API errors, edge cases, and difficult to reproduce states in general.
|
|
12
12
|
|
|
13
13
|
<picture>
|
|
14
14
|
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/ericfortis/mockaton/refs/heads/main/pixaton-tests/tests/macos/pic-for-readme.vp762x762.light.gold.png">
|
|
@@ -30,13 +30,20 @@ Test it:
|
|
|
30
30
|
```sh
|
|
31
31
|
curl localhost:2020/api/user
|
|
32
32
|
```
|
|
33
|
-
Dashboard: [localhost:2020/mockaton](http://localhost:2020/mockaton)
|
|
34
33
|
|
|
35
34
|
<!-- SKILLS_IGNORE_END -->
|
|
36
35
|
|
|
36
|
+
|
|
37
|
+
# Installation ([more options ↗](https://mockaton.com/installation))
|
|
38
|
+
Mockaton has no dependencies.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npm install -g mockaton
|
|
42
|
+
```
|
|
43
|
+
|
|
37
44
|
## Basic Usage
|
|
38
45
|
```sh
|
|
39
|
-
|
|
46
|
+
mockaton --port 2020 my-mocks-dir/
|
|
40
47
|
```
|
|
41
48
|
|
|
42
49
|
Mockaton will serve the files on the given directory. It's a file-system based router, so
|
|
@@ -71,10 +78,6 @@ npx skills add ericfortis/mockaton
|
|
|
71
78
|
```
|
|
72
79
|
<!-- SKILLS_IGNORE_END -->
|
|
73
80
|
|
|
74
|
-
## Installation ([more options ↗](https://mockaton.com/installation))
|
|
75
|
-
```sh
|
|
76
|
-
npm install mockaton
|
|
77
|
-
```
|
|
78
81
|
|
|
79
82
|
## How to create mocks?
|
|
80
83
|
Write it to your mocks directory. `.ts` files are served as JSON by default.
|
package/package.json
CHANGED
package/skills/mockaton/SKILL.md
CHANGED
|
@@ -3,9 +3,16 @@ name: Mockaton
|
|
|
3
3
|
description: Generates and serves mock HTTP APIs. Use when creating, editing, or reasoning about mock endpoints.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
|
+
# Installation ([more options ↗](https://mockaton.com/installation))
|
|
7
|
+
Mockaton has no dependencies.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g mockaton
|
|
11
|
+
```
|
|
12
|
+
|
|
6
13
|
## Basic Usage
|
|
7
14
|
```sh
|
|
8
|
-
|
|
15
|
+
mockaton --port 2020 my-mocks-dir/
|
|
9
16
|
```
|
|
10
17
|
|
|
11
18
|
Mockaton will serve the files on the given directory. It's a file-system based router, so
|
|
@@ -30,10 +37,6 @@ Similarly, each route can have different response status code variants.
|
|
|
30
37
|
|
|
31
38
|
|
|
32
39
|
|
|
33
|
-
## Installation ([more options ↗](https://mockaton.com/installation))
|
|
34
|
-
```sh
|
|
35
|
-
npm install mockaton
|
|
36
|
-
```
|
|
37
40
|
|
|
38
41
|
## How to create mocks?
|
|
39
42
|
Write it to your mocks directory. `.ts` files are served as JSON by default.
|
package/src/client/IndexHtml.js
CHANGED
|
@@ -11,7 +11,7 @@ export const IndexHtml = (hotReloadEnabled, version) => `
|
|
|
11
11
|
<html lang="en-US">
|
|
12
12
|
<head>
|
|
13
13
|
<meta charset="UTF-8">
|
|
14
|
-
<base href="${API.
|
|
14
|
+
<base href="${API.root}/">
|
|
15
15
|
|
|
16
16
|
<script type="module" src="app.js"></script>
|
|
17
17
|
<link rel="preload" href="${API.state}" as="fetch" crossorigin>
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
.Header {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex: 0 0 100%;
|
|
4
|
+
padding: 16px;
|
|
5
|
+
background: var(--colorBgHeader);
|
|
6
|
+
|
|
7
|
+
.Logo {
|
|
8
|
+
align-self: end;
|
|
9
|
+
margin-right: 18px;
|
|
10
|
+
margin-bottom: 2px;
|
|
11
|
+
transition: opacity 240ms ease-in-out;
|
|
12
|
+
|
|
13
|
+
svg {
|
|
14
|
+
width: 122px;
|
|
15
|
+
pointer-events: none;
|
|
16
|
+
fill: var(--colorText);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@media (max-width: 570px) {
|
|
21
|
+
.Logo {
|
|
22
|
+
display: none;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:has(.FallbackBackend) {
|
|
27
|
+
@media (max-width: 740px) {
|
|
28
|
+
.Logo {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
> div {
|
|
35
|
+
display: flex;
|
|
36
|
+
width: 100%;
|
|
37
|
+
flex-wrap: wrap;
|
|
38
|
+
align-items: flex-end;
|
|
39
|
+
gap: 16px 12px;
|
|
40
|
+
|
|
41
|
+
&:has(.FallbackBackend) {
|
|
42
|
+
@media (max-width: 600px) {
|
|
43
|
+
.HelpLink {
|
|
44
|
+
margin-left: unset;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.GlobalDelayWrap {
|
|
51
|
+
display: flex;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
.ResetButton {
|
|
56
|
+
padding: 6px 12px;
|
|
57
|
+
border: 1px solid var(--colorRed);
|
|
58
|
+
margin-right: 8px;
|
|
59
|
+
margin-left: 4px;
|
|
60
|
+
background: transparent;
|
|
61
|
+
border-radius: 50px;
|
|
62
|
+
color: var(--colorRed);
|
|
63
|
+
box-shadow: var(--boxShadowRimDrop);
|
|
64
|
+
|
|
65
|
+
@media (prefers-color-scheme: dark) {
|
|
66
|
+
color: var(--colorText);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&:hover {
|
|
70
|
+
background: var(--colorRed);
|
|
71
|
+
color: white;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.HelpLink {
|
|
76
|
+
width: 24px;
|
|
77
|
+
height: 24px;
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
align-self: end;
|
|
80
|
+
margin-bottom: 3px;
|
|
81
|
+
margin-left: auto;
|
|
82
|
+
box-shadow: var(--boxShadowRimDrop);
|
|
83
|
+
opacity: 0.8;
|
|
84
|
+
border-radius: 50%;
|
|
85
|
+
fill: var(--colorBgHeader);
|
|
86
|
+
background: var(--colorLabel);
|
|
87
|
+
|
|
88
|
+
&:hover {
|
|
89
|
+
background: var(--colorAccent);
|
|
90
|
+
opacity: 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
svg {
|
|
94
|
+
transform: scale(.7);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.GlobalDelay,
|
|
100
|
+
.GlobalDelayJitter,
|
|
101
|
+
.CookieSelector,
|
|
102
|
+
.FallbackBackend {
|
|
103
|
+
width: 116px;
|
|
104
|
+
|
|
105
|
+
span {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
margin-left: 8px;
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
color: var(--colorLabel);
|
|
111
|
+
font-size: 11px;
|
|
112
|
+
gap: 4px;
|
|
113
|
+
transition: color 200ms ease-out;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
&:has(input:focus-visible),
|
|
117
|
+
&:hover {
|
|
118
|
+
span {
|
|
119
|
+
color: var(--colorText);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
input[type=url],
|
|
124
|
+
input[type=number],
|
|
125
|
+
select {
|
|
126
|
+
width: 100%;
|
|
127
|
+
height: 28px;
|
|
128
|
+
padding: 4px 8px;
|
|
129
|
+
margin-top: 2px;
|
|
130
|
+
color: var(--colorText);
|
|
131
|
+
font-size: 11px;
|
|
132
|
+
background-color: var(--colorBgHeaderField);
|
|
133
|
+
border-radius: var(--radius);
|
|
134
|
+
box-shadow: var(--boxShadowRimDrop);
|
|
135
|
+
transition: background-color ease-in-out 120ms;
|
|
136
|
+
|
|
137
|
+
&:hover {
|
|
138
|
+
background-color: var(--colorBgFieldHover);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
&.GlobalDelay,
|
|
143
|
+
&.GlobalDelayJitter {
|
|
144
|
+
input {
|
|
145
|
+
&&::selection {
|
|
146
|
+
background-color: transparent;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
&::-webkit-outer-spin-button,
|
|
150
|
+
&::-webkit-inner-spin-button {
|
|
151
|
+
-webkit-appearance: none;
|
|
152
|
+
margin: 0;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
&.GlobalDelay {
|
|
158
|
+
position: relative;
|
|
159
|
+
width: 76px;
|
|
160
|
+
|
|
161
|
+
input {
|
|
162
|
+
padding-right: 6px;
|
|
163
|
+
border-bottom-right-radius: 0;
|
|
164
|
+
border-top-right-radius: 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
svg {
|
|
168
|
+
width: 12px;
|
|
169
|
+
height: 12px;
|
|
170
|
+
border: 1px solid var(--colorLabel);
|
|
171
|
+
stroke-width: 3px;
|
|
172
|
+
border-radius: 50%;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
&:focus-within {
|
|
176
|
+
z-index: 100;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&.GlobalDelayJitter {
|
|
181
|
+
width: 80px;
|
|
182
|
+
|
|
183
|
+
&:focus-within {
|
|
184
|
+
z-index: 100;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
span {
|
|
188
|
+
margin-left: 8px;
|
|
189
|
+
}
|
|
190
|
+
input {
|
|
191
|
+
border-bottom-left-radius: 0;
|
|
192
|
+
border-top-left-radius: 0;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
&.CookieSelector {
|
|
197
|
+
width: 100px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
&.FallbackBackend {
|
|
201
|
+
position: relative;
|
|
202
|
+
width: 160px;
|
|
203
|
+
|
|
204
|
+
.SaveProxiedCheckbox {
|
|
205
|
+
position: absolute;
|
|
206
|
+
top: 0;
|
|
207
|
+
right: 0;
|
|
208
|
+
display: flex;
|
|
209
|
+
width: auto;
|
|
210
|
+
min-width: unset;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: 4px;
|
|
213
|
+
|
|
214
|
+
input + .checkboxBody {
|
|
215
|
+
margin: 0;
|
|
216
|
+
margin-right: 4px;
|
|
217
|
+
}
|
|
218
|
+
input:enabled + .checkboxBody {
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
input:disabled {
|
|
223
|
+
cursor: not-allowed;
|
|
224
|
+
}
|
|
225
|
+
input:disabled + .checkboxBody {
|
|
226
|
+
opacity: 0.8;
|
|
227
|
+
cursor: not-allowed;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
package/src/client/app-header.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { createElement as r, t } from './utils/dom.js'
|
|
2
2
|
import { Logo, HelpIcon } from './graphics.js'
|
|
3
|
+
import { adoptSheet } from './utils/css.js'
|
|
3
4
|
import { store } from './app-store.js'
|
|
4
5
|
|
|
5
|
-
import CSS from './app.css' with { type: 'css' }
|
|
6
|
-
|
|
7
|
-
Object.assign(CSS, extractClassNames(CSS))
|
|
6
|
+
import CSS from './app-header.css' with { type: 'css' }
|
|
7
|
+
adoptSheet(CSS, './app-header.css')
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
export function Header() {
|
|
11
11
|
return (
|
|
12
|
-
r('header',
|
|
12
|
+
r('header', { className: CSS.Header },
|
|
13
13
|
r('a', {
|
|
14
14
|
className: CSS.Logo,
|
|
15
15
|
href: 'https://mockaton.com',
|
|
@@ -56,7 +56,7 @@ function GlobalDelayJitterField() {
|
|
|
56
56
|
min: 0,
|
|
57
57
|
max: 300,
|
|
58
58
|
step: 10,
|
|
59
|
-
value: (store.delayJitter * 100).toFixed(0),
|
|
59
|
+
value: parseInt((store.delayJitter * 100).toFixed(0), 10),
|
|
60
60
|
onChange
|
|
61
61
|
})
|
|
62
62
|
}
|
|
@@ -140,10 +140,22 @@ function HelpLink() {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
function SlidableNumberField({ className, label, onChange, min, max, step, value }) {
|
|
143
|
+
function SlidableNumberField({ name, className, label, onChange, min, max, step, value }) {
|
|
144
|
+
function clamp(val) {
|
|
145
|
+
return Math.min(Math.max(val, min), max)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function cursorFor(val) {
|
|
149
|
+
if (val === max) return 'w-resize'
|
|
150
|
+
if (val === min) return 'e-resize'
|
|
151
|
+
return 'col-resize'
|
|
152
|
+
}
|
|
153
|
+
|
|
144
154
|
function onPointerDown(event) {
|
|
145
155
|
let lastX = event.clientX
|
|
146
|
-
const input = event.target
|
|
156
|
+
const input = /** @type {HTMLInputElement} */ event.target
|
|
157
|
+
const initialVal = input.value
|
|
158
|
+
|
|
147
159
|
input.setPointerCapture(event.pointerId)
|
|
148
160
|
window.addEventListener('pointerup', onPointerUp, { once: true })
|
|
149
161
|
window.addEventListener('pointermove', onPointerMove)
|
|
@@ -151,23 +163,22 @@ function SlidableNumberField({ className, label, onChange, min, max, step, value
|
|
|
151
163
|
function onPointerUp(ev) {
|
|
152
164
|
input.releasePointerCapture(ev.pointerId)
|
|
153
165
|
window.removeEventListener('pointermove', onPointerMove)
|
|
166
|
+
if (input.value !== initialVal)
|
|
167
|
+
input.dispatchEvent(new Event('change'))
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
function onPointerMove(ev) {
|
|
157
171
|
const diff = ev.clientX - lastX
|
|
158
|
-
if (Math.abs(diff)
|
|
159
|
-
|
|
172
|
+
if (Math.abs(diff) > 10) {
|
|
173
|
+
lastX = ev.clientX
|
|
160
174
|
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
let s = step
|
|
176
|
+
if (ev.shiftKey) s *= 2
|
|
177
|
+
else if (ev.altKey) s /= 2
|
|
163
178
|
|
|
164
|
-
|
|
165
|
-
input.
|
|
166
|
-
|
|
167
|
-
input.stepDown()
|
|
168
|
-
|
|
169
|
-
if (v !== input.value)
|
|
170
|
-
input.dispatchEvent(new Event('change'))
|
|
179
|
+
input.valueAsNumber = clamp(input.valueAsNumber + Math.sign(diff) * s)
|
|
180
|
+
input.style.cursor = cursorFor(input.valueAsNumber)
|
|
181
|
+
}
|
|
171
182
|
}
|
|
172
183
|
}
|
|
173
184
|
|
|
@@ -177,7 +188,7 @@ function SlidableNumberField({ className, label, onChange, min, max, step, value
|
|
|
177
188
|
r('input', {
|
|
178
189
|
type: 'number',
|
|
179
190
|
autocomplete: 'none',
|
|
180
|
-
style: { cursor:
|
|
191
|
+
style: { cursor: cursorFor(value) },
|
|
181
192
|
min,
|
|
182
193
|
max,
|
|
183
194
|
step,
|