sendscript 2.4.3 → 2.5.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/CHANGELOG.md +14 -0
- package/README.md +50 -43
- package/README.mz +39 -40
- package/package.json +1 -1
- package/references.mjs +10 -6
- package/references.test.mjs +54 -0
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.5.1](https://github.com/bas080/sendscript/compare/v2.5.0...v2.5.1)
|
|
8
|
+
|
|
9
|
+
- Update tap and typedoc-plugin-markdown to latest [`f126401`](https://github.com/bas080/sendscript/commit/f126401ac015ba7c6a21b2c89f93b416d49765fb)
|
|
10
|
+
- Express support for Palestine [`c6996c7`](https://github.com/bas080/sendscript/commit/c6996c7f440d1cd6a4d5ca9c5c9d5d856884a472)
|
|
11
|
+
|
|
12
|
+
#### [v2.5.0](https://github.com/bas080/sendscript/compare/v2.4.3...v2.5.0)
|
|
13
|
+
|
|
14
|
+
> 17 August 2026
|
|
15
|
+
|
|
16
|
+
- Clarify native await behavior in README [`a26f1b3`](https://github.com/bas080/sendscript/commit/a26f1b3c204390690eda7950c967d31ee55aa353)
|
|
17
|
+
- Allow passing onAwait callback to references make function [`8fdb1c4`](https://github.com/bas080/sendscript/commit/8fdb1c4bfa0682fabdd9d27ca075cd1e0cfaedf7)
|
|
18
|
+
|
|
7
19
|
#### [v2.4.3](https://github.com/bas080/sendscript/compare/v2.4.2...v2.4.3)
|
|
8
20
|
|
|
21
|
+
> 17 August 2026
|
|
22
|
+
|
|
9
23
|
- Improve TypeScript section with comprehensive examples and explanations [`a3c5867`](https://github.com/bas080/sendscript/commit/a3c5867f835bbcde1e802e75c80ca6f02dd4f0e8)
|
|
10
24
|
|
|
11
25
|
#### [v2.4.2](https://github.com/bas080/sendscript/compare/v2.4.1...v2.4.2)
|
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Serialize and execute composable JavaScript function calls with JSON.
|
|
|
6
6
|
[](#tests)
|
|
7
7
|
[](https://standardjs.com)
|
|
8
8
|
[](./LICENSE.txt)
|
|
9
|
+
[](https://www.islamic-relief.org.uk/giving/appeals/palestine/)
|
|
9
10
|
|
|
10
11
|
## Features
|
|
11
12
|
|
|
@@ -306,27 +307,40 @@ behavior for the sendscript DSL and parser.
|
|
|
306
307
|
|
|
307
308
|
### await
|
|
308
309
|
|
|
309
|
-
|
|
310
|
-
the
|
|
311
|
-
|
|
310
|
+
By default, `await api.someMethod(...)` still creates a SendScript await stub
|
|
311
|
+
and keeps the result serializable with `stringify()`. It does not automatically
|
|
312
|
+
send anything over the network.
|
|
312
313
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
314
|
+
```js
|
|
315
|
+
const api = references(['add'])
|
|
316
|
+
const program = api.add(1, 2)
|
|
317
|
+
|
|
318
|
+
Stringify()(program)
|
|
319
|
+
// => "[\"call\",[\"ref\",\"add\"],[1,2]]"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
If you want native `await` to cross the transport boundary, pass an `onAwait`
|
|
323
|
+
handler when creating the references:
|
|
316
324
|
|
|
317
325
|
```js
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
unread: await fetchUnreadMessages(userId),
|
|
321
|
-
emptyTrash: await emptyTrash(userId),
|
|
322
|
-
archived: await archiveMessages(selectMessages({ old: true })),
|
|
323
|
-
}
|
|
326
|
+
import Stringify from 'sendscript/stringify.mjs'
|
|
327
|
+
import references from 'sendscript/references.mjs'
|
|
324
328
|
|
|
325
|
-
const
|
|
329
|
+
const stringify = Stringify()
|
|
330
|
+
|
|
331
|
+
const api = references(['add'], (program) => {
|
|
332
|
+
return fetch('/api', {
|
|
333
|
+
method: 'POST',
|
|
334
|
+
headers: { 'content-type': 'application/json' },
|
|
335
|
+
body: stringify(program),
|
|
336
|
+
}).then((response) => response.json())
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
const result = await api.add(1, 2)
|
|
326
340
|
```
|
|
327
341
|
|
|
328
|
-
|
|
329
|
-
|
|
342
|
+
The callback receives the generated SendScript program, and you decide how it is
|
|
343
|
+
transported or executed.
|
|
330
344
|
|
|
331
345
|
## TypeScript
|
|
332
346
|
|
|
@@ -601,36 +615,21 @@ code to create the AST.
|
|
|
601
615
|
|
|
602
616
|
### Callbacks
|
|
603
617
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
as a templating tool to make sendscript programs; just like one would use
|
|
607
|
-
JavaScript with react templates. `items.map(deleteItem)` would return an array
|
|
608
|
-
of sendscript function calls which can be given to sendscript's parse.
|
|
609
|
-
|
|
610
|
-
Client functions cannot be called by sendscript functions (as of yet) since we
|
|
611
|
-
cannot serialize client functions. No work has been done to have the server send
|
|
612
|
-
back intermediate values to perform client function calls or by performing
|
|
613
|
-
smaller sendscript program payloads that are passed to the client. Very
|
|
614
|
-
interesting stuff to look into. You can achieve this now but it looks less clean
|
|
615
|
-
because you have to do `send` calls which is a bit manual.
|
|
618
|
+
SendScript supports basic callback-style usage, such as passing a function as an
|
|
619
|
+
argument or using a callback to flip arguments into the generated program.
|
|
616
620
|
|
|
617
621
|
```js
|
|
618
|
-
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
It might be interesting to allow configuration to create references that will
|
|
622
|
-
trigger a send whenever await is called. That would remove the ability to create
|
|
623
|
-
a single payload whenever using await. You can then write the above in the
|
|
624
|
-
following manner.
|
|
622
|
+
const api = references(['map', 'add'])
|
|
625
623
|
|
|
626
|
-
|
|
627
|
-
await updateUser(id, merge(await getUser(id), { ...newValues }))
|
|
624
|
+
const result = api.map((value) => api.add(value, 1))([1, 2, 3])
|
|
628
625
|
```
|
|
629
626
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
627
|
+
This is meant for composing SendScript programs, not for executing arbitrary
|
|
628
|
+
client-side logic at runtime.
|
|
629
|
+
|
|
630
|
+
> [!WARNING] Mixing client and server functions can be confusing, because the
|
|
631
|
+
> callback may be used to build a program rather than execute immediately. Keep
|
|
632
|
+
> callback logic simple and deterministic.
|
|
634
633
|
|
|
635
634
|
### Error handling
|
|
636
635
|
|
|
@@ -650,11 +649,19 @@ npm t -- report text-summary
|
|
|
650
649
|
```
|
|
651
650
|
```
|
|
652
651
|
|
|
652
|
+
> sendscript@2.5.1 test
|
|
653
|
+
> tap -R silent
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
> sendscript@2.5.1 test
|
|
657
|
+
> tap report text-summary
|
|
658
|
+
|
|
659
|
+
|
|
653
660
|
=============================== Coverage summary ===============================
|
|
654
|
-
Statements : 100% (
|
|
655
|
-
Branches : 100% (
|
|
661
|
+
Statements : 100% ( 516/516 )
|
|
662
|
+
Branches : 100% ( 157/157 )
|
|
656
663
|
Functions : 100% ( 23/23 )
|
|
657
|
-
Lines : 100% (
|
|
664
|
+
Lines : 100% ( 516/516 )
|
|
658
665
|
================================================================================
|
|
659
666
|
```
|
|
660
667
|
|
package/README.mz
CHANGED
|
@@ -6,6 +6,7 @@ Serialize and execute composable JavaScript function calls with JSON.
|
|
|
6
6
|
[](#tests)
|
|
7
7
|
[](https://standardjs.com)
|
|
8
8
|
[](./LICENSE.txt)
|
|
9
|
+
[](https://www.islamic-relief.org.uk/giving/appeals/palestine/)
|
|
9
10
|
|
|
10
11
|
## Features
|
|
11
12
|
|
|
@@ -261,27 +262,40 @@ behavior for the sendscript DSL and parser.
|
|
|
261
262
|
|
|
262
263
|
### await
|
|
263
264
|
|
|
264
|
-
|
|
265
|
-
the
|
|
266
|
-
|
|
265
|
+
By default, `await api.someMethod(...)` still creates a SendScript await stub
|
|
266
|
+
and keeps the result serializable with `stringify()`. It does not automatically
|
|
267
|
+
send anything over the network.
|
|
267
268
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
```js
|
|
270
|
+
const api = references(['add'])
|
|
271
|
+
const program = api.add(1, 2)
|
|
272
|
+
|
|
273
|
+
Stringify()(program)
|
|
274
|
+
// => "[\"call\",[\"ref\",\"add\"],[1,2]]"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
If you want native `await` to cross the transport boundary, pass an `onAwait`
|
|
278
|
+
handler when creating the references:
|
|
271
279
|
|
|
272
280
|
```js
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
unread: await fetchUnreadMessages(userId),
|
|
276
|
-
emptyTrash: await emptyTrash(userId),
|
|
277
|
-
archived: await archiveMessages(selectMessages({ old: true })),
|
|
278
|
-
}
|
|
281
|
+
import Stringify from 'sendscript/stringify.mjs'
|
|
282
|
+
import references from 'sendscript/references.mjs'
|
|
279
283
|
|
|
280
|
-
const
|
|
284
|
+
const stringify = Stringify()
|
|
285
|
+
|
|
286
|
+
const api = references(['add'], (program) => {
|
|
287
|
+
return fetch('/api', {
|
|
288
|
+
method: 'POST',
|
|
289
|
+
headers: { 'content-type': 'application/json' },
|
|
290
|
+
body: stringify(program),
|
|
291
|
+
}).then((response) => response.json())
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
const result = await api.add(1, 2)
|
|
281
295
|
```
|
|
282
296
|
|
|
283
|
-
|
|
284
|
-
|
|
297
|
+
The callback receives the generated SendScript program, and you decide how it is
|
|
298
|
+
transported or executed.
|
|
285
299
|
|
|
286
300
|
## TypeScript
|
|
287
301
|
|
|
@@ -496,36 +510,21 @@ code to create the AST.
|
|
|
496
510
|
|
|
497
511
|
### Callbacks
|
|
498
512
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
as a templating tool to make sendscript programs; just like one would use
|
|
502
|
-
JavaScript with react templates. `items.map(deleteItem)` would return an array
|
|
503
|
-
of sendscript function calls which can be given to sendscript's parse.
|
|
504
|
-
|
|
505
|
-
Client functions cannot be called by sendscript functions (as of yet) since we
|
|
506
|
-
cannot serialize client functions. No work has been done to have the server send
|
|
507
|
-
back intermediate values to perform client function calls or by performing
|
|
508
|
-
smaller sendscript program payloads that are passed to the client. Very
|
|
509
|
-
interesting stuff to look into. You can achieve this now but it looks less clean
|
|
510
|
-
because you have to do `send` calls which is a bit manual.
|
|
513
|
+
SendScript supports basic callback-style usage, such as passing a function as an
|
|
514
|
+
argument or using a callback to flip arguments into the generated program.
|
|
511
515
|
|
|
512
516
|
```js
|
|
513
|
-
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
It might be interesting to allow configuration to create references that will
|
|
517
|
-
trigger a send whenever await is called. That would remove the ability to create
|
|
518
|
-
a single payload whenever using await. You can then write the above in the
|
|
519
|
-
following manner.
|
|
517
|
+
const api = references(['map', 'add'])
|
|
520
518
|
|
|
521
|
-
|
|
522
|
-
await updateUser(id, merge(await getUser(id), { ...newValues }))
|
|
519
|
+
const result = api.map((value) => api.add(value, 1))([1, 2, 3])
|
|
523
520
|
```
|
|
524
521
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
522
|
+
This is meant for composing SendScript programs, not for executing arbitrary
|
|
523
|
+
client-side logic at runtime.
|
|
524
|
+
|
|
525
|
+
> [!WARNING] Mixing client and server functions can be confusing, because the
|
|
526
|
+
> callback may be used to build a program rather than execute immediately. Keep
|
|
527
|
+
> callback logic simple and deterministic.
|
|
529
528
|
|
|
530
529
|
### Error handling
|
|
531
530
|
|
package/package.json
CHANGED
package/references.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { awaitSymbol, call, ref, then, referenceSymbol } from './symbol.mjs'
|
|
|
7
7
|
* @param {Array<string>} path - Path representing the function location in schema.
|
|
8
8
|
* @returns {Function} Reference function with attached control methods (.then, .catch, toJSON).
|
|
9
9
|
*/
|
|
10
|
-
function instrument (path) {
|
|
10
|
+
function instrument (path, onAwait = null) {
|
|
11
11
|
/**
|
|
12
12
|
* Creates a callable reference invocation.
|
|
13
13
|
*
|
|
@@ -15,7 +15,7 @@ function instrument (path) {
|
|
|
15
15
|
* @returns {Function} New instrumented reference node.
|
|
16
16
|
*/
|
|
17
17
|
function reference (...args) {
|
|
18
|
-
const called = instrument(path)
|
|
18
|
+
const called = instrument(path, onAwait)
|
|
19
19
|
|
|
20
20
|
called.toJSON = () => ({
|
|
21
21
|
[call]: call,
|
|
@@ -71,7 +71,7 @@ function instrument (path) {
|
|
|
71
71
|
return dotThen(resolve, reject)
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const awaited = instrument(path)
|
|
74
|
+
const awaited = instrument(path, onAwait)
|
|
75
75
|
delete awaited.then
|
|
76
76
|
|
|
77
77
|
awaited.toJSON = () => ({
|
|
@@ -79,6 +79,10 @@ function instrument (path) {
|
|
|
79
79
|
ref: reference
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
+
if (typeof onAwait === 'function') {
|
|
83
|
+
return resolve(onAwait(awaited))
|
|
84
|
+
}
|
|
85
|
+
|
|
82
86
|
return resolve(awaited)
|
|
83
87
|
}
|
|
84
88
|
|
|
@@ -106,15 +110,15 @@ function instrument (path) {
|
|
|
106
110
|
* @throws {Error} If schema format is invalid
|
|
107
111
|
* @public
|
|
108
112
|
*/
|
|
109
|
-
export default function References (schema, parentPath = []) {
|
|
113
|
+
export default function References (schema, onAwait = null, parentPath = []) {
|
|
110
114
|
return schema.reduce((acc, item) => {
|
|
111
115
|
if (typeof item === 'string') {
|
|
112
|
-
acc[item] = instrument([...parentPath, item])
|
|
116
|
+
acc[item] = instrument([...parentPath, item], onAwait)
|
|
113
117
|
} else if (Array.isArray(item)) {
|
|
114
118
|
const [name, children] = item
|
|
115
119
|
|
|
116
120
|
if (Array.isArray(children)) {
|
|
117
|
-
acc[name] = References(children, [...parentPath, name])
|
|
121
|
+
acc[name] = References(children, onAwait, [...parentPath, name])
|
|
118
122
|
} else {
|
|
119
123
|
throw new Error(`Expected children array for namespace "${name}"`)
|
|
120
124
|
}
|
package/references.test.mjs
CHANGED
|
@@ -1,8 +1,62 @@
|
|
|
1
1
|
import { test } from 'tap'
|
|
2
2
|
import references from './references.mjs'
|
|
3
|
+
import Stringify from './stringify.mjs'
|
|
3
4
|
|
|
4
5
|
test('invalid uses of references', t => {
|
|
5
6
|
t.throws(() => references([['a']]))
|
|
6
7
|
t.throws(() => references([{}]))
|
|
7
8
|
t.end()
|
|
8
9
|
})
|
|
10
|
+
|
|
11
|
+
test('native await hook is optional and backwards compatible', async t => {
|
|
12
|
+
const schema = ['add', 'square', 'identity', ['nested', ['value']]]
|
|
13
|
+
const stringify = Stringify()
|
|
14
|
+
|
|
15
|
+
const defaultApi = references(schema)
|
|
16
|
+
const defaultProgram = defaultApi.add(1, 2)
|
|
17
|
+
|
|
18
|
+
t.doesNotThrow(() => stringify(defaultProgram))
|
|
19
|
+
t.same(JSON.parse(stringify(defaultProgram))[0], 'call')
|
|
20
|
+
|
|
21
|
+
const calls = []
|
|
22
|
+
const api = references(schema, (program) => {
|
|
23
|
+
calls.push(program)
|
|
24
|
+
return 42
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
t.equal(await api.add(1, 2), 42)
|
|
28
|
+
t.equal(calls.length, 1)
|
|
29
|
+
t.equal(typeof calls[0], 'function')
|
|
30
|
+
|
|
31
|
+
const asyncApi = references(schema, async () => {
|
|
32
|
+
return await Promise.resolve(17)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
t.equal(await asyncApi.identity(9), 17)
|
|
36
|
+
|
|
37
|
+
const rejectedApi = references(schema, async () => {
|
|
38
|
+
throw new Error('boom')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
await t.rejects((async () => {
|
|
42
|
+
await rejectedApi.identity(9)
|
|
43
|
+
})(), { message: 'boom' })
|
|
44
|
+
|
|
45
|
+
const thenApi = references(schema)
|
|
46
|
+
const thenProgram = thenApi.add(1, 2).then(thenApi.square)
|
|
47
|
+
t.same(JSON.parse(stringify(thenProgram))[0], 'then')
|
|
48
|
+
|
|
49
|
+
const nestedApi = references([['outer', ['inner']]], (program) => program)
|
|
50
|
+
t.equal(typeof nestedApi.outer.inner, 'function')
|
|
51
|
+
t.same(JSON.parse(stringify(nestedApi.outer.inner(1)))[0], 'call')
|
|
52
|
+
|
|
53
|
+
const onAwaitApi = references(['add'], (program) => {
|
|
54
|
+
const serialized = stringify(program)
|
|
55
|
+
t.same(JSON.parse(serialized), ['await', ['call', ['ref', 'add'], [['leaf', '1'], ['leaf', '2']]]])
|
|
56
|
+
return 99
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
t.equal(await onAwaitApi.add(1, 2), 99)
|
|
60
|
+
|
|
61
|
+
t.end()
|
|
62
|
+
})
|