mockaton 11.2.0 → 11.2.1

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
@@ -6,8 +6,8 @@
6
6
  [![CodeQL](https://github.com/ericfortis/mockaton/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/ericfortis/mockaton/actions/workflows/github-code-scanning/codeql)
7
7
  [![codecov](https://codecov.io/github/ericfortis/mockaton/graph/badge.svg?token=90NYLMMG1J)](https://codecov.io/github/ericfortis/mockaton)
8
8
 
9
- An HTTP mock server for simulating APIs with minimal setup
10
- — ideal for testing difficult to reproduce backend states.
9
+ An HTTP mock server for simulating APIs with minimal setup — ideal
10
+ for testing difficult to reproduce backend states.
11
11
 
12
12
  ## Overview
13
13
  With Mockaton, you don’t need to write code for wiring up your
@@ -97,8 +97,8 @@ api/videos.GET.<b>500</b>.txt # Internal Server Error
97
97
  ### Option 1: Browser extension
98
98
  The companion Chrome [devtools
99
99
  extension](https://github.com/ericfortis/download-http-requests-browser-ext)
100
- lets you download all the HTTP responses, and they
101
- get saved following Mockaton’s filename convention.
100
+ lets you download all the HTTP responses and
101
+ save them following Mockaton’s filename convention.
102
102
 
103
103
  ### Option 2: Fallback to your backend
104
104
  <details>
@@ -123,24 +123,12 @@ They will be saved in your `config.mocksDir` following the filename convention.
123
123
 
124
124
  ## Motivation
125
125
 
126
- ### Testing scenarios that would otherwise be skipped
127
- - Simulate errors on third-party APIs, or on your project’s backend (if you are a frontend dev, or unfamiliar with that code)
128
- - Trigger dynamic states on an API. You can do this by using comments on mock filenames, for example, for polled alerts or notifications.
129
- - Trigger empty (no content) responses
130
- - Sometimes, the ideal flow you need is just too difficult to reproduce from the actual backend
131
-
132
- ### Works around unstable dev backends while developing UIs
133
- - Spinning up dev infrastructure
134
- - Syncing the database
135
- - Mitigates progress from being blocked by waiting for APIs
136
-
137
- ### Time travel
138
- If you commit the mocks to your repo, you don’t have to downgrade backends when:
139
- - Checking out long-lived branches
140
- - Bisecting bugs
141
-
142
126
  ### Deterministic and comprehensive states
143
- - Ideal for setting up screenshot tests, e.g., with [pixaton](https://github.com/ericfortis/pixaton)
127
+ Sometimes the flow you need to test is
128
+ too difficult to reproduce from the actual backend.
129
+
130
+ - Demo edge cases to PMs, Design, and clients
131
+ - Set up screenshot tests, e.g., with [pixaton](https://github.com/ericfortis/pixaton)
144
132
  - Spot inadvertent regressions during development. For example, the demo app in
145
133
  this repo has a list of colors containing all of their possible states, such as
146
134
  permutations for out-of-stock, new-arrival, and discontinued. This way you’ll
@@ -148,6 +136,10 @@ If you commit the mocks to your repo, you don’t have to downgrade backends whe
148
136
 
149
137
  <img src="./demo-app-vite/pixaton-tests/pic-for-readme.vp740x880.light.gold.png" alt="Mockaton Demo App Screenshot" width="740" />
150
138
 
139
+ <br/>
140
+
141
+
142
+ ## Benefits
151
143
 
152
144
  ### Standalone demo server (Docker)
153
145
  You can demo your app by compiling the frontend and putting
@@ -157,25 +149,52 @@ repo includes a demo which builds and runs a docker container.
157
149
  ```sh
158
150
  git clone https://github.com/ericfortis/mockaton.git --depth 1
159
151
  cd mockaton/demo-app-vite
160
- make start-standalone-demo
152
+ make run-standalone-demo
161
153
  ```
162
154
  - App: http://localhost:4040
163
155
  - Dashboard: http://localhost:4040/mockaton
164
156
 
157
+ ### Testing scenarios that would otherwise be skipped
158
+ - Trigger dynamic states on an API. You can do this by using comments on mock filenames, for example, for polled alerts or notifications.
159
+ - Testing retries, on-the-fly you can change an endpoint from a 500 to a 200.
160
+ - Simulate errors on third-party APIs, or on your project’s backend (if you are a frontend dev, or unfamiliar with that code)
161
+ - Generating dynamic responses on the fly. Mockaton lets you use Node’s HTTP handlers (see function mocks) when using function mocks.
162
+ So you can, e.g.:
163
+ - have an in-memory database
164
+ - read from disk
165
+ - read query string
166
+ - pretty much anything you can do with a normal backend request handler
165
167
 
166
168
  ### Privacy and security
167
169
  - Does not write to disk. Except when you select ✅ **Save Mocks** for scraping mocks from a backend
168
170
  - Does not initiate network connections (no logs, no telemetry)
169
171
  - Does not hijack your HTTP client
170
- - Auditable, organized, and small. 4 KLoC (half is UI and tests)
172
+ - Auditable
173
+ - Organized and small. 4 KLoC (half is UI and tests)
171
174
  - Zero dependencies. No runtime and no build packages.
172
175
 
173
176
  <br/>
174
177
 
178
+ ## Benefits of Mocking APIs in General
179
+ The section above highlights benefits specific to Mockaton. There are more, but
180
+ in general here are some benefits which Mockaton has but other tools have as well:
181
+
182
+ ### Works around unstable dev backends while developing UIs
183
+ - Syncing the database and spinning up dev infrastructure can be complex
184
+ - Mitigates progress from being blocked by waiting for APIs
185
+
186
+ ### Time travel
187
+ If you commit the mocks to your repo, you don’t have to downgrade
188
+ backends when checking out long-lived branches or bisecting bugs.
189
+
190
+ <br/>
175
191
 
176
- ## Usage Without Docker
177
192
 
178
- Requires Node.js. **v22.18+** support writing mocks in TypeScript.
193
+ ## Usage (without Docker)
194
+
195
+ _For Docker, see the Quick-Start section above._
196
+
197
+ Requires Node.js **v22.18+**, which supports mocks in TypeScript.
179
198
 
180
199
  1. Create a mock in the default mocks directory (`./mockaton-mocks`)
181
200
  ```sh
@@ -230,16 +249,11 @@ The CLI options override their counterparts in `mockaton.config.js`
230
249
  ## mockaton.config.js (Optional)
231
250
  Mockaton looks for a file `mockaton.config.js` in its current working directory.
232
251
 
233
- <details>
234
- <summary>Defaults Overview… </summary>
252
+ ### Defaults Overview
253
+ The next section has the documentation, but here's an overview of the defaults:
235
254
 
236
255
  ```js
237
- import {
238
- defineConfig,
239
- jsToJsonPlugin,
240
- openInBrowser,
241
- SUPPORTED_METHODS
242
- } from 'mockaton'
256
+ import { defineConfig, jsToJsonPlugin, openInBrowser, SUPPORTED_METHODS } from 'mockaton'
243
257
 
244
258
  export default defineConfig({
245
259
  mocksDir: 'mockaton-mocks',
@@ -248,11 +262,11 @@ export default defineConfig({
248
262
  watcherEnabled: true,
249
263
 
250
264
  host: '127.0.0.1',
251
- port: 0,
265
+ port: 0, // auto-assigned
252
266
 
253
267
  logLevel: 'normal',
254
268
 
255
- delay: 1200,
269
+ delay: 1200, // Global value in ms. But only applied to routes with the Delayed checkbox "ON"
256
270
  delayJitter: 0,
257
271
 
258
272
  proxyFallback: '',
@@ -280,8 +294,7 @@ export default defineConfig({
280
294
  })
281
295
  ```
282
296
 
283
- </details>
284
-
297
+ <br/>
285
298
  <details>
286
299
  <summary><b>Config Documentation…</b></summary>
287
300
 
@@ -361,7 +374,7 @@ the filename will have `[id]` in their place. For example:
361
374
  my-mocks-dir<b>/api/user/</b>[id]<b>/likes</b>.GET.200.json
362
375
  </pre>
363
376
 
364
- Your existing mocks won’t be overwritten. In other words, responses of routes with
377
+ Your existing mocks won’t be overwritten. Responses of routes with
365
378
  the ☁️ **Cloud Checkbox** selected will be saved with unique filename-comments.
366
379
 
367
380
 
@@ -411,7 +424,7 @@ If you need to send more than one cookie, you can inject them globally
411
424
  in `config.extraHeaders`, or individually in a function `.js` or `.ts` mock.
412
425
 
413
426
  By the way, the `jwtCookie` helper has a hardcoded header and signature.
414
- In other words, it’s useful only if you care about its payload.
427
+ So it’s useful only if you care about its payload.
415
428
 
416
429
  <br/>
417
430
 
@@ -572,7 +585,7 @@ For example, `api/foo.GET.200.js`
572
585
  export default { foo: 'bar' }
573
586
  ```
574
587
 
575
- ### Option B: Function (async or sync)
588
+ ### Option B: Function Mocks (async or sync)
576
589
 
577
590
  **Return** a `string | Buffer | Uint8Array`, but **don’t call** `response.end()`
578
591
 
@@ -632,7 +645,7 @@ export default function listColors() {
632
645
  **What if I need to serve a static .js or .ts?**
633
646
 
634
647
  **Option A:** Put it in your `config.staticDir` without the `.GET.200.js` extension.
635
- In other words, mocks in `staticDir` take precedence over `mocksDir/*`.
648
+ Mocks in `staticDir` take precedence over `mocksDir/*`.
636
649
 
637
650
  **Option B:** Read it and return it. For example:
638
651
  ```js
@@ -715,7 +728,7 @@ api/user<b>(default)</b>.GET.200.json
715
728
  <br/>
716
729
 
717
730
  ### Query string params
718
- The query string is ignored for routing purposes. In other words, it’s only used for
731
+ The query string is ignored for routing purposes. It’s only used for
719
732
  documenting the URL contract.
720
733
  <pre>
721
734
  api/video<b>?limit=[limit]</b>.GET.200.json
@@ -752,7 +765,6 @@ All of its methods return their `fetch` response promise.
752
765
  ```js
753
766
  import { Commander } from 'mockaton'
754
767
 
755
-
756
768
  const myMockatonAddr = 'http://localhost:4040'
757
769
  const mockaton = new Commander(myMockatonAddr)
758
770
  ```
@@ -875,6 +887,7 @@ programs hijack your browser’s HTTP client (and Node’s).
875
887
  - [Wire Mock](https://github.com/wiremock/wiremock)
876
888
  - [Mock](https://github.com/dhuan/mock)
877
889
  - [Swagger](https://swagger.io/)
890
+ - [Mockoon](https://mockoon.com)
878
891
 
879
892
 
880
893
  <br/>
package/index.js CHANGED
@@ -3,6 +3,6 @@ export { Commander } from './src/ApiCommander.js'
3
3
 
4
4
  export { jwtCookie } from './src/utils/jwt.js'
5
5
  export { jsToJsonPlugin } from './src/MockDispatcher.js'
6
- export { parseJSON } from './src/utils/http-request.js'
6
+ export { parseJSON, BodyReaderError } from './src/utils/http-request.js'
7
7
 
8
8
  export const defineConfig = opts => opts
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "mockaton",
3
3
  "description": "HTTP Mock Server",
4
4
  "type": "module",
5
- "version": "11.2.0",
5
+ "version": "11.2.1",
6
6
  "types": "./index.d.ts",
7
7
  "exports": {
8
8
  ".": {
package/src/Dashboard.css CHANGED
@@ -132,7 +132,7 @@ header {
132
132
  border-bottom: 1px solid var(--colorSecondaryActionBorder);
133
133
  background: var(--colorHeaderBackground);
134
134
 
135
- > img {
135
+ > object {
136
136
  align-self: end;
137
137
  margin-right: 22px;
138
138
  margin-bottom: 5px;
package/src/Dashboard.js CHANGED
@@ -96,9 +96,9 @@ function App() {
96
96
  function Header() {
97
97
  return (
98
98
  r('header', null,
99
- r('img', {
100
- alt: t`Mockaton`,
101
- src: 'Logo.svg',
99
+ r('object', {
100
+ data: 'Logo.svg',
101
+ type: 'image/svg+xml',
102
102
  width: 120,
103
103
  height: 22
104
104
  }),
package/src/Filename.js CHANGED
@@ -1,4 +1,4 @@
1
- const httpMethods = [ // @KeepSync with node:http.METHODS
1
+ const httpMethods = [ // @KeepSync node:http.METHODS (this file is used on the client too)
2
2
  'ACL', 'BIND', 'CHECKOUT', 'CONNECT', 'COPY', 'DELETE',
3
3
  'GET', 'HEAD', 'LINK', 'LOCK', 'M-SEARCH', 'MERGE',
4
4
  'MKACTIVITY', 'MKCALENDAR', 'MKCOL', 'MOVE', 'NOTIFY', 'OPTIONS',
@@ -28,7 +28,7 @@ export function validateFilename(file) {
28
28
  if (!httpMethods.includes(method))
29
29
  return `Unrecognized HTTP Method: "${method}"`
30
30
  }
31
- // TODO ThinkAbout 206 (reject, handle, or send in full?)
31
+ // TODO @ThinkAbout 206 (reject, handle, or send in full?)
32
32
 
33
33
 
34
34
  export function parseFilename(file) {
@@ -53,7 +53,7 @@ function responseStatusIsValid(status) {
53
53
  && status >= 100
54
54
  && status <= 599
55
55
  }
56
- // TODO ThinkAbout allowing custom status codes
56
+ // TODO @ThinkAbout allowing custom status codes
57
57
 
58
58
 
59
59
  export function makeMockFilename(url, method, status, ext) {
package/src/Logo.svg CHANGED
@@ -1,17 +1,18 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <svg width="556" height="100" version="1.1" viewBox="0 0 556 100" xmlns="http://www.w3.org/2000/svg">
3
- <style>:root { --color: #000000; }
3
+ <style>
4
4
  @media (prefers-color-scheme: light) { :root { --color: #333 } }
5
5
  @media (prefers-color-scheme: dark) { :root { --color: #eee } }
6
- path { fill: var(--color) }
6
+ path {
7
+ fill: #777;
8
+ fill: var(--color);
9
+ }
7
10
  </style>
8
- <g stroke-opacity=".99608">
9
- <path
10
- 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"/>
11
- <path
12
- 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"
13
- opacity="0.75"/>
14
- <path
15
- 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"/>
16
- </g>
11
+ <path
12
+ 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"/>
13
+ <path
14
+ 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"
15
+ opacity="0.75"/>
16
+ <path
17
+ 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"/>
17
18
  </svg>
package/src/MockBroker.js CHANGED
@@ -22,7 +22,7 @@ export class MockBroker {
22
22
 
23
23
  #sortMocks() {
24
24
  this.mocks.sort()
25
- const defaults = this.mocks.filter(file => includesComment(file, DEFAULT_MOCK_COMMENT))
25
+ const defaults = this.mocks.filter(f => includesComment(f, DEFAULT_MOCK_COMMENT))
26
26
  this.mocks = Array.from(new Set(defaults).union(new Set(this.mocks)))
27
27
  }
28
28
 
@@ -55,18 +55,19 @@ export class MockBroker {
55
55
  }
56
56
 
57
57
  toggle500() {
58
- this.proxied = false
59
- if (this.auto500 || this.status === 500)
58
+ const shouldUnset = this.auto500 || this.status === 500
59
+ if (shouldUnset)
60
60
  this.selectDefaultFile()
61
61
  else {
62
- const f = this.mocks.find(this.#is500) // TESTME
63
- if (f)
64
- this.selectFile(f)
62
+ const f500 = this.mocks.find(this.#is500)
63
+ if (f500)
64
+ this.selectFile(f500)
65
65
  else {
66
66
  this.auto500 = true
67
- this.status = 500 // TESTME
67
+ this.status = 500
68
68
  }
69
69
  }
70
+ this.proxied = false
70
71
  }
71
72
 
72
73
  setDelayed(delayed) {
@@ -31,17 +31,20 @@ export async function dispatchMock(req, response) {
31
31
  if (cookie.getCurrent())
32
32
  response.setHeader('Set-Cookie', cookie.getCurrent())
33
33
 
34
- response.statusCode = broker.auto500 ? 500 : broker.status // TESTME plugins can change it
34
+ response.statusCode = broker.auto500
35
+ ? 500
36
+ : broker.status
35
37
  const { mime, body } = broker.auto500
36
38
  ? { mime: '', body: '' }
37
39
  : await applyPlugins(join(config.mocksDir, broker.file), req, response)
38
40
 
39
- logger.accessMock(req.url, broker.file)
40
41
  response.setHeader('Content-Type', mime)
41
42
  response.setHeader('Content-Length', length(body))
42
43
 
43
44
  setTimeout(() => response.end(isHead ? null : body),
44
45
  Number(broker.delayed && calcDelay()))
46
+
47
+ logger.accessMock(req.url, broker.file)
45
48
  }
46
49
  catch (error) { // TESTME
47
50
  if (error?.code === 'ENOENT') // mock-file has been deleted
@@ -74,7 +77,7 @@ export async function jsToJsonPlugin(filePath, req, response) {
74
77
 
75
78
  function length(body) {
76
79
  if (typeof body === 'string') return Buffer.byteLength(body)
77
- if (Buffer.isBuffer(body)) return body.length
80
+ if (Buffer.isBuffer(body)) return body.length // TESTME
78
81
  if (body instanceof Uint8Array) return body.byteLength
79
82
  return 0
80
83
  }
package/src/Mockaton.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createServer } from 'node:http'
2
+ import pkgJSON from '../package.json' with { type: 'json' }
2
3
 
3
4
  import { API } from './ApiConstants.js'
4
5
  import { logger } from './utils/logger.js'
@@ -11,7 +12,10 @@ import { setCorsHeaders, isPreflight } from './utils/http-cors.js'
11
12
  import { watchMocksDir, watchStaticDir } from './Watcher.js'
12
13
  import { apiPatchRequests, apiGetRequests } from './Api.js'
13
14
  import { BodyReaderError, hasControlChars } from './utils/http-request.js'
14
- import { sendNoContent, sendInternalServerError, sendUnprocessable, sendTooLongURI, sendBadRequest } from './utils/http-response.js'
15
+ import {
16
+ setHeaders, sendNoContent, sendInternalServerError,
17
+ sendUnprocessable, sendTooLongURI, sendBadRequest
18
+ } from './utils/http-response.js'
15
19
 
16
20
 
17
21
  export function Mockaton(options) {
@@ -42,8 +46,8 @@ export function Mockaton(options) {
42
46
  async function onRequest(req, response) {
43
47
  response.on('error', logger.warn)
44
48
 
45
- for (let i = 0; i < config.extraHeaders.length; i += 2)
46
- response.setHeader(config.extraHeaders[i], config.extraHeaders[i + 1])
49
+ setHeaders(response, ['Server', `Mockaton ${pkgJSON.version}`])
50
+ setHeaders(response, config.extraHeaders)
47
51
 
48
52
  const url = req.url || ''
49
53
 
package/src/config.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { resolve } from 'node:path'
2
- import pkgJSON from '../package.json' with { type: 'json' }
3
2
 
4
3
  import { logger } from './utils/logger.js'
5
4
  import { isDirectory } from './utils/fs.js'
@@ -82,13 +81,10 @@ export function setup(options) {
82
81
  Object.assign(config, options)
83
82
  validate(config, ConfigValidator)
84
83
  logger.setLevel(config.logLevel)
85
- config.extraHeaders.push('Server', `Mockaton ${pkgJSON.version}`)
86
84
  }
87
85
 
88
-
89
86
  export const isFileAllowed = f => !config.ignore.test(f)
90
87
 
91
88
  export const calcDelay = () => config.delayJitter
92
89
  ? config.delay * (1 + Math.random() * config.delayJitter)
93
90
  : config.delay
94
-
@@ -70,10 +70,8 @@ function filenameIsValid(file) {
70
70
 
71
71
  export function unregisterMock(file) {
72
72
  const broker = brokerByFilename(file)
73
- if (!broker) // TESTME
74
- return
75
- const isEmpty = broker.unregister(file)
76
- if (isEmpty) {
73
+ const hasNoMoreMocks = broker?.unregister(file)
74
+ if (hasNoMoreMocks) {
77
75
  const { method, urlMask } = parseFilename(file)
78
76
  delete collection[method][urlMask]
79
77
  if (!Object.keys(collection[method]).length)
@@ -96,9 +94,7 @@ export function brokerByFilename(file) {
96
94
  * worry about the primacy of array-like keys when iterating.
97
95
  @returns {MockBroker | undefined} */
98
96
  export function brokerByRoute(method, url) {
99
- if (!collection[method])
100
- return
101
- const brokers = Object.values(collection[method])
97
+ const brokers = Object.values(collection[method] || {})
102
98
  for (let i = brokers.length - 1; i >= 0; i--)
103
99
  if (brokers[i].urlMaskMatches(url))
104
100
  return brokers[i]
@@ -29,7 +29,7 @@ export function init() {
29
29
 
30
30
  /** @returns {boolean} registered */
31
31
  export function registerMock(relativeFile) {
32
- if (!isFileAllowed(basename(relativeFile))) // TESTME
32
+ if (!isFileAllowed(basename(relativeFile)))
33
33
  return false
34
34
 
35
35
  const route = '/' + relativeFile
@@ -1,9 +1,15 @@
1
1
  import fs, { readFileSync } from 'node:fs'
2
+
2
3
  import { logger } from './logger.js'
3
4
  import { mimeFor } from './mime.js'
4
5
  import { HEADER_502 } from '../ApiConstants.js'
5
6
 
6
7
 
8
+ export function setHeaders(response, headers) {
9
+ for (let i = 0; i < headers.length; i += 2)
10
+ response.setHeader(headers[i], headers[i + 1])
11
+ }
12
+
7
13
  export function sendOK(response) {
8
14
  logger.access(response)
9
15
  response.end()