@srfnstack/spliffy 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/content.mjs +5 -3
- package/src/decorator.mjs +52 -45
- package/src/handler.mjs +30 -21
- package/src/middleware.mjs +2 -4
- package/src/routes.mjs +34 -9
- package/src/server.mjs +3 -3
- package/src/serverConfig.mjs +15 -3
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@srfnstack/spliffy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"author": "snowbldr",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/narcolepticsnowman/spliffy",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"type": "module",
|
|
8
9
|
"files": [
|
|
9
10
|
"src/*",
|
|
10
11
|
"LICENSE.txt",
|
|
11
12
|
"README.md"
|
|
12
13
|
],
|
|
13
|
-
"main": "src/index.
|
|
14
|
+
"main": "src/index.mjs",
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
|
16
17
|
"url": "git@github.com:narcolepticsnowman/spliffy.git"
|
package/src/content.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { parseQuery } from './url.mjs'
|
|
|
3
3
|
|
|
4
4
|
const defaultHandler = {
|
|
5
5
|
deserialize: o => {
|
|
6
|
+
if (!o) return o
|
|
6
7
|
try {
|
|
7
8
|
return JSON.parse(o && o.toString())
|
|
8
9
|
} catch (e) {
|
|
@@ -10,6 +11,7 @@ const defaultHandler = {
|
|
|
10
11
|
}
|
|
11
12
|
},
|
|
12
13
|
serialize: o => {
|
|
14
|
+
if (!o) return { data: o }
|
|
13
15
|
if (typeof o === 'string') {
|
|
14
16
|
return {
|
|
15
17
|
contentType: 'text/plain',
|
|
@@ -41,8 +43,8 @@ const toFormData = (key, value) => {
|
|
|
41
43
|
|
|
42
44
|
const contentHandlers = {
|
|
43
45
|
'application/json': {
|
|
44
|
-
deserialize: s => JSON.parse(s && s.toString()),
|
|
45
|
-
serialize: o => JSON.stringify(o)
|
|
46
|
+
deserialize: s => s && JSON.parse(s && s.toString()),
|
|
47
|
+
serialize: o => o && JSON.stringify(o)
|
|
46
48
|
},
|
|
47
49
|
'text/plain': {
|
|
48
50
|
deserialize: s => s && s.toString(),
|
|
@@ -51,7 +53,7 @@ const contentHandlers = {
|
|
|
51
53
|
'application/octet-stream': defaultHandler,
|
|
52
54
|
'application/x-www-form-urlencoded': {
|
|
53
55
|
deserialize: s => s && parseQuery(s.toString(), true),
|
|
54
|
-
serialize: o => Object.keys(o).map(toFormData).flat().join('&')
|
|
56
|
+
serialize: o => o && Object.keys(o).map(toFormData).flat().join('&')
|
|
55
57
|
},
|
|
56
58
|
'*/*': defaultHandler
|
|
57
59
|
}
|
package/src/decorator.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import cookie from 'cookie'
|
|
2
2
|
import http from 'http'
|
|
3
|
-
import { parseQuery
|
|
3
|
+
import { parseQuery } from './url.mjs'
|
|
4
4
|
import log from './log.mjs'
|
|
5
5
|
import { v4 as uuid } from 'uuid'
|
|
6
6
|
import stream from 'stream'
|
|
7
7
|
import httpStatusCodes, { defaultStatusMessages } from './httpStatusCodes.mjs'
|
|
8
|
+
|
|
8
9
|
const { Writable } = stream
|
|
9
10
|
|
|
10
11
|
const addressArrayBufferToString = addrBuf => String.fromCharCode.apply(null, new Int8Array(addrBuf))
|
|
@@ -26,42 +27,54 @@ export const setCookie = (res) => function () {
|
|
|
26
27
|
return res.setHeader('Set-Cookie', [...(res.getHeader('Set-Cookie') || []), cookie.serialize(...arguments)])
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
export function decorateRequest (uwsReq, pathParameters, res, {
|
|
30
|
+
export function decorateRequest (uwsReq, pathParameters, res, {
|
|
31
|
+
decodeQueryParameters,
|
|
32
|
+
decodePathParameters,
|
|
33
|
+
parseCookie,
|
|
34
|
+
extendIncomingMessage
|
|
35
|
+
} = {}) {
|
|
30
36
|
// uwsReq can't be used in async functions because it gets de-allocated when the handler function returns
|
|
31
37
|
const req = {}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
if (extendIncomingMessage) {
|
|
39
|
+
// frameworks like passport like to modify the message prototype
|
|
40
|
+
// Setting the prototype of req is not desirable because the entire api of IncomingMessage is not supported
|
|
41
|
+
for (const p of reqProtoProps()) {
|
|
42
|
+
if (!req[p]) req[p] = http.IncomingMessage.prototype[p]
|
|
43
|
+
}
|
|
36
44
|
}
|
|
37
45
|
const query = uwsReq.getQuery()
|
|
38
46
|
req.path = uwsReq.getUrl()
|
|
39
47
|
req.url = `${req.path}${query ? '?' + query : ''}`
|
|
40
48
|
req.spliffyUrl = {
|
|
41
|
-
path:
|
|
42
|
-
query: parseQuery(
|
|
49
|
+
path: req.path,
|
|
50
|
+
query: query && parseQuery(query, decodeQueryParameters),
|
|
51
|
+
pathParameters: {}
|
|
43
52
|
}
|
|
44
|
-
req.spliffyUrl.pathParameters = {}
|
|
45
53
|
if (pathParameters && pathParameters.length > 0) {
|
|
46
54
|
for (const i in pathParameters) {
|
|
47
|
-
req.spliffyUrl.pathParameters[pathParameters[i]] =
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
req.spliffyUrl.pathParameters[pathParameters[i]] =
|
|
56
|
+
decodePathParameters
|
|
57
|
+
? decodeURIComponent(uwsReq.getParameter(i))
|
|
58
|
+
: uwsReq.getParameter(i)
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
req.params = req.spliffyUrl.pathParameters
|
|
53
62
|
req.headers = {}
|
|
63
|
+
uwsReq.forEach((header, value) => { req.headers[header] = value })
|
|
54
64
|
req.method = uwsReq.getMethod().toUpperCase()
|
|
55
65
|
req.remoteAddress = addressArrayBufferToString(res.getRemoteAddressAsText())
|
|
56
66
|
req.proxiedRemoteAddress = addressArrayBufferToString(res.getProxiedRemoteAddressAsText())
|
|
57
|
-
|
|
58
|
-
req.get = header => req.headers[normalizeHeader(header)]
|
|
67
|
+
req.get = header => req.headers[header]
|
|
59
68
|
if (parseCookie && req.headers.cookie) {
|
|
60
69
|
req.cookies = cookie.parse(req.headers.cookie) || {}
|
|
61
70
|
}
|
|
62
71
|
return req
|
|
63
72
|
}
|
|
64
73
|
|
|
74
|
+
function toArrayBuffer (buffer) {
|
|
75
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)
|
|
76
|
+
}
|
|
77
|
+
|
|
65
78
|
export function decorateResponse (res, req, finalizeResponse, errorTransformer, endError, { acceptsDefault }) {
|
|
66
79
|
res.onAborted(() => {
|
|
67
80
|
res.ended = true
|
|
@@ -114,31 +127,37 @@ export function decorateResponse (res, req, finalizeResponse, errorTransformer,
|
|
|
114
127
|
return this
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
|
|
118
|
-
|
|
130
|
+
res.writeArrayBuffer = res.write
|
|
131
|
+
res.write = (chunk, encoding, cb) => {
|
|
132
|
+
try {
|
|
133
|
+
res.streaming = true
|
|
134
|
+
res.flushHeaders()
|
|
135
|
+
let result
|
|
136
|
+
if (chunk instanceof Buffer) {
|
|
137
|
+
result = res.writeArrayBuffer(toArrayBuffer(chunk))
|
|
138
|
+
} else if (typeof chunk === 'string') {
|
|
139
|
+
result = res.writeArrayBuffer(toArrayBuffer(Buffer.from(chunk, encoding || 'utf8')))
|
|
140
|
+
} else {
|
|
141
|
+
result = res.writeArrayBuffer(toArrayBuffer(Buffer.from(JSON.stringify(chunk), encoding || 'utf8')))
|
|
142
|
+
}
|
|
143
|
+
if (typeof cb === 'function') {
|
|
144
|
+
cb()
|
|
145
|
+
}
|
|
146
|
+
return result
|
|
147
|
+
} catch (e) {
|
|
148
|
+
if (typeof cb === 'function') {
|
|
149
|
+
cb(e)
|
|
150
|
+
} else {
|
|
151
|
+
throw e
|
|
152
|
+
}
|
|
153
|
+
}
|
|
119
154
|
}
|
|
120
|
-
|
|
121
155
|
let outStream
|
|
122
156
|
res.getWritable = () => {
|
|
123
157
|
if (!outStream) {
|
|
124
158
|
res.streaming = true
|
|
125
159
|
outStream = new Writable({
|
|
126
|
-
write:
|
|
127
|
-
try {
|
|
128
|
-
res.flushHeaders()
|
|
129
|
-
const result = res.write(chunk)
|
|
130
|
-
if (typeof cb === 'function') {
|
|
131
|
-
cb()
|
|
132
|
-
}
|
|
133
|
-
return result
|
|
134
|
-
} catch (e) {
|
|
135
|
-
if (typeof cb === 'function') {
|
|
136
|
-
cb(e)
|
|
137
|
-
} else {
|
|
138
|
-
throw e
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
160
|
+
write: res.write
|
|
142
161
|
})
|
|
143
162
|
.on('finish', res.end)
|
|
144
163
|
.on('end', res.end)
|
|
@@ -152,18 +171,6 @@ export function decorateResponse (res, req, finalizeResponse, errorTransformer,
|
|
|
152
171
|
}
|
|
153
172
|
return outStream
|
|
154
173
|
}
|
|
155
|
-
res.writeArrayBuffer = res.write
|
|
156
|
-
res.write = chunk => {
|
|
157
|
-
res.streaming = true
|
|
158
|
-
res.flushHeaders()
|
|
159
|
-
if (chunk instanceof Buffer) {
|
|
160
|
-
return res.writeArrayBuffer(toArrayBuffer(chunk))
|
|
161
|
-
} else if (typeof chunk === 'string') {
|
|
162
|
-
return res.writeArrayBuffer(toArrayBuffer(Buffer.from(chunk, 'utf8')))
|
|
163
|
-
} else {
|
|
164
|
-
return res.writeArrayBuffer(toArrayBuffer(Buffer.from(JSON.stringify(chunk), 'utf8')))
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
174
|
|
|
168
175
|
const uwsEnd = res.end
|
|
169
176
|
res.ended = false
|
package/src/handler.mjs
CHANGED
|
@@ -11,14 +11,18 @@ const { Readable } = stream
|
|
|
11
11
|
* @param url The url being requested
|
|
12
12
|
* @param res The uws response object
|
|
13
13
|
* @param req The uws request object
|
|
14
|
-
* @param
|
|
14
|
+
* @param bodyPromise The request body promise
|
|
15
15
|
* @param handler The handler function for the route
|
|
16
16
|
* @param middleware The middleware that applies to this request
|
|
17
17
|
* @param errorTransformer An errorTransformer to convert error objects into response data
|
|
18
18
|
*/
|
|
19
|
-
const executeHandler = async (url, res, req,
|
|
19
|
+
const executeHandler = async (url, res, req, bodyPromise, handler, middleware, errorTransformer) => {
|
|
20
20
|
try {
|
|
21
|
-
|
|
21
|
+
bodyPromise = bodyPromise.then(bodyContent => {
|
|
22
|
+
if (bodyContent instanceof Readable) return bodyContent
|
|
23
|
+
if (res.writableEnded) return
|
|
24
|
+
return deserializeBody(bodyContent, req.headers['content-type'], res.acceptsDefault)
|
|
25
|
+
})
|
|
22
26
|
} catch (e) {
|
|
23
27
|
log.error('Failed to parse request.', e)
|
|
24
28
|
end(res, 400, handler.statusCodeOverride)
|
|
@@ -26,7 +30,7 @@ const executeHandler = async (url, res, req, body, handler, middleware, errorTra
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
try {
|
|
29
|
-
const handled = await handler({ url,
|
|
33
|
+
const handled = await handler({ url, bodyPromise, headers: req.headers, req, res })
|
|
30
34
|
finalizeResponse(req, res, handled, handler.statusCodeOverride)
|
|
31
35
|
} catch (e) {
|
|
32
36
|
const refId = uuid()
|
|
@@ -103,15 +107,10 @@ const pipeResponse = (res, readStream, errorTransformer) => {
|
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
const doSerializeBody = (body, res) => {
|
|
106
|
-
|
|
107
|
-
if (typeof body === 'string') {
|
|
108
|
-
if (!contentType) {
|
|
109
|
-
res.headers['content-type'] = 'text/plain'
|
|
110
|
-
}
|
|
111
|
-
return body
|
|
112
|
-
} else if (body instanceof Readable) {
|
|
110
|
+
if (!body || typeof body === 'string' || body instanceof Readable) {
|
|
113
111
|
return body
|
|
114
112
|
}
|
|
113
|
+
const contentType = res.getHeader('content-type')
|
|
115
114
|
const serialized = serializeBody(body, contentType, res.acceptsDefault)
|
|
116
115
|
|
|
117
116
|
if (serialized?.contentType && !contentType) {
|
|
@@ -139,17 +138,12 @@ async function executeMiddleware (middleware, req, res, errorTransformer, refId,
|
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
let currentDate = new Date().toUTCString()
|
|
143
|
-
setInterval(() => { currentDate = new Date().toUTCString() }, 1000)
|
|
144
|
-
|
|
145
141
|
const handleRequest = async (req, res, handler, middleware, errorTransformer) => {
|
|
146
|
-
res.headers.date = currentDate
|
|
147
|
-
|
|
148
142
|
try {
|
|
149
143
|
let reqBody
|
|
150
144
|
if (!handler.streamRequestBody) {
|
|
151
145
|
let buffer
|
|
152
|
-
reqBody =
|
|
146
|
+
reqBody = new Promise(
|
|
153
147
|
resolve =>
|
|
154
148
|
res.onData(async (data, isLast) => {
|
|
155
149
|
if (isLast) {
|
|
@@ -160,18 +154,19 @@ const handleRequest = async (req, res, handler, middleware, errorTransformer) =>
|
|
|
160
154
|
})
|
|
161
155
|
)
|
|
162
156
|
} else {
|
|
163
|
-
|
|
157
|
+
const readable = new Readable({
|
|
164
158
|
read: () => {
|
|
165
159
|
}
|
|
166
160
|
})
|
|
167
161
|
res.onData(async (data, isLast) => {
|
|
168
162
|
if (data.byteLength === 0 && !isLast) return
|
|
169
163
|
// data must be copied so it isn't lost
|
|
170
|
-
|
|
164
|
+
readable.push(Buffer.concat([Buffer.from(data)]))
|
|
171
165
|
if (isLast) {
|
|
172
|
-
|
|
166
|
+
readable.push(null)
|
|
173
167
|
}
|
|
174
168
|
})
|
|
169
|
+
reqBody = Promise.resolve(readable)
|
|
175
170
|
}
|
|
176
171
|
await executeMiddleware(middleware, req, res, errorTransformer)
|
|
177
172
|
if (!res.writableEnded && !res.ended) {
|
|
@@ -187,6 +182,9 @@ const handleRequest = async (req, res, handler, middleware, errorTransformer) =>
|
|
|
187
182
|
|
|
188
183
|
export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD', 'CONNECT', 'TRACE']
|
|
189
184
|
|
|
185
|
+
let currentDate = new Date().toISOString()
|
|
186
|
+
setInterval(() => { currentDate = new Date().toISOString() }, 1000)
|
|
187
|
+
|
|
190
188
|
export const createHandler = (handler, middleware, pathParameters, config) => function (res, req) {
|
|
191
189
|
try {
|
|
192
190
|
req = decorateRequest(req, pathParameters, res, config)
|
|
@@ -195,10 +193,21 @@ export const createHandler = (handler, middleware, pathParameters, config) => fu
|
|
|
195
193
|
if (config.logAccess) {
|
|
196
194
|
res.onEnd = writeAccess(req, res)
|
|
197
195
|
}
|
|
196
|
+
|
|
197
|
+
if (config.writeDateHeader) {
|
|
198
|
+
res.headers.date = currentDate
|
|
199
|
+
}
|
|
200
|
+
|
|
198
201
|
handleRequest(req, res, handler, middleware, config.errorTransformer)
|
|
199
|
-
.catch(e =>
|
|
202
|
+
.catch(e => {
|
|
203
|
+
log.error('Failed handling request', e)
|
|
204
|
+
res.statusCode = 500
|
|
205
|
+
res.end()
|
|
206
|
+
})
|
|
200
207
|
} catch (e) {
|
|
201
208
|
log.error('Failed handling request', e)
|
|
209
|
+
res.statusCode = 500
|
|
210
|
+
res.end()
|
|
202
211
|
}
|
|
203
212
|
}
|
|
204
213
|
|
package/src/middleware.mjs
CHANGED
|
@@ -37,9 +37,7 @@ export const cloneMiddleware = (middleware) => {
|
|
|
37
37
|
* @param middleware
|
|
38
38
|
*/
|
|
39
39
|
export const validateMiddleware = (middleware) => {
|
|
40
|
-
if (Array.isArray(middleware)) {
|
|
41
|
-
validateMiddlewareArray(middleware)
|
|
42
|
-
} else if (typeof middleware === 'object') {
|
|
40
|
+
if (!Array.isArray(middleware) && typeof middleware === 'object') {
|
|
43
41
|
for (const method in middleware) {
|
|
44
42
|
// ensure methods are always available as uppercase
|
|
45
43
|
const upMethod = method.toUpperCase()
|
|
@@ -47,7 +45,7 @@ export const validateMiddleware = (middleware) => {
|
|
|
47
45
|
validateMiddlewareArray(middleware[upMethod])
|
|
48
46
|
}
|
|
49
47
|
} else {
|
|
50
|
-
|
|
48
|
+
validateMiddlewareArray(middleware)
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
|
package/src/routes.mjs
CHANGED
|
@@ -27,12 +27,16 @@ const getPathPart = name => {
|
|
|
27
27
|
const filterTestFiles = config => f => (!f.name.endsWith('.test.js') && !f.name.endsWith('.test.mjs')) || config.allowTestFileRoutes
|
|
28
28
|
const filterIgnoredFiles = config => f => !config.ignoreFilesMatching.filter(p => p).find(pattern => f.name.match(pattern))
|
|
29
29
|
const ignoreHandlerFields = { middleware: true, streamRequestBody: true }
|
|
30
|
+
|
|
31
|
+
const isRouteFile = name => name.endsWith('.rt.js') || name.endsWith('.rt.mjs') || name.endsWith('.rt.cjs')
|
|
32
|
+
const isMiddlewareFile = name => name.endsWith('.mw.js') || name.endsWith('.mw.mjs') || name.endsWith('.mw.cjs')
|
|
33
|
+
|
|
30
34
|
const doFindRoutes = async (config, currentFile, filePath, urlPath, pathParameters, inheritedMiddleware) => {
|
|
31
35
|
const routes = []
|
|
32
36
|
const name = currentFile.name
|
|
33
37
|
if (currentFile.isDirectory()) {
|
|
34
38
|
routes.push(...(await findRoutesInDir(name, filePath, urlPath, inheritedMiddleware, pathParameters, config)))
|
|
35
|
-
} else if (!config.staticMode && (name
|
|
39
|
+
} else if (!config.staticMode && isRouteFile(name)) {
|
|
36
40
|
routes.push(await buildJSHandlerRoute(name, filePath, urlPath, inheritedMiddleware, pathParameters))
|
|
37
41
|
} else {
|
|
38
42
|
routes.push(...buildStaticRoutes(name, filePath, urlPath, inheritedMiddleware, pathParameters, config))
|
|
@@ -47,6 +51,14 @@ const importModules = async (config, dirPath, files) => Promise.all(
|
|
|
47
51
|
.map(f => path.join(dirPath, f.name))
|
|
48
52
|
.map(mwPath => import(`file://${mwPath}`)
|
|
49
53
|
.then(module => ({ module, mwPath }))
|
|
54
|
+
.catch(e => {
|
|
55
|
+
// Hack to workaround https://github.com/nodejs/modules/issues/471
|
|
56
|
+
if (e instanceof SyntaxError) {
|
|
57
|
+
const newError = new SyntaxError(`${e.message}. In file: ${mwPath}`)
|
|
58
|
+
newError.stack = e.stack
|
|
59
|
+
throw newError
|
|
60
|
+
}
|
|
61
|
+
})
|
|
50
62
|
))
|
|
51
63
|
|
|
52
64
|
const findRoutesInDir = async (name, filePath, urlPath, inheritedMiddleware, pathParameters, config) => {
|
|
@@ -55,9 +67,7 @@ const findRoutesInDir = async (name, filePath, urlPath, inheritedMiddleware, pat
|
|
|
55
67
|
}
|
|
56
68
|
const files = await readdir(filePath, { withFileTypes: true })
|
|
57
69
|
|
|
58
|
-
const middlewareModules = await importModules(config, filePath,
|
|
59
|
-
files.filter(f => f.name.endsWith('.mw.js') || f.name.endsWith('.mw.mjs'))
|
|
60
|
-
)
|
|
70
|
+
const middlewareModules = await importModules(config, filePath, files.filter(f => isMiddlewareFile(f.name)))
|
|
61
71
|
const dirMiddleware = middlewareModules.map(({ module, mwPath }) => {
|
|
62
72
|
const middleware = module.middleware || module.default?.middleware
|
|
63
73
|
if (!middleware) {
|
|
@@ -73,7 +83,7 @@ const findRoutesInDir = async (name, filePath, urlPath, inheritedMiddleware, pat
|
|
|
73
83
|
.reduce((result, incoming) => mergeMiddleware(incoming, result), inheritedMiddleware)
|
|
74
84
|
|
|
75
85
|
return Promise.all(files
|
|
76
|
-
.filter(f => !
|
|
86
|
+
.filter(f => !isMiddlewareFile(f.name))
|
|
77
87
|
.filter(filterTestFiles(config))
|
|
78
88
|
.filter(filterIgnoredFiles(config))
|
|
79
89
|
.map(
|
|
@@ -90,7 +100,7 @@ const findRoutesInDir = async (name, filePath, urlPath, inheritedMiddleware, pat
|
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
const buildJSHandlerRoute = async (name, filePath, urlPath, inheritedMiddleware, pathParameters) => {
|
|
93
|
-
if (name.endsWith('.mjs')) {
|
|
103
|
+
if (name.endsWith('.mjs') || name.endsWith('.cjs')) {
|
|
94
104
|
name = name.substr(0, name.length - '.rt.mjs'.length)
|
|
95
105
|
} else {
|
|
96
106
|
name = name.substr(0, name.length - '.rt.js'.length)
|
|
@@ -104,10 +114,25 @@ const buildJSHandlerRoute = async (name, filePath, urlPath, inheritedMiddleware,
|
|
|
104
114
|
filePath,
|
|
105
115
|
handlers: {}
|
|
106
116
|
}
|
|
107
|
-
|
|
117
|
+
let module
|
|
118
|
+
try {
|
|
119
|
+
module = await import(`file://${filePath}`)
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// Hack to workaround https://github.com/nodejs/modules/issues/471
|
|
122
|
+
if (e instanceof SyntaxError) {
|
|
123
|
+
const newError = new SyntaxError(`${e.message}. In file: ${filePath}`)
|
|
124
|
+
newError.stack = e.stack
|
|
125
|
+
throw newError
|
|
126
|
+
}
|
|
127
|
+
}
|
|
108
128
|
const handlers = module.default
|
|
109
|
-
|
|
110
|
-
|
|
129
|
+
try {
|
|
130
|
+
route.middleware = mergeMiddleware(handlers.middleware || [], inheritedMiddleware)
|
|
131
|
+
} catch (e) {
|
|
132
|
+
const err = new Error(`Failed to load middleware for route: ${filePath}`)
|
|
133
|
+
err.stack += `\nCaused By: ${e.stack}`
|
|
134
|
+
throw err
|
|
135
|
+
}
|
|
111
136
|
for (const method of Object.keys(handlers).filter(k => !ignoreHandlerFields[k])) {
|
|
112
137
|
if (HTTP_METHODS.indexOf(method) === -1) {
|
|
113
138
|
throw new Error(`Method: ${method} in file ${filePath} is not a valid http method. It must be one of: ${HTTP_METHODS}. Method names must be all uppercase.`)
|
package/src/server.mjs
CHANGED
|
@@ -21,14 +21,14 @@ const appMethods = {
|
|
|
21
21
|
CONNECT: 'connect',
|
|
22
22
|
TRACE: 'trace'
|
|
23
23
|
}
|
|
24
|
-
const optionsHandler = (config, methods) => {
|
|
24
|
+
const optionsHandler = (config, middleware, methods) => {
|
|
25
25
|
return createHandler(() => ({
|
|
26
26
|
headers: {
|
|
27
27
|
allow: methods
|
|
28
28
|
},
|
|
29
29
|
statusCode: 204
|
|
30
30
|
}),
|
|
31
|
-
|
|
31
|
+
middleware,
|
|
32
32
|
[],
|
|
33
33
|
config
|
|
34
34
|
)
|
|
@@ -102,7 +102,7 @@ export async function startServer (config) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
if (!route.handlers.OPTIONS) {
|
|
105
|
-
app.options(route.urlPath, optionsHandler(config, Object.keys(route.handlers).join(', ')))
|
|
105
|
+
app.options(route.urlPath, optionsHandler(config, route.middleware, Object.keys(route.handlers).join(', ')))
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
package/src/serverConfig.mjs
CHANGED
|
@@ -27,11 +27,23 @@ export async function initConfig (userConfig) {
|
|
|
27
27
|
const config = Object.assign({}, userConfig)
|
|
28
28
|
|
|
29
29
|
if (!('decodePathParameters' in config)) {
|
|
30
|
-
config.decodePathParameters =
|
|
30
|
+
config.decodePathParameters = false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!('decodeQueryParams' in config)) {
|
|
34
|
+
config.decodeQueryParams = false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!('extendIncomingMessage' in config)) {
|
|
38
|
+
config.extendIncomingMessage = false
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
if (!('parseCookie' in config)) {
|
|
34
|
-
config.parseCookie =
|
|
42
|
+
config.parseCookie = false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!('writeDateHeader' in config)) {
|
|
46
|
+
config.writeDateHeader = false
|
|
35
47
|
}
|
|
36
48
|
|
|
37
49
|
config.acceptsDefault = config.acceptsDefault || defaultHeaders.acceptsDefault
|
|
@@ -55,7 +67,7 @@ export async function initConfig (userConfig) {
|
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
if (!('logAccess' in config)) {
|
|
58
|
-
config.logAccess =
|
|
70
|
+
config.logAccess = false
|
|
59
71
|
}
|
|
60
72
|
if ('logLevel' in config) {
|
|
61
73
|
log.setLogLevel(config.logLevel)
|