sendscript 2.3.9 → 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 +8 -0
- package/README.md +40 -5
- 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/stringify.mjs +98 -63
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,16 @@ 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
|
+
|
|
7
13
|
#### [v2.3.9](https://github.com/bas080/sendscript/compare/v2.3.8...v2.3.9)
|
|
8
14
|
|
|
15
|
+
> 14 April 2026
|
|
16
|
+
|
|
9
17
|
- Remove schema description from reference and schema shape [`7126829`](https://github.com/bas080/sendscript/commit/7126829b5968588d7c9377ee643b88465f3fe16e)
|
|
10
18
|
|
|
11
19
|
#### [v2.3.8](https://github.com/bas080/sendscript/compare/v2.3.7...v2.3.8)
|
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,6 +157,8 @@ Returns **(any | [Promise](https://developer.mozilla.org/docs/Web/JavaScript/Ref
|
|
|
155
157
|
|
|
156
158
|
### References
|
|
157
159
|
|
|
160
|
+
[references.mjs:109-127](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/references.mjs#L109-L127 "Source code on GitHub")
|
|
161
|
+
|
|
158
162
|
Builds a nested API structure from a schema definition.
|
|
159
163
|
|
|
160
164
|
#### Parameters
|
|
@@ -170,12 +174,16 @@ Returns **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/G
|
|
|
170
174
|
|
|
171
175
|
### SchemaNode
|
|
172
176
|
|
|
177
|
+
[schema.mjs:1-19](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/schema.mjs#L1-L6 "Source code on GitHub")
|
|
178
|
+
|
|
173
179
|
A single schema node.
|
|
174
180
|
|
|
175
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)])
|
|
176
182
|
|
|
177
183
|
### Schema
|
|
178
184
|
|
|
185
|
+
[schema.mjs:1-19](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/schema.mjs#L8-L18 "Source code on GitHub")
|
|
186
|
+
|
|
179
187
|
A schema defines the structure of the runtime API tree.
|
|
180
188
|
|
|
181
189
|
* string → leaf node
|
|
@@ -187,6 +195,8 @@ Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global
|
|
|
187
195
|
|
|
188
196
|
### defaultLeafStringify
|
|
189
197
|
|
|
198
|
+
[stringify.mjs:37-47](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/stringify.mjs#L37-L47 "Source code on GitHub")
|
|
199
|
+
|
|
190
200
|
Default strict serializer for leaf values.
|
|
191
201
|
|
|
192
202
|
Rejects non-JSON-safe values.
|
|
@@ -199,6 +209,8 @@ Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/G
|
|
|
199
209
|
|
|
200
210
|
### Stringify
|
|
201
211
|
|
|
212
|
+
[stringify.mjs:56-157](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/stringify.mjs#L56-L157 "Source code on GitHub")
|
|
213
|
+
|
|
202
214
|
Creates a stringify function for SendScript AST structures.
|
|
203
215
|
|
|
204
216
|
#### Parameters
|
|
@@ -209,6 +221,8 @@ Returns **[stringify](#stringify)** 
|
|
|
209
221
|
|
|
210
222
|
### stringify
|
|
211
223
|
|
|
224
|
+
[stringify.mjs:64-156](https://github.com/bas080/sendscript/blob/5249a85a08b10485f0d64058fd2c8bcaae84edda/stringify.mjs#L64-L156 "Source code on GitHub")
|
|
225
|
+
|
|
212
226
|
Serializes a program into a JSON string representation.
|
|
213
227
|
|
|
214
228
|
#### Parameters
|
|
@@ -219,6 +233,9 @@ Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/G
|
|
|
219
233
|
|
|
220
234
|
## Socket example
|
|
221
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
|
+
|
|
222
239
|
For this example we'll use [socket.io][socket.io].
|
|
223
240
|
|
|
224
241
|
### Module
|
|
@@ -325,6 +342,24 @@ run it by simply typing `sendscript` in their console.
|
|
|
325
342
|
> Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for
|
|
326
343
|
> printingonly sendscript logs.
|
|
327
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
|
+
|
|
328
363
|
## Promises
|
|
329
364
|
|
|
330
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/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
|
}
|