-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.mjs
112 lines (83 loc) · 2.16 KB
/
api.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
import _debug from './debug.mjs'
import isNil from './is-nil.mjs'
const debug = _debug.extend('replacer')
const ref = Symbol('ref')
const call = Symbol('call')
const awaitSymbol = Symbol('await')
/**
* Create stubs to be used to build the program.
*
* @param {string[]} schema - Names of stubs.
* @param {Function} call - Function to call when awaited.
*
* @return {Object} An object containing the named stubs.
*/
export default function api (schema, sendFn) {
return schema.reduce((api, name) => {
const fn = (ref, ...args) => {
const returned = (...args) =>
fn(() => returned, ...args)
returned.toJSON = () => ({
[call]: call,
ref: ref(),
args
})
returned.then = (resolve) => resolve(send(sendFn, {
[awaitSymbol]: awaitSymbol,
ref: returned
}))
return returned
}
const bound = fn.bind(null, () => bound)
bound.toJSON = () => ({ [ref]: ref, name })
bound.then = (resolve) => resolve(send(sendFn, {
[awaitSymbol]: awaitSymbol,
ref: bound
}))
api[name] = bound
return api
}, {})
}
const send = (sendFn, program) => {
return sendFn(JSON.stringify(program, replacer))
}
const replaced = Symbol('api')
const keywords = [
'ref',
'call',
'quote',
'await'
]
const isKeyword = v => keywords.includes(v)
function replacer (key, value) {
debug(key, value)
if (isNil(value)) {
return value
}
if (value[ref]) {
const result = ['ref', value.name]
result[replaced] = replaced
return result
}
if (value[call]) {
const result = ['call', value.ref, value.args]
result[replaced] = replaced
return result
}
if (value[awaitSymbol]) {
const result = ['await', value.ref]
result[replaced] = replaced
return result
}
// Quote only the reserved string and not the complete array. Quoted values
// will be unquoted on parse. A quoted quote also.
if (!value[replaced] && Array.isArray(value)) {
const [operator, ...rest] = value
if (isKeyword(operator)) {
const quoted = ['quote', operator]
quoted[replaced] = replaced
return [quoted, ...rest]
}
}
return value
}