-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.mz
251 lines (174 loc) · 6.03 KB
/
README.mz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# SendScript
Write JS code that you can run on servers, browsers or other clients.
[![NPM](https://img.shields.io/npm/v/sendscript?color=blue&style=flat-square)](https://www.npmjs.com/package/sendscript)
[![100% Code Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square)](#tests)
[![Standard Code Style](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com)
[![License](https://img.shields.io/npm/l/sendscript?color=brightgreen&style=flat-square)](./LICENSE.txt)
<!-- toc -->
SendScript leaves it up to you to choose HTTP, web-sockets or any other
method of communication between servers and clients that best fits your
needs.
## Socket example
For this example we'll use [socket.io][socket.io].
```bash bash > /dev/null
npm install --no-save socket.io socket.io-client
```
> We use the `--no-save` option because it's only for demonstration purposes.
### Module
We write a simple module.
```js cat - > ./example/math.mjs
// ./example/math.mjs
export const add = (a, b) => a + b
export const square = a => a * a
```
### Server
Here a socket.io server that runs SendScript programs.
```js cat - > ./example/server.socket.io.mjs
// ./example/server.socket.io.mjs
import { Server } from 'socket.io'
import exec from '../exec.mjs'
import * as math from './math.mjs'
const server = new Server()
const port = process.env.PORT || 3000
server.on('connection', (socket) => {
socket.on('message', async (program, callback) => {
try {
const result = await exec(math, program)
callback(null, result) // Pass null as the first argument to indicate success
} catch (error) {
callback(error) // Pass the error to the callback
}
})
})
server.listen(port)
process.title = 'sendscript'
```
### Client
Now for a client that sends a program to the server.
```js cat - > ./example/client.socket.io.mjs
// ./example/client.socket.io.mjs
import socketClient from 'socket.io-client'
import api from '../api.mjs'
const port = process.env.PORT || 3000
const client = socketClient(`http://localhost:${port}`)
const exec = program => {
return new Promise((resolve, reject) => {
client.emit('message', program, (error, result) => {
error
? reject(error)
: resolve(result)
})
})
}
const { add, square } = api(['add', 'square'], exec)
console.log(
await square(add(1, add(add(2, 3), 4)))
)
process.exit(0)
```
Now we run this server and a client script.
```bash bash
# Run the server
node ./example/server.socket.io.mjs&
# Run the client example
node ./example/client.socket.io.mjs
pkill sendscript
```
## Reference
SendScript is essentially a way to serialize a program to then send over the
wire and execute it somewhere else.
We only have two modules. One that helps you write programs that can be sent
over the wire and another for running that program.
### `sendscript/api.mjs`
The api module exports a function that takes two arguments.
1. The schema, which represents the values that are available.
2. The function that will be called with the serializable version of the
program.
It returns an object that contains functions which are defined in the schema.
These functions are a JavaScript API for writing programs that can be sent to
a server.
```js
import api from './api.mjs'
const { add, subtract } = api(
['add', 'subtract'],
serializableProgram => sendSomewhereToBeExecuted(serializableProgram)
)
await add(1, 2) // => 3
await subtract(1, 2) // => -1
await add(1, subtract(2, 3)) // => 0
```
The add and subtract functions are thennable. The execute function is called as
soon as await or `.then` is used.
> Notice that you do not have to await the subtract call. You only need to
> await when you want to execute the program.
This API is composable and wrappable.
### `sendscript/exec.mjs`
The exec function takes an environment object and any valid SendScript program.
```js
import exec from './exec.mjs'
exec({
add: (a, b) => a + b,
subtract: (a, b) => a - b
}, ['add', 1, [subtract, 1, 2]])
```
The array you see here is the LISP that SendScript uses to represent programs.
You could use SendScript without knowing the details of how the LISP works. It
is an implementation detail and might change over time.
## TypeScript
There is a good use-case to write an environment module in TypeScript.
1. Obviously the module would have the benefits that TypeScript offers when
coding.
2. You can use tools like [typedoc][typedoc] to generate docs from your types to
share with consumers of your API.
3. You can use the types of the module to coerce your client to adopt the
modules type.
```bash
# Create pretty docs for your module.
npx typedoc my-module.ts
```
Now we can use the `my-module.ts` file for the client API.
```ts
import type * as MyModule from './my-module'
import sendScriptApi from 'sendscript/api.mjs'
export default sendScriptApi([
fnOne,
fnTwo,
], /* perform websocket request */) as typeof MyModule
```
> [!NOTE]
> Although type coercion on the client side can improve the development
> experience, it does not represent the actual type.
> Values are likely subject to serialization and deserialization,
> particularly when interfacing with JSON formats.
## Tests
Tests with 100% code coverage.
```bash bash
npm t -- -R silent
npm t -- report text-summary
```
## Formatting
Standard because no config.
```bash bash
npx standard
```
## Changelog
The [changelog][changelog] is generated using the useful
[auto-changelog][auto-changelog] project.
```bash bash > /dev/null
npx auto-changelog -p
```
## Dependencies
Check if packages are up to date on release.
```bash bash
npm outdated && echo 'No outdated packages found'
```
## License
See the [LICENSE.txt][license] file for details.
## Roadmap
- [ ] Support lambdas for users to defined callbacks.
- [ ] Support promises by not awaiting/resolving unless explicitly defined.
[license]:./LICENSE.txt
[socket.io]:https://socket.io/
[changelog]:./CHANGELOG.md
[auto-changelog]:https://www.npmjs.com/package/auto-changelog
[typedoc]:https://github.com/TypeStrong/typedoc