mockaton 13.3.4 → 13.4.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.
Files changed (41) hide show
  1. package/README.md +28 -16
  2. package/index.d.ts +1 -1
  3. package/index.js +1 -1
  4. package/package.json +1 -1
  5. package/src/client/ApiCommander.js +1 -1
  6. package/src/client/ApiConstants.js +2 -2
  7. package/src/client/IndexHtml.js +18 -18
  8. package/src/client/app-header.js +3 -2
  9. package/src/client/app-payload-viewer.js +4 -7
  10. package/src/client/app-store.js +20 -78
  11. package/src/client/app-store.test.js +1 -25
  12. package/src/client/app.css +9 -5
  13. package/src/client/app.js +7 -7
  14. package/src/client/dir/dittoSplitPaths.js +25 -0
  15. package/src/client/dir/dittoSplitPaths.test.js +28 -0
  16. package/src/client/{dirStructure.js → dir/groupByFolder.js} +17 -13
  17. package/src/client/dir/groupByFolder.test.js +82 -0
  18. package/src/client/graphics.js +2 -2
  19. package/src/client/utils/LocalStorage.js +69 -0
  20. package/src/client/utils/css.js +16 -0
  21. package/src/client/utils/css.test.js +74 -0
  22. package/src/client/{dom-utils.js → utils/dom.js} +2 -21
  23. package/src/client/utils/watcherDev.js +46 -0
  24. package/src/server/Api.js +16 -3
  25. package/src/server/MockDispatcher.js +11 -8
  26. package/src/server/Mockaton.js +18 -8
  27. package/src/server/Mockaton.test.js +14 -9
  28. package/src/server/ProxyRelay.js +9 -3
  29. package/src/server/{utils/UrlParsers.js → UrlParsers.js} +10 -4
  30. package/src/server/{utils/UrlParsers.test.js → UrlParsers.test.js} +14 -14
  31. package/src/server/config.js +2 -0
  32. package/src/server/utils/HttpServerResponse.js +18 -39
  33. package/src/server/{WatcherDevClient.js → utils/WatcherDevClient.js} +3 -17
  34. package/src/server/utils/fs.js +1 -1
  35. package/src/server/utils/logger.js +4 -4
  36. package/src/server/utils/mime.js +11 -11
  37. package/src/server/utils/mime.test.js +15 -11
  38. package/www/src/assets/openapi.json +147 -147
  39. package/src/client/dirStructure.test.js +0 -81
  40. package/src/client/dom-utils.test.js +0 -76
  41. package/src/client/watcherDev.js +0 -39
@@ -1,9 +1,7 @@
1
1
  import http from 'node:http'
2
- import fs, { readFileSync } from 'node:fs'
2
+ import fs from 'node:fs'
3
3
 
4
- import { logger } from './logger.js'
5
4
  import { mimeFor } from './mime.js'
6
- import { HEADER_502 } from '../../client/ApiConstants.js'
7
5
 
8
6
 
9
7
  export class ServerResponse extends http.ServerResponse {
@@ -13,83 +11,64 @@ export class ServerResponse extends http.ServerResponse {
13
11
  }
14
12
 
15
13
  ok() {
16
- logger.access(this)
17
14
  this.end()
18
15
  }
19
16
 
20
17
  html(html, csp) {
21
- logger.access(this)
22
18
  this.setHeader('Content-Type', mimeFor('.html'))
23
19
  this.setHeader('Content-Security-Policy', csp)
24
20
  this.end(html)
25
21
  }
26
22
 
27
23
  json(payload) {
28
- logger.access(this)
29
24
  this.setHeader('Content-Type', mimeFor('.json'))
30
25
  this.end(JSON.stringify(payload))
31
26
  }
32
27
 
33
- file(file) {
34
- logger.access(this)
28
+ async file(file) {
35
29
  this.setHeader('Content-Type', mimeFor(file))
36
- this.end(readFileSync(file, 'utf8'))
30
+ this.end(await fs.promises.readFile(file, 'utf8'))
37
31
  }
38
32
 
39
33
  noContent() {
40
34
  this.statusCode = 204
41
- logger.access(this)
42
35
  this.end()
43
36
  }
44
37
 
45
38
 
46
39
  badRequest() {
47
40
  this.statusCode = 400
48
- logger.access(this)
49
41
  this.end()
50
42
  }
51
43
 
52
44
  forbidden() {
53
45
  this.statusCode = 403
54
- logger.access(this)
55
46
  this.end()
56
47
  }
57
48
 
58
49
  notFound() {
59
50
  this.statusCode = 404
60
- logger.access(this)
61
- this.end()
62
- }
63
-
64
- mockNotFound() {
65
- this.statusCode = 404
66
- logger.accessMock(this.req.url, '404')
67
51
  this.end()
68
52
  }
69
53
 
70
54
  uriTooLong() {
71
55
  this.statusCode = 414
72
- logger.access(this)
73
56
  this.end()
74
57
  }
75
58
 
76
59
  unprocessable(error) {
77
- logger.access(this, error)
78
60
  this.statusCode = 422
79
61
  this.end(error)
80
62
  }
81
63
 
82
64
 
83
- internalServerError(error) {
84
- logger.error(500, this.req.url, error?.message || error, error?.stack || '')
65
+ internalServerError() {
85
66
  this.statusCode = 500
86
67
  this.end()
87
68
  }
88
69
 
89
- badGateway(error) {
90
- logger.warn('Fallback Proxy Error:', error.cause.message)
70
+ badGateway() {
91
71
  this.statusCode = 502
92
- this.setHeader(HEADER_502, 1)
93
72
  this.end()
94
73
  }
95
74
 
@@ -104,20 +83,20 @@ export class ServerResponse extends http.ServerResponse {
104
83
  this.statusCode = 416 // Range Not Satisfiable
105
84
  this.setHeader('Content-Range', `bytes */${size}`)
106
85
  this.end()
86
+ return
107
87
  }
108
- else {
109
- this.statusCode = 206 // Partial Content
110
- this.setHeader('Accept-Ranges', 'bytes')
111
- this.setHeader('Content-Range', `bytes ${start}-${end}/${size}`)
112
- this.setHeader('Content-Type', mimeFor(file))
88
+
89
+ this.statusCode = 206 // Partial Content
90
+ this.setHeader('Accept-Ranges', 'bytes')
91
+ this.setHeader('Content-Range', `bytes ${start}-${end}/${size}`)
92
+ this.setHeader('Content-Type', mimeFor(file))
93
+
94
+ return new Promise((resolve, reject) => {
113
95
  const reader = fs.createReadStream(file, { start, end })
114
- const response = this
115
- reader.on('open', function () {
116
- this.pipe(response)
117
- })
118
- reader.on('error', error => {
119
- this.internalServerError(error)
120
- })
121
- }
96
+ this.on('error', reject)
97
+ reader.on('error', reject)
98
+ reader.on('end', resolve)
99
+ reader.pipe(this)
100
+ })
122
101
  }
123
102
  }
@@ -1,12 +1,5 @@
1
- import { join } from 'node:path'
1
+ import { watch } from 'node:fs'
2
2
  import { EventEmitter } from 'node:events'
3
- import { watch, readdirSync } from 'node:fs'
4
-
5
- import { config } from './config.js'
6
-
7
-
8
- export const CLIENT_DIR = join(import.meta.dirname, '../client')
9
- export const DASHBOARD_ASSETS = readdirSync(CLIENT_DIR)
10
3
 
11
4
 
12
5
  const devClientWatcher = new class extends EventEmitter {
@@ -18,20 +11,14 @@ const devClientWatcher = new class extends EventEmitter {
18
11
 
19
12
  // Although `client/IndexHtml.js` is watched, it returns a stale version.
20
13
  // i.e., it would need to be a dynamic import + cache busting.
21
- export function watchDevSPA() {
22
- watch(CLIENT_DIR, (_, file) => {
14
+ export function watchDevSPA(dir) {
15
+ watch(dir, (_, file) => {
23
16
  devClientWatcher.emit(file)
24
17
  })
25
18
  }
26
19
 
27
-
28
20
  /** Realtime notify Dev UI changes */
29
21
  export function sseClientHotReload(req, response) {
30
- if (!config.hotReload) {
31
- response.notFound()
32
- return
33
- }
34
-
35
22
  response.writeHead(200, {
36
23
  'Content-Type': 'text/event-stream',
37
24
  'Cache-Control': 'no-cache',
@@ -42,7 +29,6 @@ export function sseClientHotReload(req, response) {
42
29
  function onDevChange(file = '') {
43
30
  response.write(`data: ${file}\n\n`)
44
31
  }
45
-
46
32
  devClientWatcher.subscribe(onDevChange)
47
33
 
48
34
  const keepAlive = setInterval(() => {
@@ -1,5 +1,5 @@
1
- import { join, dirname, sep, posix, resolve } from 'node:path'
2
1
  import { lstatSync, readdirSync } from 'node:fs'
2
+ import { join, dirname, sep, posix, resolve } from 'node:path'
3
3
  import { mkdir, writeFile, unlink, realpath } from 'node:fs/promises'
4
4
 
5
5
 
@@ -13,15 +13,15 @@ export const logger = new class {
13
13
  console.info(this.#msg('INFO', ...msg))
14
14
  }
15
15
 
16
- accessMock(url, ...msg) {
16
+ normal(tag = 'NORMAL', url, ...msg) {
17
17
  if (this.#level !== 'quiet')
18
- console.log(this.#msg('MOCK', url, ...msg))
18
+ console.log(this.#msg(tag, url, ...msg))
19
19
  }
20
20
 
21
- access(response, error = '') {
21
+ verbose(tag = 'VERBOSE', response, error = '') {
22
22
  if (this.#level === 'verbose')
23
23
  console.log(this.#msg(
24
- 'ACCESS',
24
+ tag,
25
25
  response.req.method,
26
26
  response.statusCode,
27
27
  response.req.url,
@@ -1,6 +1,4 @@
1
1
  import { MIMEType } from 'node:util'
2
- import { config } from '../config.js'
3
- import { EXT_UNKNOWN_MIME, EXT_EMPTY } from '../../client/ApiConstants.js'
4
2
 
5
3
 
6
4
  // Generated with:
@@ -119,7 +117,14 @@ const extToMime = {
119
117
  zst: 'application/zstd'
120
118
  }
121
119
 
122
- const mimeToExt = mapMimeToExt(extToMime)
120
+ const mimeToExt = {
121
+ data: mapMimeToExt(extToMime)
122
+ }
123
+
124
+ export function registerMimes(obj) {
125
+ Object.assign(extToMime, obj)
126
+ mimeToExt.data = mapMimeToExt(extToMime)
127
+ }
123
128
 
124
129
  function mapMimeToExt(e2m) {
125
130
  const m = {}
@@ -130,7 +135,7 @@ function mapMimeToExt(e2m) {
130
135
 
131
136
  export function mimeFor(filename) {
132
137
  const ext = extname(filename).toLowerCase()
133
- return config.extraMimes[ext] || extToMime[ext] || ''
138
+ return extToMime[ext] || ''
134
139
  }
135
140
  function extname(filename) {
136
141
  const i = filename.lastIndexOf('.')
@@ -142,12 +147,7 @@ function extname(filename) {
142
147
 
143
148
  export function extFor(mime) {
144
149
  return mime
145
- ? findExt(mime)
146
- : EXT_EMPTY
147
- }
148
- function findExt(rawMime) {
149
- const m = new MIMEType(rawMime).essence
150
- const extraMimeToExt = mapMimeToExt(config.extraMimes)
151
- return extraMimeToExt[m] || mimeToExt[m] || EXT_UNKNOWN_MIME
150
+ ? mimeToExt.data[new MIMEType(mime).essence]
151
+ : ''
152
152
  }
153
153
 
@@ -3,15 +3,19 @@ import { equal } from 'node:assert/strict'
3
3
  import { extFor, mimeFor } from './mime.js'
4
4
 
5
5
 
6
- test('extFor', () => [
7
- 'text/html',
8
- 'Text/html',
9
- 'text/Html; charset=UTF-16'
10
- ].map(input =>
11
- equal(extFor(input), 'html')))
6
+ test('extFor', () => {
7
+ [
8
+ 'text/html',
9
+ 'Text/html',
10
+ 'text/Html; charset=UTF-16'
11
+ ].map(input =>
12
+ equal(extFor(input), 'html'))
13
+ })
12
14
 
13
- test('mimeFor', () => [
14
- 'file.html',
15
- 'file.HTmL'
16
- ].map(input =>
17
- equal(mimeFor(input), 'text/html')))
15
+ test('mimeFor', () => {
16
+ [
17
+ 'file.html',
18
+ 'file.HTmL'
19
+ ].map(input =>
20
+ equal(mimeFor(input), 'text/html'))
21
+ })