sendscript 2.3.8 → 2.4.0
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/CHANGELOG.md +14 -0
- package/README.md +41 -11
- package/README.mz +21 -5
- package/example/typescript/docs/functions/add.md +1 -1
- package/example/typescript/docs/functions/square.md +1 -1
- package/index.test.mjs +45 -0
- package/package.json +1 -1
- package/parse.mjs +73 -55
- package/references.mjs +0 -5
- package/stringify.mjs +98 -63
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
#### [v2.4.0](https://github.com/bas080/sendscript/compare/v2.3.9...v2.4.0)
|
|
8
|
+
|
|
9
|
+
- Update tap to 4.5.4 [`5249a85`](https://github.com/bas080/sendscript/commit/5249a85a08b10485f0d64058fd2c8bcaae84edda)
|
|
10
|
+
- Add fn and args support [`6110654`](https://github.com/bas080/sendscript/commit/6110654fa039d17cd9ba0e15f749756b615b5d09)
|
|
11
|
+
- Move transport agnostic section to socket example [`3a92257`](https://github.com/bas080/sendscript/commit/3a9225787df16a0fd83293bfa14d91b74a46d070)
|
|
12
|
+
|
|
13
|
+
#### [v2.3.9](https://github.com/bas080/sendscript/compare/v2.3.8...v2.3.9)
|
|
14
|
+
|
|
15
|
+
> 14 April 2026
|
|
16
|
+
|
|
17
|
+
- Remove schema description from reference and schema shape [`7126829`](https://github.com/bas080/sendscript/commit/7126829b5968588d7c9377ee643b88465f3fe16e)
|
|
18
|
+
|
|
7
19
|
#### [v2.3.8](https://github.com/bas080/sendscript/compare/v2.3.7...v2.3.8)
|
|
8
20
|
|
|
21
|
+
> 14 April 2026
|
|
22
|
+
|
|
9
23
|
- Move typedoc dep to dev deps [`a084b1d`](https://github.com/bas080/sendscript/commit/a084b1dad2f76ceb3b79fac390210f1ccfa368f8)
|
|
10
24
|
|
|
11
25
|
#### [v2.3.7](https://github.com/bas080/sendscript/compare/v2.3.6...v2.3.7)
|
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ Write JS code that you can run on servers, browsers or other clients.
|
|
|
25
25
|
* [Server](#server)
|
|
26
26
|
* [Client](#client)
|
|
27
27
|
- [Repl](#repl)
|
|
28
|
+
- [Functions](#functions)
|
|
28
29
|
- [Promises](#promises)
|
|
29
30
|
* [.then / .catch](#then--catch)
|
|
30
31
|
* [await](#await)
|
|
@@ -114,17 +115,14 @@ core or around it. Things like supporting more complex (de)serializers, errors
|
|
|
114
115
|
and maybe mixing client functions with sendscript programs. Contact me if I have
|
|
115
116
|
piqued your interest.
|
|
116
117
|
|
|
117
|
-
***
|
|
118
|
-
|
|
119
|
-
SendScript leaves it up to you to choose HTTP, web-sockets or any other method
|
|
120
|
-
of communication between servers and clients that best fits your needs.
|
|
121
|
-
|
|
122
118
|
## Reference
|
|
123
119
|
|
|
124
120
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
|
|
125
121
|
|
|
126
122
|
### defaultLeafParse
|
|
127
123
|
|
|
124
|
+
[parse.mjs:49-49](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/parse.mjs#L49-L49 "Source code on GitHub")
|
|
125
|
+
|
|
128
126
|
Default deserializer for leaf nodes.
|
|
129
127
|
|
|
130
128
|
#### Parameters
|
|
@@ -135,6 +133,8 @@ Returns **any** 
|
|
|
135
133
|
|
|
136
134
|
### Parse
|
|
137
135
|
|
|
136
|
+
[parse.mjs:68-215](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/parse.mjs#L68-L215 "Source code on GitHub")
|
|
137
|
+
|
|
138
138
|
#### Parameters
|
|
139
139
|
|
|
140
140
|
* `schema` **[Schema](#schema)** 
|
|
@@ -145,6 +145,8 @@ Returns **[parse](#parse)** 
|
|
|
145
145
|
|
|
146
146
|
### parse
|
|
147
147
|
|
|
148
|
+
[parse.mjs:78-214](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/parse.mjs#L78-L214 "Source code on GitHub")
|
|
149
|
+
|
|
148
150
|
Parses and executes a serialized program.
|
|
149
151
|
|
|
150
152
|
#### Parameters
|
|
@@ -155,17 +157,14 @@ Returns **(any | [Promise](https://developer.mozilla.org/docs/Web/JavaScript/Ref
|
|
|
155
157
|
|
|
156
158
|
### References
|
|
157
159
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Schema supports:
|
|
160
|
+
[references.mjs:109-127](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/references.mjs#L109-L127 "Source code on GitHub")
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
* \[name, children] => namespace with nested schema
|
|
162
|
+
Builds a nested API structure from a schema definition.
|
|
164
163
|
|
|
165
164
|
#### Parameters
|
|
166
165
|
|
|
167
166
|
* `schema` **[Schema](#schema)** 
|
|
168
|
-
* `parentPath`
|
|
167
|
+
* `parentPath` (optional, default `[]`)
|
|
169
168
|
|
|
170
169
|
<!---->
|
|
171
170
|
|
|
@@ -175,12 +174,16 @@ Returns **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/G
|
|
|
175
174
|
|
|
176
175
|
### SchemaNode
|
|
177
176
|
|
|
177
|
+
[schema.mjs:1-19](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/schema.mjs#L1-L6 "Source code on GitHub")
|
|
178
|
+
|
|
178
179
|
A single schema node.
|
|
179
180
|
|
|
180
181
|
Type: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [Schema](#schema)])
|
|
181
182
|
|
|
182
183
|
### Schema
|
|
183
184
|
|
|
185
|
+
[schema.mjs:1-19](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/schema.mjs#L8-L18 "Source code on GitHub")
|
|
186
|
+
|
|
184
187
|
A schema defines the structure of the runtime API tree.
|
|
185
188
|
|
|
186
189
|
* string → leaf node
|
|
@@ -192,6 +195,8 @@ Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global
|
|
|
192
195
|
|
|
193
196
|
### defaultLeafStringify
|
|
194
197
|
|
|
198
|
+
[stringify.mjs:37-47](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/stringify.mjs#L37-L47 "Source code on GitHub")
|
|
199
|
+
|
|
195
200
|
Default strict serializer for leaf values.
|
|
196
201
|
|
|
197
202
|
Rejects non-JSON-safe values.
|
|
@@ -204,6 +209,8 @@ Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/G
|
|
|
204
209
|
|
|
205
210
|
### Stringify
|
|
206
211
|
|
|
212
|
+
[stringify.mjs:56-157](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/stringify.mjs#L56-L157 "Source code on GitHub")
|
|
213
|
+
|
|
207
214
|
Creates a stringify function for SendScript AST structures.
|
|
208
215
|
|
|
209
216
|
#### Parameters
|
|
@@ -214,6 +221,8 @@ Returns **[stringify](#stringify)** 
|
|
|
214
221
|
|
|
215
222
|
### stringify
|
|
216
223
|
|
|
224
|
+
[stringify.mjs:64-156](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/stringify.mjs#L64-L156 "Source code on GitHub")
|
|
225
|
+
|
|
217
226
|
Serializes a program into a JSON string representation.
|
|
218
227
|
|
|
219
228
|
#### Parameters
|
|
@@ -224,6 +233,9 @@ Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/G
|
|
|
224
233
|
|
|
225
234
|
## Socket example
|
|
226
235
|
|
|
236
|
+
SendScript leaves it up to you to choose HTTP, web-sockets or any other method
|
|
237
|
+
of communication between servers and clients that best fits your needs.
|
|
238
|
+
|
|
227
239
|
For this example we'll use [socket.io][socket.io].
|
|
228
240
|
|
|
229
241
|
### Module
|
|
@@ -330,6 +342,24 @@ run it by simply typing `sendscript` in their console.
|
|
|
330
342
|
> Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for
|
|
331
343
|
> printingonly sendscript logs.
|
|
332
344
|
|
|
345
|
+
## Functions
|
|
346
|
+
|
|
347
|
+
SendScript as of `v2.4` supports functions to do basic templating. It does not
|
|
348
|
+
support async functions and will throw an error when you define one.
|
|
349
|
+
|
|
350
|
+
Under the hood the function is called when it is being parsed. Make sure you
|
|
351
|
+
understand what you are doing when mixing client and server functions.
|
|
352
|
+
|
|
353
|
+
```js
|
|
354
|
+
map((a) => add(a, 1))([1, 2, 3]))
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
It also supports nested functions.
|
|
358
|
+
|
|
359
|
+
```js
|
|
360
|
+
map(call)(map((a) => () => add(a, 1))([1, 2, 3]))
|
|
361
|
+
```
|
|
362
|
+
|
|
333
363
|
## Promises
|
|
334
364
|
|
|
335
365
|
### .then / .catch
|
package/README.mz
CHANGED
|
@@ -74,17 +74,15 @@ core or around it. Things like supporting more complex (de)serializers, errors
|
|
|
74
74
|
and maybe mixing client functions with sendscript programs. Contact me if I have
|
|
75
75
|
piqued your interest.
|
|
76
76
|
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
SendScript leaves it up to you to choose HTTP, web-sockets or any other method
|
|
80
|
-
of communication between servers and clients that best fits your needs.
|
|
81
|
-
|
|
82
77
|
## Reference
|
|
83
78
|
|
|
84
79
|
<!-- Reference -->
|
|
85
80
|
|
|
86
81
|
## Socket example
|
|
87
82
|
|
|
83
|
+
SendScript leaves it up to you to choose HTTP, web-sockets or any other method
|
|
84
|
+
of communication between servers and clients that best fits your needs.
|
|
85
|
+
|
|
88
86
|
For this example we'll use [socket.io][socket.io].
|
|
89
87
|
|
|
90
88
|
### Module
|
|
@@ -189,6 +187,24 @@ run it by simply typing `sendscript` in their console.
|
|
|
189
187
|
> Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for
|
|
190
188
|
> printingonly sendscript logs.
|
|
191
189
|
|
|
190
|
+
## Functions
|
|
191
|
+
|
|
192
|
+
SendScript as of `v2.4` supports functions to do basic templating. It does not
|
|
193
|
+
support async functions and will throw an error when you define one.
|
|
194
|
+
|
|
195
|
+
Under the hood the function is called when it is being parsed. Make sure you
|
|
196
|
+
understand what you are doing when mixing client and server functions.
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
map((a) => add(a, 1))([1, 2, 3]))
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
It also supports nested functions.
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
map(call)(map((a) => () => add(a, 1))([1, 2, 3]))
|
|
206
|
+
```
|
|
207
|
+
|
|
192
208
|
## Promises
|
|
193
209
|
|
|
194
210
|
### .then / .catch
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
> **add**(`a`, `b`): `number`
|
|
10
10
|
|
|
11
|
-
Defined in: [math.ts:1](https://github.com/bas080/sendscript/blob/
|
|
11
|
+
Defined in: [math.ts:1](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/example/typescript/math.ts#L1)
|
|
12
12
|
|
|
13
13
|
## Parameters
|
|
14
14
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
> **square**(`a`): `number`
|
|
10
10
|
|
|
11
|
-
Defined in: [math.ts:2](https://github.com/bas080/sendscript/blob/
|
|
11
|
+
Defined in: [math.ts:2](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/example/typescript/math.ts#L2)
|
|
12
12
|
|
|
13
13
|
## Parameters
|
|
14
14
|
|
package/index.test.mjs
CHANGED
|
@@ -34,6 +34,7 @@ const myModuleOrig = {
|
|
|
34
34
|
hello: 'world',
|
|
35
35
|
noop: () => {},
|
|
36
36
|
resolve: (x) => Promise.resolve(x),
|
|
37
|
+
call: (fn, ...args) => fn(...args),
|
|
37
38
|
reject: x => Promise.reject(x),
|
|
38
39
|
asyncFn: async () => 'my-async-function',
|
|
39
40
|
instanceOf: (x, t) => x instanceof t,
|
|
@@ -85,15 +86,59 @@ test('should evaluate basic expressions correctly', async (t) => {
|
|
|
85
86
|
always,
|
|
86
87
|
multiply3,
|
|
87
88
|
sayHello,
|
|
89
|
+
call,
|
|
88
90
|
nested
|
|
89
91
|
} = api
|
|
90
92
|
|
|
93
|
+
t.test('throws when async fn is provided', async t => {
|
|
94
|
+
t.throws(() => run(identity(async () => {})))
|
|
95
|
+
t.end()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
t.test('supports fns with arguments', async t => {
|
|
99
|
+
t.same(run(map((a) => add(a, 1))([1, 2, 3])), [2, 3, 4])
|
|
100
|
+
t.end()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
t.test('nested fns with arguments', async t => {
|
|
104
|
+
t.same(run(
|
|
105
|
+
map(call)(
|
|
106
|
+
map((a) => () => add(a, 1))([1, 2, 3])
|
|
107
|
+
)), [2, 3, 4])
|
|
108
|
+
t.end()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
t.test('supports fns without arguments', async t => {
|
|
112
|
+
t.equal(run(() => add(1, 2))(), 3)
|
|
113
|
+
t.equal(run(always(() => 42)()()), 42)
|
|
114
|
+
t.equal(run(always(x => add(x, 42))()(2)), 44)
|
|
115
|
+
t.end()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
t.test('supports binding reference functions on client', async t => {
|
|
119
|
+
const plusOne = add.bind(null, 1)
|
|
120
|
+
|
|
121
|
+
t.equal(run(plusOne(1)), 2)
|
|
122
|
+
t.equal(await run(resolve(1).then(plusOne)), 2)
|
|
123
|
+
|
|
124
|
+
// Even this works.
|
|
125
|
+
t.equal(await run(resolve(1).then(v => plusOne(v))), 2)
|
|
126
|
+
|
|
127
|
+
t.end()
|
|
128
|
+
})
|
|
129
|
+
|
|
91
130
|
t.test('supports .then', async t => {
|
|
131
|
+
const something = 30
|
|
132
|
+
|
|
92
133
|
t.rejects(run(reject('error')))
|
|
93
134
|
|
|
94
135
|
t.equal(await run(reject('error').catch(sayHello)), 'hello error')
|
|
95
136
|
t.equal(await run(reject('error').then(null, sayHello)), 'hello error')
|
|
96
137
|
|
|
138
|
+
// .then with fns just works and I do not know why.
|
|
139
|
+
t.equal(await run(resolve(32).then(() => add(something, 1))), 31)
|
|
140
|
+
t.equal(await run(resolve(32).then(value => add(something, value))), 62)
|
|
141
|
+
|
|
97
142
|
t.equal(await run(resolve('value').then(sayHello)), 'hello value')
|
|
98
143
|
t.equal(await run(resolve('value').then(sayHello, noop)), 'hello value')
|
|
99
144
|
|
package/package.json
CHANGED
package/parse.mjs
CHANGED
|
@@ -39,58 +39,6 @@ const spy = (type, fn) => (...args) => {
|
|
|
39
39
|
return value
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const evaluate = spy('eval', (value, awaits = []) => {
|
|
43
|
-
if (value === undefinedSentinel) return undefined
|
|
44
|
-
|
|
45
|
-
if (Array.isArray(value)) {
|
|
46
|
-
const [operator, ...rest] = value
|
|
47
|
-
|
|
48
|
-
if (operator === 'await') {
|
|
49
|
-
const [index] = rest
|
|
50
|
-
return awaits[index]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (operator === 'then') {
|
|
54
|
-
const [v, onResolve, onReject] = rest
|
|
55
|
-
return evaluate(v, awaits).then(
|
|
56
|
-
evaluate(onResolve, awaits),
|
|
57
|
-
evaluate(onReject, awaits)
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (operator === 'call') {
|
|
62
|
-
let [fn, args] = rest
|
|
63
|
-
fn = evaluate(fn, awaits)
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < args.length; i++) {
|
|
66
|
-
args[i] = evaluate(args[i], awaits)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return fn(...args)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (operator === 'quote') {
|
|
73
|
-
const [quoted] = rest
|
|
74
|
-
return quoted
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
for (let i = 0; i < value.length; i++) {
|
|
78
|
-
value[i] = evaluate(value[i], awaits)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return value
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (isPlainObject(value)) {
|
|
85
|
-
for (const key of Object.keys(value)) {
|
|
86
|
-
value[key] = evaluate(value[key], awaits)
|
|
87
|
-
}
|
|
88
|
-
return value
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return value
|
|
92
|
-
})
|
|
93
|
-
|
|
94
42
|
/**
|
|
95
43
|
* Default deserializer for leaf nodes.
|
|
96
44
|
*
|
|
@@ -131,6 +79,76 @@ export default function Parse (schema, env, leafParse = defaultLeafParse) {
|
|
|
131
79
|
debug('program', program)
|
|
132
80
|
const awaits = []
|
|
133
81
|
|
|
82
|
+
const evaluateOuter = spy('eval', (env, value) => {
|
|
83
|
+
const evaluate = evaluateOuter.bind(null, env)
|
|
84
|
+
|
|
85
|
+
if (value === undefinedSentinel) return undefined
|
|
86
|
+
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
const [operator, ...rest] = value
|
|
89
|
+
|
|
90
|
+
if (operator === 'await') {
|
|
91
|
+
const [index] = rest
|
|
92
|
+
return awaits[index]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (operator === 'then') {
|
|
96
|
+
const [v, onResolve, onReject] = rest
|
|
97
|
+
return evaluate(v).then(
|
|
98
|
+
evaluate(onResolve),
|
|
99
|
+
evaluate(onReject)
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (operator === 'fn') {
|
|
104
|
+
const [argIds, body] = [...rest]
|
|
105
|
+
|
|
106
|
+
return (...args) => {
|
|
107
|
+
const newEnv = Object.create(env)
|
|
108
|
+
|
|
109
|
+
argIds.forEach((id, index) => {
|
|
110
|
+
newEnv[id] = args[index]
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return evaluateOuter(newEnv, body)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (operator === 'arg') {
|
|
118
|
+
const [id] = rest
|
|
119
|
+
|
|
120
|
+
return env[id]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (operator === 'call') {
|
|
124
|
+
let [fn, args] = rest
|
|
125
|
+
fn = evaluate(fn)
|
|
126
|
+
|
|
127
|
+
return fn(...args.map(evaluate))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (operator === 'quote') {
|
|
131
|
+
const [quoted] = rest
|
|
132
|
+
return quoted
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < value.length; i++) {
|
|
136
|
+
value[i] = evaluate(value[i])
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return value
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (isPlainObject(value)) {
|
|
143
|
+
for (const key of Object.keys(value)) {
|
|
144
|
+
value[key] = evaluate(value[key])
|
|
145
|
+
}
|
|
146
|
+
return value
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return value
|
|
150
|
+
})
|
|
151
|
+
|
|
134
152
|
/**
|
|
135
153
|
* JSON reviver used during parsing.
|
|
136
154
|
* Converts encoded operators into runtime structures.
|
|
@@ -184,14 +202,14 @@ export default function Parse (schema, env, leafParse = defaultLeafParse) {
|
|
|
184
202
|
|
|
185
203
|
return (async function () {
|
|
186
204
|
for (let i = 0; i < awaits.length; i++) {
|
|
187
|
-
awaits[i] = await
|
|
205
|
+
awaits[i] = await evaluateOuter({}, awaits[i])
|
|
188
206
|
}
|
|
189
207
|
|
|
190
208
|
debug('awaits(awaited)', awaits)
|
|
191
|
-
return
|
|
209
|
+
return evaluateOuter({}, parsed)
|
|
192
210
|
})()
|
|
193
211
|
}
|
|
194
212
|
|
|
195
|
-
return
|
|
213
|
+
return evaluateOuter({}, parsed)
|
|
196
214
|
}
|
|
197
215
|
}
|
package/references.mjs
CHANGED
|
@@ -100,12 +100,7 @@ function instrument (path) {
|
|
|
100
100
|
/**
|
|
101
101
|
* Builds a nested API structure from a schema definition.
|
|
102
102
|
*
|
|
103
|
-
* Schema supports:
|
|
104
|
-
* - string => leaf function node
|
|
105
|
-
* - [name, children] => namespace with nested schema
|
|
106
|
-
*
|
|
107
103
|
* @param {Schema} schema
|
|
108
|
-
* @param {Array<string>} [parentPath=[]]
|
|
109
104
|
* @returns {Object} Nested instrumented API object
|
|
110
105
|
*
|
|
111
106
|
* @throws {Error} If schema format is invalid
|
package/stringify.mjs
CHANGED
|
@@ -2,14 +2,20 @@ import Debug from './debug.mjs'
|
|
|
2
2
|
import { SendScriptSerializationError } from './error.mjs'
|
|
3
3
|
import {
|
|
4
4
|
awaitSymbol,
|
|
5
|
+
referenceSymbol,
|
|
5
6
|
then,
|
|
6
7
|
call,
|
|
7
8
|
ref
|
|
8
9
|
} from './symbol.mjs'
|
|
9
10
|
|
|
11
|
+
const asyncFunction = async function () {}
|
|
12
|
+
asyncFunction()
|
|
13
|
+
const AsyncFunction = (asyncFunction).constructor
|
|
14
|
+
|
|
10
15
|
const debug = Debug.extend('stringify')
|
|
11
16
|
|
|
12
|
-
const keywords = ['ref', 'call', 'quote', 'await', 'leaf']
|
|
17
|
+
const keywords = ['ref', 'call', 'quote', 'await', 'leaf', 'arg', 'fn']
|
|
18
|
+
const argSymbol = Symbol('arg')
|
|
13
19
|
|
|
14
20
|
const isKeyword = (v) => keywords.includes(v)
|
|
15
21
|
|
|
@@ -19,67 +25,6 @@ const isPlainObject = (value) => {
|
|
|
19
25
|
return proto === Object.prototype || proto === null
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
function transformValue (value, leafSerializer) {
|
|
23
|
-
debug(value)
|
|
24
|
-
|
|
25
|
-
if (value === null) return null
|
|
26
|
-
|
|
27
|
-
// unwrap function wrappers (instrumented nodes)
|
|
28
|
-
if (typeof value === 'function' && typeof value.toJSON === 'function') {
|
|
29
|
-
return transformValue(value.toJSON(), leafSerializer)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (value && value[ref]) {
|
|
33
|
-
return ['ref', ...value.path]
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (value && value[call]) {
|
|
37
|
-
return [
|
|
38
|
-
'call',
|
|
39
|
-
transformValue(value.ref, leafSerializer),
|
|
40
|
-
transformValue(value.args, leafSerializer)
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (value && value[awaitSymbol]) {
|
|
45
|
-
return ['await', transformValue(value.ref, leafSerializer)]
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (value && value[then]) {
|
|
49
|
-
return [
|
|
50
|
-
'then',
|
|
51
|
-
transformValue(value.ref, leafSerializer),
|
|
52
|
-
transformValue(value.resolve || null, leafSerializer),
|
|
53
|
-
transformValue(value.reject || null, leafSerializer)
|
|
54
|
-
]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (Array.isArray(value)) {
|
|
58
|
-
const [operator, ...rest] = value
|
|
59
|
-
|
|
60
|
-
if (isKeyword(operator)) {
|
|
61
|
-
return [
|
|
62
|
-
['quote', operator],
|
|
63
|
-
...rest.map((item) => transformValue(item, leafSerializer))
|
|
64
|
-
]
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return value.map((item) => transformValue(item, leafSerializer))
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (isPlainObject(value)) {
|
|
71
|
-
const result = {}
|
|
72
|
-
|
|
73
|
-
for (const key of Object.keys(value)) {
|
|
74
|
-
result[key] = transformValue(value[key], leafSerializer)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return result
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return ['leaf', leafSerializer(value)]
|
|
81
|
-
}
|
|
82
|
-
|
|
83
28
|
/**
|
|
84
29
|
* Default strict serializer for leaf values.
|
|
85
30
|
*
|
|
@@ -117,6 +62,96 @@ export default function Stringify (leafStringify = defaultLeafStringify) {
|
|
|
117
62
|
* @public
|
|
118
63
|
*/
|
|
119
64
|
return function stringify (program) {
|
|
120
|
-
|
|
65
|
+
// Reset the argId
|
|
66
|
+
let argId = 0
|
|
67
|
+
|
|
68
|
+
function transformValue (value) {
|
|
69
|
+
debug(value)
|
|
70
|
+
|
|
71
|
+
if (value === null) return null
|
|
72
|
+
|
|
73
|
+
// unwrap function wrappers (instrumented nodes)
|
|
74
|
+
if (value && value[referenceSymbol]) {
|
|
75
|
+
return transformValue(value.toJSON())
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Is a simple function. We call it to get the template.
|
|
79
|
+
if (typeof value === 'function') {
|
|
80
|
+
if (value instanceof AsyncFunction) {
|
|
81
|
+
throw new SendScriptSerializationError('Sendscript does not support async functions as of yet.')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const args = []
|
|
85
|
+
const argIds = []
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < value.length; i++) {
|
|
88
|
+
const arg = argId++
|
|
89
|
+
const placeholder = ['arg', arg]
|
|
90
|
+
|
|
91
|
+
placeholder[argSymbol] = argSymbol
|
|
92
|
+
|
|
93
|
+
args.push(placeholder)
|
|
94
|
+
argIds.push(arg)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return ['fn', argIds, transformValue(value(...args))]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (value && value[argSymbol]) {
|
|
101
|
+
return value
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (value && value[ref]) {
|
|
105
|
+
return ['ref', ...value.path]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (value && value[call]) {
|
|
109
|
+
return [
|
|
110
|
+
'call',
|
|
111
|
+
transformValue(value.ref),
|
|
112
|
+
transformValue(value.args)
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (value && value[awaitSymbol]) {
|
|
117
|
+
return ['await', transformValue(value.ref)]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (value && value[then]) {
|
|
121
|
+
return [
|
|
122
|
+
'then',
|
|
123
|
+
transformValue(value.ref),
|
|
124
|
+
transformValue(value.resolve || null),
|
|
125
|
+
transformValue(value.reject || null)
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
const [operator, ...rest] = value
|
|
131
|
+
|
|
132
|
+
if (isKeyword(operator)) {
|
|
133
|
+
return [
|
|
134
|
+
['quote', operator],
|
|
135
|
+
...rest.map((item) => transformValue(item))
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return value.map((item) => transformValue(item))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (isPlainObject(value)) {
|
|
143
|
+
const result = {}
|
|
144
|
+
|
|
145
|
+
for (const key of Object.keys(value)) {
|
|
146
|
+
result[key] = transformValue(value[key])
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return result
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return ['leaf', leafStringify(value)]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return JSON.stringify(transformValue(program))
|
|
121
156
|
}
|
|
122
157
|
}
|