sendscript 2.2.0 → 2.3.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 CHANGED
@@ -4,8 +4,21 @@ 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.1](https://github.com/bas080/sendscript/compare/v2.3.0...v2.3.1)
8
+
9
+ - Update typedoc dev dep to 0.28.19 [`69e8cc4`](https://github.com/bas080/sendscript/commit/69e8cc48251af650c02faf3214ff0f9c2b61dbc9)
10
+
11
+ #### [v2.3.0](https://github.com/bas080/sendscript/compare/v2.2.0...v2.3.0)
12
+
13
+ > 14 April 2026
14
+
15
+ - Add support for then and catch [`c0bbea8`](https://github.com/bas080/sendscript/commit/c0bbea8efcc6b262c091d9043e96ac8c3f7606e1)
16
+ - Document .catch and .then support [`b35009a`](https://github.com/bas080/sendscript/commit/b35009ace9761d32c0c6ef1d1d045bed2ecfe74e)
17
+
7
18
  #### [v2.2.0](https://github.com/bas080/sendscript/compare/v2.1.0...v2.2.0)
8
19
 
20
+ > 10 April 2026
21
+
9
22
  - Have default leaf serializer throw if value has no JSON equivalant [`6752f2f`](https://github.com/bas080/sendscript/commit/6752f2f3ed51e252ab93a31b632fe8fbae51d7cb)
10
23
  - Test that prototype chain works when parsing [`2cf0e7e`](https://github.com/bas080/sendscript/commit/2cf0e7e7ced32258102382e7ff1e3f8e91a2cc77)
11
24
 
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)
@@ -215,7 +217,20 @@ Sendscript ships with a barebones (no-dependencies) node-repl script. One can ru
215
217
 
216
218
  > Use the `DEBUG='*'` to enable all logs or `DEBUG='sendscript:*'` for printingonly sendscript logs.
217
219
 
218
- ## Async/Await
220
+ ## Promises
221
+
222
+ ### .then / .catch
223
+
224
+ Supported since vs `v2.3`.
225
+
226
+ ```js
227
+ const getOrCreatePost = send(createPost(title).catch(createPost(title)))
228
+ ```
229
+
230
+ 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
231
+ this basic behavior for the sendscript DSL and parser.
232
+
233
+ ### await
219
234
 
220
235
  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
236
 
@@ -429,19 +444,19 @@ npm t -- report text-summary
429
444
  ```
430
445
  ```
431
446
 
432
- > sendscript@2.2.0 test
447
+ > sendscript@2.3.1 test
433
448
  > tap -R silent
434
449
 
435
450
 
436
- > sendscript@2.2.0 test
451
+ > sendscript@2.3.1 test
437
452
  > tap report text-summary
438
453
 
439
454
 
440
455
  =============================== Coverage summary ===============================
441
- Statements : 100% ( 338/338 )
442
- Branches : 100% ( 123/123 )
443
- Functions : 100% ( 22/22 )
444
- Lines : 100% ( 338/338 )
456
+ Statements : 100% ( 372/372 )
457
+ Branches : 100% ( 137/137 )
458
+ Functions : 100% ( 24/24 )
459
+ Lines : 100% ( 372/372 )
445
460
  ================================================================================
446
461
  ```
447
462
 
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/69e8cc48251af650c02faf3214ff0f9c2b61dbc9/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/69e8cc48251af650c02faf3214ff0f9c2b61dbc9/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.1",
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')