sendscript 2.3.1 → 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 +8 -0
- package/CONTRIBUTING.md +25 -3
- package/README.md +77 -83
- 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,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.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
|
+
|
|
7
13
|
#### [v2.3.1](https://github.com/bas080/sendscript/compare/v2.3.0...v2.3.1)
|
|
8
14
|
|
|
15
|
+
> 14 April 2026
|
|
16
|
+
|
|
9
17
|
- Update typedoc dev dep to 0.28.19 [`69e8cc4`](https://github.com/bas080/sendscript/commit/69e8cc48251af650c02faf3214ff0f9c2b61dbc9)
|
|
10
18
|
|
|
11
19
|
#### [v2.3.0](https://github.com/bas080/sendscript/compare/v2.2.0...v2.3.0)
|
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,9 +26,7 @@ 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
|
-
- [Dependencies](#dependencies)
|
|
32
30
|
- [License](#license)
|
|
33
31
|
- [Roadmap](#roadmap)
|
|
34
32
|
|
|
@@ -36,23 +34,25 @@ Write JS code that you can run on servers, browsers or other clients.
|
|
|
36
34
|
|
|
37
35
|
## Introduction
|
|
38
36
|
|
|
39
|
-
There has been interest in improving APIs by allowing aggregations in a
|
|
40
|
-
|
|
37
|
+
There has been interest in improving APIs by allowing aggregations in a single
|
|
38
|
+
request. Examples include
|
|
41
39
|
|
|
42
|
-
- [JSON-RPC](https://json-rpc.dev/) which allows you to do multiple requests
|
|
43
|
-
|
|
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
|
|
44
42
|
input/arguments of another.
|
|
45
43
|
|
|
46
|
-
- [GraphQL](https://graphql.org/) is very cool but also introduces a new
|
|
47
|
-
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.
|
|
48
46
|
|
|
49
|
-
What SendScript attempts is to allow for very expressive queries and mutations
|
|
50
|
-
that read and write like ordinary JS. That means that the
|
|
51
|
-
that are sent to the server from a client can also
|
|
52
|
-
|
|
53
|
-
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.
|
|
54
53
|
|
|
55
|
-
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.
|
|
56
56
|
|
|
57
57
|
```js
|
|
58
58
|
import Stringify from 'sendscript/stringify.mjs'
|
|
@@ -91,22 +91,22 @@ console.log(parse(program))
|
|
|
91
91
|
SendScript does more than a simple function call. It supports function
|
|
92
92
|
composition and even await.
|
|
93
93
|
|
|
94
|
-
This package is nothing more than the absolute core of sendscript. It
|
|
95
|
-
includes:
|
|
94
|
+
This package is nothing more than the absolute core of sendscript. It includes:
|
|
96
95
|
|
|
97
96
|
- The `references` function to create stubs to write the programs.
|
|
98
97
|
- `stringify` which takes the program and returns a JSON string.
|
|
99
|
-
- `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.
|
|
100
100
|
|
|
101
|
-
The naming could use more love and there are many things to solve either in the
|
|
102
|
-
Things like supporting more complex (de)serializers, errors
|
|
103
|
-
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.
|
|
104
105
|
|
|
105
106
|
---
|
|
106
107
|
|
|
107
|
-
SendScript leaves it up to you to choose HTTP, web-sockets or any other
|
|
108
|
-
|
|
109
|
-
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.
|
|
110
110
|
|
|
111
111
|
## Socket example
|
|
112
112
|
|
|
@@ -120,7 +120,7 @@ We write a simple module.
|
|
|
120
120
|
// ./example/math.mjs
|
|
121
121
|
|
|
122
122
|
export const add = (a, b) => a + b
|
|
123
|
-
export const square = a => a * a
|
|
123
|
+
export const square = (a) => a * a
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
### Server
|
|
@@ -172,12 +172,10 @@ const stringify = Stringify()
|
|
|
172
172
|
const port = process.env.PORT || 3000
|
|
173
173
|
const client = socketClient(`http://localhost:${port}`)
|
|
174
174
|
|
|
175
|
-
const send = program => {
|
|
175
|
+
const send = (program) => {
|
|
176
176
|
return new Promise((resolve, reject) => {
|
|
177
177
|
client.emit('message', stringify(program), (error, result) => {
|
|
178
|
-
error
|
|
179
|
-
? reject(error)
|
|
180
|
-
: resolve(result)
|
|
178
|
+
error ? reject(error) : resolve(result)
|
|
181
179
|
})
|
|
182
180
|
})
|
|
183
181
|
}
|
|
@@ -213,9 +211,11 @@ Result: 100
|
|
|
213
211
|
|
|
214
212
|
## Repl
|
|
215
213
|
|
|
216
|
-
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.
|
|
217
216
|
|
|
218
|
-
> 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.
|
|
219
219
|
|
|
220
220
|
## Promises
|
|
221
221
|
|
|
@@ -227,27 +227,33 @@ Supported since vs `v2.3`.
|
|
|
227
227
|
const getOrCreatePost = send(createPost(title).catch(createPost(title)))
|
|
228
228
|
```
|
|
229
229
|
|
|
230
|
-
You will likely need to define better helpers that makes it safer to handle
|
|
231
|
-
|
|
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.
|
|
232
233
|
|
|
233
234
|
### await
|
|
234
235
|
|
|
235
|
-
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.
|
|
236
239
|
|
|
237
|
-
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.
|
|
238
243
|
|
|
239
244
|
```js
|
|
240
245
|
const userId = 'user-123'
|
|
241
246
|
const program = {
|
|
242
247
|
unread: await fetchUnreadMessages(userId),
|
|
243
248
|
emptyTrash: await emptyTrash(userId),
|
|
244
|
-
archived: await archiveMessages(selectMessages({ old: true }))
|
|
249
|
+
archived: await archiveMessages(selectMessages({ old: true })),
|
|
245
250
|
}
|
|
246
251
|
|
|
247
252
|
const result = await send(program)
|
|
248
253
|
```
|
|
249
254
|
|
|
250
|
-
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.
|
|
251
257
|
|
|
252
258
|
## TypeScript
|
|
253
259
|
|
|
@@ -300,15 +306,16 @@ npx typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./e
|
|
|
300
306
|
|
|
301
307
|
You can see the docs [here](./example/typescript/docs/globals.md)
|
|
302
308
|
|
|
303
|
-
> [!NOTE]
|
|
304
|
-
>
|
|
305
|
-
>
|
|
306
|
-
> Values are subject to serialization and deserialization.
|
|
307
|
-
|
|
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.
|
|
308
312
|
|
|
309
313
|
## Schema and Nested Modules
|
|
310
314
|
|
|
311
|
-
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.
|
|
312
319
|
|
|
313
320
|
### Defining a Nested Module
|
|
314
321
|
|
|
@@ -340,17 +347,13 @@ Functions are referenced via their **path in the module tree**:
|
|
|
340
347
|
```js
|
|
341
348
|
const { math, vector } = references(schema)
|
|
342
349
|
|
|
343
|
-
math.add(
|
|
344
|
-
1,
|
|
345
|
-
vector.length(
|
|
346
|
-
vector.multiply([1,2], 3)
|
|
347
|
-
)
|
|
348
|
-
)
|
|
350
|
+
math.add(1, vector.length(vector.multiply([1, 2], 3)))
|
|
349
351
|
```
|
|
350
352
|
|
|
351
353
|
## Validation (using Zod)
|
|
352
354
|
|
|
353
|
-
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).
|
|
354
357
|
|
|
355
358
|
### Validating structured input
|
|
356
359
|
|
|
@@ -358,7 +361,7 @@ SendScript focuses on program serialization and execution. For runtime input val
|
|
|
358
361
|
const userSchema = z.object({
|
|
359
362
|
id: z.string().uuid(),
|
|
360
363
|
name: z.string(),
|
|
361
|
-
roles: z.array(z.string())
|
|
364
|
+
roles: z.array(z.string()),
|
|
362
365
|
})
|
|
363
366
|
|
|
364
367
|
export function createUser(user) {
|
|
@@ -376,13 +379,19 @@ export function createUser(user) {
|
|
|
376
379
|
|
|
377
380
|
## Leaf Serializer
|
|
378
381
|
|
|
379
|
-
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.
|
|
380
386
|
|
|
381
|
-
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.
|
|
382
390
|
|
|
383
391
|
### Example with superjson
|
|
384
392
|
|
|
385
|
-
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:
|
|
386
395
|
|
|
387
396
|
```js
|
|
388
397
|
import SuperJSON from 'superjson'
|
|
@@ -412,7 +421,10 @@ const program = {
|
|
|
412
421
|
pattern: /foo/gi,
|
|
413
422
|
count: BigInt('9007199254740992'),
|
|
414
423
|
items: new Set([1, 2, 3]),
|
|
415
|
-
mapping: new Map([
|
|
424
|
+
mapping: new Map([
|
|
425
|
+
['a', 1],
|
|
426
|
+
['b', 2],
|
|
427
|
+
]),
|
|
416
428
|
}
|
|
417
429
|
|
|
418
430
|
// Serialize with custom leaf serializer
|
|
@@ -422,8 +434,8 @@ const json = stringify(processData(program))
|
|
|
422
434
|
const env = {
|
|
423
435
|
processData: (data) => ({
|
|
424
436
|
success: true,
|
|
425
|
-
received: data
|
|
426
|
-
})
|
|
437
|
+
received: data,
|
|
438
|
+
}),
|
|
427
439
|
}
|
|
428
440
|
|
|
429
441
|
// Parse with custom leaf deserializer
|
|
@@ -432,7 +444,8 @@ const parse = Parse(schema, env, leadDeserializer)
|
|
|
432
444
|
const result = parse(json)
|
|
433
445
|
```
|
|
434
446
|
|
|
435
|
-
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.
|
|
436
449
|
|
|
437
450
|
## Tests
|
|
438
451
|
|
|
@@ -444,11 +457,11 @@ npm t -- report text-summary
|
|
|
444
457
|
```
|
|
445
458
|
```
|
|
446
459
|
|
|
447
|
-
> sendscript@2.3.
|
|
460
|
+
> sendscript@2.3.2 test
|
|
448
461
|
> tap -R silent
|
|
449
462
|
|
|
450
463
|
|
|
451
|
-
> sendscript@2.3.
|
|
464
|
+
> sendscript@2.3.2 test
|
|
452
465
|
> tap report text-summary
|
|
453
466
|
|
|
454
467
|
|
|
@@ -460,14 +473,6 @@ Lines : 100% ( 372/372 )
|
|
|
460
473
|
================================================================================
|
|
461
474
|
```
|
|
462
475
|
|
|
463
|
-
## Formatting
|
|
464
|
-
|
|
465
|
-
Standard because no config.
|
|
466
|
-
|
|
467
|
-
```bash
|
|
468
|
-
npx standard
|
|
469
|
-
```
|
|
470
|
-
|
|
471
476
|
## Changelog
|
|
472
477
|
|
|
473
478
|
The [changelog][changelog] is generated using the useful
|
|
@@ -477,17 +482,6 @@ The [changelog][changelog] is generated using the useful
|
|
|
477
482
|
npx auto-changelog -p
|
|
478
483
|
```
|
|
479
484
|
|
|
480
|
-
## Dependencies
|
|
481
|
-
|
|
482
|
-
Check if packages are up to date on release.
|
|
483
|
-
|
|
484
|
-
```bash
|
|
485
|
-
npm outdated && echo 'No outdated packages found'
|
|
486
|
-
```
|
|
487
|
-
```
|
|
488
|
-
No outdated packages found
|
|
489
|
-
```
|
|
490
|
-
|
|
491
485
|
## License
|
|
492
486
|
|
|
493
487
|
See the [LICENSE.txt][license] file for details.
|
|
@@ -496,8 +490,8 @@ See the [LICENSE.txt][license] file for details.
|
|
|
496
490
|
|
|
497
491
|
- [ ] Support for simple lambdas to compose functions more easily.
|
|
498
492
|
|
|
499
|
-
[license]
|
|
500
|
-
[socket.io]:https://socket.io/
|
|
501
|
-
[changelog]
|
|
502
|
-
[auto-changelog]:https://www.npmjs.com/package/auto-changelog
|
|
503
|
-
[typedoc]:https://github.com/TypeStrong/typedoc
|
|
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
|
|