mockaton 13.1.0 → 13.2.0

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.1.0",
5
+ "version": "13.2.0",
6
6
  "exports": {
7
7
  ".": {
8
8
  "import": "./index.js",
@@ -61,7 +61,8 @@ export class Commander {
61
61
 
62
62
 
63
63
  /**
64
- * SSE - Streams an incremental version when a mock is added, deleted, or renamed
64
+ * SSE - Streams an incremental version when a mock is added, deleted, or renamed.
65
+ * Also, when the internal state changes.
65
66
  * @returns {Promise<Response>}
66
67
  */
67
68
  getSyncVersion = () => fetch(this.addr + API.syncVersion)
@@ -10,6 +10,7 @@ export const store = {
10
10
  onError(err) {},
11
11
  render() {},
12
12
  renderRow(method, urlMask) {},
13
+ skipNextRender: false,
13
14
 
14
15
  brokersByMethod: /** @type ClientBrokersByMethod */ {},
15
16
 
@@ -58,49 +59,53 @@ export const store = {
58
59
  if (store.showProxyField === null) // isFirstCall
59
60
  store.showProxyField = Boolean(store.proxyFallback)
60
61
 
61
- store.render()
62
+ if (store.skipNextRender)
63
+ store.skipNextRender = false
64
+ else
65
+ store.render()
62
66
  })
63
67
  },
64
68
 
65
69
  reset() {
66
70
  store._request(api.reset, () => {
67
71
  store.setChosenLink('', '')
68
- store.fetchState()
69
72
  })
70
73
  },
71
74
 
72
75
  bulkSelectByComment(value) {
73
- store._request(() => api.bulkSelectByComment(value), () => {
74
- store.fetchState()
75
- })
76
+ store._request(() => api.bulkSelectByComment(value))
76
77
  },
77
78
 
78
79
  setGlobalDelay(value) {
80
+ store.skipNextRender = true
79
81
  store._request(() => api.setGlobalDelay(value), () => {
80
82
  store.delay = value
81
83
  })
82
84
  },
83
85
 
84
86
  setGlobalDelayJitter(value) {
87
+ store.skipNextRender = true
85
88
  store._request(() => api.setGlobalDelayJitter(value), () => {
86
89
  store.delayJitter = value
87
90
  })
88
91
  },
89
92
 
90
93
  selectCookie(name) {
94
+ store.skipNextRender = true
91
95
  store._request(() => api.selectCookie(name), async response => {
92
96
  store.cookies = await response.json()
93
97
  })
94
98
  },
95
99
 
96
100
  setProxyFallback(value) {
101
+ store.skipNextRender = true
97
102
  store._request(() => api.setProxyFallback(value), () => {
98
103
  store.proxyFallback = value
99
- store.render()
100
104
  })
101
105
  },
102
106
 
103
107
  setCollectProxied(checked) {
108
+ store.skipNextRender = true
104
109
  store._request(() => api.setCollectProxied(checked), () => {
105
110
  store.collectProxied = checked
106
111
  })
@@ -111,7 +116,7 @@ export const store = {
111
116
  return store.brokersByMethod[method]?.[urlMask]
112
117
  },
113
118
 
114
- setBroker(broker) {
119
+ _setBroker(broker) {
115
120
  const { method, urlMask } = parseFilename(broker.file)
116
121
  store.brokersByMethod[method] ??= {}
117
122
  store.brokersByMethod[method][urlMask] = broker
@@ -153,33 +158,37 @@ export const store = {
153
158
  },
154
159
 
155
160
  selectFile(file) {
161
+ store.skipNextRender = true
156
162
  store._request(() => api.select(file), async response => {
157
163
  const { method, urlMask } = parseFilename(file)
158
- store.setBroker(await response.json())
164
+ store._setBroker(await response.json())
159
165
  store.setChosenLink(method, urlMask)
160
166
  store.renderRow(method, urlMask)
161
167
  })
162
168
  },
163
169
 
164
170
  toggleStatus(method, urlMask, status) {
171
+ store.skipNextRender = true
165
172
  store._request(() => api.toggleStatus(method, urlMask, status), async response => {
166
- store.setBroker(await response.json())
173
+ store._setBroker(await response.json())
167
174
  store.setChosenLink(method, urlMask)
168
175
  store.renderRow(method, urlMask)
169
176
  })
170
177
  },
171
178
 
172
179
  setProxied(method, urlMask, checked) {
180
+ store.skipNextRender = true
173
181
  store._request(() => api.setRouteIsProxied(method, urlMask, checked), async response => {
174
- store.setBroker(await response.json())
182
+ store._setBroker(await response.json())
175
183
  store.setChosenLink(method, urlMask)
176
184
  store.renderRow(method, urlMask)
177
185
  })
178
186
  },
179
187
 
180
188
  setDelayed(method, urlMask, checked) {
189
+ store.skipNextRender = true
181
190
  store._request(() => api.setRouteIsDelayed(method, urlMask, checked), async response => {
182
- store.setBroker(await response.json())
191
+ store._setBroker(await response.json())
183
192
  })
184
193
  }
185
194
  }
package/src/server/Api.js CHANGED
@@ -5,24 +5,18 @@
5
5
 
6
6
  import { join } from 'node:path'
7
7
 
8
- import {
9
- sseClientHotReload,
10
- DASHBOARD_ASSETS,
11
- CLIENT_DIR
12
- } from './WatcherDevClient.js'
13
- import { startWatchers, stopWatchers, sseClientSyncVersion, notifyARR } from './Watcher.js'
14
-
15
8
  import pkgJSON from '../../package.json' with { type: 'json' }
16
9
 
10
+ import { sseClientHotReload, DASHBOARD_ASSETS, CLIENT_DIR } from './WatcherDevClient.js'
11
+ import { stopMocksDirWatcher, sseClientSyncVersion, uiSyncVersion, watchMocksDir } from './Watcher.js'
12
+
17
13
  import { API } from '../client/ApiConstants.js'
18
14
  import { IndexHtml, CSP } from '../client/IndexHtml.js'
19
15
 
20
16
  import { cookie } from './cookie.js'
21
17
  import { config, ConfigValidator } from './config.js'
22
-
23
- import { write, rm, isFile, resolveIn } from './utils/fs.js'
24
-
25
18
  import * as mockBrokersCollection from './mockBrokersCollection.js'
19
+ import { write, rm, isFile, resolveIn } from './utils/fs.js'
26
20
 
27
21
 
28
22
  export const apiGetReqs = new Map([
@@ -95,6 +89,7 @@ function getState(_, response) {
95
89
  function reset(_, response) {
96
90
  mockBrokersCollection.init()
97
91
  response.ok()
92
+ uiSyncVersion.increment()
98
93
  }
99
94
 
100
95
 
@@ -106,21 +101,7 @@ async function setCorsAllowed(req, response) {
106
101
  else {
107
102
  config.corsAllowed = corsAllowed
108
103
  response.ok()
109
- }
110
- }
111
-
112
-
113
- async function setWatchMocks(req, response) {
114
- const enabled = await req.json()
115
-
116
- if (typeof enabled !== 'boolean')
117
- response.unprocessable(`Expected boolean for "watchMocks"`)
118
- else {
119
- if (enabled)
120
- startWatchers()
121
- else
122
- stopWatchers()
123
- response.ok()
104
+ uiSyncVersion.increment()
124
105
  }
125
106
  }
126
107
 
@@ -132,7 +113,9 @@ async function setGlobalDelay(req, response) {
132
113
  response.unprocessable(`Expected non-negative integer for "delay"`)
133
114
  else {
134
115
  config.delay = delay
116
+ uiSyncVersion.increment()
135
117
  response.ok()
118
+ uiSyncVersion.increment()
136
119
  }
137
120
  }
138
121
 
@@ -144,6 +127,7 @@ async function setGlobalDelayJitter(req, response) {
144
127
  else {
145
128
  config.delayJitter = jitter
146
129
  response.ok()
130
+ uiSyncVersion.increment()
147
131
  }
148
132
  }
149
133
 
@@ -154,8 +138,10 @@ async function selectCookie(req, response) {
154
138
  const error = cookie.setCurrent(cookieKey)
155
139
  if (error)
156
140
  response.unprocessable(error?.message || error)
157
- else
141
+ else {
158
142
  response.json(cookie.list())
143
+ uiSyncVersion.increment()
144
+ }
159
145
  }
160
146
 
161
147
 
@@ -167,6 +153,7 @@ async function setProxyFallback(req, response) {
167
153
  else {
168
154
  config.proxyFallback = fallback
169
155
  response.ok()
156
+ uiSyncVersion.increment()
170
157
  }
171
158
  }
172
159
 
@@ -178,6 +165,7 @@ async function setCollectProxied(req, response) {
178
165
  else {
179
166
  config.collectProxied = collectProxied
180
167
  response.ok()
168
+ uiSyncVersion.increment()
181
169
  }
182
170
  }
183
171
 
@@ -188,6 +176,7 @@ async function bulkUpdateBrokersByCommentTag(req, response) {
188
176
 
189
177
  mockBrokersCollection.setMocksMatchingComment(comment)
190
178
  response.ok()
179
+ uiSyncVersion.increment()
191
180
  }
192
181
 
193
182
 
@@ -200,6 +189,7 @@ async function selectMock(req, response) {
200
189
  else {
201
190
  broker.selectFile(file)
202
191
  response.json(broker)
192
+ uiSyncVersion.increment()
203
193
  }
204
194
  }
205
195
 
@@ -213,6 +203,7 @@ async function toggleRouteStatus(req, response) {
213
203
  else {
214
204
  broker.toggleStatus(status)
215
205
  response.json(broker)
206
+ uiSyncVersion.increment()
216
207
  }
217
208
  }
218
209
 
@@ -228,6 +219,7 @@ async function setRouteIsDelayed(req, response) {
228
219
  else {
229
220
  broker.setDelayed(delayed)
230
221
  response.json(broker)
222
+ uiSyncVersion.increment()
231
223
  }
232
224
  }
233
225
 
@@ -245,6 +237,7 @@ async function setRouteIsProxied(req, response) {
245
237
  else {
246
238
  broker.setProxied(proxied)
247
239
  response.json(broker)
240
+ uiSyncVersion.increment()
248
241
  }
249
242
  }
250
243
 
@@ -263,7 +256,7 @@ async function writeMock(req, response) {
263
256
 
264
257
  if (!config.watcherEnabled) {
265
258
  mockBrokersCollection.registerMock(file, true)
266
- notifyARR()
259
+ uiSyncVersion.increment()
267
260
  }
268
261
  response.ok()
269
262
  }
@@ -286,10 +279,24 @@ async function deleteMock(req, response) {
286
279
 
287
280
  if (!config.watcherEnabled) {
288
281
  mockBrokersCollection.unregisterMock(file)
289
- notifyARR()
282
+ uiSyncVersion.increment()
290
283
  }
291
284
  response.ok()
292
285
  }
293
286
 
294
287
 
295
288
 
289
+ async function setWatchMocks(req, response) {
290
+ const enabled = await req.json()
291
+
292
+ if (typeof enabled !== 'boolean')
293
+ response.unprocessable(`Expected boolean for "watchMocks"`)
294
+ else {
295
+ if (enabled)
296
+ watchMocksDir()
297
+ else
298
+ stopMocksDirWatcher()
299
+ response.ok()
300
+ }
301
+ }
302
+
@@ -12,24 +12,22 @@ let mocksWatcher = null
12
12
 
13
13
 
14
14
  /**
15
- * ARR Event = Add, Remove, or Rename Mock
16
- *
17
15
  * The emitter is debounced so it handles e.g. bulk deletes,
18
16
  * and also renames, which are two events (delete + add).
19
17
  */
20
- const uiSyncVersion = new class extends EventEmitter {
18
+ export const uiSyncVersion = new class extends EventEmitter {
21
19
  version = 0
22
20
 
23
21
  increment = /** @type {function} */ this.#debounce(() => {
24
22
  this.version++
25
- super.emit('ARR')
23
+ super.emit('INC')
26
24
  })
27
25
 
28
26
  subscribe(listener) {
29
- this.on('ARR', listener)
27
+ this.on('INC', listener)
30
28
  }
31
29
  unsubscribe(listener) {
32
- this.removeListener('ARR', listener)
30
+ this.removeListener('INC', listener)
33
31
  }
34
32
 
35
33
  #debounce(fn) { // TESTME
@@ -42,11 +40,6 @@ const uiSyncVersion = new class extends EventEmitter {
42
40
  }
43
41
 
44
42
 
45
- export function notifyARR() {
46
- uiSyncVersion.increment()
47
- }
48
-
49
-
50
43
  export function watchMocksDir() {
51
44
  const dir = config.mocksDir
52
45
  mocksWatcher = mocksWatcher || watch(dir, { recursive: true, persistent: false }, (_, file) => {
@@ -69,8 +62,12 @@ export function watchMocksDir() {
69
62
  })
70
63
  }
71
64
 
65
+ export function stopMocksDirWatcher() {
66
+ mocksWatcher?.close()
67
+ mocksWatcher = null
68
+ }
69
+
72
70
 
73
- /** Realtime notify ARR Events */
74
71
  export function sseClientSyncVersion(req, response) {
75
72
  response.writeHead(200, {
76
73
  'Content-Type': 'text/event-stream',
@@ -99,11 +96,3 @@ export function sseClientSyncVersion(req, response) {
99
96
  }
100
97
 
101
98
 
102
- export function startWatchers() {
103
- watchMocksDir()
104
- }
105
-
106
- export function stopWatchers() {
107
- mocksWatcher?.close()
108
- mocksWatcher = null
109
- }