-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.mjs
115 lines (83 loc) · 2.42 KB
/
server.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { exec } from './lisp.mjs'
import _debug from './debug.mjs'
import { FurverExpressionError } from './error.mjs'
import curry from './curry.mjs'
const isNotNil = x => x != null
const debug = _debug.extend('server')
const debugError = debug.extend('error')
const furverSymbol = Symbol('server')
const withRequest = method => function (...args) {
const request = this[furverSymbol].request
return method.call(this, request, ...args)
}
const withResponse = method => function (...args) {
const response = this[furverSymbol].response
return method.call(this, response, ...args)
}
const withConfig = curry((config, api) => {
debug('Configured with', config)
api[furverSymbol] = config
return api
})
const array = (...args) => args
function server (api) {
debug('API config: ', Boolean(api[furverSymbol]))
api.array = api.array || array
const {
onRequest,
onResponse,
onError
} = api[furverSymbol] || {}
deepFreeze(api)
debug('API', api)
return async function onFurverRequest (request, response, program) {
if (isNotNil(onRequest)) {
request.body = program
await onRequest(request, response)
}
try {
debug('Evaluate', program)
// Create a copy of env to reduce the chance of mutations.
const requestEnv = { ...api }
requestEnv[furverSymbol] = { request, response }
const result = await exec(requestEnv, program)
debug('Result', result)
if (isNotNil(onResponse)) {
return await onResponse(request, response, result)
}
return result
} catch (error) {
// Add some sane defaults for the status codes.
if (error instanceof FurverExpressionError) {
error.status = 404
}
debugError(error)
if (isNotNil(onError)) {
await onError(request, response, error)
throw error
}
throw error
}
}
}
function deepFreeze (object, name = '', frozen = new Set()) {
if (isPrimitive(object) || frozen.has(object)) {
return object
}
try {
Object.freeze(object)
debug('Froze', name)
} catch (error) {
debugError('Cannot freeze', object)
}
frozen.add(object)
for (const key in object) {
deepFreeze(object[key], key, frozen)
}
return object
}
function isPrimitive (value) {
return (typeof value !== 'object' && typeof value !== 'function') || value === null
}
export default server
export { server, withRequest, withResponse, withConfig }