mockaton 9.1.1 → 9.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": "9.1.1",
5
+ "version": "9.2.0",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
package/src/Api.js CHANGED
@@ -27,6 +27,7 @@ export const apiGetRequests = new Map([
27
27
 
28
28
  [API.state, getState],
29
29
  [API.syncVersion, longPollClientSyncVersion],
30
+ [API.throws, () => { throw new Error('Test500') }]
30
31
  ])
31
32
 
32
33
  export const apiPatchRequests = new Map([
@@ -51,7 +52,7 @@ function serveDashboardAsset(f) {
51
52
  return (_, response) => {
52
53
  if (f.endsWith('.html'))
53
54
  response.setHeader('Content-Security-Policy', `default-src 'self'; img-src data: blob: 'self'`)
54
- return sendFile(response, join(import.meta.dirname, f))
55
+ sendFile(response, join(import.meta.dirname, f))
55
56
  }
56
57
  }
57
58
 
@@ -14,7 +14,8 @@ export const API = {
14
14
  select: MOUNT + '/select',
15
15
  state: MOUNT + '/state',
16
16
  staticStatus: MOUNT + '/static-status',
17
- syncVersion: MOUNT + '/sync-version'
17
+ syncVersion: MOUNT + '/sync-version',
18
+ throws: MOUNT + '/throws',
18
19
  }
19
20
 
20
21
  export const DF = { // Dashboard Fields (XHR)
package/src/Dashboard.js CHANGED
@@ -87,7 +87,7 @@ const state = {
87
87
  delay: 0,
88
88
  collectProxied: false,
89
89
  proxyFallback: '',
90
-
90
+
91
91
  groupByMethod: true, // TODO read from localstorage
92
92
 
93
93
  get canProxy() {
@@ -692,13 +692,18 @@ async function updatePayloadViewer(method, urlMask, response) {
692
692
  const body = await response.text() || Strings.empty_response_body
693
693
  if (mime === 'application/json')
694
694
  payloadViewerRef.current.replaceChildren(r('span', className(CSS.json), syntaxJSON(body)))
695
- else if (mime === 'application/xml')
695
+ else if (isXML(mime))
696
696
  payloadViewerRef.current.replaceChildren(syntaxXML(body))
697
697
  else
698
698
  payloadViewerRef.current.innerText = body
699
699
  }
700
700
  }
701
701
 
702
+ function isXML(mime) {
703
+ return ['text/html', 'text/xml', 'application/xml'].includes(mime)
704
+ || /application\/.*\+xml/.test(mime)
705
+ }
706
+
702
707
 
703
708
  function trFor(method, urlMask) {
704
709
  return document.querySelector(`tr[data-method="${method}"][data-urlMask="${urlMask}"]`)
@@ -811,30 +816,25 @@ function className(...args) {
811
816
  }
812
817
 
813
818
 
814
- function createElement(elem, props, ...children) {
815
- if (typeof elem === 'function')
816
- return elem(props)
819
+ function createElement(tag, props, ...children) {
820
+ if (typeof tag === 'function')
821
+ return tag(props)
817
822
 
818
- const node = document.createElement(elem)
819
- for (const [key, value] of Object.entries(props || {}))
820
- if (key === 'ref')
821
- value.current = node
822
- else if (key.startsWith('on'))
823
- node.addEventListener(key.replace(/^on/, '').toLowerCase(), value)
824
- else if (key === 'style')
825
- Object.assign(node.style, value)
826
- else if (key in node)
827
- node[key] = value
828
- else
829
- node.setAttribute(key, value)
823
+ const node = document.createElement(tag)
824
+ for (const [k, v] of Object.entries(props || {}))
825
+ if (k === 'ref') v.current = node
826
+ else if (k === 'style') Object.assign(node.style, v)
827
+ else if (k.startsWith('on')) node.addEventListener(k.replace(/^on/, '').toLowerCase(), v)
828
+ else if (k in node) node[k] = v
829
+ else node.setAttribute(k, v)
830
830
  node.append(...children.flat().filter(Boolean))
831
831
  return node
832
832
  }
833
833
 
834
834
  function createSvgElement(tagName, props, ...children) {
835
835
  const elem = document.createElementNS('http://www.w3.org/2000/svg', tagName)
836
- for (const [key, value] of Object.entries(props))
837
- elem.setAttribute(key, value)
836
+ for (const [k, v] of Object.entries(props))
837
+ elem.setAttribute(k, v)
838
838
  elem.append(...children.flat().filter(Boolean))
839
839
  return elem
840
840
  }
@@ -7,9 +7,8 @@ import { proxy } from './ProxyRelay.js'
7
7
  import { cookie } from './cookie.js'
8
8
  import { mimeFor } from './utils/mime.js'
9
9
  import { config, calcDelay } from './config.js'
10
- import { BodyReaderError } from './utils/http-request.js'
11
10
  import * as mockBrokerCollection from './mockBrokersCollection.js'
12
- import { sendInternalServerError, sendNotFound, sendUnprocessableContent } from './utils/http-response.js'
11
+ import { sendInternalServerError, sendNotFound } from './utils/http-response.js'
13
12
 
14
13
 
15
14
  export async function dispatchMock(req, response) {
@@ -40,11 +39,9 @@ export async function dispatchMock(req, response) {
40
39
  setTimeout(() => response.end(body), Number(broker.delayed && calcDelay()))
41
40
  }
42
41
  catch (error) {
43
- if (error instanceof BodyReaderError)
44
- sendUnprocessableContent(response, error.name)
45
- else if (error.code === 'ENOENT') // mock-file has been deleted
42
+ if (error?.code === 'ENOENT') // mock-file has been deleted
46
43
  sendNotFound(response)
47
- else if (error.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
44
+ else if (error?.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
48
45
  if (error.toString().includes('Unknown file extension ".ts'))
49
46
  log.warn('\nLooks like you need a TypeScript compiler\n')
50
47
  sendInternalServerError(response, error)
package/src/Mockaton.js CHANGED
@@ -14,15 +14,8 @@ import { apiPatchRequests, apiGetRequests } from './Api.js'
14
14
  import { sendNoContent, sendInternalServerError, sendUnprocessableContent } from './utils/http-response.js'
15
15
 
16
16
 
17
- process.on('unhandledRejection', error => { throw error })
18
-
19
17
  export function Mockaton(options) {
20
- const error = setup(options)
21
- if (error) {
22
- log.error(error)
23
- process.exitCode = 1
24
- return
25
- }
18
+ setup(options)
26
19
 
27
20
  mockBrokerCollection.init()
28
21
  staticCollection.init()
@@ -31,12 +24,7 @@ export function Mockaton(options) {
31
24
 
32
25
  const server = createServer(onRequest)
33
26
 
34
- server.listen(config.port, config.host, function (error) {
35
- if (error) {
36
- log.error(error)
37
- process.exit(1)
38
- return
39
- }
27
+ server.listen(config.port, config.host, function () {
40
28
  const { address, port } = this.address()
41
29
  const url = `http://${address}:${port}`
42
30
  log.info('Listening', url)
@@ -44,11 +32,6 @@ export function Mockaton(options) {
44
32
  config.onReady(url + API.dashboard)
45
33
  })
46
34
 
47
- server.on('error', error => {
48
- log.error(error.message)
49
- process.exit(1)
50
- })
51
-
52
35
  return server
53
36
  }
54
37
 
package/src/cli.js CHANGED
@@ -8,26 +8,35 @@ import { Mockaton } from '../index.js'
8
8
  import pkgJSON from '../package.json' with { type: 'json' }
9
9
 
10
10
 
11
- const args = parseArgs({
12
- options: {
13
- config: { short: 'c', type: 'string' },
11
+ process.on('unhandledRejection', error => { throw error })
14
12
 
15
- port: { short: 'p', type: 'string' },
16
- host: { short: 'H', type: 'string' },
13
+ let args
14
+ try {
15
+ args = parseArgs({
16
+ options: {
17
+ config: { short: 'c', type: 'string' },
17
18
 
18
- 'mocks-dir': { short: 'm', type: 'string' },
19
- 'static-dir': { short: 's', type: 'string' },
19
+ port: { short: 'p', type: 'string' },
20
+ host: { short: 'H', type: 'string' },
20
21
 
21
- quiet: { short: 'q', type: 'boolean' },
22
- 'no-open': { short: 'n', type: 'boolean' },
23
-
24
- help: { short: 'h', type: 'boolean' },
25
- version: { short: 'v', type: 'boolean' },
26
- }
27
- }).values
22
+ 'mocks-dir': { short: 'm', type: 'string' },
23
+ 'static-dir': { short: 's', type: 'string' },
24
+
25
+ quiet: { short: 'q', type: 'boolean' },
26
+ 'no-open': { short: 'n', type: 'boolean' },
28
27
 
28
+ help: { short: 'h', type: 'boolean' },
29
+ version: { short: 'v', type: 'boolean' }
30
+ }
31
+ }).values
32
+ }
33
+ catch (error) {
34
+ console.error(error.message)
35
+ process.exit(1)
36
+ }
29
37
 
30
- if (args.version)
38
+
39
+ if (args.version)
31
40
  console.log(pkgJSON.version)
32
41
 
33
42
  else if (args.help)
@@ -73,5 +82,14 @@ else {
73
82
  if (args.quiet) opts.logLevel = 'quiet'
74
83
  if (args['no-open']) opts.onReady = () => {}
75
84
 
76
- Mockaton(opts)
85
+ try {
86
+ Mockaton(opts).on('error', error => {
87
+ console.error(error.message)
88
+ process.exit(1)
89
+ })
90
+ }
91
+ catch (err) {
92
+ console.error(err?.message || err)
93
+ process.exit(1)
94
+ }
77
95
  }
package/src/config.js CHANGED
@@ -22,9 +22,9 @@ const schema = {
22
22
 
23
23
  host: ['127.0.0.1', is(String)],
24
24
  port: [0, port => Number.isInteger(port) && port >= 0 && port < 2 ** 16], // 0 means auto-assigned
25
-
25
+
26
26
  logLevel: ['normal', val => ['normal', 'quiet'].includes(val)],
27
-
27
+
28
28
  delay: [1200, ms => Number.isInteger(ms) && ms >= 0],
29
29
  delayJitter: [0, percent => percent >= 0 && percent <= 3],
30
30
 
@@ -43,13 +43,13 @@ const schema = {
43
43
  corsExposedHeaders: [[], Array.isArray],
44
44
  corsCredentials: [true, is(Boolean)],
45
45
  corsMaxAge: [0, is(Number)],
46
-
46
+
47
47
  plugins: [
48
48
  [
49
49
  [/\.(js|ts)$/, jsToJsonPlugin]
50
50
  ], Array.isArray],
51
51
 
52
- onReady: [await openInBrowser, is(Function)],
52
+ onReady: [await openInBrowser, is(Function)]
53
53
  }
54
54
 
55
55
 
@@ -77,15 +77,10 @@ export function setup(options) {
77
77
 
78
78
  if (!options.staticDir && !isDirectory(defaults.staticDir))
79
79
  options.staticDir = ''
80
-
81
- try {
82
- Object.assign(config, options)
83
- validate(config, ConfigValidator)
84
- log.setLevel(config.logLevel)
85
- }
86
- catch (err) {
87
- return err.message
88
- }
80
+
81
+ Object.assign(config, options)
82
+ validate(config, ConfigValidator)
83
+ log.setLevel(config.logLevel)
89
84
  }
90
85
 
91
86
 
@@ -35,7 +35,7 @@ export function sendUnprocessableContent(response, error) {
35
35
  }
36
36
 
37
37
  export function sendInternalServerError(response, error) {
38
- log.error(error)
38
+ log.error(error?.message || error, error?.stack || undefined)
39
39
  response.statusCode = 500
40
40
  response.end()
41
41
  }