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.
Files changed (68) hide show
  1. package/.idea/dictionaries/efortis.xml +3 -0
  2. package/.idea/encodings.xml +6 -0
  3. package/.idea/inspectionProfiles/Project_Default.xml +105 -0
  4. package/.idea/jsLibraryMappings.xml +6 -0
  5. package/.idea/jsLinters/eslint.xml +6 -0
  6. package/.idea/misc.xml +115 -0
  7. package/.idea/mockaton.iml +8 -0
  8. package/.idea/modules.xml +8 -0
  9. package/.idea/shelf/custom_methods,_but_nodejs_does_not_support_them_https___github_com_nodejs_llhttp_pull_54/shelved.patch +260 -0
  10. package/.idea/shelf/custom_methods__but_nodejs_does_not_support_them_https___github_com_nodejs_llhttp_pull_54.xml +4 -0
  11. package/.idea/shelf/preflight/shelved.patch +122 -0
  12. package/.idea/shelf/preflight.xml +4 -0
  13. package/.idea/shelf/preflight1/shelved.patch +289 -0
  14. package/.idea/shelf/preflight1.xml +4 -0
  15. package/.idea/shelf/ts/shelved.patch +229 -0
  16. package/.idea/shelf/ts.xml +4 -0
  17. package/.idea/vcs.xml +6 -0
  18. package/.idea/webResources.xml +14 -0
  19. package/.idea/workspace.xml +248 -0
  20. package/README.md +17 -7
  21. package/index.d.ts +5 -0
  22. package/index.js +3 -1
  23. package/package.json +5 -4
  24. package/src/Dashboard.css +10 -9
  25. package/src/Dashboard.js +1 -3
  26. package/src/mockaton-logo.svg +1 -1
  27. package/src/utils/http-response.js +4 -4
  28. package/.editorconfig +0 -11
  29. package/README-dashboard-dark.png +0 -0
  30. package/README-dashboard-light.png +0 -0
  31. package/sample-mocks/api/user/avatar.GET.200.png +0 -0
  32. package/sample-mocks/api/user/edit-name.PATCH.200.json +0 -3
  33. package/sample-mocks/api/user/edit-name.PATCH.422.json +0 -3
  34. package/sample-mocks/api/user/friends.GET.200.json +0 -3
  35. package/sample-mocks/api/user/friends.GET.204.json +0 -4
  36. package/sample-mocks/api/user/friends.GET.500.txt +0 -7
  37. package/sample-mocks/api/user/likes.GET.200.js +0 -9
  38. package/sample-mocks/api/user/links.GET.200.js +0 -9
  39. package/sample-mocks/api/user/logout.POST.200.json +0 -1
  40. package/sample-mocks/api/user/typescript-scores-full.GET.200.ts +0 -9
  41. package/sample-mocks/api/user/typescript-scores.GET.200.ts +0 -6
  42. package/sample-mocks/api/user/videos(all variants).GET.200.json +0 -13
  43. package/sample-mocks/api/user/videos(default).GET.200.json +0 -13
  44. package/sample-mocks/api/user/videos(verified)(another comment).GET.200.json +0 -13
  45. package/sample-mocks/api/user/yaml-fruits.GET.200.yml +0 -5
  46. package/sample-mocks/api/user.GET.200.json +0 -4
  47. package/sample-mocks/api/video/[id].GET.200.json +0 -4
  48. package/sample-mocks/api/video/stat/[stat-id]/[video-id].GET.200.json +0 -3
  49. package/sample-mocks/api/video/stat/[stat-id]/all-videos?limit=[limit].GET.200.json +0 -4
  50. package/sample-static/another-entry/index.html +0 -35
  51. package/sample-static/assets/video.mp4 +0 -0
  52. package/sample-static/index.html +0 -37
  53. package/ui-tests/_setup.js +0 -32
  54. package/ui-tests/bulk-select.test.js +0 -10
  55. package/ui-tests/bulk-select.vp1024x800.dark.gold.png +0 -0
  56. package/ui-tests/bulk-select.vp1024x800.light.gold.png +0 -0
  57. package/ui-tests/initial-dashboard-state.test.js +0 -4
  58. package/ui-tests/initial-dashboard-state.vp1024x800.dark.gold.png +0 -0
  59. package/ui-tests/initial-dashboard-state.vp1024x800.light.gold.png +0 -0
  60. package/ui-tests/payload-viewer-formats-json.test.js +0 -9
  61. package/ui-tests/payload-viewer-formats-json.vp1024x800.dark.gold.png +0 -0
  62. package/ui-tests/payload-viewer-formats-json.vp1024x800.light.gold.png +0 -0
  63. package/ui-tests/payload-viewer-renders-images.test.js +0 -9
  64. package/ui-tests/payload-viewer-renders-images.vp1024x800.dark.gold.png +0 -0
  65. package/ui-tests/payload-viewer-renders-images.vp1024x800.light.gold.png +0 -0
  66. package/ui-tests/select-mock-variant.test.js +0 -10
  67. package/ui-tests/select-mock-variant.vp1024x800.dark.gold.png +0 -0
  68. 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,4 @@
1
+ <changelist name="preflight" date="1727236759186" recycled="false">
2
+ <option name="PATH" value="$PROJECT_DIR$/.idea/shelf/preflight/shelved.patch" />
3
+ <option name="DESCRIPTION" value="preflight" />
4
+ </changelist>
@@ -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,4 @@
1
+ <changelist name="preflight1" date="1727485751125" recycled="false">
2
+ <option name="PATH" value="$PROJECT_DIR$/.idea/shelf/preflight1/shelved.patch" />
3
+ <option name="DESCRIPTION" value="preflight" />
4
+ </changelist>
@@ -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),
@@ -0,0 +1,4 @@
1
+ <changelist name="ts" date="1728249621403" recycled="false">
2
+ <option name="PATH" value="$PROJECT_DIR$/.idea/shelf/ts/shelved.patch" />
3
+ <option name="DESCRIPTION" value="ts" />
4
+ </changelist>