mockaton 8.16.4 → 8.16.5
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/README.md +12 -6
- package/dev-config.js +18 -0
- package/index.js +1 -1
- package/package.json +1 -1
- package/src/Api.js +13 -13
- package/src/ApiConstants.js +1 -1
- package/src/MockDispatcher.js +27 -2
- package/src/Mockaton.js +6 -5
- package/src/StaticDispatcher.js +5 -57
- package/src/Watcher.js +2 -2
- package/src/config.js +1 -1
- package/src/mockBrokersCollection.js +6 -6
- package/src/staticCollection.js +56 -0
- package/src/MockDispatcherPlugins.js +0 -25
package/README.md
CHANGED
|
@@ -62,14 +62,20 @@ api/videos.GET.<b>500</b>.txt # Internal Server Error
|
|
|
62
62
|
|
|
63
63
|
<br/>
|
|
64
64
|
|
|
65
|
-
##
|
|
66
|
-
No need to mock everything. You can forward requests to your backend for routes
|
|
67
|
-
you don’t have mocks for, or routes that have the ☁️ **Cloud Checkbox** checked.
|
|
65
|
+
## Scraping mocks from your Backend
|
|
68
66
|
|
|
67
|
+
### Option 1: Browser Extension
|
|
68
|
+
This [devtools extension](https://github.com/ericfortis/devtools-ext-zip-http-requests)
|
|
69
|
+
lets you download a ZIP with all the responses following the filename convention.
|
|
69
70
|
|
|
70
|
-
###
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
### Option 2: Fallback to Your Backend
|
|
72
|
+
This option could be a bit elaborate if your backend uses third-party auth,
|
|
73
|
+
because in that case you’d have to inject the cookies manually.
|
|
74
|
+
|
|
75
|
+
At any rate, you can forward requests to your backend for routes you don’t have
|
|
76
|
+
mocks for, or routes that have the ☁️ **Cloud Checkbox** checked. By checking
|
|
77
|
+
✅ **Save Mocks**, you can collect the responses that hit your backend. They
|
|
78
|
+
will be saved in your `config.mocksDir` following the filename convention.
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
<br/>
|
package/dev-config.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { jwtCookie } from './index.js'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export const devConfig = {
|
|
6
|
+
port: 2345,
|
|
7
|
+
mocksDir: join(import.meta.dirname, 'fixtures-mocks'),
|
|
8
|
+
staticDir: join(import.meta.dirname, 'fixtures-static-mocks'),
|
|
9
|
+
cookies: {
|
|
10
|
+
'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
|
|
11
|
+
'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
|
|
12
|
+
'My JWT': jwtCookie('my-cookie', {
|
|
13
|
+
email: 'john.doe@example.com',
|
|
14
|
+
picture: 'https://cdn.auth0.com/avatars/jd.png'
|
|
15
|
+
}),
|
|
16
|
+
'None': ''
|
|
17
|
+
}
|
|
18
|
+
}
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { Mockaton } from './src/Mockaton.js'
|
|
2
2
|
export { Commander } from './src/Commander.js'
|
|
3
|
-
export { jsToJsonPlugin } from './src/MockDispatcherPlugins.js'
|
|
4
3
|
|
|
5
4
|
export { jwtCookie } from './src/utils/jwt.js'
|
|
6
5
|
export { parseJSON } from './src/utils/http-request.js'
|
|
6
|
+
export { jsToJsonPlugin } from './src/MockDispatcher.js'
|
package/package.json
CHANGED
package/src/Api.js
CHANGED
|
@@ -7,11 +7,11 @@ import { join } from 'node:path'
|
|
|
7
7
|
import { cookie } from './cookie.js'
|
|
8
8
|
import { parseJSON } from './utils/http-request.js'
|
|
9
9
|
import { uiSyncVersion } from './Watcher.js'
|
|
10
|
+
import * as staticCollection from './staticCollection.js'
|
|
10
11
|
import * as mockBrokersCollection from './mockBrokersCollection.js'
|
|
11
12
|
import { config, ConfigValidator } from './config.js'
|
|
12
13
|
import { DF, API, LONG_POLL_SERVER_TIMEOUT } from './ApiConstants.js'
|
|
13
14
|
import { sendOK, sendJSON, sendUnprocessableContent, sendFile } from './utils/http-response.js'
|
|
14
|
-
import { getStaticFilesCollection, findStaticBrokerByRoute, initStaticCollection } from './StaticDispatcher.js'
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
const dashboardAssets = [
|
|
@@ -25,9 +25,7 @@ const dashboardAssets = [
|
|
|
25
25
|
|
|
26
26
|
export const apiGetRequests = new Map([
|
|
27
27
|
[API.dashboard, serveDashboard],
|
|
28
|
-
...dashboardAssets.map(f => [
|
|
29
|
-
API.dashboard + f, serveDashboardAsset(f)
|
|
30
|
-
]),
|
|
28
|
+
...dashboardAssets.map(f => [API.dashboard + f, serveDashboardAsset(f)]),
|
|
31
29
|
[API.cors, getIsCorsAllowed],
|
|
32
30
|
[API.static, listStaticFiles],
|
|
33
31
|
[API.mocks, listMockBrokers],
|
|
@@ -68,12 +66,14 @@ function serveDashboardAsset(f) {
|
|
|
68
66
|
|
|
69
67
|
function listCookies(_, response) { sendJSON(response, cookie.list()) }
|
|
70
68
|
function listComments(_, response) { sendJSON(response, mockBrokersCollection.extractAllComments()) }
|
|
71
|
-
|
|
69
|
+
|
|
70
|
+
function listStaticFiles(_, response) { sendJSON(response, staticCollection.all()) }
|
|
71
|
+
function listMockBrokers(_, response) { sendJSON(response, mockBrokersCollection.all()) }
|
|
72
|
+
|
|
72
73
|
function getGlobalDelay(_, response) { sendJSON(response, config.delay) }
|
|
73
|
-
function listMockBrokers(_, response) { sendJSON(response, mockBrokersCollection.getAll()) }
|
|
74
74
|
function getProxyFallback(_, response) { sendJSON(response, config.proxyFallback) }
|
|
75
|
-
function getIsCorsAllowed(_, response) { sendJSON(response, config.corsAllowed) }
|
|
76
75
|
function getCollectProxied(_, response) { sendJSON(response, config.collectProxied) }
|
|
76
|
+
function getIsCorsAllowed(_, response) { sendJSON(response, config.corsAllowed) }
|
|
77
77
|
|
|
78
78
|
function longPollClientSyncVersion(req, response) {
|
|
79
79
|
if (uiSyncVersion.version !== Number(req.headers[DF.syncVersion])) {
|
|
@@ -101,7 +101,7 @@ function longPollClientSyncVersion(req, response) {
|
|
|
101
101
|
|
|
102
102
|
function reinitialize(_, response) {
|
|
103
103
|
mockBrokersCollection.init()
|
|
104
|
-
|
|
104
|
+
staticCollection.init() // TESTME
|
|
105
105
|
sendOK(response)
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -115,7 +115,7 @@ async function selectCookie(req, response) {
|
|
|
115
115
|
|
|
116
116
|
async function selectMock(req, response) {
|
|
117
117
|
const file = await parseJSON(req)
|
|
118
|
-
const broker = mockBrokersCollection.
|
|
118
|
+
const broker = mockBrokersCollection.brokerByFilename(file)
|
|
119
119
|
if (!broker || !broker.hasMock(file))
|
|
120
120
|
sendUnprocessableContent(response, `Missing Mock: ${file}`)
|
|
121
121
|
else {
|
|
@@ -127,7 +127,7 @@ async function selectMock(req, response) {
|
|
|
127
127
|
async function setRouteIsDelayed(req, response) {
|
|
128
128
|
const body = await parseJSON(req)
|
|
129
129
|
const delayed = body[DF.delayed]
|
|
130
|
-
const broker = mockBrokersCollection.
|
|
130
|
+
const broker = mockBrokersCollection.brokerByRoute(
|
|
131
131
|
body[DF.routeMethod],
|
|
132
132
|
body[DF.routeUrlMask])
|
|
133
133
|
|
|
@@ -144,7 +144,7 @@ async function setRouteIsDelayed(req, response) {
|
|
|
144
144
|
async function setRouteIsProxied(req, response) { // TESTME
|
|
145
145
|
const body = await parseJSON(req)
|
|
146
146
|
const proxied = body[DF.proxied]
|
|
147
|
-
const broker = mockBrokersCollection.
|
|
147
|
+
const broker = mockBrokersCollection.brokerByRoute(
|
|
148
148
|
body[DF.routeMethod],
|
|
149
149
|
body[DF.routeUrlMask])
|
|
150
150
|
|
|
@@ -201,7 +201,7 @@ async function setGlobalDelay(req, response) { // TESTME
|
|
|
201
201
|
async function setStaticRouteStatusCode(req, response) {
|
|
202
202
|
const body = await parseJSON(req)
|
|
203
203
|
const status = Number(body[DF.statusCode])
|
|
204
|
-
const broker =
|
|
204
|
+
const broker = staticCollection.brokerByRoute(body[DF.routeUrlMask])
|
|
205
205
|
|
|
206
206
|
if (!broker) // TESTME
|
|
207
207
|
sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeUrlMask]}`)
|
|
@@ -217,7 +217,7 @@ async function setStaticRouteStatusCode(req, response) {
|
|
|
217
217
|
async function setStaticRouteIsDelayed(req, response) {
|
|
218
218
|
const body = await parseJSON(req)
|
|
219
219
|
const delayed = body[DF.delayed]
|
|
220
|
-
const broker =
|
|
220
|
+
const broker = staticCollection.brokerByRoute(body[DF.routeUrlMask])
|
|
221
221
|
|
|
222
222
|
if (!broker) // TESTME
|
|
223
223
|
sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeUrlMask]}`)
|
package/src/ApiConstants.js
CHANGED
|
@@ -31,7 +31,7 @@ export const DF = { // Dashboard Fields (XHR)
|
|
|
31
31
|
export const DEFAULT_500_COMMENT = '(Mockaton 500)'
|
|
32
32
|
export const DEFAULT_MOCK_COMMENT = '(default)'
|
|
33
33
|
export const EXT_FOR_UNKNOWN_MIME = 'unknown'
|
|
34
|
-
|
|
35
34
|
export const LONG_POLL_SERVER_TIMEOUT = 8_000
|
|
36
35
|
|
|
36
|
+
|
|
37
37
|
export const HEADER_FOR_502 = 'mockaton502'
|
package/src/MockDispatcher.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { pathToFileURL } from 'node:url'
|
|
2
4
|
|
|
3
5
|
import { proxy } from './ProxyRelay.js'
|
|
4
6
|
import { cookie } from './cookie.js'
|
|
5
|
-
import {
|
|
7
|
+
import { mimeFor } from './utils/mime.js'
|
|
6
8
|
import { config, calcDelay } from './config.js'
|
|
7
9
|
import { BodyReaderError } from './utils/http-request.js'
|
|
8
10
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
@@ -11,7 +13,7 @@ import { sendInternalServerError, sendNotFound, sendUnprocessableContent } from
|
|
|
11
13
|
|
|
12
14
|
export async function dispatchMock(req, response) {
|
|
13
15
|
try {
|
|
14
|
-
const broker = mockBrokerCollection.
|
|
16
|
+
const broker = mockBrokerCollection.brokerByRoute(req.method, req.url)
|
|
15
17
|
if (!broker || broker.proxied) {
|
|
16
18
|
if (config.proxyFallback)
|
|
17
19
|
await proxy(req, response, Number(broker?.delayed && calcDelay()))
|
|
@@ -50,3 +52,26 @@ export async function dispatchMock(req, response) {
|
|
|
50
52
|
sendInternalServerError(response, error)
|
|
51
53
|
}
|
|
52
54
|
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async function applyPlugins(filePath, req, response) {
|
|
58
|
+
for (const [regex, plugin] of config.plugins)
|
|
59
|
+
if (regex.test(filePath))
|
|
60
|
+
return await plugin(filePath, req, response)
|
|
61
|
+
return {
|
|
62
|
+
mime: mimeFor(filePath),
|
|
63
|
+
body: readFileSync(filePath)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
export async function jsToJsonPlugin(filePath, req, response) {
|
|
69
|
+
const jsExport = (await import(pathToFileURL(filePath) + '?' + Date.now())).default // date for cache busting
|
|
70
|
+
const body = typeof jsExport === 'function'
|
|
71
|
+
? await jsExport(req, response)
|
|
72
|
+
: JSON.stringify(jsExport, null, 2)
|
|
73
|
+
return {
|
|
74
|
+
mime: response.getHeader('Content-Type') || mimeFor('.json'), // jsFunc are allowed to set it
|
|
75
|
+
body
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/Mockaton.js
CHANGED
|
@@ -3,12 +3,13 @@ import { createServer } from 'node:http'
|
|
|
3
3
|
import { API } from './ApiConstants.js'
|
|
4
4
|
import { config, setup } from './config.js'
|
|
5
5
|
import { dispatchMock } from './MockDispatcher.js'
|
|
6
|
+
import { dispatchStatic } from './StaticDispatcher.js'
|
|
7
|
+
import * as staticCollection from './staticCollection.js'
|
|
6
8
|
import { BodyReaderError } from './utils/http-request.js'
|
|
7
9
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
8
10
|
import { setCorsHeaders, isPreflight } from './utils/http-cors.js'
|
|
11
|
+
import { watchMocksDir, watchStaticDir } from './Watcher.js'
|
|
9
12
|
import { apiPatchRequests, apiGetRequests } from './Api.js'
|
|
10
|
-
import { watchMocksDir, watchStaticMocksDir } from './Watcher.js'
|
|
11
|
-
import { dispatchStatic, initStaticCollection, findStaticBrokerByRoute } from './StaticDispatcher.js'
|
|
12
13
|
import { sendNoContent, sendInternalServerError, sendUnprocessableContent } from './utils/http-response.js'
|
|
13
14
|
|
|
14
15
|
|
|
@@ -17,9 +18,9 @@ process.on('unhandledRejection', error => { throw error })
|
|
|
17
18
|
export function Mockaton(options) {
|
|
18
19
|
setup(options)
|
|
19
20
|
mockBrokerCollection.init()
|
|
20
|
-
|
|
21
|
+
staticCollection.init()
|
|
21
22
|
watchMocksDir()
|
|
22
|
-
|
|
23
|
+
watchStaticDir()
|
|
23
24
|
|
|
24
25
|
return createServer(onRequest).listen(config.port, config.host, function (error) {
|
|
25
26
|
if (error) {
|
|
@@ -54,7 +55,7 @@ async function onRequest(req, response) {
|
|
|
54
55
|
else if (method === 'GET' && apiGetRequests.has(url))
|
|
55
56
|
apiGetRequests.get(url)(req, response)
|
|
56
57
|
|
|
57
|
-
else if (method === 'GET' &&
|
|
58
|
+
else if (method === 'GET' && staticCollection.brokerByRoute(url))
|
|
58
59
|
await dispatchStatic(req, response)
|
|
59
60
|
|
|
60
61
|
else
|
package/src/StaticDispatcher.js
CHANGED
|
@@ -1,65 +1,14 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
1
2
|
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { join, basename } from 'node:path'
|
|
3
3
|
|
|
4
4
|
import { mimeFor } from './utils/mime.js'
|
|
5
|
-
import {
|
|
6
|
-
import { config,
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class StaticBroker {
|
|
11
|
-
constructor(route) {
|
|
12
|
-
this.route = route
|
|
13
|
-
this.delayed = false
|
|
14
|
-
this.status = 200 // 200 or 404
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
setDelayed(value) { this.delayed = value }
|
|
18
|
-
setStatus(value) { this.status = value }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** @type {{ [route: string]: StaticBroker }} */
|
|
22
|
-
let collection = {}
|
|
23
|
-
|
|
24
|
-
export function initStaticCollection() {
|
|
25
|
-
collection = {}
|
|
26
|
-
listFilesRecursively(config.staticDir)
|
|
27
|
-
.sort()
|
|
28
|
-
.forEach(registerStaticMock)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/** @returns {boolean} registered */
|
|
33
|
-
export function registerStaticMock(relativeFile) {
|
|
34
|
-
if (!isFileAllowed(basename(relativeFile)))
|
|
35
|
-
return false
|
|
36
|
-
|
|
37
|
-
const route = '/' + relativeFile
|
|
38
|
-
if (findStaticBrokerByRoute(route))
|
|
39
|
-
return false
|
|
40
|
-
|
|
41
|
-
collection[route] = new StaticBroker(route)
|
|
42
|
-
return true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export function unregisterStaticMock(relativeFile) {
|
|
47
|
-
delete collection['/' + relativeFile]
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
/** @returns {StaticBroker | undefined} */
|
|
52
|
-
export function findStaticBrokerByRoute(route) {
|
|
53
|
-
return collection[route] || collection[join(route, 'index.html')]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function getStaticFilesCollection() {
|
|
57
|
-
return collection
|
|
58
|
-
}
|
|
5
|
+
import { brokerByRoute } from './staticCollection.js'
|
|
6
|
+
import { config, calcDelay } from './config.js'
|
|
7
|
+
import { sendNotFound, sendPartialContent } from './utils/http-response.js'
|
|
59
8
|
|
|
60
9
|
|
|
61
10
|
export async function dispatchStatic(req, response) {
|
|
62
|
-
const broker =
|
|
11
|
+
const broker = brokerByRoute(req.url)
|
|
63
12
|
|
|
64
13
|
setTimeout(async () => {
|
|
65
14
|
if (!broker || broker.status === 404) { // TESTME
|
|
@@ -75,4 +24,3 @@ export async function dispatchStatic(req, response) {
|
|
|
75
24
|
}
|
|
76
25
|
}, Number(broker.delayed && calcDelay()))
|
|
77
26
|
}
|
|
78
|
-
|
package/src/Watcher.js
CHANGED
|
@@ -5,7 +5,7 @@ import { EventEmitter } from 'node:events'
|
|
|
5
5
|
import { config } from './config.js'
|
|
6
6
|
import { isFile } from './utils/fs.js'
|
|
7
7
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
8
|
-
import { registerStaticMock, unregisterStaticMock } from './
|
|
8
|
+
import { registerStaticMock, unregisterStaticMock } from './staticCollection.js'
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
/** # AR = Add or Remove Mock Event */
|
|
@@ -40,7 +40,7 @@ export function watchMocksDir() {
|
|
|
40
40
|
})
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function
|
|
43
|
+
export function watchStaticDir() {
|
|
44
44
|
const dir = config.staticDir
|
|
45
45
|
if (!dir)
|
|
46
46
|
return
|
package/src/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { realpathSync } from 'node:fs'
|
|
2
2
|
import { isDirectory } from './utils/fs.js'
|
|
3
3
|
import { openInBrowser } from './utils/openInBrowser.js'
|
|
4
|
-
import { jsToJsonPlugin } from './
|
|
4
|
+
import { jsToJsonPlugin } from './MockDispatcher.js'
|
|
5
5
|
import { optional, is, validate } from './utils/validate.js'
|
|
6
6
|
import { SUPPORTED_METHODS } from './utils/http-request.js'
|
|
7
7
|
import { validateCorsAllowedMethods, validateCorsAllowedOrigins } from './utils/http-cors.js'
|
|
@@ -23,6 +23,8 @@ import { parseFilename, filenameIsValid } from './Filename.js'
|
|
|
23
23
|
*/
|
|
24
24
|
let collection = {}
|
|
25
25
|
|
|
26
|
+
export const all = () => collection
|
|
27
|
+
|
|
26
28
|
export function init() {
|
|
27
29
|
collection = {}
|
|
28
30
|
cookie.init(config.cookies)
|
|
@@ -39,7 +41,7 @@ export function init() {
|
|
|
39
41
|
|
|
40
42
|
/** @returns {boolean} registered */
|
|
41
43
|
export function registerMock(file, isFromWatcher = false) {
|
|
42
|
-
if (
|
|
44
|
+
if (brokerByFilename(file)?.hasMock(file)
|
|
43
45
|
|| !isFileAllowed(basename(file)) // TESTME
|
|
44
46
|
|| !filenameIsValid(file))
|
|
45
47
|
return false
|
|
@@ -62,7 +64,7 @@ export function registerMock(file, isFromWatcher = false) {
|
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
export function unregisterMock(file) {
|
|
65
|
-
const broker =
|
|
67
|
+
const broker = brokerByFilename(file)
|
|
66
68
|
if (!broker)
|
|
67
69
|
return
|
|
68
70
|
const isEmpty = broker.unregister(file)
|
|
@@ -74,11 +76,9 @@ export function unregisterMock(file) {
|
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
export const getAll = () => collection
|
|
78
|
-
|
|
79
79
|
|
|
80
80
|
/** @returns {MockBroker | undefined} */
|
|
81
|
-
export function
|
|
81
|
+
export function brokerByFilename(file) {
|
|
82
82
|
const { method, urlMask } = parseFilename(file)
|
|
83
83
|
if (collection[method])
|
|
84
84
|
return collection[method][urlMask]
|
|
@@ -91,7 +91,7 @@ export function findBrokerByFilename(file) {
|
|
|
91
91
|
* BTW, `urlMasks` always start with "/", so there’s no need to
|
|
92
92
|
* worry about the primacy of array-like keys when iterating.
|
|
93
93
|
@returns {MockBroker | undefined} */
|
|
94
|
-
export function
|
|
94
|
+
export function brokerByRoute(method, url) {
|
|
95
95
|
if (!collection[method])
|
|
96
96
|
return
|
|
97
97
|
const brokers = Object.values(collection[method])
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { join, basename } from 'node:path'
|
|
2
|
+
import { listFilesRecursively } from './utils/fs.js'
|
|
3
|
+
import { config, isFileAllowed } from './config.js'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class StaticBroker {
|
|
7
|
+
constructor(route) {
|
|
8
|
+
this.route = route
|
|
9
|
+
this.delayed = false
|
|
10
|
+
this.status = 200 // 200 or 404
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setDelayed(value) { this.delayed = value }
|
|
14
|
+
setStatus(value) { this.status = value }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** @type {{ [route: string]: StaticBroker }} */
|
|
18
|
+
let collection = {}
|
|
19
|
+
|
|
20
|
+
export const all = () => collection
|
|
21
|
+
|
|
22
|
+
export function init() {
|
|
23
|
+
collection = {}
|
|
24
|
+
listFilesRecursively(config.staticDir)
|
|
25
|
+
.sort()
|
|
26
|
+
.forEach(registerStaticMock)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/** @returns {boolean} registered */
|
|
31
|
+
export function registerStaticMock(relativeFile) {
|
|
32
|
+
if (!isFileAllowed(basename(relativeFile)))
|
|
33
|
+
return false
|
|
34
|
+
|
|
35
|
+
const route = '/' + relativeFile
|
|
36
|
+
if (brokerByRoute(route))
|
|
37
|
+
return false
|
|
38
|
+
|
|
39
|
+
collection[route] = new StaticBroker(route)
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
export function unregisterStaticMock(relativeFile) {
|
|
45
|
+
delete collection['/' + relativeFile]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/** @returns {StaticBroker | undefined} */
|
|
50
|
+
export function brokerByRoute(route) {
|
|
51
|
+
return collection[route] || collection[join(route, 'index.html')]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { readFileSync as read } from 'node:fs'
|
|
2
|
-
import { mimeFor } from './utils/mime.js'
|
|
3
|
-
import { config } from './config.js'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export async function applyPlugins(filePath, req, response) {
|
|
7
|
-
for (const [regex, plugin] of config.plugins)
|
|
8
|
-
if (regex.test(filePath))
|
|
9
|
-
return await plugin(filePath, req, response)
|
|
10
|
-
return {
|
|
11
|
-
mime: mimeFor(filePath),
|
|
12
|
-
body: read(filePath)
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function jsToJsonPlugin(filePath, req, response) {
|
|
17
|
-
const jsExport = (await import(filePath + '?' + Date.now())).default // date for cache busting
|
|
18
|
-
const body = typeof jsExport === 'function'
|
|
19
|
-
? await jsExport(req, response)
|
|
20
|
-
: JSON.stringify(jsExport, null, 2)
|
|
21
|
-
return {
|
|
22
|
-
mime: response.getHeader('Content-Type') || mimeFor('.json'), // jsFunc are allowed to set it
|
|
23
|
-
body
|
|
24
|
-
}
|
|
25
|
-
}
|