sendscript 2.2.0 → 2.3.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 CHANGED
@@ -4,8 +4,15 @@ 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.0](https://github.com/bas080/sendscript/compare/v2.2.0...v2.3.0)
8
+
9
+ - Add support for then and catch [`c0bbea8`](https://github.com/bas080/sendscript/commit/c0bbea8efcc6b262c091d9043e96ac8c3f7606e1)
10
+ - Document .catch and .then support [`b35009a`](https://github.com/bas080/sendscript/commit/b35009ace9761d32c0c6ef1d1d045bed2ecfe74e)
11
+
7
12
  #### [v2.2.0](https://github.com/bas080/sendscript/compare/v2.1.0...v2.2.0)
8
13
 
14
+ > 10 April 2026
15
+
9
16
  - Have default leaf serializer throw if value has no JSON equivalant [`6752f2f`](https://github.com/bas080/sendscript/commit/6752f2f3ed51e252ab93a31b632fe8fbae51d7cb)
10
17
  - Test that prototype chain works when parsing [`2cf0e7e`](https://github.com/bas080/sendscript/commit/2cf0e7e7ced32258102382e7ff1e3f8e91a2cc77)
11
18
 
package/README.md CHANGED
@@ -15,7 +15,9 @@ Write JS code that you can run on servers, browsers or other clients.
15
15
  * [Server](#server)
16
16
  * [Client](#client)
17
17
  - [Repl](#repl)
18
- - [Async/Await](#asyncawait)
18
+ - [Promises](#promises)
19
+ * [.then / .catch](#then--catch)
20
+ * [await](#await)
19
21
  - [TypeScript](#typescript)
20
22
  - [Schema and Nested Modules](#schema-and-nested-modules)
21
23
  * [Defining a Nested Module](#defining-a-nested-module)
@@ -27,8 +29,6 @@ Write JS code that you can run on servers, browsers or other clients.
27
29
  - [Formatting](#formatting)
28
30
  - [Changelog](#changelog)
29
31
  - [Dependencies](#dependencies)
30
- - [License](#license)
31
- - [Roadmap](#roadmap)
32
32
 
33
33
  <!-- tocstop -->
34
34
 
@@ -215,7 +215,20 @@ Sendscript ships with a barebones (no-dependencies) node-repl script. One can ru
215
215
 
216
216
  > Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for printingonly sendscript logs.
217
217
 
218
- ## Async/Await
218
+ ## Promises
219
+
220
+ ### .then / .catch
221
+
222
+ Supported since vs `v2.3`.
223
+
224
+ ```js
225
+ const getOrCreatePost = send(createPost(title).catch(createPost(title)))
226
+ ```
227
+
228
+ You will likely need to define better helpers that makes it safer to handle rejections and work with promises. It is however sensible to have
229
+ this basic behavior for the sendscript DSL and parser.
230
+
231
+ ### await
219
232
 
220
233
  SendScript supports async/await seamlessly within a single request. This avoids the performance pitfalls of waterfall-style messaging, which can be especially slow on high-latency networks.
221
234
 
@@ -429,19 +442,19 @@ npm t -- report text-summary
429
442
  ```
430
443
  ```
431
444
 
432
- > sendscript@2.2.0 test
445
+ > sendscript@2.3.0 test
433
446
  > tap -R silent
434
447
 
435
448
 
436
- > sendscript@2.2.0 test
449
+ > sendscript@2.3.0 test
437
450
  > tap report text-summary
438
451
 
439
452
 
440
453
  =============================== Coverage summary ===============================
441
- Statements : 100% ( 338/338 )
442
- Branches : 100% ( 123/123 )
443
- Functions : 100% ( 22/22 )
444
- Lines : 100% ( 338/338 )
454
+ Statements : 100% ( 372/372 )
455
+ Branches : 100% ( 137/137 )
456
+ Functions : 100% ( 24/24 )
457
+ Lines : 100% ( 372/372 )
445
458
  ================================================================================
446
459
  ```
447
460
 
@@ -470,19 +483,6 @@ Check if packages are up to date on release.
470
483
  npm outdated && echo 'No outdated packages found'
471
484
  ```
472
485
  ```
473
- No outdated packages found
486
+ Package Current Wanted Latest Location Depended by
487
+ typedoc 0.28.18 0.28.19 0.28.19 node_modules/typedoc master
474
488
  ```
475
-
476
- ## License
477
-
478
- See the [LICENSE.txt][license] file for details.
479
-
480
- ## Roadmap
481
-
482
- - [ ] Support for simple lambdas to compose functions more easily.
483
-
484
- [license]:./LICENSE.txt
485
- [socket.io]:https://socket.io/
486
- [changelog]:./CHANGELOG.md
487
- [auto-changelog]:https://www.npmjs.com/package/auto-changelog
488
- [typedoc]:https://github.com/TypeStrong/typedoc
package/README.mz CHANGED
@@ -183,7 +183,20 @@ Sendscript ships with a barebones (no-dependencies) node-repl script. One can ru
183
183
 
184
184
  > Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for printingonly sendscript logs.
185
185
 
186
- ## Async/Await
186
+ ## Promises
187
+
188
+ ### .then / .catch
189
+
190
+ Supported since vs `v2.3`.
191
+
192
+ ```js
193
+ const getOrCreatePost = send(createPost(title).catch(createPost(title)))
194
+ ```
195
+
196
+ You will likely need to define better helpers that makes it safer to handle rejections and work with promises. It is however sensible to have
197
+ this basic behavior for the sendscript DSL and parser.
198
+
199
+ ### await
187
200
 
188
201
  SendScript supports async/await seamlessly within a single request. This avoids the performance pitfalls of waterfall-style messaging, which can be especially slow on high-latency networks.
189
202
 
@@ -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/2cf0e7e7ced32258102382e7ff1e3f8e91a2cc77/example/typescript/math.ts#L1)
11
+ Defined in: [math.ts:1](https://github.com/bas080/sendscript/blob/b35009ace9761d32c0c6ef1d1d045bed2ecfe74e/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/2cf0e7e7ced32258102382e7ff1e3f8e91a2cc77/example/typescript/math.ts#L2)
11
+ Defined in: [math.ts:2](https://github.com/bas080/sendscript/blob/b35009ace9761d32c0c6ef1d1d045bed2ecfe74e/example/typescript/math.ts#L2)
12
12
 
13
13
  ## Parameters
14
14
 
package/index.test.mjs CHANGED
@@ -28,11 +28,13 @@ const myModuleOrig = {
28
28
  toArray: (...array) => array,
29
29
  always: (x) => () => x,
30
30
  multiply3: (a) => (b) => (c) => a * b * c,
31
+ sayHello: x => `hello ${x}`,
31
32
  map: (fn) => (array) => array.map(fn),
32
33
  filter: (pred) => (array) => array.filter(pred),
33
34
  hello: 'world',
34
35
  noop: () => {},
35
36
  resolve: (x) => Promise.resolve(x),
37
+ reject: x => Promise.reject(x),
36
38
  asyncFn: async () => 'my-async-function',
37
39
  instanceOf: (x, t) => x instanceof t,
38
40
  asyncAdd: async (a, b) => a + b,
@@ -67,6 +69,7 @@ test('should evaluate basic expressions correctly', async (t) => {
67
69
  aPromise,
68
70
  asyncAdd,
69
71
  resolve,
72
+ reject,
70
73
  delay,
71
74
  delayedIdentity,
72
75
  noop,
@@ -81,9 +84,27 @@ test('should evaluate basic expressions correctly', async (t) => {
81
84
  identity,
82
85
  always,
83
86
  multiply3,
87
+ sayHello,
84
88
  nested
85
89
  } = api
86
90
 
91
+ t.test('supports .then', async t => {
92
+ t.rejects(run(reject('error')))
93
+
94
+ t.equal(await run(reject('error').catch(sayHello)), 'hello error')
95
+ t.equal(await run(reject('error').then(null, sayHello)), 'hello error')
96
+
97
+ t.equal(await run(resolve('value').then(sayHello)), 'hello value')
98
+ t.equal(await run(resolve('value').then(sayHello, noop)), 'hello value')
99
+
100
+ t.end()
101
+ })
102
+
103
+ t.test('resolve and reject callback works for .then', async t => {
104
+ t.equal(await run(resolve(42).then(multiply3(1)(2))), 84)
105
+ t.end()
106
+ })
107
+
87
108
  t.test('await evaluation order matches JS semantics', async (t) => {
88
109
  const lorder = []
89
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sendscript",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Blur the line between server and client code.",
5
5
  "module": true,
6
6
  "main": "index.mjs",
package/parse.mjs CHANGED
@@ -32,8 +32,14 @@ const isPlainObject = (value) => {
32
32
  return proto === Object.prototype || proto === null
33
33
  }
34
34
 
35
+ const spy = (type, fn) => (...args) => {
36
+ const value = fn(...args)
37
+ debug(type, args, ' => ', value)
38
+ return value
39
+ }
40
+
35
41
  // Recursively resolve awaited values in a parsed tree
36
- const evaluate = (value, awaits = []) => {
42
+ const evaluate = spy('eval', (value, awaits = []) => {
37
43
  if (value === undefinedSentinel) return undefined
38
44
 
39
45
  if (Array.isArray(value)) {
@@ -48,6 +54,11 @@ const evaluate = (value, awaits = []) => {
48
54
  return awaits[index]
49
55
  }
50
56
 
57
+ if (operator === 'then') {
58
+ const [v, onResolve, onReject] = rest
59
+ return evaluate(v, awaits).then(evaluate(onResolve, awaits), evaluate(onReject, awaits))
60
+ }
61
+
51
62
  if (operator === 'call') {
52
63
  // Step 1: evaluate the function itself
53
64
  let [fn, args] = rest
@@ -86,13 +97,7 @@ const evaluate = (value, awaits = []) => {
86
97
  }
87
98
 
88
99
  return value
89
- }
90
-
91
- const spy = (fn) => (...args) => {
92
- const value = fn(...args)
93
- debug(args, ' => ', value)
94
- return value
95
- }
100
+ })
96
101
 
97
102
  const defaultLeafDeserializer = (text) => JSON.parse(text)
98
103
 
@@ -105,7 +110,7 @@ export default (schemaArg, env, deserialize = defaultLeafDeserializer) => {
105
110
 
106
111
  // Creates the list of awaits that will resolve in order
107
112
  // and also deserializes the leaves.
108
- const reviver = spy((key, value) => {
113
+ const reviver = spy('revive', (key, value) => {
109
114
  if (value === null) return value
110
115
 
111
116
  if (!Array.isArray(value)) {
package/references.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { awaitSymbol, call, ref } from './symbol.mjs'
1
+ import { awaitSymbol, call, ref, then, referenceSymbol } from './symbol.mjs'
2
2
 
3
3
  function instrument (path) {
4
4
  function reference (...args) {
@@ -6,7 +6,6 @@ function instrument (path) {
6
6
 
7
7
  called.toJSON = () => ({
8
8
  [call]: call,
9
- call: true,
10
9
  ref: reference,
11
10
  args
12
11
  })
@@ -14,13 +13,35 @@ function instrument (path) {
14
13
  return called
15
14
  }
16
15
 
17
- reference.then = (resolve) => {
16
+ function dotThen (resolve, reject) {
17
+ const node = instrument(path)
18
+
19
+ node.toJSON = () => ({
20
+ [then]: then,
21
+ ref: reference,
22
+ resolve,
23
+ reject
24
+ })
25
+
26
+ return node
27
+ }
28
+
29
+ reference.catch = (reject) => {
30
+ return dotThen(null, reject)
31
+ }
32
+
33
+ reference.then = (resolve, reject) => {
34
+ // That is how we know if it is an await or a .then call.
35
+ if (resolve?.[referenceSymbol] || reject?.[referenceSymbol]) {
36
+ return dotThen(resolve, reject)
37
+ }
38
+
18
39
  const awaited = instrument(path)
40
+ // Prevent infinite recur
19
41
  delete awaited.then
20
42
 
21
43
  awaited.toJSON = () => ({
22
44
  [awaitSymbol]: awaitSymbol,
23
- await: true,
24
45
  ref: reference
25
46
  })
26
47
 
@@ -29,10 +50,11 @@ function instrument (path) {
29
50
 
30
51
  reference.toJSON = () => ({
31
52
  [ref]: ref,
32
- reference: true,
33
53
  path
34
54
  })
35
55
 
56
+ reference[referenceSymbol] = referenceSymbol
57
+
36
58
  return reference
37
59
  }
38
60
 
package/stringify.mjs CHANGED
@@ -2,6 +2,7 @@ import Debug from './debug.mjs'
2
2
  import { SendScriptSerializationError } from './error.mjs'
3
3
  import {
4
4
  awaitSymbol,
5
+ then,
5
6
  call,
6
7
  ref
7
8
  } from './symbol.mjs'
@@ -43,6 +44,10 @@ function transformValue (value, leafSerializer) {
43
44
  return ['await', transformValue(value.ref, leafSerializer)]
44
45
  }
45
46
 
47
+ if (value && value[then]) {
48
+ return ['then', transformValue(value.ref, leafSerializer), transformValue(value.resolve || null, leafSerializer), transformValue(value.reject || null, leafSerializer)]
49
+ }
50
+
46
51
  // Handle arrays: quote keyword operators, transform other arrays recursively
47
52
  if (Array.isArray(value)) {
48
53
  const [operator, ...rest] = value
package/symbol.mjs CHANGED
@@ -1,3 +1,5 @@
1
1
  export const ref = Symbol('ref')
2
2
  export const call = Symbol('call')
3
3
  export const awaitSymbol = Symbol('await')
4
+ export const then = Symbol('then')
5
+ export const referenceSymbol = Symbol('referenceSymbol')