Write JS code that you can run on servers, browsers or other clients.
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.
For this example we'll use socket.io.
npm link &&
npm install --no-save \
socket.io \
socket.io-client \
sendscript
We use the
--no-save
option because it's only for demonstration purposes.
We write a simple module.
// ./example/math.mjs
export const add = (a, b) => a + b
export const square = a => a * a
Here a socket.io server that runs SendScript programs.
// ./example/server.socket.io.mjs
import { Server } from 'socket.io'
import Parse from 'sendscript/parse.mjs'
import * as math from './math.mjs'
const parse = Parse(math)
const server = new Server()
const port = process.env.PORT || 3000
server.on('connection', (socket) => {
socket.on('message', async (program, callback) => {
try {
const result = parse(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'
Now for a client that sends a program to the server.
// ./example/client.socket.io.mjs
import socketClient from 'socket.io-client'
import stringify from 'sendscript/stringify.mjs'
import module from 'sendscript/module.mjs'
import * as math from './math.mjs'
import assert from 'node:assert'
const port = process.env.PORT || 3000
const client = socketClient(`http://localhost:${port}`)
const send = program => {
return new Promise((resolve, reject) => {
client.emit('message', stringify(program), (error, result) => {
error
? reject(error)
: resolve(result)
})
})
}
const { add, square } = module(math)
// The program to be sent over the wire
const program = square(add(1, add(add(2, 3), 4)))
const result = await send(program)
console.log('Result: ', result)
assert.equal(result, 100)
process.exit(0)
Now we run this server and a client script.
set -e
# Run the server
node ./example/server.socket.io.mjs&
# Run the client example
node ./example/client.socket.io.mjs
pkill sendscript
Result: 100
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.
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.
const userId = 'user-123'
const program = {
unread: await fetchUnreadMessages(userId),
emptyTrash: await emptyTrash(userId),
archived: await archiveMessages(selectMessages({ old: true }))
}
const result = await send(program)
This operation is done in a single round-trip. The result is an object with the defined properties and returned values.
There is a good use-case to write a module in TypeScript.
- Obviously the module would have the benefits that TypeScript offers when coding.
- You can use tools like typedoc to generate docs from your types to share with consumers of your API.
- You can use the types of the module to coerce your client to adopt the module's type.
Let's say we have this module which we use on the server.
cat ./example/typescript/math.ts
export const add = (a: number, b: number) => a + b
export const square = (a: number) => a * a
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.
cat ./example/typescript/math.client.ts
import module from 'sendscript/module.mjs'
import type * as mathTypes from './math.ts'
const math = module([
'add',
'square'
]) as typeof mathTypes
export default math
We now use the client version of this module.
cat ./example/typescript/client.ts
import stringify from 'sendscript/stringify.mjs'
async function send<T>(program: T): Promise<T>{
return (await fetch('/api', {
method: 'POST',
body: stringify(program)
})).json()
}
import math from './math.client.ts'
const { add, square } = math
send(square(add(1, 2)))
We'll also generate the docs for this module.
npm install --no-save \
typedoc \
typedoc-plugin-markdown
typedoc --plugin typedoc-plugin-markdown --out ./example/typescript/docs ./example/typescript/math.ts
You can see the docs here
[!NOTE] Although type coercion on the client side can improve the development experience, it does not represent the actual type. Values are subject to serialization and deserialization.
Tests with 100% code coverage.
npm t -- -R silent
npm t -- report text-summary
> sendscript@1.0.3 test
> tap -R silent
> sendscript@1.0.3 test
> tap report text-summary
=============================== Coverage summary ===============================
Statements : 100% ( 245/245 )
Branches : 100% ( 74/74 )
Functions : 100% ( 18/18 )
Lines : 100% ( 245/245 )
================================================================================
Standard because no config.
npx standard
The changelog is generated using the useful auto-changelog project.
npx auto-changelog -p
Check if packages are up to date on release.
npm outdated && echo 'No outdated packages found'
No outdated packages found
See the LICENSE.txt file for details.
- [ ] Support for simple lambdas to compose functions more easily.