sendscript 1.0.1 → 1.0.3

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,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
+ #### [v1.0.3](https://github.com/bas080/sendscript/compare/v1.0.2...v1.0.3)
8
+
9
+ - Make examples import module not relative file [`0a5e1c8`](https://github.com/bas080/sendscript/commit/0a5e1c8ec2527d63d1e248f63568668058de388c)
10
+ - Fix repository url in package.json [`d4f1e79`](https://github.com/bas080/sendscript/commit/d4f1e79a75daea28905c9b680b31e517526588d3)
11
+
12
+ #### [v1.0.2](https://github.com/bas080/sendscript/compare/v1.0.1...v1.0.2)
13
+
14
+ > 4 May 2025
15
+
16
+ - Improve the typescript example [`6f75ed6`](https://github.com/bas080/sendscript/commit/6f75ed6a4b4db94217fde41ae50bc6b92a2cffda)
17
+ - Move errors to own module [`90d69a3`](https://github.com/bas080/sendscript/commit/90d69a337f92518c8dd5c2ad47dd9b1add80d0c3)
18
+
7
19
  #### [v1.0.1](https://github.com/bas080/sendscript/compare/v1.0.0...v1.0.1)
8
20
 
21
+ > 3 May 2025
22
+
9
23
  - Add async await section to readme [`7e56024`](https://github.com/bas080/sendscript/commit/7e560246e2a7b5b34f1de0ce5eb0df62897cc454)
10
24
 
11
25
  ### [v1.0.0](https://github.com/bas080/sendscript/compare/v0.1.4...v1.0.0)
package/README.md CHANGED
@@ -33,7 +33,11 @@ needs.
33
33
  For this example we'll use [socket.io][socket.io].
34
34
 
35
35
  ```bash
36
- npm install --no-save socket.io socket.io-client
36
+ npm link &&
37
+ npm install --no-save \
38
+ socket.io \
39
+ socket.io-client \
40
+ sendscript
37
41
  ```
38
42
 
39
43
  > We use the `--no-save` option because it's only for demonstration purposes.
@@ -57,7 +61,7 @@ Here a socket.io server that runs SendScript programs.
57
61
  // ./example/server.socket.io.mjs
58
62
 
59
63
  import { Server } from 'socket.io'
60
- import Parse from '../parse.mjs'
64
+ import Parse from 'sendscript/parse.mjs'
61
65
  import * as math from './math.mjs'
62
66
 
63
67
  const parse = Parse(math)
@@ -87,8 +91,8 @@ Now for a client that sends a program to the server.
87
91
  // ./example/client.socket.io.mjs
88
92
 
89
93
  import socketClient from 'socket.io-client'
90
- import stringify from '../stringify.mjs'
91
- import module from '../module.mjs'
94
+ import stringify from 'sendscript/stringify.mjs'
95
+ import module from 'sendscript/module.mjs'
92
96
  import * as math from './math.mjs'
93
97
  import assert from 'node:assert'
94
98
 
@@ -166,16 +170,66 @@ There is a good use-case to write a module in TypeScript.
166
170
  3. You can use the types of the module to coerce your client to adopt the
167
171
  module's type.
168
172
 
173
+ Let's say we have this module which we use on the server.
174
+
175
+ ```bash
176
+ cat ./example/typescript/math.ts
177
+ ```
169
178
  ```ts
170
- import type * as math from './example/math.ts'
179
+ export const add = (a: number, b: number) => a + b
180
+ export const square = (a: number) => a * a
181
+ ```
182
+
183
+ 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.
171
184
 
185
+ ```bash
186
+ cat ./example/typescript/math.client.ts
187
+ ```
188
+ ```ts
172
189
  import module from 'sendscript/module.mjs'
190
+ import type * as mathTypes from './math.ts'
173
191
 
174
- export default module([
175
- add,
176
- squer,
177
- ]) as typeof math
192
+ const math = module([
193
+ 'add',
194
+ 'square'
195
+ ]) as typeof mathTypes
196
+
197
+ export default math
198
+ ```
199
+
200
+ We now use the client version of this module.
201
+
202
+ ```bash
203
+ cat ./example/typescript/client.ts
178
204
  ```
205
+ ```ts
206
+ import stringify from 'sendscript/stringify.mjs'
207
+
208
+ async function send<T>(program: T): Promise<T>{
209
+ return (await fetch('/api', {
210
+ method: 'POST',
211
+ body: stringify(program)
212
+ })).json()
213
+ }
214
+
215
+ import math from './math.client.ts'
216
+
217
+ const { add, square } = math
218
+
219
+ send(square(add(1, 2)))
220
+ ```
221
+
222
+ We'll also generate the docs for this module.
223
+
224
+ ```bash
225
+ npm install --no-save \
226
+ typedoc \
227
+ typedoc-plugin-markdown
228
+
229
+ typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./example/typescript/math.ts
230
+ ```
231
+
232
+ You can see the docs [here](./example/typescript/docs/globals.md)
179
233
 
180
234
  > [!NOTE]
181
235
  > Although type coercion on the client side can improve the development
@@ -192,19 +246,19 @@ npm t -- report text-summary
192
246
  ```
193
247
  ```
194
248
 
195
- > sendscript@1.0.1 test
249
+ > sendscript@1.0.3 test
196
250
  > tap -R silent
197
251
 
198
252
 
199
- > sendscript@1.0.1 test
253
+ > sendscript@1.0.3 test
200
254
  > tap report text-summary
201
255
 
202
256
 
203
257
  =============================== Coverage summary ===============================
204
- Statements : 100% ( 239/239 )
205
- Branches : 100% ( 71/71 )
258
+ Statements : 100% ( 245/245 )
259
+ Branches : 100% ( 74/74 )
206
260
  Functions : 100% ( 18/18 )
207
- Lines : 100% ( 239/239 )
261
+ Lines : 100% ( 245/245 )
208
262
  ================================================================================
209
263
  ```
210
264
 
package/README.mz CHANGED
@@ -18,7 +18,11 @@ needs.
18
18
  For this example we'll use [socket.io][socket.io].
19
19
 
20
20
  ```bash bash > /dev/null
21
- npm install --no-save socket.io socket.io-client
21
+ npm link &&
22
+ npm install --no-save \
23
+ socket.io \
24
+ socket.io-client \
25
+ sendscript
22
26
  ```
23
27
 
24
28
  > We use the `--no-save` option because it's only for demonstration purposes.
@@ -42,7 +46,7 @@ Here a socket.io server that runs SendScript programs.
42
46
  // ./example/server.socket.io.mjs
43
47
 
44
48
  import { Server } from 'socket.io'
45
- import Parse from '../parse.mjs'
49
+ import Parse from 'sendscript/parse.mjs'
46
50
  import * as math from './math.mjs'
47
51
 
48
52
  const parse = Parse(math)
@@ -72,8 +76,8 @@ Now for a client that sends a program to the server.
72
76
  // ./example/client.socket.io.mjs
73
77
 
74
78
  import socketClient from 'socket.io-client'
75
- import stringify from '../stringify.mjs'
76
- import module from '../module.mjs'
79
+ import stringify from 'sendscript/stringify.mjs'
80
+ import module from 'sendscript/module.mjs'
77
81
  import * as math from './math.mjs'
78
82
  import assert from 'node:assert'
79
83
 
@@ -148,17 +152,36 @@ There is a good use-case to write a module in TypeScript.
148
152
  3. You can use the types of the module to coerce your client to adopt the
149
153
  module's type.
150
154
 
151
- ```ts
152
- import type * as math from './example/math.ts'
155
+ Let's say we have this module which we use on the server.
153
156
 
154
- import module from 'sendscript/module.mjs'
157
+ ```bash|ts bash
158
+ cat ./example/typescript/math.ts
159
+ ```
160
+
161
+ 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.
162
+
163
+ ```bash|ts bash
164
+ cat ./example/typescript/math.client.ts
165
+ ```
155
166
 
156
- export default module([
157
- add,
158
- squer,
159
- ]) as typeof math
167
+ We now use the client version of this module.
168
+
169
+ ```bash|ts bash
170
+ cat ./example/typescript/client.ts
171
+ ```
172
+
173
+ We'll also generate the docs for this module.
174
+
175
+ ```bash bash 1>&2
176
+ npm install --no-save \
177
+ typedoc \
178
+ typedoc-plugin-markdown
179
+
180
+ typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./example/typescript/math.ts
160
181
  ```
161
182
 
183
+ You can see the docs [here](./example/typescript/docs/globals.md)
184
+
162
185
  > [!NOTE]
163
186
  > Although type coercion on the client side can improve the development
164
187
  > 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 {}
@@ -1,8 +1,8 @@
1
1
  // ./example/client.socket.io.mjs
2
2
 
3
3
  import socketClient from 'socket.io-client'
4
- import stringify from '../stringify.mjs'
5
- import module from '../module.mjs'
4
+ import stringify from 'sendscript/stringify.mjs'
5
+ import module from 'sendscript/module.mjs'
6
6
  import * as math from './math.mjs'
7
7
  import assert from 'node:assert'
8
8
 
@@ -1,7 +1,7 @@
1
1
  // ./example/server.socket.io.mjs
2
2
 
3
3
  import { Server } from 'socket.io'
4
- import Parse from '../parse.mjs'
4
+ import Parse from 'sendscript/parse.mjs'
5
5
  import * as math from './math.mjs'
6
6
 
7
7
  const parse = Parse(math)
@@ -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,211 @@
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 link &&
26
+ npm install --no-save \
27
+ socket.io \
28
+ socket.io-client \
29
+ sendscript
30
+ ```
31
+
32
+ > We use the `--no-save` option because it's only for demonstration purposes.
33
+
34
+ ### Module
35
+
36
+ We write a simple module.
37
+
38
+ ```js
39
+ // ./example/math.mjs
40
+
41
+ export const add = (a, b) => a + b
42
+ export const square = a => a * a
43
+ ```
44
+
45
+ ### Server
46
+
47
+ Here a socket.io server that runs SendScript programs.
48
+
49
+ ```js
50
+ // ./example/server.socket.io.mjs
51
+
52
+ import { Server } from 'socket.io'
53
+ import Parse from 'sendscript/parse.mjs'
54
+ import * as math from './math.mjs'
55
+
56
+ const parse = Parse(math)
57
+ const server = new Server()
58
+ const port = process.env.PORT || 3000
59
+
60
+ server.on('connection', (socket) => {
61
+ socket.on('message', async (program, callback) => {
62
+ try {
63
+ const result = parse(program)
64
+ callback(null, result) // Pass null as the first argument to indicate success
65
+ } catch (error) {
66
+ callback(error) // Pass the error to the callback
67
+ }
68
+ })
69
+ })
70
+
71
+ server.listen(port)
72
+ process.title = 'sendscript'
73
+ ```
74
+
75
+ ### Client
76
+
77
+ Now for a client that sends a program to the server.
78
+
79
+ ```js
80
+ // ./example/client.socket.io.mjs
81
+
82
+ import socketClient from 'socket.io-client'
83
+ import stringify from 'sendscript/stringify.mjs'
84
+ import module from 'sendscript/module.mjs'
85
+ import * as math from './math.mjs'
86
+ import assert from 'node:assert'
87
+
88
+ const port = process.env.PORT || 3000
89
+ const client = socketClient(`http://localhost:${port}`)
90
+
91
+ const send = program => {
92
+ return new Promise((resolve, reject) => {
93
+ client.emit('message', stringify(program), (error, result) => {
94
+ error
95
+ ? reject(error)
96
+ : resolve(result)
97
+ })
98
+ })
99
+ }
100
+
101
+ const { add, square } = module(math)
102
+
103
+ // The program to be sent over the wire
104
+ const program = square(add(1, add(add(2, 3), 4)))
105
+
106
+ const result = await send(program)
107
+
108
+ console.log('Result: ', result)
109
+
110
+ assert.equal(result, 100)
111
+
112
+ process.exit(0)
113
+ ```
114
+
115
+ Now we run this server and a client script.
116
+
117
+ ```bash
118
+ set -e
119
+
120
+ # Run the server
121
+ node ./example/server.socket.io.mjs&
122
+
123
+ # Run the client example
124
+ node ./example/client.socket.io.mjs
125
+
126
+ pkill sendscript
127
+ ```
128
+ ```
129
+ Result: 100
130
+ ```
131
+
132
+ ## Async/Await
133
+
134
+ 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.
135
+
136
+ 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.
137
+
138
+ ```js
139
+ const userId = 'user-123'
140
+ const program = {
141
+ unread: await fetchUnreadMessages(userId),
142
+ emptyTrash: await emptyTrash(userId),
143
+ archived: await archiveMessages(selectMessages({ old: true }))
144
+ }
145
+
146
+ const result = await send(program)
147
+ ```
148
+
149
+ This operation is done in a single round-trip. The result is an object with the defined properties and returned values.
150
+
151
+ ## TypeScript
152
+
153
+ There is a good use-case to write a module in TypeScript.
154
+
155
+ 1. Obviously the module would have the benefits that TypeScript offers when
156
+ coding.
157
+ 2. You can use tools like [typedoc][typedoc] to generate docs from your types to
158
+ share with consumers of your API.
159
+ 3. You can use the types of the module to coerce your client to adopt the
160
+ module's type.
161
+
162
+ Let's say we have this module which we use on the server.
163
+
164
+ ```bash
165
+ cat ./example/typescript/math.ts
166
+ ```
167
+ ```ts
168
+ export const add = (a: number, b: number) => a + b
169
+ export const square = (a: number) => a * a
170
+ ```
171
+
172
+ 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.
173
+
174
+ ```bash
175
+ cat ./example/typescript/math.client.ts
176
+ ```
177
+ ```ts
178
+ import module from 'sendscript/module.mjs'
179
+ import type * as mathTypes from './math.ts'
180
+
181
+ const math = module([
182
+ 'add',
183
+ 'square'
184
+ ]) as typeof mathTypes
185
+
186
+ export default math
187
+ ```
188
+
189
+ We now use the client version of this module.
190
+
191
+ ```bash
192
+ cat ./example/typescript/client.ts
193
+ ```
194
+ ```ts
195
+ import stringify from 'sendscript/stringify.mjs'
196
+
197
+ async function send<T>(program: T): Promise<T>{
198
+ return (await fetch('/api', {
199
+ method: 'POST',
200
+ body: stringify(program)
201
+ })).json()
202
+ }
203
+
204
+ import math from './math.client.ts'
205
+
206
+ const { add, square } = math
207
+
208
+ send(square(add(1, 2)))
209
+ ```
210
+
211
+ 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/0a5e1c8ec2527d63d1e248f63568668058de388c/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/0a5e1c8ec2527d63d1e248f63568668058de388c/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.1",
3
+ "version": "1.0.3",
4
4
  "description": "Blur the line between server and client code.",
5
5
  "module": true,
6
6
  "main": "index.mjs",
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "git@github.com:bas080/sendscript.git"
16
+ "url": "git+ssh://git@github.com/bas080/sendscript.git"
17
17
  },
18
18
  "scripts": {
19
19
  "test": "tap",
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