mockaton 8.2.0 → 8.2.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/.idea/dictionaries/efortis.xml +3 -0
- package/.idea/encodings.xml +6 -0
- package/.idea/inspectionProfiles/Project_Default.xml +105 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/jsLinters/eslint.xml +6 -0
- package/.idea/misc.xml +115 -0
- package/.idea/mockaton.iml +8 -0
- package/.idea/modules.xml +8 -0
- package/.idea/shelf/custom_methods,_but_nodejs_does_not_support_them_https___github_com_nodejs_llhttp_pull_54/shelved.patch +260 -0
- package/.idea/shelf/custom_methods__but_nodejs_does_not_support_them_https___github_com_nodejs_llhttp_pull_54.xml +4 -0
- package/.idea/shelf/preflight/shelved.patch +122 -0
- package/.idea/shelf/preflight.xml +4 -0
- package/.idea/shelf/preflight1/shelved.patch +289 -0
- package/.idea/shelf/preflight1.xml +4 -0
- package/.idea/shelf/ts/shelved.patch +229 -0
- package/.idea/shelf/ts.xml +4 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/webResources.xml +14 -0
- package/.idea/workspace.xml +248 -0
- package/README.md +17 -7
- package/index.d.ts +5 -0
- package/index.js +3 -1
- package/package.json +5 -4
- package/src/Dashboard.css +10 -9
- package/src/Dashboard.js +1 -3
- package/src/mockaton-logo.svg +1 -1
- package/src/utils/http-response.js +4 -4
- package/.editorconfig +0 -11
- package/README-dashboard-dark.png +0 -0
- package/README-dashboard-light.png +0 -0
- package/sample-mocks/api/user/avatar.GET.200.png +0 -0
- package/sample-mocks/api/user/edit-name.PATCH.200.json +0 -3
- package/sample-mocks/api/user/edit-name.PATCH.422.json +0 -3
- package/sample-mocks/api/user/friends.GET.200.json +0 -3
- package/sample-mocks/api/user/friends.GET.204.json +0 -4
- package/sample-mocks/api/user/friends.GET.500.txt +0 -7
- package/sample-mocks/api/user/likes.GET.200.js +0 -9
- package/sample-mocks/api/user/links.GET.200.js +0 -9
- package/sample-mocks/api/user/logout.POST.200.json +0 -1
- package/sample-mocks/api/user/typescript-scores-full.GET.200.ts +0 -9
- package/sample-mocks/api/user/typescript-scores.GET.200.ts +0 -6
- package/sample-mocks/api/user/videos(all variants).GET.200.json +0 -13
- package/sample-mocks/api/user/videos(default).GET.200.json +0 -13
- package/sample-mocks/api/user/videos(verified)(another comment).GET.200.json +0 -13
- package/sample-mocks/api/user/yaml-fruits.GET.200.yml +0 -5
- package/sample-mocks/api/user.GET.200.json +0 -4
- package/sample-mocks/api/video/[id].GET.200.json +0 -4
- package/sample-mocks/api/video/stat/[stat-id]/[video-id].GET.200.json +0 -3
- package/sample-mocks/api/video/stat/[stat-id]/all-videos?limit=[limit].GET.200.json +0 -4
- package/sample-static/another-entry/index.html +0 -35
- package/sample-static/assets/video.mp4 +0 -0
- package/sample-static/index.html +0 -37
- package/ui-tests/_setup.js +0 -32
- package/ui-tests/bulk-select.test.js +0 -10
- package/ui-tests/bulk-select.vp1024x800.dark.gold.png +0 -0
- package/ui-tests/bulk-select.vp1024x800.light.gold.png +0 -0
- package/ui-tests/initial-dashboard-state.test.js +0 -4
- package/ui-tests/initial-dashboard-state.vp1024x800.dark.gold.png +0 -0
- package/ui-tests/initial-dashboard-state.vp1024x800.light.gold.png +0 -0
- package/ui-tests/payload-viewer-formats-json.test.js +0 -9
- package/ui-tests/payload-viewer-formats-json.vp1024x800.dark.gold.png +0 -0
- package/ui-tests/payload-viewer-formats-json.vp1024x800.light.gold.png +0 -0
- package/ui-tests/payload-viewer-renders-images.test.js +0 -9
- package/ui-tests/payload-viewer-renders-images.vp1024x800.dark.gold.png +0 -0
- package/ui-tests/payload-viewer-renders-images.vp1024x800.light.gold.png +0 -0
- package/ui-tests/select-mock-variant.test.js +0 -10
- package/ui-tests/select-mock-variant.vp1024x800.dark.gold.png +0 -0
- package/ui-tests/select-mock-variant.vp1024x800.light.gold.png +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Index: src/Mockaton.js
|
|
2
|
+
IDEA additional info:
|
|
3
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
4
|
+
<+>import { createServer } from 'node:http'\n\nimport { API } from './ApiConstants.js'\nimport { Config, setup } from './Config.js'\nimport { dispatchMock } from './MockDispatcher.js'\nimport * as mockBrokerCollection from './mockBrokersCollection.js'\nimport { dispatchStatic, isStatic } from './StaticDispatcher.js'\nimport { apiPatchRequests, apiGetRequests } from './Api.js'\n\n\nexport function Mockaton(options) {\n\tsetup(options)\n\tmockBrokerCollection.init()\n\tconst server = createServer(onRequest)\n\tserver.listen(Config.port, Config.host, (error) => {\n\t\tconst { address, port } = server.address()\n\t\tconst url = `http://${address}:${port}`\n\t\tconsole.log('Listening', url)\n\t\tconsole.log('Dashboard', url + API.dashboard)\n\t\tif (error)\n\t\t\tconsole.error(error)\n\t\telse\n\t\t\tConfig.onReady(url + API.dashboard)\n\t})\n\treturn server\n}\n\nasync function onRequest(req, response) {\n\tresponse.setHeader('Server', 'Mockaton')\n\n\tconst { url, method } = req\n\tif (method === 'GET' && apiGetRequests.has(url))\n\t\tapiGetRequests.get(url)(req, response)\n\n\telse if (method === 'PATCH' && apiPatchRequests.has(url))\n\t\tawait apiPatchRequests.get(url)(req, response)\n\n\telse if (isStatic(req))\n\t\tawait dispatchStatic(req, response)\n\n\telse\n\t\tawait dispatchMock(req, response)\n}\n
|
|
5
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
6
|
+
<+>UTF-8
|
|
7
|
+
===================================================================
|
|
8
|
+
diff --git a/src/Mockaton.js b/src/Mockaton.js
|
|
9
|
+
--- a/src/Mockaton.js (revision 016945139309b5f30833795740b816a95516ddcf)
|
|
10
|
+
+++ b/src/Mockaton.js (date 1727235901568)
|
|
11
|
+
@@ -1,11 +1,12 @@
|
|
12
|
+
import { createServer } from 'node:http'
|
|
13
|
+
|
|
14
|
+
import { API } from './ApiConstants.js'
|
|
15
|
+
-import { Config, setup } from './Config.js'
|
|
16
|
+
import { dispatchMock } from './MockDispatcher.js'
|
|
17
|
+
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
18
|
+
import { dispatchStatic, isStatic } from './StaticDispatcher.js'
|
|
19
|
+
import { apiPatchRequests, apiGetRequests } from './Api.js'
|
|
20
|
+
+import { Config, setup, extraHeadersToMap } from './Config.js'
|
|
21
|
+
+import { onPreflight, isPreflight } from './utils/http-preflight.js'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export function Mockaton(options) {
|
|
25
|
+
@@ -29,7 +30,10 @@
|
|
26
|
+
response.setHeader('Server', 'Mockaton')
|
|
27
|
+
|
|
28
|
+
const { url, method } = req
|
|
29
|
+
- if (method === 'GET' && apiGetRequests.has(url))
|
|
30
|
+
+ if (isPreflight(req))
|
|
31
|
+
+ onPreflight(req, response, extraHeadersToMap())
|
|
32
|
+
+
|
|
33
|
+
+ else if (method === 'GET' && apiGetRequests.has(url))
|
|
34
|
+
apiGetRequests.get(url)(req, response)
|
|
35
|
+
|
|
36
|
+
else if (method === 'PATCH' && apiPatchRequests.has(url))
|
|
37
|
+
@@ -41,3 +45,6 @@
|
|
38
|
+
else
|
|
39
|
+
await dispatchMock(req, response)
|
|
40
|
+
}
|
|
41
|
+
+
|
|
42
|
+
+
|
|
43
|
+
+
|
|
44
|
+
Index: src/utils/http-request.js
|
|
45
|
+
IDEA additional info:
|
|
46
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
47
|
+
<+>export class JsonBodyParserError extends Error {}\n\nexport function parseJSON(req) {\n\treturn new Promise((resolve, reject) => {\n\t\tconst MAX_BODY_SIZE = 200 * 1024\n\t\tconst expectedLength = req.headers['content-length'] | 0\n\t\tlet lengthSoFar = 0\n\t\tconst body = []\n\t\treq.on('data', onData)\n\t\treq.on('end', onEnd)\n\t\treq.on('error', onEnd)\n\n\t\tfunction onData(chunk) {\n\t\t\tlengthSoFar += chunk.length\n\t\t\tif (lengthSoFar > MAX_BODY_SIZE)\n\t\t\t\tonEnd()\n\t\t\telse\n\t\t\t\tbody.push(chunk)\n\t\t}\n\n\t\tfunction onEnd() {\n\t\t\treq.removeListener('data', onData)\n\t\t\treq.removeListener('end', onEnd)\n\t\t\treq.removeListener('error', onEnd)\n\t\t\tif (lengthSoFar !== expectedLength)\n\t\t\t\treject(new JsonBodyParserError())\n\t\t\telse\n\t\t\t\ttry {\n\t\t\t\t\tresolve(JSON.parse(Buffer.concat(body).toString()))\n\t\t\t\t}\n\t\t\t\tcatch (_) {\n\t\t\t\t\treject(new JsonBodyParserError())\n\t\t\t\t}\n\t\t}\n\t})\n}
|
|
48
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
49
|
+
<+>UTF-8
|
|
50
|
+
===================================================================
|
|
51
|
+
diff --git a/src/utils/http-request.js b/src/utils/http-request.js
|
|
52
|
+
--- a/src/utils/http-request.js (revision 016945139309b5f30833795740b816a95516ddcf)
|
|
53
|
+
+++ b/src/utils/http-request.js (date 1727235901643)
|
|
54
|
+
@@ -33,4 +33,4 @@
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
-}
|
|
59
|
+
|
|
60
|
+
+}
|
|
61
|
+
Index: src/utils/http-preflight.js
|
|
62
|
+
IDEA additional info:
|
|
63
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
64
|
+
<+>UTF-8
|
|
65
|
+
===================================================================
|
|
66
|
+
diff --git a/src/utils/http-preflight.js b/src/utils/http-preflight.js
|
|
67
|
+
new file mode 100644
|
|
68
|
+
--- /dev/null (date 1727236616100)
|
|
69
|
+
+++ b/src/utils/http-preflight.js (date 1727236616100)
|
|
70
|
+
@@ -0,0 +1,23 @@
|
|
71
|
+
+// https://www.w3.org/TR/2020/SPSD-cors-20200602/#resource-preflight-requests
|
|
72
|
+
+
|
|
73
|
+
+export function isPreflight(req) {
|
|
74
|
+
+ return req.method === 'OPTIONS' && 'origin' in req.headers && 'access-control-request-method' in req.headers
|
|
75
|
+
+}
|
|
76
|
+
+
|
|
77
|
+
+export function onPreflight(req, response, extraHeadersMap) {
|
|
78
|
+
+ const reqOrigin = req.headers['origin']
|
|
79
|
+
+ const allowedOrigins = (extraHeadersMap.get('access-control-allow-origin') || '').split(' ')
|
|
80
|
+
+ if (!allowedOrigins.includes(reqOrigin)) {
|
|
81
|
+
+ response.statusCode = 204
|
|
82
|
+
+ response.end()
|
|
83
|
+
+ return
|
|
84
|
+
+ }
|
|
85
|
+
+
|
|
86
|
+
+ response.setHeader('Access-Control-Allow-Origin', extraHeadersMap.get('access-control-allow-origin') || '')
|
|
87
|
+
+
|
|
88
|
+
+
|
|
89
|
+
+ response.setHeader('Access-Control-Allow-Methods', extraHeadersMap.get('access-control-allow-methods') || '')
|
|
90
|
+
+ // TODO credentials
|
|
91
|
+
+ response.statusCode = 204
|
|
92
|
+
+ response.end()
|
|
93
|
+
+}
|
|
94
|
+
Index: src/Config.js
|
|
95
|
+
IDEA additional info:
|
|
96
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
97
|
+
<+>import { openInBrowser } from './utils/openInBrowser.js'\nimport { validate, is, optional } from './utils/validate.js'\nimport { isDirectory } from './utils/fs.js'\n\n\nexport const Config = Object.seal({\n\tmocksDir: '',\n\tignore: /(\\.DS_Store|~)$/,\n\n\tstaticDir: '',\n\n\thost: '127.0.0.1',\n\tport: 0, // auto-assigned\n\tproxyFallback: '', // e.g. http://localhost:9999\n\n\tdelay: 1200, // milliseconds\n\tcookies: {}, // defaults to the first kv\n\textraHeaders: [],\n\textraMimes: {},\n\n\tonReady: openInBrowser\n})\n\n\nexport function setup(options) {\n\tObject.assign(Config, options)\n\tvalidate(Config, {\n\t\tmocksDir: isDirectory,\n\t\tignore: is(RegExp),\n\n\t\tstaticDir: optional(isDirectory),\n\n\t\thost: is(String),\n\t\tport: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,\n\t\tproxyFallback: optional(URL.canParse),\n\n\t\tdelay: ms => Number.isInteger(ms) && ms > 0,\n\t\tcookies: is(Object),\n\t\textraHeaders: Array.isArray,\n\t\textraMimes: is(Object),\n\n\t\tonReady: is(Function)\n\t})\n}\n\n\n
|
|
98
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
99
|
+
<+>UTF-8
|
|
100
|
+
===================================================================
|
|
101
|
+
diff --git a/src/Config.js b/src/Config.js
|
|
102
|
+
--- a/src/Config.js (revision 016945139309b5f30833795740b816a95516ddcf)
|
|
103
|
+
+++ b/src/Config.js (date 1727230220421)
|
|
104
|
+
@@ -36,7 +36,7 @@
|
|
105
|
+
|
|
106
|
+
delay: ms => Number.isInteger(ms) && ms > 0,
|
|
107
|
+
cookies: is(Object),
|
|
108
|
+
- extraHeaders: Array.isArray,
|
|
109
|
+
+ extraHeaders: val => Array.isArray(val) && val.length % 2 === 0,
|
|
110
|
+
extraMimes: is(Object),
|
|
111
|
+
|
|
112
|
+
onReady: is(Function)
|
|
113
|
+
@@ -44,3 +44,9 @@
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
+export function extraHeadersToMap() {
|
|
118
|
+
+ const m = new Map()
|
|
119
|
+
+ for (let i = 0; i < Config.extraHeaders.length / 2; i += 2)
|
|
120
|
+
+ m.set(Config.extraHeaders[i].toLowerCase(), Config.extraHeaders[i + 1])
|
|
121
|
+
+ return m
|
|
122
|
+
+}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Index: src/utils/http-preflight.test.js
|
|
2
|
+
IDEA additional info:
|
|
3
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
4
|
+
<+>UTF-8
|
|
5
|
+
===================================================================
|
|
6
|
+
diff --git a/src/utils/http-preflight.test.js b/src/utils/http-preflight.test.js
|
|
7
|
+
new file mode 100644
|
|
8
|
+
--- /dev/null (date 1727484394842)
|
|
9
|
+
+++ b/src/utils/http-preflight.test.js (date 1727484394842)
|
|
10
|
+
@@ -0,0 +1,122 @@
|
|
11
|
+
+import { equal } from 'node:assert/strict'
|
|
12
|
+
+import { promisify } from 'node:util'
|
|
13
|
+
+import { createServer } from 'node:http'
|
|
14
|
+
+import { describe, it, after } from 'node:test'
|
|
15
|
+
+import { isPreflight, onPreflight, PreflightHeader as PH } from './http-preflight.js'
|
|
16
|
+
+
|
|
17
|
+
+
|
|
18
|
+
+await describe('preflight', async () => {
|
|
19
|
+
+ let corsResponseHeaders = {}
|
|
20
|
+
+
|
|
21
|
+
+ const server = createServer((req, response) => {
|
|
22
|
+
+ if (isPreflight(req)) {
|
|
23
|
+
+ onPreflight(req, response, corsResponseHeaders)
|
|
24
|
+
+ return
|
|
25
|
+
+ }
|
|
26
|
+
+ response.end('NON_PREFLIGHT')
|
|
27
|
+
+ })
|
|
28
|
+
+ await promisify(server.listen).bind(server, 0, '127.0.0.1')()
|
|
29
|
+
+
|
|
30
|
+
+ after(() => {
|
|
31
|
+
+ server.close()
|
|
32
|
+
+ })
|
|
33
|
+
+
|
|
34
|
+
+ function preflight(headers, method = 'OPTIONS') {
|
|
35
|
+
+ const { address, port } = server.address()
|
|
36
|
+
+ return fetch(`http://${address}:${port}/`, { method, headers })
|
|
37
|
+
+ }
|
|
38
|
+
+
|
|
39
|
+
+ await describe('Identifies preflight requests', async () => {
|
|
40
|
+
+ const requiredRequestHeaders = {
|
|
41
|
+
+ [PH.Origin]: 'http://locahost:9999',
|
|
42
|
+
+ [PH.AccessControlRequestMethod]: 'POST'
|
|
43
|
+
+ }
|
|
44
|
+
+
|
|
45
|
+
+ await it('Ignores non-OPTIONS method', async () => {
|
|
46
|
+
+ const res = await preflight(requiredRequestHeaders, 'POST')
|
|
47
|
+
+ equal(await res.text(), 'NON_PREFLIGHT')
|
|
48
|
+
+ })
|
|
49
|
+
+
|
|
50
|
+
+ await it(`Ignores non-parseable req ${PH.Origin} header`, async () => {
|
|
51
|
+
+ const headers = {
|
|
52
|
+
+ ...requiredRequestHeaders,
|
|
53
|
+
+ [PH.Origin]: 'non-url'
|
|
54
|
+
+ }
|
|
55
|
+
+ const res = await preflight(headers)
|
|
56
|
+
+ equal(await res.text(), 'NON_PREFLIGHT')
|
|
57
|
+
+ })
|
|
58
|
+
+
|
|
59
|
+
+ await it(`Ignores missing method in ${PH.AccessControlRequestMethod} header`, async () => {
|
|
60
|
+
+ // Doesn’t validate the method in order to support custom methods.
|
|
61
|
+
+ const headers = { ...requiredRequestHeaders }
|
|
62
|
+
+ delete headers[PH.AccessControlRequestMethod]
|
|
63
|
+
+ const res = await preflight(headers)
|
|
64
|
+
+ equal(await res.text(), 'NON_PREFLIGHT')
|
|
65
|
+
+ })
|
|
66
|
+
+
|
|
67
|
+
+ await it('204 valid preflights', async () => {
|
|
68
|
+
+ const res = await preflight(requiredRequestHeaders)
|
|
69
|
+
+ equal(res.status, 204)
|
|
70
|
+
+ })
|
|
71
|
+
+ })
|
|
72
|
+
+
|
|
73
|
+
+ await describe('Origin', async () => {
|
|
74
|
+
+ await it(`204 without ${PH.AccessControlAllowOrigin} when request Origin mismatches`, async () => {
|
|
75
|
+
+ corsResponseHeaders = {
|
|
76
|
+
+ [PH.AccessControlAllowOrigin]: 'http://allowed.com'
|
|
77
|
+
+ }
|
|
78
|
+
+ const res = await preflight({
|
|
79
|
+
+ [PH.Origin]: 'http://not-allowed.com',
|
|
80
|
+
+ [PH.AccessControlRequestMethod]: 'GET'
|
|
81
|
+
+ })
|
|
82
|
+
+ equal(res.status, 204)
|
|
83
|
+
+ equal(res.headers.get(PH.AccessControlAllowOrigin), null)
|
|
84
|
+
+ })
|
|
85
|
+
+
|
|
86
|
+
+ await it(`204 when there is no ${PH.AccessControlAllowOrigin}`, async () => {
|
|
87
|
+
+ corsResponseHeaders = {}
|
|
88
|
+
+ const res = await preflight({
|
|
89
|
+
+ [PH.Origin]: 'http://not-allowed.com',
|
|
90
|
+
+ [PH.AccessControlRequestMethod]: 'GET'
|
|
91
|
+
+ })
|
|
92
|
+
+ equal(res.status, 204)
|
|
93
|
+
+ equal(res.headers.get(PH.AccessControlAllowOrigin), null)
|
|
94
|
+
+ })
|
|
95
|
+
+
|
|
96
|
+
+ await it(`204 with ${PH.AccessControlAllowOrigin} when request Origin matches exactly`, async () => {
|
|
97
|
+
+ corsResponseHeaders = {
|
|
98
|
+
+ [PH.AccessControlAllowOrigin]: 'http://allowed.com'
|
|
99
|
+
+ }
|
|
100
|
+
+ const res = await preflight({
|
|
101
|
+
+ [PH.Origin]: 'http://allowed.com',
|
|
102
|
+
+ [PH.AccessControlRequestMethod]: 'GET'
|
|
103
|
+
+ })
|
|
104
|
+
+ equal(res.status, 204)
|
|
105
|
+
+ equal(res.headers.get(PH.AccessControlAllowOrigin), 'http://allowed.com')
|
|
106
|
+
+ })
|
|
107
|
+
+
|
|
108
|
+
+ await it(`204 with wildcard ${PH.AccessControlAllowOrigin}`, async () => {
|
|
109
|
+
+ corsResponseHeaders = {
|
|
110
|
+
+ [PH.AccessControlAllowOrigin]: '*'
|
|
111
|
+
+ }
|
|
112
|
+
+ const res = await preflight({
|
|
113
|
+
+ [PH.Origin]: 'http://foo.com',
|
|
114
|
+
+ [PH.AccessControlRequestMethod]: 'GET'
|
|
115
|
+
+ })
|
|
116
|
+
+ equal(res.status, 204)
|
|
117
|
+
+ equal(res.headers.get(PH.AccessControlAllowOrigin), 'http://foo.com')
|
|
118
|
+
+ })
|
|
119
|
+
+
|
|
120
|
+
+ await it(`204 with multiple ${PH.AccessControlAllowOrigin} matching`, async () => {
|
|
121
|
+
+ corsResponseHeaders = {
|
|
122
|
+
+ [PH.AccessControlAllowOrigin]: 'http://good.com http://better.com'
|
|
123
|
+
+ }
|
|
124
|
+
+ const res = await preflight({
|
|
125
|
+
+ [PH.Origin]: 'http://good.com',
|
|
126
|
+
+ [PH.AccessControlRequestMethod]: 'GET'
|
|
127
|
+
+ })
|
|
128
|
+
+ equal(res.status, 204)
|
|
129
|
+
+ equal(res.headers.get(PH.AccessControlAllowOrigin), 'http://good.com')
|
|
130
|
+
+ })
|
|
131
|
+
+ })
|
|
132
|
+
+})
|
|
133
|
+
Index: src/Mockaton.js
|
|
134
|
+
IDEA additional info:
|
|
135
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
136
|
+
<+>import { createServer } from 'node:http'\n\nimport { API } from './ApiConstants.js'\nimport { Config, setup } from './Config.js'\nimport { dispatchMock } from './MockDispatcher.js'\nimport * as mockBrokerCollection from './mockBrokersCollection.js'\nimport { dispatchStatic, isStatic } from './StaticDispatcher.js'\nimport { apiPatchRequests, apiGetRequests } from './Api.js'\n\n\nexport function Mockaton(options) {\n\tsetup(options)\n\tmockBrokerCollection.init()\n\tconst server = createServer(onRequest)\n\tserver.listen(Config.port, Config.host, (error) => {\n\t\tconst { address, port } = server.address()\n\t\tconst url = `http://${address}:${port}`\n\t\tconsole.log('Listening', url)\n\t\tconsole.log('Dashboard', url + API.dashboard)\n\t\tif (error)\n\t\t\tconsole.error(error)\n\t\telse\n\t\t\tConfig.onReady(url + API.dashboard)\n\t})\n\treturn server\n}\n\nasync function onRequest(req, response) {\n\tresponse.setHeader('Server', 'Mockaton')\n\n\tconst { url, method } = req\n\tif (method === 'GET' && apiGetRequests.has(url))\n\t\tapiGetRequests.get(url)(req, response)\n\n\telse if (method === 'PATCH' && apiPatchRequests.has(url))\n\t\tawait apiPatchRequests.get(url)(req, response)\n\n\telse if (isStatic(req))\n\t\tawait dispatchStatic(req, response)\n\n\telse\n\t\tawait dispatchMock(req, response)\n}\n
|
|
137
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
138
|
+
<+>UTF-8
|
|
139
|
+
===================================================================
|
|
140
|
+
diff --git a/src/Mockaton.js b/src/Mockaton.js
|
|
141
|
+
--- a/src/Mockaton.js (revision 016945139309b5f30833795740b816a95516ddcf)
|
|
142
|
+
+++ b/src/Mockaton.js (date 1727303646064)
|
|
143
|
+
@@ -1,11 +1,12 @@
|
|
144
|
+
import { createServer } from 'node:http'
|
|
145
|
+
|
|
146
|
+
import { API } from './ApiConstants.js'
|
|
147
|
+
-import { Config, setup } from './Config.js'
|
|
148
|
+
import { dispatchMock } from './MockDispatcher.js'
|
|
149
|
+
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
150
|
+
+import { onPreflight, isPreflight } from './utils/http-preflight.js'
|
|
151
|
+
import { dispatchStatic, isStatic } from './StaticDispatcher.js'
|
|
152
|
+
import { apiPatchRequests, apiGetRequests } from './Api.js'
|
|
153
|
+
+import { Config, setup, extraHeadersToObj } from './Config.js'
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
export function Mockaton(options) {
|
|
157
|
+
@@ -29,7 +30,10 @@
|
|
158
|
+
response.setHeader('Server', 'Mockaton')
|
|
159
|
+
|
|
160
|
+
const { url, method } = req
|
|
161
|
+
- if (method === 'GET' && apiGetRequests.has(url))
|
|
162
|
+
+ if (isPreflight(req))
|
|
163
|
+
+ onPreflight(req, response, extraHeadersToObj())
|
|
164
|
+
+
|
|
165
|
+
+ else if (method === 'GET' && apiGetRequests.has(url))
|
|
166
|
+
apiGetRequests.get(url)(req, response)
|
|
167
|
+
|
|
168
|
+
else if (method === 'PATCH' && apiPatchRequests.has(url))
|
|
169
|
+
Index: src/utils/http-request.js
|
|
170
|
+
IDEA additional info:
|
|
171
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
172
|
+
<+>export class JsonBodyParserError extends Error {}\n\nexport function parseJSON(req) {\n\treturn new Promise((resolve, reject) => {\n\t\tconst MAX_BODY_SIZE = 200 * 1024\n\t\tconst expectedLength = req.headers['content-length'] | 0\n\t\tlet lengthSoFar = 0\n\t\tconst body = []\n\t\treq.on('data', onData)\n\t\treq.on('end', onEnd)\n\t\treq.on('error', onEnd)\n\n\t\tfunction onData(chunk) {\n\t\t\tlengthSoFar += chunk.length\n\t\t\tif (lengthSoFar > MAX_BODY_SIZE)\n\t\t\t\tonEnd()\n\t\t\telse\n\t\t\t\tbody.push(chunk)\n\t\t}\n\n\t\tfunction onEnd() {\n\t\t\treq.removeListener('data', onData)\n\t\t\treq.removeListener('end', onEnd)\n\t\t\treq.removeListener('error', onEnd)\n\t\t\tif (lengthSoFar !== expectedLength)\n\t\t\t\treject(new JsonBodyParserError())\n\t\t\telse\n\t\t\t\ttry {\n\t\t\t\t\tresolve(JSON.parse(Buffer.concat(body).toString()))\n\t\t\t\t}\n\t\t\t\tcatch (_) {\n\t\t\t\t\treject(new JsonBodyParserError())\n\t\t\t\t}\n\t\t}\n\t})\n}
|
|
173
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
174
|
+
<+>UTF-8
|
|
175
|
+
===================================================================
|
|
176
|
+
diff --git a/src/utils/http-request.js b/src/utils/http-request.js
|
|
177
|
+
--- a/src/utils/http-request.js (revision 016945139309b5f30833795740b816a95516ddcf)
|
|
178
|
+
+++ b/src/utils/http-request.js (date 1727485714775)
|
|
179
|
+
@@ -33,4 +33,4 @@
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
-}
|
|
184
|
+
|
|
185
|
+
+}
|
|
186
|
+
Index: src/utils/http-preflight.js
|
|
187
|
+
IDEA additional info:
|
|
188
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
189
|
+
<+>UTF-8
|
|
190
|
+
===================================================================
|
|
191
|
+
diff --git a/src/utils/http-preflight.js b/src/utils/http-preflight.js
|
|
192
|
+
new file mode 100644
|
|
193
|
+
--- /dev/null (date 1727484919524)
|
|
194
|
+
+++ b/src/utils/http-preflight.js (date 1727484919524)
|
|
195
|
+
@@ -0,0 +1,65 @@
|
|
196
|
+
+// https://www.w3.org/TR/2020/SPSD-cors-20200602/#resource-preflight-requests
|
|
197
|
+
+
|
|
198
|
+
+export const PreflightHeader = {
|
|
199
|
+
+ // request
|
|
200
|
+
+ Origin: 'origin',
|
|
201
|
+
+ AccessControlRequestMethod: 'access-control-request-method',
|
|
202
|
+
+ AccessControlRequestHeaders: 'access-control-request-headers',
|
|
203
|
+
+
|
|
204
|
+
+ // response
|
|
205
|
+
+ AccessControlAllowOrigin: 'Access-Control-Allow-Origin', // '*' | null | Space delimited
|
|
206
|
+
+ AccessControlAllowMethods: 'Access-Control-Allow-Methods', // '*' | Comma delimited
|
|
207
|
+
+ AccessControlAllowHeaders: 'Access-Control-Allow-Headers', // '*' | Comma delimited
|
|
208
|
+
+ AccessControlAllowCredentials: 'Access-Control-Allow-Credentials'
|
|
209
|
+
+}
|
|
210
|
+
+const PH = PreflightHeader
|
|
211
|
+
+
|
|
212
|
+
+export const SimpleMethods = ['GET', 'HEAD', 'POST']
|
|
213
|
+
+
|
|
214
|
+
+export function isPreflight(req) {
|
|
215
|
+
+ return req.method === 'OPTIONS'
|
|
216
|
+
+ && URL.canParse(req.headers[PH.Origin])
|
|
217
|
+
+ && Boolean(req.headers[PH.AccessControlRequestMethod])
|
|
218
|
+
+}
|
|
219
|
+
+
|
|
220
|
+
+export function onPreflight(req, response, extraHeaders) {
|
|
221
|
+
+ const supportsCredentials = /^true$/.test(extraHeaders[PH.AccessControlAllowCredentials])
|
|
222
|
+
+
|
|
223
|
+
+ /* Origin */
|
|
224
|
+
+ const requestOrigin = req.headers[PH.Origin].trim()
|
|
225
|
+
+ const allowedOrigins = parseSpaceDelimited(extraHeaders[PH.AccessControlAllowOrigin])
|
|
226
|
+
+ const hasWildcard = allowedOrigins.some(ao => ao === '*')
|
|
227
|
+
+
|
|
228
|
+
+ if (supportsCredentials)
|
|
229
|
+
+
|
|
230
|
+
+ if (!(hasWildcard || allowedOrigins.includes(requestOrigin))) {
|
|
231
|
+
+ response.statusCode = 204
|
|
232
|
+
+ response.end()
|
|
233
|
+
+ return
|
|
234
|
+
+ }
|
|
235
|
+
+ response.setHeader(PH.AccessControlAllowOrigin, requestOrigin)
|
|
236
|
+
+
|
|
237
|
+
+ const method = req.headers[PH.AccessControlRequestMethod]
|
|
238
|
+
+ const headerFieldNames = parseCommaDelimited(req.headers[PH.AccessControlRequestHeaders])
|
|
239
|
+
+
|
|
240
|
+
+ // response.setHeader(PH.AccessControlAllowMethods, extraHeaders.get(PH.AccessControlAllowMethods) || '')
|
|
241
|
+
+ response.statusCode = 204
|
|
242
|
+
+ response.end()
|
|
243
|
+
+}
|
|
244
|
+
+
|
|
245
|
+
+
|
|
246
|
+
+function parseCommaDelimited(str = '') {
|
|
247
|
+
+ return str.split(',').map(s => s.trim())
|
|
248
|
+
+}
|
|
249
|
+
+
|
|
250
|
+
+function parseSpaceDelimited(str = '') {
|
|
251
|
+
+ return str.split(' ').map(s => s.trim())
|
|
252
|
+
+}
|
|
253
|
+
+
|
|
254
|
+
+
|
|
255
|
+
+
|
|
256
|
+
+// extraHeaders: [
|
|
257
|
+
+// 'Access-Control-Allow-Origin', 'http://localhost:7001',
|
|
258
|
+
+// 'Access-Control-Allow-Methods', '*',
|
|
259
|
+
+// 'Access-Control-Allow-Credentials', true
|
|
260
|
+
+// ]
|
|
261
|
+
Index: src/Config.js
|
|
262
|
+
IDEA additional info:
|
|
263
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
264
|
+
<+>import { openInBrowser } from './utils/openInBrowser.js'\nimport { validate, is, optional } from './utils/validate.js'\nimport { isDirectory } from './utils/fs.js'\n\n\nexport const Config = Object.seal({\n\tmocksDir: '',\n\tignore: /(\\.DS_Store|~)$/,\n\n\tstaticDir: '',\n\n\thost: '127.0.0.1',\n\tport: 0, // auto-assigned\n\tproxyFallback: '', // e.g. http://localhost:9999\n\n\tdelay: 1200, // milliseconds\n\tcookies: {}, // defaults to the first kv\n\textraHeaders: [],\n\textraMimes: {},\n\n\tonReady: openInBrowser\n})\n\n\nexport function setup(options) {\n\tObject.assign(Config, options)\n\tvalidate(Config, {\n\t\tmocksDir: isDirectory,\n\t\tignore: is(RegExp),\n\n\t\tstaticDir: optional(isDirectory),\n\n\t\thost: is(String),\n\t\tport: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,\n\t\tproxyFallback: optional(URL.canParse),\n\n\t\tdelay: ms => Number.isInteger(ms) && ms > 0,\n\t\tcookies: is(Object),\n\t\textraHeaders: Array.isArray,\n\t\textraMimes: is(Object),\n\n\t\tonReady: is(Function)\n\t})\n}\n\n\n
|
|
265
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
266
|
+
<+>UTF-8
|
|
267
|
+
===================================================================
|
|
268
|
+
diff --git a/src/Config.js b/src/Config.js
|
|
269
|
+
--- a/src/Config.js (revision 016945139309b5f30833795740b816a95516ddcf)
|
|
270
|
+
+++ b/src/Config.js (date 1727303629962)
|
|
271
|
+
@@ -36,7 +36,7 @@
|
|
272
|
+
|
|
273
|
+
delay: ms => Number.isInteger(ms) && ms > 0,
|
|
274
|
+
cookies: is(Object),
|
|
275
|
+
- extraHeaders: Array.isArray,
|
|
276
|
+
+ extraHeaders: val => Array.isArray(val) && val.length % 2 === 0,
|
|
277
|
+
extraMimes: is(Object),
|
|
278
|
+
|
|
279
|
+
onReady: is(Function)
|
|
280
|
+
@@ -44,3 +44,9 @@
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
+export function extraHeadersToObj() {
|
|
285
|
+
+ const obj = Object.create(null)
|
|
286
|
+
+ for (let i = 0; i < Config.extraHeaders.length / 2; i += 2)
|
|
287
|
+
+ obj[Config.extraHeaders[i].toLowerCase()] = Config.extraHeaders[i + 1]
|
|
288
|
+
+ return obj
|
|
289
|
+
+}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Index: index.d.ts
|
|
2
|
+
IDEA additional info:
|
|
3
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
4
|
+
<+>import { Server } from 'node:http';\n\ninterface Config {\n\tmocksDir: string\n\tignore?: RegExp\n\n\tstaticDir?: string\n\n\thost?: string,\n\tport?: number\n\tproxyFallback?: string\n\n\tdelay?: number\n\tcookies?: { [label: string]: string }\n\textraHeaders?: [string, string][]\n\textraMimes?: { [fileExt: string]: string }\n\n\tcorsAllowed?: boolean,\n\tcorsOrigins: string[]\n\tcorsMethods: string[]\n\tcorsHeaders: string[]\n\tcorsExposedHeaders: string[]\n\tcorsCredentials: boolean\n\tcorsMaxAge: number\n\n\tonReady?: (address: string) => void\n}\n\n\nexport function Mockaton(options: Config): Server\n\n\nexport function jwtCookie(cookieName: string, payload: any): string\n\n\nexport class Commander {\n\tconstructor(addr: string)\n\n\tlistMocks(): Promise<Response>\n\n\tselect(file: string): Promise<Response>\n\n\tbulkSelectByComment(comment: string): Promise<Response>\n\n\n\tsetRouteIsDelayed(routeMethod: string, routeUrlMask: string, delayed: boolean): Promise<Response>\n\n\n\tlistCookies(): Promise<Response>\n\n\tselectCookie(cookieKey: string): Promise<Response>\n\n\n\tlistComments(): Promise<Response>\n\n\tsetProxyFallback(proxyAddr: string): Promise<Response>\n\n\treset(): Promise<Response>\n\n\n\tgetCorsAllowed(): Promise<Response>\n\n\tsetCorsAllowed(value: boolean): Promise<Response>\n}\n
|
|
5
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
6
|
+
<+>UTF-8
|
|
7
|
+
===================================================================
|
|
8
|
+
diff --git a/index.d.ts b/index.d.ts
|
|
9
|
+
--- a/index.d.ts (revision 93871c35bcfe76db61408d4392c372db2017dd69)
|
|
10
|
+
+++ b/index.d.ts (date 1728248407124)
|
|
11
|
+
@@ -2,6 +2,7 @@
|
|
12
|
+
|
|
13
|
+
interface Config {
|
|
14
|
+
mocksDir: string
|
|
15
|
+
+ tsMocksOutDir?: string,
|
|
16
|
+
ignore?: RegExp
|
|
17
|
+
|
|
18
|
+
staticDir?: string
|
|
19
|
+
Index: package-lock.json
|
|
20
|
+
IDEA additional info:
|
|
21
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
22
|
+
<+>UTF-8
|
|
23
|
+
===================================================================
|
|
24
|
+
diff --git a/package-lock.json b/package-lock.json
|
|
25
|
+
new file mode 100644
|
|
26
|
+
--- /dev/null (date 1728248906436)
|
|
27
|
+
+++ b/package-lock.json (date 1728248906436)
|
|
28
|
+
@@ -0,0 +1,30 @@
|
|
29
|
+
+{
|
|
30
|
+
+ "name": "mockaton",
|
|
31
|
+
+ "version": "7.6.2",
|
|
32
|
+
+ "lockfileVersion": 3,
|
|
33
|
+
+ "requires": true,
|
|
34
|
+
+ "packages": {
|
|
35
|
+
+ "": {
|
|
36
|
+
+ "name": "mockaton",
|
|
37
|
+
+ "version": "7.6.2",
|
|
38
|
+
+ "license": "MIT",
|
|
39
|
+
+ "optionalDependencies": {
|
|
40
|
+
+ "typescript": "5.6.2"
|
|
41
|
+
+ }
|
|
42
|
+
+ },
|
|
43
|
+
+ "node_modules/typescript": {
|
|
44
|
+
+ "version": "5.6.2",
|
|
45
|
+
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
|
46
|
+
+ "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
|
47
|
+
+ "license": "Apache-2.0",
|
|
48
|
+
+ "optional": true,
|
|
49
|
+
+ "bin": {
|
|
50
|
+
+ "tsc": "bin/tsc",
|
|
51
|
+
+ "tsserver": "bin/tsserver"
|
|
52
|
+
+ },
|
|
53
|
+
+ "engines": {
|
|
54
|
+
+ "node": ">=14.17"
|
|
55
|
+
+ }
|
|
56
|
+
+ }
|
|
57
|
+
+ }
|
|
58
|
+
+}
|
|
59
|
+
Index: .gitignore
|
|
60
|
+
IDEA additional info:
|
|
61
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
62
|
+
<+>.idea\nnode_modules/\n
|
|
63
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
64
|
+
<+>UTF-8
|
|
65
|
+
===================================================================
|
|
66
|
+
diff --git a/.gitignore b/.gitignore
|
|
67
|
+
--- a/.gitignore (revision 93871c35bcfe76db61408d4392c372db2017dd69)
|
|
68
|
+
+++ b/.gitignore (date 1728248923825)
|
|
69
|
+
@@ -1,2 +1,3 @@
|
|
70
|
+
.idea
|
|
71
|
+
node_modules/
|
|
72
|
+
+sample-mocks-ts-dist/
|
|
73
|
+
Index: sample-mocks/api/user/loves.GET.200.ts
|
|
74
|
+
IDEA additional info:
|
|
75
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
76
|
+
<+>UTF-8
|
|
77
|
+
===================================================================
|
|
78
|
+
diff --git a/sample-mocks/api/user/loves.GET.200.ts b/sample-mocks/api/user/loves.GET.200.ts
|
|
79
|
+
new file mode 100644
|
|
80
|
+
--- /dev/null (date 1728240548518)
|
|
81
|
+
+++ b/sample-mocks/api/user/loves.GET.200.ts (date 1728240548518)
|
|
82
|
+
@@ -0,0 +1,6 @@
|
|
83
|
+
+export default [
|
|
84
|
+
+ { id: 100 },
|
|
85
|
+
+ { id: 101 },
|
|
86
|
+
+ { id: 102 },
|
|
87
|
+
+]
|
|
88
|
+
+
|
|
89
|
+
Index: sample-mocks/api/user/loves2.GET.200.ts
|
|
90
|
+
IDEA additional info:
|
|
91
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
92
|
+
<+>UTF-8
|
|
93
|
+
===================================================================
|
|
94
|
+
diff --git a/sample-mocks/api/user/loves2.GET.200.ts b/sample-mocks/api/user/loves2.GET.200.ts
|
|
95
|
+
new file mode 100644
|
|
96
|
+
--- /dev/null (date 1728249529512)
|
|
97
|
+
+++ b/sample-mocks/api/user/loves2.GET.200.ts (date 1728249529512)
|
|
98
|
+
@@ -0,0 +1,11 @@
|
|
99
|
+
+import loves from './loves.GET.200'
|
|
100
|
+
+
|
|
101
|
+
+export default [
|
|
102
|
+
+ ...loves,
|
|
103
|
+
+ { id: 100 },
|
|
104
|
+
+ { id: 101 },
|
|
105
|
+
+ { id: 102 },
|
|
106
|
+
+ { id: 102 },
|
|
107
|
+
+ { id: 102 },
|
|
108
|
+
+]
|
|
109
|
+
+
|
|
110
|
+
Index: _usage_example.js
|
|
111
|
+
IDEA additional info:
|
|
112
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
113
|
+
<+>#!/usr/bin/env node\n\nimport { join } from 'node:path'\nimport { Mockaton, jwtCookie } from './index.js' // from 'mockaton'\n\nMockaton({\n\tport: 2345,\n\tmocksDir: join(import.meta.dirname, 'sample-mocks'),\n\tstaticDir: join(import.meta.dirname, 'sample-static'),\n\tcookies: {\n\t\t'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',\n\t\t'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',\n\t\t'My JWT': jwtCookie('my-cookie', {\n\t\t\temail: 'john.doe@example.com',\n\t\t\tpicture: 'https://cdn.auth0.com/avatars/jd.png'\n\t\t})\n\t}\n})\n
|
|
114
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
115
|
+
<+>UTF-8
|
|
116
|
+
===================================================================
|
|
117
|
+
diff --git a/_usage_example.js b/_usage_example.js
|
|
118
|
+
--- a/_usage_example.js (revision 93871c35bcfe76db61408d4392c372db2017dd69)
|
|
119
|
+
+++ b/_usage_example.js (date 1728248445991)
|
|
120
|
+
@@ -6,6 +6,7 @@
|
|
121
|
+
Mockaton({
|
|
122
|
+
port: 2345,
|
|
123
|
+
mocksDir: join(import.meta.dirname, 'sample-mocks'),
|
|
124
|
+
+ tsMocksOutDir: join(import.meta.dirname, 'sample-mocks-ts-dist'),
|
|
125
|
+
staticDir: join(import.meta.dirname, 'sample-static'),
|
|
126
|
+
cookies: {
|
|
127
|
+
'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
|
|
128
|
+
Index: src/MockDispatcher.js
|
|
129
|
+
IDEA additional info:
|
|
130
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
131
|
+
<+>import { join } from 'node:path'\nimport { readFileSync as read } from 'node:fs'\n\nimport { proxy } from './ProxyRelay.js'\nimport { cookie } from './cookie.js'\nimport { Config } from './Config.js'\nimport { mimeFor } from './utils/mime.js'\nimport * as mockBrokerCollection from './mockBrokersCollection.js'\nimport { JsonBodyParserError } from './utils/http-request.js'\nimport { sendInternalServerError, sendNotFound, sendBadRequest } from './utils/http-response.js'\n\n\nexport async function dispatchMock(req, response) {\n\ttry {\n\t\tconst broker = mockBrokerCollection.getBrokerForUrl(req.method, req.url)\n\t\tif (!broker) {\n\t\t\tif (Config.proxyFallback)\n\t\t\t\tawait proxy(req, response)\n\t\t\telse\n\t\t\t\tsendNotFound(response)\n\t\t\treturn\n\t\t}\n\n\t\tconst { file, status, delay } = broker\n\t\tconsole.log(decodeURIComponent(req.url), ' → ', file)\n\t\tconst filePath = join(Config.mocksDir, file)\n\n\t\tresponse.statusCode = status\n\n\t\tif (cookie.getCurrent())\n\t\t\tresponse.setHeader('Set-Cookie', cookie.getCurrent())\n\n\t\tfor (let i = 0; i < Config.extraHeaders.length; i += 2)\n\t\t\tresponse.setHeader(Config.extraHeaders[i], Config.extraHeaders[i + 1])\n\n\t\tconst [mime, mockBody] = broker.isTemp500\n\t\t\t? temp500Plugin(filePath, req, response)\n\t\t\t: await preprocessPlugins(filePath, req, response)\n\n\t\tresponse.setHeader('Content-Type', mime)\n\t\tsetTimeout(() => response.end(mockBody), delay)\n\t}\n\tcatch (error) {\n\t\tif (error instanceof JsonBodyParserError)\n\t\t\tsendBadRequest(response, error)\n\t\telse if (error.code === 'ENOENT')\n\t\t\tsendNotFound(response) // file has been deleted\n\t\telse\n\t\t\tsendInternalServerError(response, error)\n\t}\n}\n\nasync function preprocessPlugins(filePath, req, response) {\n\tif (filePath.endsWith('.js'))\n\t\treturn await jsPlugin(filePath, req, response)\n\treturn readPlugin(filePath, req, response)\n}\n\nfunction temp500Plugin(filePath) {\n\treturn [mimeFor(filePath), '']\n}\n\nasync function jsPlugin(filePath, req, response) {\n\tconst jsExport = (await import(filePath + '?' + Date.now())).default // date for cache busting\n\tconst mockBody = typeof jsExport === 'function'\n\t\t? await jsExport(req, response)\n\t\t: JSON.stringify(jsExport, null, 2)\n\tconst mime = response.getHeader('Content-Type') // jsFunc are allowed to set it\n\t\t|| mimeFor('.json')\n\treturn [mime, mockBody]\n}\n\nfunction readPlugin(filePath) {\n\treturn [mimeFor(filePath), read(filePath)]\n}\n
|
|
132
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
133
|
+
<+>UTF-8
|
|
134
|
+
===================================================================
|
|
135
|
+
diff --git a/src/MockDispatcher.js b/src/MockDispatcher.js
|
|
136
|
+
--- a/src/MockDispatcher.js (revision 93871c35bcfe76db61408d4392c372db2017dd69)
|
|
137
|
+
+++ b/src/MockDispatcher.js (date 1728248531839)
|
|
138
|
+
@@ -53,6 +53,8 @@
|
|
139
|
+
async function preprocessPlugins(filePath, req, response) {
|
|
140
|
+
if (filePath.endsWith('.js'))
|
|
141
|
+
return await jsPlugin(filePath, req, response)
|
|
142
|
+
+ if (filePath.endsWith('.ts'))
|
|
143
|
+
+ return await tsPlugin(filePath, req, response)
|
|
144
|
+
return readPlugin(filePath, req, response)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@@ -69,6 +71,19 @@
|
|
148
|
+
|| mimeFor('.json')
|
|
149
|
+
return [mime, mockBody]
|
|
150
|
+
}
|
|
151
|
+
+
|
|
152
|
+
+async function tsPlugin(filePath, req, response) {
|
|
153
|
+
+ const tsAltPath = filePath
|
|
154
|
+
+ .replace(Config.mocksDir, Config.tsMocksOutDir)
|
|
155
|
+
+ .replace(/\.ts$/, '.js')
|
|
156
|
+
+ const tsExport = (await import(tsAltPath + '?' + Date.now())).default // date for cache busting
|
|
157
|
+
+ const mockBody = typeof tsExport === 'function'
|
|
158
|
+
+ ? await tsExport(req, response)
|
|
159
|
+
+ : JSON.stringify(tsExport, null, 2)
|
|
160
|
+
+ const mime = response.getHeader('Content-Type') // tsFunc are allowed to set it
|
|
161
|
+
+ || mimeFor('.json')
|
|
162
|
+
+ return [mime, mockBody]
|
|
163
|
+
+}
|
|
164
|
+
|
|
165
|
+
function readPlugin(filePath) {
|
|
166
|
+
return [mimeFor(filePath), read(filePath)]
|
|
167
|
+
Index: tsconfig.json
|
|
168
|
+
IDEA additional info:
|
|
169
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
170
|
+
<+>UTF-8
|
|
171
|
+
===================================================================
|
|
172
|
+
diff --git a/tsconfig.json b/tsconfig.json
|
|
173
|
+
new file mode 100644
|
|
174
|
+
--- /dev/null (date 1728249529515)
|
|
175
|
+
+++ b/tsconfig.json (date 1728249529515)
|
|
176
|
+
@@ -0,0 +1,8 @@
|
|
177
|
+
+{
|
|
178
|
+
+ "compilerOptions": {
|
|
179
|
+
+ "target": "esnext",
|
|
180
|
+
+ "skipLibCheck": true,
|
|
181
|
+
+ "rootDir": "./sample-mocks",
|
|
182
|
+
+ "outDir": "./sample-mocks-ts-dist"
|
|
183
|
+
+ }
|
|
184
|
+
+}
|
|
185
|
+
Index: package.json
|
|
186
|
+
IDEA additional info:
|
|
187
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
188
|
+
<+>{\n\t\"name\": \"mockaton\",\n\t\"description\": \"A deterministic server-side for developing and testing frontend clients\",\n\t\"type\": \"module\",\n\t\"version\": \"7.6.2\",\n\t\"main\": \"index.js\",\n\t\"types\": \"index.d.ts\",\n\t\"license\": \"MIT\",\n\t\"repository\": \"https://github.com/ericfortis/mockaton\",\n\t\"scripts\": {\n\t\t\"test\": \"node --test\",\n\t\t\"demo\": \"./_usage_example.js\"\n\t}\n}\n
|
|
189
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
190
|
+
<+>UTF-8
|
|
191
|
+
===================================================================
|
|
192
|
+
diff --git a/package.json b/package.json
|
|
193
|
+
--- a/package.json (revision 93871c35bcfe76db61408d4392c372db2017dd69)
|
|
194
|
+
+++ b/package.json (date 1728248906434)
|
|
195
|
+
@@ -10,5 +10,8 @@
|
|
196
|
+
"scripts": {
|
|
197
|
+
"test": "node --test",
|
|
198
|
+
"demo": "./_usage_example.js"
|
|
199
|
+
+ },
|
|
200
|
+
+ "optionalDependencies": {
|
|
201
|
+
+ "typescript": "5.6.2"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
Index: src/Config.js
|
|
205
|
+
IDEA additional info:
|
|
206
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
|
|
207
|
+
<+>import { isDirectory } from './utils/fs.js'\nimport { openInBrowser } from './utils/openInBrowser.js'\nimport { StandardMethods } from './utils/http-request.js'\nimport { validate, is, optional } from './utils/validate.js'\n\n\nexport const Config = Object.seal({\n\tmocksDir: '',\n\tignore: /(\\.DS_Store|~)$/,\n\n\tstaticDir: '',\n\n\thost: '127.0.0.1',\n\tport: 0, // auto-assigned\n\tproxyFallback: '', // e.g. http://localhost:9999\n\n\tdelay: 1200, // milliseconds\n\tcookies: {}, // defaults to the first kv\n\textraHeaders: [],\n\textraMimes: {},\n\n\tcorsAllowed: false,\n\tcorsOrigins: ['*'],\n\tcorsMethods: StandardMethods,\n\tcorsHeaders: ['content-type'],\n\tcorsExposedHeaders: [],\n\tcorsCredentials: true,\n\tcorsMaxAge: 0,\n\n\tonReady: openInBrowser\n})\n\n\nexport function setup(options) {\n\tObject.assign(Config, options)\n\tvalidate(Config, {\n\t\tmocksDir: isDirectory,\n\t\tignore: is(RegExp),\n\n\t\tstaticDir: optional(isDirectory),\n\n\t\thost: is(String),\n\t\tport: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,\n\t\tproxyFallback: optional(URL.canParse),\n\n\t\tdelay: ms => Number.isInteger(ms) && ms > 0,\n\t\tcookies: is(Object),\n\t\textraHeaders: val => Array.isArray(val) && val.length % 2 === 0,\n\t\textraMimes: is(Object),\n\n\t\tcorsAllowed: is(Boolean),\n\t\tcorsOrigins: validateCorsAllowedOrigins,\n\t\tcorsMethods: validateCorsAllowedMethods,\n\t\tcorsHeaders: Array.isArray,\n\t\tcorsExposedHeaders: Array.isArray,\n\t\tcorsCredentials: is(Boolean),\n\t\tcorsMaxAge: is(Number),\n\n\t\tonReady: is(Function)\n\t})\n}\n\n\nfunction validateCorsAllowedOrigins(arr) {\n\tif (!Array.isArray(arr))\n\t\treturn false\n\n\tif (arr.length === 1 && arr[0] === '*')\n\t\treturn true\n\n\treturn arr.every(o => URL.canParse(o))\n}\n\n\nfunction validateCorsAllowedMethods(arr) {\n\tif (!Array.isArray(arr))\n\t\treturn false\n\n\treturn arr.every(m => StandardMethods.includes(m))\n}\n
|
|
208
|
+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
|
209
|
+
<+>UTF-8
|
|
210
|
+
===================================================================
|
|
211
|
+
diff --git a/src/Config.js b/src/Config.js
|
|
212
|
+
--- a/src/Config.js (revision 93871c35bcfe76db61408d4392c372db2017dd69)
|
|
213
|
+
+++ b/src/Config.js (date 1728248407129)
|
|
214
|
+
@@ -6,6 +6,7 @@
|
|
215
|
+
|
|
216
|
+
export const Config = Object.seal({
|
|
217
|
+
mocksDir: '',
|
|
218
|
+
+ tsMocksOutDir: '',
|
|
219
|
+
ignore: /(\.DS_Store|~)$/,
|
|
220
|
+
|
|
221
|
+
staticDir: '',
|
|
222
|
+
@@ -35,6 +36,7 @@
|
|
223
|
+
Object.assign(Config, options)
|
|
224
|
+
validate(Config, {
|
|
225
|
+
mocksDir: isDirectory,
|
|
226
|
+
+ tsMocksOutDir: optional(isDirectory),
|
|
227
|
+
ignore: is(RegExp),
|
|
228
|
+
|
|
229
|
+
staticDir: optional(isDirectory),
|