sendscript 2.3.0 → 2.3.2
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/.prettierrc +6 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +25 -3
- package/README.md +85 -76
- package/README.mz +75 -76
- package/example/client.socket.io.mjs +2 -4
- package/example/math.mjs +1 -1
- package/example/typescript/docs/functions/add.md +1 -1
- package/example/typescript/docs/functions/square.md +1 -1
- package/package.json +1 -1
package/.prettierrc
ADDED
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.3.2](https://github.com/bas080/sendscript/compare/v2.3.1...v2.3.2)
|
|
8
|
+
|
|
9
|
+
- Format the README.mz file [`df43dbb`](https://github.com/bas080/sendscript/commit/df43dbb8b790cfc612536f49571f9fe4aad03837)
|
|
10
|
+
- Move some commands from README to CONTRIBUTING [`45659c5`](https://github.com/bas080/sendscript/commit/45659c51354ac3bbe3bed5ef3826785d8a1a4a32)
|
|
11
|
+
- Add format markdown check in contributing md [`e68d4d6`](https://github.com/bas080/sendscript/commit/e68d4d6aea26a7a54c2f933ad98a8e363d7f3357)
|
|
12
|
+
|
|
13
|
+
#### [v2.3.1](https://github.com/bas080/sendscript/compare/v2.3.0...v2.3.1)
|
|
14
|
+
|
|
15
|
+
> 14 April 2026
|
|
16
|
+
|
|
17
|
+
- Update typedoc dev dep to 0.28.19 [`69e8cc4`](https://github.com/bas080/sendscript/commit/69e8cc48251af650c02faf3214ff0f9c2b61dbc9)
|
|
18
|
+
|
|
7
19
|
#### [v2.3.0](https://github.com/bas080/sendscript/compare/v2.2.0...v2.3.0)
|
|
8
20
|
|
|
21
|
+
> 14 April 2026
|
|
22
|
+
|
|
9
23
|
- Add support for then and catch [`c0bbea8`](https://github.com/bas080/sendscript/commit/c0bbea8efcc6b262c091d9043e96ac8c3f7606e1)
|
|
10
24
|
- Document .catch and .then support [`b35009a`](https://github.com/bas080/sendscript/commit/b35009ace9761d32c0c6ef1d1d045bed2ecfe74e)
|
|
11
25
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
# Contributing
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
For the README.mz examples to work we need sendscript to be linked.
|
|
5
4
|
|
|
6
5
|
```bash bash > /dev/null
|
|
7
6
|
set -e
|
|
8
7
|
|
|
8
|
+
npm ci
|
|
9
9
|
npm link
|
|
10
10
|
npm link sendscript
|
|
11
11
|
cd ./example
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Check if packages are up to date on release.
|
|
15
|
+
|
|
16
|
+
```bash bash
|
|
17
|
+
npm outdated && echo 'No outdated packages found'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Check if no vulnerable dependencies
|
|
21
|
+
|
|
22
|
+
```bash bash
|
|
23
|
+
npm audit
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Check if code follows standard formatting.
|
|
27
|
+
|
|
28
|
+
```bash bash
|
|
29
|
+
npx standard
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Check if markdown is correctly formatted.
|
|
33
|
+
|
|
34
|
+
```bash bash
|
|
35
|
+
npx prettier --check --parser markdown ./README.mz ./CONTRIBUTING.md
|
|
14
36
|
```
|
|
15
37
|
|
|
16
38
|
Generate the README from the mz file.
|
package/README.md
CHANGED
|
@@ -26,31 +26,33 @@ Write JS code that you can run on servers, browsers or other clients.
|
|
|
26
26
|
- [Leaf Serializer](#leaf-serializer)
|
|
27
27
|
* [Example with superjson](#example-with-superjson)
|
|
28
28
|
- [Tests](#tests)
|
|
29
|
-
- [Formatting](#formatting)
|
|
30
29
|
- [Changelog](#changelog)
|
|
31
|
-
- [
|
|
30
|
+
- [License](#license)
|
|
31
|
+
- [Roadmap](#roadmap)
|
|
32
32
|
|
|
33
33
|
<!-- tocstop -->
|
|
34
34
|
|
|
35
35
|
## Introduction
|
|
36
36
|
|
|
37
|
-
There has been interest in improving APIs by allowing aggregations in a
|
|
38
|
-
|
|
37
|
+
There has been interest in improving APIs by allowing aggregations in a single
|
|
38
|
+
request. Examples include
|
|
39
39
|
|
|
40
|
-
- [JSON-RPC](https://json-rpc.dev/) which allows you to do multiple requests
|
|
41
|
-
|
|
40
|
+
- [JSON-RPC](https://json-rpc.dev/) which allows you to do multiple requests but
|
|
41
|
+
it does not allow you to compose the return value of one endpoint to be the
|
|
42
42
|
input/arguments of another.
|
|
43
43
|
|
|
44
|
-
- [GraphQL](https://graphql.org/) is very cool but also introduces a new
|
|
45
|
-
tooling that is required to wield it.
|
|
44
|
+
- [GraphQL](https://graphql.org/) is very cool but also introduces a new
|
|
45
|
+
languages and the tooling that is required to wield it.
|
|
46
46
|
|
|
47
|
-
What SendScript attempts is to allow for very expressive queries and mutations
|
|
48
|
-
that read and write like ordinary JS. That means that the
|
|
49
|
-
that are sent to the server from a client can also
|
|
50
|
-
|
|
51
|
-
using more advanced
|
|
47
|
+
What SendScript attempts is to allow for very expressive queries and mutations
|
|
48
|
+
to be performed that read and write like ordinary JS. That means that the
|
|
49
|
+
queries and complete programs that are sent to the server from a client can also
|
|
50
|
+
just run on the server as is. The only limitation being the serialization which
|
|
51
|
+
by default is limited by JSON and could be extended by using more advanced
|
|
52
|
+
(de)serialization libraries.
|
|
52
53
|
|
|
53
|
-
SendScript produces an intermediate JSON representation of the program. Let's
|
|
54
|
+
SendScript produces an intermediate JSON representation of the program. Let's
|
|
55
|
+
see what that looks like.
|
|
54
56
|
|
|
55
57
|
```js
|
|
56
58
|
import Stringify from 'sendscript/stringify.mjs'
|
|
@@ -89,22 +91,22 @@ console.log(parse(program))
|
|
|
89
91
|
SendScript does more than a simple function call. It supports function
|
|
90
92
|
composition and even await.
|
|
91
93
|
|
|
92
|
-
This package is nothing more than the absolute core of sendscript. It
|
|
93
|
-
includes:
|
|
94
|
+
This package is nothing more than the absolute core of sendscript. It includes:
|
|
94
95
|
|
|
95
96
|
- The `references` function to create stubs to write the programs.
|
|
96
97
|
- `stringify` which takes the program and returns a JSON string.
|
|
97
|
-
- `parse` which takes the `stringify` JSON string and a real module and returns
|
|
98
|
+
- `parse` which takes the `stringify` JSON string and a real module and returns
|
|
99
|
+
the result.
|
|
98
100
|
|
|
99
|
-
The naming could use more love and there are many things to solve either in the
|
|
100
|
-
Things like supporting more complex (de)serializers, errors
|
|
101
|
-
sendscript programs. Contact me if I have
|
|
101
|
+
The naming could use more love and there are many things to solve either in the
|
|
102
|
+
core or around it. Things like supporting more complex (de)serializers, errors
|
|
103
|
+
and maybe mixing client functions with sendscript programs. Contact me if I have
|
|
104
|
+
piqued your interest.
|
|
102
105
|
|
|
103
106
|
---
|
|
104
107
|
|
|
105
|
-
SendScript leaves it up to you to choose HTTP, web-sockets or any other
|
|
106
|
-
|
|
107
|
-
needs.
|
|
108
|
+
SendScript leaves it up to you to choose HTTP, web-sockets or any other method
|
|
109
|
+
of communication between servers and clients that best fits your needs.
|
|
108
110
|
|
|
109
111
|
## Socket example
|
|
110
112
|
|
|
@@ -118,7 +120,7 @@ We write a simple module.
|
|
|
118
120
|
// ./example/math.mjs
|
|
119
121
|
|
|
120
122
|
export const add = (a, b) => a + b
|
|
121
|
-
export const square = a => a * a
|
|
123
|
+
export const square = (a) => a * a
|
|
122
124
|
```
|
|
123
125
|
|
|
124
126
|
### Server
|
|
@@ -170,12 +172,10 @@ const stringify = Stringify()
|
|
|
170
172
|
const port = process.env.PORT || 3000
|
|
171
173
|
const client = socketClient(`http://localhost:${port}`)
|
|
172
174
|
|
|
173
|
-
const send = program => {
|
|
175
|
+
const send = (program) => {
|
|
174
176
|
return new Promise((resolve, reject) => {
|
|
175
177
|
client.emit('message', stringify(program), (error, result) => {
|
|
176
|
-
error
|
|
177
|
-
? reject(error)
|
|
178
|
-
: resolve(result)
|
|
178
|
+
error ? reject(error) : resolve(result)
|
|
179
179
|
})
|
|
180
180
|
})
|
|
181
181
|
}
|
|
@@ -211,9 +211,11 @@ Result: 100
|
|
|
211
211
|
|
|
212
212
|
## Repl
|
|
213
213
|
|
|
214
|
-
Sendscript ships with a barebones (no-dependencies) node-repl script. One can
|
|
214
|
+
Sendscript ships with a barebones (no-dependencies) node-repl script. One can
|
|
215
|
+
run it by simply typing `sendscript` in their console.
|
|
215
216
|
|
|
216
|
-
> Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for
|
|
217
|
+
> Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for
|
|
218
|
+
> printingonly sendscript logs.
|
|
217
219
|
|
|
218
220
|
## Promises
|
|
219
221
|
|
|
@@ -225,27 +227,33 @@ Supported since vs `v2.3`.
|
|
|
225
227
|
const getOrCreatePost = send(createPost(title).catch(createPost(title)))
|
|
226
228
|
```
|
|
227
229
|
|
|
228
|
-
You will likely need to define better helpers that makes it safer to handle
|
|
229
|
-
|
|
230
|
+
You will likely need to define better helpers that makes it safer to handle
|
|
231
|
+
rejections and work with promises. It is however sensible to have this basic
|
|
232
|
+
behavior for the sendscript DSL and parser.
|
|
230
233
|
|
|
231
234
|
### await
|
|
232
235
|
|
|
233
|
-
SendScript supports async/await seamlessly within a single request. This avoids
|
|
236
|
+
SendScript supports async/await seamlessly within a single request. This avoids
|
|
237
|
+
the performance pitfalls of waterfall-style messaging, which can be especially
|
|
238
|
+
slow on high-latency networks.
|
|
234
239
|
|
|
235
|
-
While it's possible to chain promises manually or use utility functions, native
|
|
240
|
+
While it's possible to chain promises manually or use utility functions, native
|
|
241
|
+
async/await support makes your code more readable, modern, and easier to reason
|
|
242
|
+
about — aligning SendScript with today’s JavaScript best practices.
|
|
236
243
|
|
|
237
244
|
```js
|
|
238
245
|
const userId = 'user-123'
|
|
239
246
|
const program = {
|
|
240
247
|
unread: await fetchUnreadMessages(userId),
|
|
241
248
|
emptyTrash: await emptyTrash(userId),
|
|
242
|
-
archived: await archiveMessages(selectMessages({ old: true }))
|
|
249
|
+
archived: await archiveMessages(selectMessages({ old: true })),
|
|
243
250
|
}
|
|
244
251
|
|
|
245
252
|
const result = await send(program)
|
|
246
253
|
```
|
|
247
254
|
|
|
248
|
-
This operation is done in a single round-trip. The result is an object with the
|
|
255
|
+
This operation is done in a single round-trip. The result is an object with the
|
|
256
|
+
defined properties and returned values.
|
|
249
257
|
|
|
250
258
|
## TypeScript
|
|
251
259
|
|
|
@@ -298,15 +306,16 @@ npx typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./e
|
|
|
298
306
|
|
|
299
307
|
You can see the docs [here](./example/typescript/docs/globals.md)
|
|
300
308
|
|
|
301
|
-
> [!NOTE]
|
|
302
|
-
>
|
|
303
|
-
>
|
|
304
|
-
> Values are subject to serialization and deserialization.
|
|
305
|
-
|
|
309
|
+
> [!NOTE] Although type coercion on the client side can improve the development
|
|
310
|
+
> experience, it does not represent the actual type. Values are subject to
|
|
311
|
+
> serialization and deserialization.
|
|
306
312
|
|
|
307
313
|
## Schema and Nested Modules
|
|
308
314
|
|
|
309
|
-
Sendscript allows you to define your API as a **nested object of functions**,
|
|
315
|
+
Sendscript allows you to define your API as a **nested object of functions**,
|
|
316
|
+
making it easy to organize your DSL into modules and submodules. Each function
|
|
317
|
+
is instrumented so that when serialized, it produces a structured reference that
|
|
318
|
+
can be safely sent and executed elsewhere.
|
|
310
319
|
|
|
311
320
|
### Defining a Nested Module
|
|
312
321
|
|
|
@@ -338,17 +347,13 @@ Functions are referenced via their **path in the module tree**:
|
|
|
338
347
|
```js
|
|
339
348
|
const { math, vector } = references(schema)
|
|
340
349
|
|
|
341
|
-
math.add(
|
|
342
|
-
1,
|
|
343
|
-
vector.length(
|
|
344
|
-
vector.multiply([1,2], 3)
|
|
345
|
-
)
|
|
346
|
-
)
|
|
350
|
+
math.add(1, vector.length(vector.multiply([1, 2], 3)))
|
|
347
351
|
```
|
|
348
352
|
|
|
349
353
|
## Validation (using Zod)
|
|
350
354
|
|
|
351
|
-
SendScript focuses on program serialization and execution. For runtime input
|
|
355
|
+
SendScript focuses on program serialization and execution. For runtime input
|
|
356
|
+
validation, you can use [Zod](https://zod.dev).
|
|
352
357
|
|
|
353
358
|
### Validating structured input
|
|
354
359
|
|
|
@@ -356,7 +361,7 @@ SendScript focuses on program serialization and execution. For runtime input val
|
|
|
356
361
|
const userSchema = z.object({
|
|
357
362
|
id: z.string().uuid(),
|
|
358
363
|
name: z.string(),
|
|
359
|
-
roles: z.array(z.string())
|
|
364
|
+
roles: z.array(z.string()),
|
|
360
365
|
})
|
|
361
366
|
|
|
362
367
|
export function createUser(user) {
|
|
@@ -374,13 +379,19 @@ export function createUser(user) {
|
|
|
374
379
|
|
|
375
380
|
## Leaf Serializer
|
|
376
381
|
|
|
377
|
-
By default, SendScript uses JSON for serialization, which limits support to
|
|
382
|
+
By default, SendScript uses JSON for serialization, which limits support to
|
|
383
|
+
primitives and plain objects/arrays. To support richer JavaScript types like
|
|
384
|
+
`Date`, `RegExp`, `BigInt`, `Map`, `Set`, and `undefined`, you can provide
|
|
385
|
+
custom serialization functions.
|
|
378
386
|
|
|
379
|
-
The `stringify` function accepts an optional `leafSerializer` parameter, and
|
|
387
|
+
The `stringify` function accepts an optional `leafSerializer` parameter, and
|
|
388
|
+
`parse` accepts an optional `leafDeserializer` parameter. These functions
|
|
389
|
+
control how non-SendScript values (leaves) are encoded and decoded.
|
|
380
390
|
|
|
381
391
|
### Example with superjson
|
|
382
392
|
|
|
383
|
-
Here's how to use [superjson](https://github.com/blitz-js/superjson) to support
|
|
393
|
+
Here's how to use [superjson](https://github.com/blitz-js/superjson) to support
|
|
394
|
+
extended types:
|
|
384
395
|
|
|
385
396
|
```js
|
|
386
397
|
import SuperJSON from 'superjson'
|
|
@@ -410,7 +421,10 @@ const program = {
|
|
|
410
421
|
pattern: /foo/gi,
|
|
411
422
|
count: BigInt('9007199254740992'),
|
|
412
423
|
items: new Set([1, 2, 3]),
|
|
413
|
-
mapping: new Map([
|
|
424
|
+
mapping: new Map([
|
|
425
|
+
['a', 1],
|
|
426
|
+
['b', 2],
|
|
427
|
+
]),
|
|
414
428
|
}
|
|
415
429
|
|
|
416
430
|
// Serialize with custom leaf serializer
|
|
@@ -420,8 +434,8 @@ const json = stringify(processData(program))
|
|
|
420
434
|
const env = {
|
|
421
435
|
processData: (data) => ({
|
|
422
436
|
success: true,
|
|
423
|
-
received: data
|
|
424
|
-
})
|
|
437
|
+
received: data,
|
|
438
|
+
}),
|
|
425
439
|
}
|
|
426
440
|
|
|
427
441
|
// Parse with custom leaf deserializer
|
|
@@ -430,7 +444,8 @@ const parse = Parse(schema, env, leadDeserializer)
|
|
|
430
444
|
const result = parse(json)
|
|
431
445
|
```
|
|
432
446
|
|
|
433
|
-
The leaf wrapper format is `['leaf', serializedPayload]`, making it unambiguous
|
|
447
|
+
The leaf wrapper format is `['leaf', serializedPayload]`, making it unambiguous
|
|
448
|
+
and safe from colliding with SendScript operators.
|
|
434
449
|
|
|
435
450
|
## Tests
|
|
436
451
|
|
|
@@ -442,11 +457,11 @@ npm t -- report text-summary
|
|
|
442
457
|
```
|
|
443
458
|
```
|
|
444
459
|
|
|
445
|
-
> sendscript@2.3.
|
|
460
|
+
> sendscript@2.3.2 test
|
|
446
461
|
> tap -R silent
|
|
447
462
|
|
|
448
463
|
|
|
449
|
-
> sendscript@2.3.
|
|
464
|
+
> sendscript@2.3.2 test
|
|
450
465
|
> tap report text-summary
|
|
451
466
|
|
|
452
467
|
|
|
@@ -458,14 +473,6 @@ Lines : 100% ( 372/372 )
|
|
|
458
473
|
================================================================================
|
|
459
474
|
```
|
|
460
475
|
|
|
461
|
-
## Formatting
|
|
462
|
-
|
|
463
|
-
Standard because no config.
|
|
464
|
-
|
|
465
|
-
```bash
|
|
466
|
-
npx standard
|
|
467
|
-
```
|
|
468
|
-
|
|
469
476
|
## Changelog
|
|
470
477
|
|
|
471
478
|
The [changelog][changelog] is generated using the useful
|
|
@@ -475,14 +482,16 @@ The [changelog][changelog] is generated using the useful
|
|
|
475
482
|
npx auto-changelog -p
|
|
476
483
|
```
|
|
477
484
|
|
|
478
|
-
##
|
|
485
|
+
## License
|
|
479
486
|
|
|
480
|
-
|
|
487
|
+
See the [LICENSE.txt][license] file for details.
|
|
481
488
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
+
## Roadmap
|
|
490
|
+
|
|
491
|
+
- [ ] Support for simple lambdas to compose functions more easily.
|
|
492
|
+
|
|
493
|
+
[license]: ./LICENSE.txt
|
|
494
|
+
[socket.io]: https://socket.io/
|
|
495
|
+
[changelog]: ./CHANGELOG.md
|
|
496
|
+
[auto-changelog]: https://www.npmjs.com/package/auto-changelog
|
|
497
|
+
[typedoc]: https://github.com/TypeStrong/typedoc
|
package/README.mz
CHANGED
|
@@ -11,23 +11,25 @@ Write JS code that you can run on servers, browsers or other clients.
|
|
|
11
11
|
|
|
12
12
|
## Introduction
|
|
13
13
|
|
|
14
|
-
There has been interest in improving APIs by allowing aggregations in a
|
|
15
|
-
|
|
14
|
+
There has been interest in improving APIs by allowing aggregations in a single
|
|
15
|
+
request. Examples include
|
|
16
16
|
|
|
17
|
-
- [JSON-RPC](https://json-rpc.dev/) which allows you to do multiple requests
|
|
18
|
-
|
|
17
|
+
- [JSON-RPC](https://json-rpc.dev/) which allows you to do multiple requests but
|
|
18
|
+
it does not allow you to compose the return value of one endpoint to be the
|
|
19
19
|
input/arguments of another.
|
|
20
20
|
|
|
21
|
-
- [GraphQL](https://graphql.org/) is very cool but also introduces a new
|
|
22
|
-
tooling that is required to wield it.
|
|
21
|
+
- [GraphQL](https://graphql.org/) is very cool but also introduces a new
|
|
22
|
+
languages and the tooling that is required to wield it.
|
|
23
23
|
|
|
24
|
-
What SendScript attempts is to allow for very expressive queries and mutations
|
|
25
|
-
that read and write like ordinary JS. That means that the
|
|
26
|
-
that are sent to the server from a client can also
|
|
27
|
-
|
|
28
|
-
using more advanced
|
|
24
|
+
What SendScript attempts is to allow for very expressive queries and mutations
|
|
25
|
+
to be performed that read and write like ordinary JS. That means that the
|
|
26
|
+
queries and complete programs that are sent to the server from a client can also
|
|
27
|
+
just run on the server as is. The only limitation being the serialization which
|
|
28
|
+
by default is limited by JSON and could be extended by using more advanced
|
|
29
|
+
(de)serialization libraries.
|
|
29
30
|
|
|
30
|
-
SendScript produces an intermediate JSON representation of the program. Let's
|
|
31
|
+
SendScript produces an intermediate JSON representation of the program. Let's
|
|
32
|
+
see what that looks like.
|
|
31
33
|
|
|
32
34
|
```js|json node --input-type=module | tee /tmp/sendscript.json
|
|
33
35
|
import Stringify from 'sendscript/stringify.mjs'
|
|
@@ -60,22 +62,22 @@ console.log(parse(program))
|
|
|
60
62
|
SendScript does more than a simple function call. It supports function
|
|
61
63
|
composition and even await.
|
|
62
64
|
|
|
63
|
-
This package is nothing more than the absolute core of sendscript. It
|
|
64
|
-
includes:
|
|
65
|
+
This package is nothing more than the absolute core of sendscript. It includes:
|
|
65
66
|
|
|
66
67
|
- The `references` function to create stubs to write the programs.
|
|
67
68
|
- `stringify` which takes the program and returns a JSON string.
|
|
68
|
-
- `parse` which takes the `stringify` JSON string and a real module and returns
|
|
69
|
+
- `parse` which takes the `stringify` JSON string and a real module and returns
|
|
70
|
+
the result.
|
|
69
71
|
|
|
70
|
-
The naming could use more love and there are many things to solve either in the
|
|
71
|
-
Things like supporting more complex (de)serializers, errors
|
|
72
|
-
sendscript programs. Contact me if I have
|
|
72
|
+
The naming could use more love and there are many things to solve either in the
|
|
73
|
+
core or around it. Things like supporting more complex (de)serializers, errors
|
|
74
|
+
and maybe mixing client functions with sendscript programs. Contact me if I have
|
|
75
|
+
piqued your interest.
|
|
73
76
|
|
|
74
77
|
---
|
|
75
78
|
|
|
76
|
-
SendScript leaves it up to you to choose HTTP, web-sockets or any other
|
|
77
|
-
|
|
78
|
-
needs.
|
|
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.
|
|
79
81
|
|
|
80
82
|
## Socket example
|
|
81
83
|
|
|
@@ -89,7 +91,7 @@ We write a simple module.
|
|
|
89
91
|
// ./example/math.mjs
|
|
90
92
|
|
|
91
93
|
export const add = (a, b) => a + b
|
|
92
|
-
export const square = a => a * a
|
|
94
|
+
export const square = (a) => a * a
|
|
93
95
|
```
|
|
94
96
|
|
|
95
97
|
### Server
|
|
@@ -141,12 +143,10 @@ const stringify = Stringify()
|
|
|
141
143
|
const port = process.env.PORT || 3000
|
|
142
144
|
const client = socketClient(`http://localhost:${port}`)
|
|
143
145
|
|
|
144
|
-
const send = program => {
|
|
146
|
+
const send = (program) => {
|
|
145
147
|
return new Promise((resolve, reject) => {
|
|
146
148
|
client.emit('message', stringify(program), (error, result) => {
|
|
147
|
-
error
|
|
148
|
-
? reject(error)
|
|
149
|
-
: resolve(result)
|
|
149
|
+
error ? reject(error) : resolve(result)
|
|
150
150
|
})
|
|
151
151
|
})
|
|
152
152
|
}
|
|
@@ -179,9 +179,11 @@ pkill sendscript
|
|
|
179
179
|
|
|
180
180
|
## Repl
|
|
181
181
|
|
|
182
|
-
Sendscript ships with a barebones (no-dependencies) node-repl script. One can
|
|
182
|
+
Sendscript ships with a barebones (no-dependencies) node-repl script. One can
|
|
183
|
+
run it by simply typing `sendscript` in their console.
|
|
183
184
|
|
|
184
|
-
> Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for
|
|
185
|
+
> Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for
|
|
186
|
+
> printingonly sendscript logs.
|
|
185
187
|
|
|
186
188
|
## Promises
|
|
187
189
|
|
|
@@ -193,27 +195,33 @@ Supported since vs `v2.3`.
|
|
|
193
195
|
const getOrCreatePost = send(createPost(title).catch(createPost(title)))
|
|
194
196
|
```
|
|
195
197
|
|
|
196
|
-
You will likely need to define better helpers that makes it safer to handle
|
|
197
|
-
|
|
198
|
+
You will likely need to define better helpers that makes it safer to handle
|
|
199
|
+
rejections and work with promises. It is however sensible to have this basic
|
|
200
|
+
behavior for the sendscript DSL and parser.
|
|
198
201
|
|
|
199
202
|
### await
|
|
200
203
|
|
|
201
|
-
SendScript supports async/await seamlessly within a single request. This avoids
|
|
204
|
+
SendScript supports async/await seamlessly within a single request. This avoids
|
|
205
|
+
the performance pitfalls of waterfall-style messaging, which can be especially
|
|
206
|
+
slow on high-latency networks.
|
|
202
207
|
|
|
203
|
-
While it's possible to chain promises manually or use utility functions, native
|
|
208
|
+
While it's possible to chain promises manually or use utility functions, native
|
|
209
|
+
async/await support makes your code more readable, modern, and easier to reason
|
|
210
|
+
about — aligning SendScript with today’s JavaScript best practices.
|
|
204
211
|
|
|
205
212
|
```js
|
|
206
213
|
const userId = 'user-123'
|
|
207
214
|
const program = {
|
|
208
215
|
unread: await fetchUnreadMessages(userId),
|
|
209
216
|
emptyTrash: await emptyTrash(userId),
|
|
210
|
-
archived: await archiveMessages(selectMessages({ old: true }))
|
|
217
|
+
archived: await archiveMessages(selectMessages({ old: true })),
|
|
211
218
|
}
|
|
212
219
|
|
|
213
220
|
const result = await send(program)
|
|
214
221
|
```
|
|
215
222
|
|
|
216
|
-
This operation is done in a single round-trip. The result is an object with the
|
|
223
|
+
This operation is done in a single round-trip. The result is an object with the
|
|
224
|
+
defined properties and returned values.
|
|
217
225
|
|
|
218
226
|
## TypeScript
|
|
219
227
|
|
|
@@ -246,15 +254,16 @@ npx typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./e
|
|
|
246
254
|
|
|
247
255
|
You can see the docs [here](./example/typescript/docs/globals.md)
|
|
248
256
|
|
|
249
|
-
> [!NOTE]
|
|
250
|
-
>
|
|
251
|
-
>
|
|
252
|
-
> Values are subject to serialization and deserialization.
|
|
253
|
-
|
|
257
|
+
> [!NOTE] Although type coercion on the client side can improve the development
|
|
258
|
+
> experience, it does not represent the actual type. Values are subject to
|
|
259
|
+
> serialization and deserialization.
|
|
254
260
|
|
|
255
261
|
## Schema and Nested Modules
|
|
256
262
|
|
|
257
|
-
Sendscript allows you to define your API as a **nested object of functions**,
|
|
263
|
+
Sendscript allows you to define your API as a **nested object of functions**,
|
|
264
|
+
making it easy to organize your DSL into modules and submodules. Each function
|
|
265
|
+
is instrumented so that when serialized, it produces a structured reference that
|
|
266
|
+
can be safely sent and executed elsewhere.
|
|
258
267
|
|
|
259
268
|
### Defining a Nested Module
|
|
260
269
|
|
|
@@ -286,17 +295,13 @@ Functions are referenced via their **path in the module tree**:
|
|
|
286
295
|
```js
|
|
287
296
|
const { math, vector } = references(schema)
|
|
288
297
|
|
|
289
|
-
math.add(
|
|
290
|
-
1,
|
|
291
|
-
vector.length(
|
|
292
|
-
vector.multiply([1,2], 3)
|
|
293
|
-
)
|
|
294
|
-
)
|
|
298
|
+
math.add(1, vector.length(vector.multiply([1, 2], 3)))
|
|
295
299
|
```
|
|
296
300
|
|
|
297
301
|
## Validation (using Zod)
|
|
298
302
|
|
|
299
|
-
SendScript focuses on program serialization and execution. For runtime input
|
|
303
|
+
SendScript focuses on program serialization and execution. For runtime input
|
|
304
|
+
validation, you can use [Zod](https://zod.dev).
|
|
300
305
|
|
|
301
306
|
### Validating structured input
|
|
302
307
|
|
|
@@ -304,7 +309,7 @@ SendScript focuses on program serialization and execution. For runtime input val
|
|
|
304
309
|
const userSchema = z.object({
|
|
305
310
|
id: z.string().uuid(),
|
|
306
311
|
name: z.string(),
|
|
307
|
-
roles: z.array(z.string())
|
|
312
|
+
roles: z.array(z.string()),
|
|
308
313
|
})
|
|
309
314
|
|
|
310
315
|
export function createUser(user) {
|
|
@@ -322,13 +327,19 @@ export function createUser(user) {
|
|
|
322
327
|
|
|
323
328
|
## Leaf Serializer
|
|
324
329
|
|
|
325
|
-
By default, SendScript uses JSON for serialization, which limits support to
|
|
330
|
+
By default, SendScript uses JSON for serialization, which limits support to
|
|
331
|
+
primitives and plain objects/arrays. To support richer JavaScript types like
|
|
332
|
+
`Date`, `RegExp`, `BigInt`, `Map`, `Set`, and `undefined`, you can provide
|
|
333
|
+
custom serialization functions.
|
|
326
334
|
|
|
327
|
-
The `stringify` function accepts an optional `leafSerializer` parameter, and
|
|
335
|
+
The `stringify` function accepts an optional `leafSerializer` parameter, and
|
|
336
|
+
`parse` accepts an optional `leafDeserializer` parameter. These functions
|
|
337
|
+
control how non-SendScript values (leaves) are encoded and decoded.
|
|
328
338
|
|
|
329
339
|
### Example with superjson
|
|
330
340
|
|
|
331
|
-
Here's how to use [superjson](https://github.com/blitz-js/superjson) to support
|
|
341
|
+
Here's how to use [superjson](https://github.com/blitz-js/superjson) to support
|
|
342
|
+
extended types:
|
|
332
343
|
|
|
333
344
|
```js
|
|
334
345
|
import SuperJSON from 'superjson'
|
|
@@ -358,7 +369,10 @@ const program = {
|
|
|
358
369
|
pattern: /foo/gi,
|
|
359
370
|
count: BigInt('9007199254740992'),
|
|
360
371
|
items: new Set([1, 2, 3]),
|
|
361
|
-
mapping: new Map([
|
|
372
|
+
mapping: new Map([
|
|
373
|
+
['a', 1],
|
|
374
|
+
['b', 2],
|
|
375
|
+
]),
|
|
362
376
|
}
|
|
363
377
|
|
|
364
378
|
// Serialize with custom leaf serializer
|
|
@@ -368,8 +382,8 @@ const json = stringify(processData(program))
|
|
|
368
382
|
const env = {
|
|
369
383
|
processData: (data) => ({
|
|
370
384
|
success: true,
|
|
371
|
-
received: data
|
|
372
|
-
})
|
|
385
|
+
received: data,
|
|
386
|
+
}),
|
|
373
387
|
}
|
|
374
388
|
|
|
375
389
|
// Parse with custom leaf deserializer
|
|
@@ -378,7 +392,8 @@ const parse = Parse(schema, env, leadDeserializer)
|
|
|
378
392
|
const result = parse(json)
|
|
379
393
|
```
|
|
380
394
|
|
|
381
|
-
The leaf wrapper format is `['leaf', serializedPayload]`, making it unambiguous
|
|
395
|
+
The leaf wrapper format is `['leaf', serializedPayload]`, making it unambiguous
|
|
396
|
+
and safe from colliding with SendScript operators.
|
|
382
397
|
|
|
383
398
|
## Tests
|
|
384
399
|
|
|
@@ -389,14 +404,6 @@ npm t -- -R silent
|
|
|
389
404
|
npm t -- report text-summary
|
|
390
405
|
```
|
|
391
406
|
|
|
392
|
-
## Formatting
|
|
393
|
-
|
|
394
|
-
Standard because no config.
|
|
395
|
-
|
|
396
|
-
```bash bash
|
|
397
|
-
npx standard
|
|
398
|
-
```
|
|
399
|
-
|
|
400
407
|
## Changelog
|
|
401
408
|
|
|
402
409
|
The [changelog][changelog] is generated using the useful
|
|
@@ -406,14 +413,6 @@ The [changelog][changelog] is generated using the useful
|
|
|
406
413
|
npx auto-changelog -p
|
|
407
414
|
```
|
|
408
415
|
|
|
409
|
-
## Dependencies
|
|
410
|
-
|
|
411
|
-
Check if packages are up to date on release.
|
|
412
|
-
|
|
413
|
-
```bash bash
|
|
414
|
-
npm outdated && echo 'No outdated packages found'
|
|
415
|
-
```
|
|
416
|
-
|
|
417
416
|
## License
|
|
418
417
|
|
|
419
418
|
See the [LICENSE.txt][license] file for details.
|
|
@@ -422,8 +421,8 @@ See the [LICENSE.txt][license] file for details.
|
|
|
422
421
|
|
|
423
422
|
- [ ] Support for simple lambdas to compose functions more easily.
|
|
424
423
|
|
|
425
|
-
[license]
|
|
426
|
-
[socket.io]:https://socket.io/
|
|
427
|
-
[changelog]
|
|
428
|
-
[auto-changelog]:https://www.npmjs.com/package/auto-changelog
|
|
429
|
-
[typedoc]:https://github.com/TypeStrong/typedoc
|
|
424
|
+
[license]: ./LICENSE.txt
|
|
425
|
+
[socket.io]: https://socket.io/
|
|
426
|
+
[changelog]: ./CHANGELOG.md
|
|
427
|
+
[auto-changelog]: https://www.npmjs.com/package/auto-changelog
|
|
428
|
+
[typedoc]: https://github.com/TypeStrong/typedoc
|
|
@@ -11,12 +11,10 @@ const stringify = Stringify()
|
|
|
11
11
|
const port = process.env.PORT || 3000
|
|
12
12
|
const client = socketClient(`http://localhost:${port}`)
|
|
13
13
|
|
|
14
|
-
const send = program => {
|
|
14
|
+
const send = (program) => {
|
|
15
15
|
return new Promise((resolve, reject) => {
|
|
16
16
|
client.emit('message', stringify(program), (error, result) => {
|
|
17
|
-
error
|
|
18
|
-
? reject(error)
|
|
19
|
-
: resolve(result)
|
|
17
|
+
error ? reject(error) : resolve(result)
|
|
20
18
|
})
|
|
21
19
|
})
|
|
22
20
|
}
|
package/example/math.mjs
CHANGED
|
@@ -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/45659c51354ac3bbe3bed5ef3826785d8a1a4a32/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/45659c51354ac3bbe3bed5ef3826785d8a1a4a32/example/typescript/math.ts#L2)
|
|
12
12
|
|
|
13
13
|
## Parameters
|
|
14
14
|
|