sendscript 1.0.0 → 1.0.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/CHANGELOG.md CHANGED
@@ -4,7 +4,20 @@ 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
- #### [v1.0.0](https://github.com/bas080/sendscript/compare/v0.1.4...v1.0.0)
7
+ #### [v1.0.2](https://github.com/bas080/sendscript/compare/v1.0.1...v1.0.2)
8
+
9
+ - Improve the typescript example [`6f75ed6`](https://github.com/bas080/sendscript/commit/6f75ed6a4b4db94217fde41ae50bc6b92a2cffda)
10
+ - Move errors to own module [`90d69a3`](https://github.com/bas080/sendscript/commit/90d69a337f92518c8dd5c2ad47dd9b1add80d0c3)
11
+
12
+ #### [v1.0.1](https://github.com/bas080/sendscript/compare/v1.0.0...v1.0.1)
13
+
14
+ > 3 May 2025
15
+
16
+ - Add async await section to readme [`7e56024`](https://github.com/bas080/sendscript/commit/7e560246e2a7b5b34f1de0ce5eb0df62897cc454)
17
+
18
+ ### [v1.0.0](https://github.com/bas080/sendscript/compare/v0.1.4...v1.0.0)
19
+
20
+ > 3 May 2025
8
21
 
9
22
  - Add support for async await [`e949184`](https://github.com/bas080/sendscript/commit/e949184ab31a3bfdf4c6181463dcdd7250aca3a8)
10
23
  - Update tap to latest version on 2024-03-17 [`1164994`](https://github.com/bas080/sendscript/commit/11649947737ffd152ac46d1db01946ad0c1261e7)
package/README.md CHANGED
@@ -13,6 +13,7 @@ Write JS code that you can run on servers, browsers or other clients.
13
13
  * [Module](#module)
14
14
  * [Server](#server)
15
15
  * [Client](#client)
16
+ - [Async/Await](#asyncawait)
16
17
  - [TypeScript](#typescript)
17
18
  - [Tests](#tests)
18
19
  - [Formatting](#formatting)
@@ -135,6 +136,25 @@ pkill sendscript
135
136
  Result: 100
136
137
  ```
137
138
 
139
+ ## Async/Await
140
+
141
+ 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.
142
+
143
+ While it's possible to chain promises manually or use utility functions, native async/await support makes your code more readable, modern, and easier to reason about — aligning SendScript with today’s JavaScript best practices.
144
+
145
+ ```js
146
+ const userId = 'user-123'
147
+ const program = {
148
+ unread: await fetchUnreadMessages(userId),
149
+ emptyTrash: await emptyTrash(userId),
150
+ archived: await archiveMessages(selectMessages({ old: true }))
151
+ }
152
+
153
+ const result = await send(program)
154
+ ```
155
+
156
+ This operation is done in a single round-trip. The result is an object with the defined properties and returned values.
157
+
138
158
  ## TypeScript
139
159
 
140
160
  There is a good use-case to write a module in TypeScript.
@@ -146,24 +166,67 @@ There is a good use-case to write a module in TypeScript.
146
166
  3. You can use the types of the module to coerce your client to adopt the
147
167
  module's type.
148
168
 
169
+ Let's say we have this module which we use on the server.
170
+
149
171
  ```bash
150
- # Create pretty docs for your module.
151
- npx typedoc my-module.ts
172
+ cat ./example/typescript/math.ts
173
+ ```
174
+ ```ts
175
+ export const add = (a: number, b: number) => a + b
176
+ export const square = (a: number) => a * a
152
177
  ```
153
178
 
154
- Now we can use the `my-module.ts` file for the client API.
179
+ We want to use this module on the client. We create a client version of that module and coerce the types to match those of the server.
155
180
 
181
+ ```bash
182
+ cat ./example/typescript/math.client.ts
183
+ ```
156
184
  ```ts
157
- import type * as MyModule from './my-module'
185
+ import module from 'sendscript/module.mjs'
186
+ import type * as mathTypes from './math.ts'
158
187
 
159
- import sendScriptApi from 'sendscript/api.mjs'
188
+ const math = module([
189
+ 'add',
190
+ 'square'
191
+ ]) as typeof mathTypes
160
192
 
161
- export default sendScriptApi([
162
- fnOne,
163
- fnTwo,
164
- ], /* perform websocket request */) as typeof MyModule
193
+ export default math
165
194
  ```
166
195
 
196
+ We now use the client version of this module.
197
+
198
+ ```bash
199
+ cat ./example/typescript/client.ts
200
+ ```
201
+ ```ts
202
+ import stringify from 'sendscript/stringify.mjs'
203
+
204
+ async function send<T>(program: T): Promise<T>{
205
+ return (await fetch('/api', {
206
+ method: 'POST',
207
+ body: stringify(program)
208
+ })).json()
209
+ }
210
+
211
+ import math from './math.client.ts'
212
+
213
+ const { add, square } = math
214
+
215
+ send(square(add(1, 2)))
216
+ ```
217
+
218
+ We'll also generate the docs for this module.
219
+
220
+ ```bash
221
+ typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./example/typescript/math.ts
222
+ ```
223
+ ```
224
+ [info] Loaded plugin typedoc-plugin-markdown
225
+ [info] markdown generated at ./example/typescript/docs
226
+ ```
227
+
228
+ You can see the docs [here](./example/typescript/docs/globals.md)
229
+
167
230
  > [!NOTE]
168
231
  > Although type coercion on the client side can improve the development
169
232
  > experience, it does not represent the actual type.
@@ -179,19 +242,19 @@ npm t -- report text-summary
179
242
  ```
180
243
  ```
181
244
 
182
- > sendscript@1.0.0 test
245
+ > sendscript@1.0.2 test
183
246
  > tap -R silent
184
247
 
185
248
 
186
- > sendscript@1.0.0 test
249
+ > sendscript@1.0.2 test
187
250
  > tap report text-summary
188
251
 
189
252
 
190
253
  =============================== Coverage summary ===============================
191
- Statements : 100% ( 239/239 )
192
- Branches : 100% ( 71/71 )
254
+ Statements : 100% ( 245/245 )
255
+ Branches : 100% ( 74/74 )
193
256
  Functions : 100% ( 18/18 )
194
- Lines : 100% ( 239/239 )
257
+ Lines : 100% ( 245/245 )
195
258
  ================================================================================
196
259
  ```
197
260
 
package/README.mz CHANGED
@@ -118,6 +118,25 @@ node ./example/client.socket.io.mjs
118
118
  pkill sendscript
119
119
  ```
120
120
 
121
+ ## Async/Await
122
+
123
+ 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.
124
+
125
+ While it's possible to chain promises manually or use utility functions, native async/await support makes your code more readable, modern, and easier to reason about — aligning SendScript with today’s JavaScript best practices.
126
+
127
+ ```js
128
+ const userId = 'user-123'
129
+ const program = {
130
+ unread: await fetchUnreadMessages(userId),
131
+ emptyTrash: await emptyTrash(userId),
132
+ archived: await archiveMessages(selectMessages({ old: true }))
133
+ }
134
+
135
+ const result = await send(program)
136
+ ```
137
+
138
+ This operation is done in a single round-trip. The result is an object with the defined properties and returned values.
139
+
121
140
  ## TypeScript
122
141
 
123
142
  There is a good use-case to write a module in TypeScript.
@@ -129,24 +148,32 @@ There is a good use-case to write a module in TypeScript.
129
148
  3. You can use the types of the module to coerce your client to adopt the
130
149
  module's type.
131
150
 
132
- ```bash
133
- # Create pretty docs for your module.
134
- npx typedoc my-module.ts
151
+ Let's say we have this module which we use on the server.
152
+
153
+ ```bash|ts bash
154
+ cat ./example/typescript/math.ts
155
+ ```
156
+
157
+ We want to use this module on the client. We create a client version of that module and coerce the types to match those of the server.
158
+
159
+ ```bash|ts bash
160
+ cat ./example/typescript/math.client.ts
135
161
  ```
136
162
 
137
- Now we can use the `my-module.ts` file for the client API.
163
+ We now use the client version of this module.
138
164
 
139
- ```ts
140
- import type * as MyModule from './my-module'
165
+ ```bash|ts bash
166
+ cat ./example/typescript/client.ts
167
+ ```
141
168
 
142
- import sendScriptApi from 'sendscript/api.mjs'
169
+ We'll also generate the docs for this module.
143
170
 
144
- export default sendScriptApi([
145
- fnOne,
146
- fnTwo,
147
- ], /* perform websocket request */) as typeof MyModule
171
+ ```bash bash
172
+ typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./example/typescript/math.ts
148
173
  ```
149
174
 
175
+ You can see the docs [here](./example/typescript/docs/globals.md)
176
+
150
177
  > [!NOTE]
151
178
  > Although type coercion on the client side can improve the development
152
179
  > experience, it does not represent the actual type.
package/error.mjs CHANGED
@@ -1,9 +1,2 @@
1
- class BlendError extends Error { }
2
- class BlendExpressionError extends BlendError {}
3
- class BlendInvalidSchemaError extends BlendError {}
4
-
5
- export {
6
- BlendError,
7
- BlendExpressionError,
8
- BlendInvalidSchemaError
9
- }
1
+ export class SendScriptError extends Error {}
2
+ export class SendScriptReferenceError extends SendScriptError {}
@@ -0,0 +1,14 @@
1
+ import stringify from 'sendscript/stringify.mjs'
2
+
3
+ async function send<T>(program: T): Promise<T>{
4
+ return (await fetch('/api', {
5
+ method: 'POST',
6
+ body: stringify(program)
7
+ })).json()
8
+ }
9
+
10
+ import math from './math.client.ts'
11
+
12
+ const { add, square } = math
13
+
14
+ send(square(add(1, 2)))
@@ -0,0 +1,207 @@
1
+ **sendscript**
2
+
3
+ ***
4
+
5
+ # SendScript
6
+
7
+ Write JS code that you can run on servers, browsers or other clients.
8
+
9
+ [![NPM](https://img.shields.io/npm/v/sendscript?color=blue&style=flat-square)](https://www.npmjs.com/package/sendscript)
10
+ [![100% Code Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square)](#tests)
11
+ [![Standard Code Style](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com)
12
+ [![License](https://img.shields.io/npm/l/sendscript?color=brightgreen&style=flat-square)](./LICENSE.txt)
13
+
14
+ <!-- toc -->
15
+
16
+ SendScript leaves it up to you to choose HTTP, web-sockets or any other
17
+ method of communication between servers and clients that best fits your
18
+ needs.
19
+
20
+ ## Socket example
21
+
22
+ For this example we'll use [socket.io][socket.io].
23
+
24
+ ```bash
25
+ npm install --no-save socket.io socket.io-client
26
+ ```
27
+
28
+ > We use the `--no-save` option because it's only for demonstration purposes.
29
+
30
+ ### Module
31
+
32
+ We write a simple module.
33
+
34
+ ```js
35
+ // ./example/math.mjs
36
+
37
+ export const add = (a, b) => a + b
38
+ export const square = a => a * a
39
+ ```
40
+
41
+ ### Server
42
+
43
+ Here a socket.io server that runs SendScript programs.
44
+
45
+ ```js
46
+ // ./example/server.socket.io.mjs
47
+
48
+ import { Server } from 'socket.io'
49
+ import Parse from '../parse.mjs'
50
+ import * as math from './math.mjs'
51
+
52
+ const parse = Parse(math)
53
+ const server = new Server()
54
+ const port = process.env.PORT || 3000
55
+
56
+ server.on('connection', (socket) => {
57
+ socket.on('message', async (program, callback) => {
58
+ try {
59
+ const result = parse(program)
60
+ callback(null, result) // Pass null as the first argument to indicate success
61
+ } catch (error) {
62
+ callback(error) // Pass the error to the callback
63
+ }
64
+ })
65
+ })
66
+
67
+ server.listen(port)
68
+ process.title = 'sendscript'
69
+ ```
70
+
71
+ ### Client
72
+
73
+ Now for a client that sends a program to the server.
74
+
75
+ ```js
76
+ // ./example/client.socket.io.mjs
77
+
78
+ import socketClient from 'socket.io-client'
79
+ import stringify from '../stringify.mjs'
80
+ import module from '../module.mjs'
81
+ import * as math from './math.mjs'
82
+ import assert from 'node:assert'
83
+
84
+ const port = process.env.PORT || 3000
85
+ const client = socketClient(`http://localhost:${port}`)
86
+
87
+ const send = program => {
88
+ return new Promise((resolve, reject) => {
89
+ client.emit('message', stringify(program), (error, result) => {
90
+ error
91
+ ? reject(error)
92
+ : resolve(result)
93
+ })
94
+ })
95
+ }
96
+
97
+ const { add, square } = module(math)
98
+
99
+ // The program to be sent over the wire
100
+ const program = square(add(1, add(add(2, 3), 4)))
101
+
102
+ const result = await send(program)
103
+
104
+ console.log('Result: ', result)
105
+
106
+ assert.equal(result, 100)
107
+
108
+ process.exit(0)
109
+ ```
110
+
111
+ Now we run this server and a client script.
112
+
113
+ ```bash
114
+ set -e
115
+
116
+ # Run the server
117
+ node ./example/server.socket.io.mjs&
118
+
119
+ # Run the client example
120
+ node ./example/client.socket.io.mjs
121
+
122
+ pkill sendscript
123
+ ```
124
+ ```
125
+ Result: 100
126
+ ```
127
+
128
+ ## Async/Await
129
+
130
+ 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.
131
+
132
+ While it's possible to chain promises manually or use utility functions, native async/await support makes your code more readable, modern, and easier to reason about — aligning SendScript with today’s JavaScript best practices.
133
+
134
+ ```js
135
+ const userId = 'user-123'
136
+ const program = {
137
+ unread: await fetchUnreadMessages(userId),
138
+ emptyTrash: await emptyTrash(userId),
139
+ archived: await archiveMessages(selectMessages({ old: true }))
140
+ }
141
+
142
+ const result = await send(program)
143
+ ```
144
+
145
+ This operation is done in a single round-trip. The result is an object with the defined properties and returned values.
146
+
147
+ ## TypeScript
148
+
149
+ There is a good use-case to write a module in TypeScript.
150
+
151
+ 1. Obviously the module would have the benefits that TypeScript offers when
152
+ coding.
153
+ 2. You can use tools like [typedoc][typedoc] to generate docs from your types to
154
+ share with consumers of your API.
155
+ 3. You can use the types of the module to coerce your client to adopt the
156
+ module's type.
157
+
158
+ Let's say we have this module which we use on the server.
159
+
160
+ ```bash
161
+ cat ./example/typescript/math.ts
162
+ ```
163
+ ```ts
164
+ export const add = (a: number, b: number) => a + b
165
+ export const square = (a: number) => a * a
166
+ ```
167
+
168
+ We want to use this module on the client. We create a client version of that module and coerce the types to match those of the server.
169
+
170
+ ```bash
171
+ cat ./example/typescript/math.client.ts
172
+ ```
173
+ ```ts
174
+ import module from 'sendscript/module.mjs'
175
+ import type * as mathTypes from './math.ts'
176
+
177
+ const math = module([
178
+ 'add',
179
+ 'square'
180
+ ]) as typeof mathTypes
181
+
182
+ export default math
183
+ ```
184
+
185
+ We now use the client version of this module.
186
+
187
+ ```bash
188
+ cat ./example/typescript/client.ts
189
+ ```
190
+ ```ts
191
+ import stringify from 'sendscript/stringify.mjs'
192
+
193
+ async function send<T>(program: T): Promise<T>{
194
+ return (await fetch('/api', {
195
+ method: 'POST',
196
+ body: stringify(program)
197
+ })).json()
198
+ }
199
+
200
+ import math from './math.client.ts'
201
+
202
+ const { add, square } = math
203
+
204
+ send(square(add(1, 2)))
205
+ ```
206
+
207
+ We'll also generate the docs for this module.
@@ -0,0 +1,25 @@
1
+ [**sendscript**](../README.md)
2
+
3
+ ***
4
+
5
+ [sendscript](../globals.md) / add
6
+
7
+ # Function: add()
8
+
9
+ > **add**(`a`, `b`): `number`
10
+
11
+ Defined in: [math.ts:1](https://github.com/bas080/sendscript/blob/6f75ed6a4b4db94217fde41ae50bc6b92a2cffda/example/typescript/math.ts#L1)
12
+
13
+ ## Parameters
14
+
15
+ ### a
16
+
17
+ `number`
18
+
19
+ ### b
20
+
21
+ `number`
22
+
23
+ ## Returns
24
+
25
+ `number`
@@ -0,0 +1,21 @@
1
+ [**sendscript**](../README.md)
2
+
3
+ ***
4
+
5
+ [sendscript](../globals.md) / square
6
+
7
+ # Function: square()
8
+
9
+ > **square**(`a`): `number`
10
+
11
+ Defined in: [math.ts:2](https://github.com/bas080/sendscript/blob/6f75ed6a4b4db94217fde41ae50bc6b92a2cffda/example/typescript/math.ts#L2)
12
+
13
+ ## Parameters
14
+
15
+ ### a
16
+
17
+ `number`
18
+
19
+ ## Returns
20
+
21
+ `number`
@@ -0,0 +1,10 @@
1
+ [**sendscript**](README.md)
2
+
3
+ ***
4
+
5
+ # sendscript
6
+
7
+ ## Functions
8
+
9
+ - [add](functions/add.md)
10
+ - [square](functions/square.md)
@@ -0,0 +1,9 @@
1
+ import module from 'sendscript/module.mjs'
2
+ import type * as mathTypes from './math.ts'
3
+
4
+ const math = module([
5
+ 'add',
6
+ 'square'
7
+ ]) as typeof mathTypes
8
+
9
+ export default math
@@ -0,0 +1,2 @@
1
+ export const add = (a: number, b: number) => a + b
2
+ export const square = (a: number) => a * a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sendscript",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Blur the line between server and client code.",
5
5
  "module": true,
6
6
  "main": "index.mjs",
package/parse.mjs CHANGED
@@ -1,8 +1,6 @@
1
1
  import Debug from './debug.mjs'
2
2
  import isNil from './is-nil.mjs'
3
-
4
- class SendScriptError extends Error {}
5
- class SendScriptReferenceError extends SendScriptError {}
3
+ import { SendScriptReferenceError } from './error.mjs'
6
4
 
7
5
  const debug = Debug.extend('parse')
8
6
 
@@ -14,13 +12,19 @@ export default (env) =>
14
12
  const resolved = {}
15
13
 
16
14
  JSON.parse(program, (key, value) => {
17
- if (Array.isArray(value) && value[0] === 'await') {
18
- awaits.push(((program, awaitId) => async () => {
19
- const value = await JSON.parse(JSON.stringify(program), reviver)
20
- resolved[awaitId] = value
15
+ if (!Array.isArray(value)) return value
16
+
17
+ const [operator, ...rest] = value
21
18
 
22
- debug('awaits', awaits)
23
- })(value[1], value[2]))
19
+ if (operator === 'await') {
20
+ const [program, awaitId] = rest
21
+
22
+ awaits.push(((program, awaitId) => async () => {
23
+ resolved[awaitId] = await JSON.parse(
24
+ JSON.stringify(program),
25
+ reviver
26
+ )
27
+ })(program, awaitId))
24
28
  }
25
29
 
26
30
  return value