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 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, designed
11
- for testing difficult to reproduce backend states with minimal setup.
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
- npx mockaton --port 2020 my-mocks-dir/
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
@@ -2,7 +2,7 @@
2
2
  "name": "mockaton",
3
3
  "description": "HTTP Mock Server",
4
4
  "type": "module",
5
- "version": "13.11.0",
5
+ "version": "13.11.2",
6
6
  "exports": {
7
7
  ".": {
8
8
  "import": "./index.js",
@@ -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
- npx mockaton --port 2020 my-mocks-dir/
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.
@@ -3,7 +3,7 @@
3
3
  const MOUNT = '/mockaton'
4
4
 
5
5
  export const API = {
6
- dashboard: MOUNT,
6
+ root: MOUNT,
7
7
 
8
8
  reset: MOUNT + '/reset',
9
9
  select: MOUNT + '/select',
@@ -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.dashboard}/">
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
+ }
@@ -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
- import { extractClassNames } from './utils/css.js'
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', null,
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) < 10)
159
- return
172
+ if (Math.abs(diff) > 10) {
173
+ lastX = ev.clientX
160
174
 
161
- lastX = ev.clientX
162
- const v = input.value
175
+ let s = step
176
+ if (ev.shiftKey) s *= 2
177
+ else if (ev.altKey) s /= 2
163
178
 
164
- if (diff > 0)
165
- input.stepUp()
166
- else
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: 'col-resize' },
191
+ style: { cursor: cursorFor(value) },
181
192
  min,
182
193
  max,
183
194
  step,