Async Call
async-call-rpc
is a JSON RPC server and client written in TypeScript for any ECMAScript environment.
Links
CHANGELOG.md | Document of AsyncCall | Document of AsyncGeneratorCall | Playground
Chapters:
Features
- Zero dependencies!
- Running in any ES6 environment, no requirement on any Web or Node API
- Simple to define a RPC server and simple to use as a RPC client
- Full TypeScript support
- Use a custom encoder to communicate with complex data types
- Support async generator (Require both server and client supports 4 JSON RPC internal methods listed below and async generator exists in the environment)
Cautions
- This package is shipping ECMAScript 2018 syntax (
async function
). - The async generator support leaks memory on the server. Use it with caution.
- NOT support ES5.
- NOT support JSON RPC 1.0
Installation
Install via npm
npm i async-call-rpc
yarn add async-call-rpc
pnpm i async-call-rpc
Install via jsr
This package is published as @works/json-rpc
on the jsr.
If you install it via jsr, you should import this package from "@works/json-rpc"
instead of "async-call-rpc"
deno add @works/json-rpc
npx jsr add @works/json-rpc
yarn dlx jsr add @works/json-rpc
pnpm dlx jsr add @works/json-rpc
bunx jsr add @works/json-rpc
Import from browser
You can access https://www.jsdelivr.com/package/npm/async-call-rpc?path=out to get the latest URL and SRI.
Note: the utils/
entrypoint is not published on the jsr.
import { AsyncCall } from 'https://cdn.jsdelivr.net/npm/async-call-rpc@latest/out/base.mjs'
UMD
<script src="https://cdn.jsdelivr.net/npm/async-call-rpc@2.0.1/out/base.js"></script>
<script>
const { AsyncCall } = globalThis.AsyncCall
</script>
Other JS environments
Load the out/base.mjs
(ES Module) or out/base.js
(UMD, CommonJS, or AMD) to your project.
channel
To communicate with the server/client, you need to implement one of the following interfaces:
There are some built-in channels you can simplify the usage.
The following document will assume you have defined your channel
.
encoder
This library does not have any opinionated data transmitting format. You need to implement one of the following interfaces:
Example
Server example
export function add(x: number, y: number) {
return x + y
}
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
import { AsyncCall } from 'async-call-rpc'
import * as server from './server.ts'
AsyncCall(server, { channel })
Client example
import { AsyncCall } from 'async-call-rpc'
import type * as server from './server.ts'
const server = AsyncCall<typeof server>({}, { channel })
server.add(2, 40).then(console.log)
Notifications and Batch requests
AsyncCall can send notifications.
Using notifications means results or remote errors are never sent back. Local errors will not be omitted, e.g. encoder errors or network errors.
import { AsyncCall, notify } from 'async-call-rpc'
const server = notify(AsyncCall<typeof server>({}, { channel }))
server.add(1, 2).then(console.log)
AsyncCall can send batch request too.
import { AsyncCall, batch } from 'async-call-rpc'
const [server, emit, drop] = batch(AsyncCall<typeof server>({}, { channel }))
const a = server.req1()
const b = server.req2()
const c = server.req3()
emit()
const d = server.req1()
drop()
Package entries
This library has 2 entries. base
and full
. base
is the default entry point. The full
version includes the AsyncGeneratorCall
but the base
version doesn't.
Browser / Deno
Please check out https://www.jsdelivr.com/package/npm/async-call-rpc?path=out
Node:
require('async-rpc-call/full')
import { } from 'async-rpc-call/full'
require('async-rpc-call')
import { } from 'async-rpc-call'
Builtin channels
They're not part of the core library but are provided as utils to increase usability.
(Node) WebSocket
(Deno) WebSocket
(Web) WebSocket
| Server & Client |
---|
Entry point | https://cdn.jsdelivr.net/npm/async-call-rpc@latest/utils/web/broadcast.channel.js (Source code) |
Entry point type | ES Module |
Dependencies | Nothing |
Example | TBD |
Main thread: new WorkerChannel(new Worker(...))
Worker: new WorkerChannel()
Implemented JSON RPC internal methods
These four methods are used to implement AsyncGeneratorCall
support.
interface JSONRPC_Internal_Methods {
'rpc.async-iterator.start'(method: string, params: unknown[]): Promise<string>
'rpc.async-iterator.next'(id: string, value: unknown): Promise<IteratorResult<unknown>>
'rpc.async-iterator.return'(id: string, value: unknown): Promise<IteratorResult<unknown>>
'rpc.async-iterator.throw'(id: string, value: unknown): Promise<IteratorResult<unknown>>
}
Non-standard extension to JSON RPC specification
remoteStack on the Request object
This library can send the client the call stack to the server to make the logger better.
Controlled by option.log.sendLocalStack
. Default to false
.
interface JSONRPC_Request_object {
remoteStack?: string
}
"undef" on Response object
This is a non-standard property that appears when using the deprecated JSONSerialization due to JSON doesn't support undefined
. It's a hint to the client, that the result is undefined
.
This behavior is controlled by the 3rd parameter of JSONSerialization(replacerAndReceiver?, space?, undefinedKeepingBehavior?: false | "keep" | "null" = "null"). Default to "null"
. To turn on this feature to "keep" undefined values, change the 3rd option to "keep".
interface JSONRPC_Response_object {
undef?: boolean
}
The implementation-defined Error data
In the JSON RPC specification, this is implementation-defined. This is controlled by the option options.mapError
This library will try to "Recover" the Error object if there is enough information from another side.
interface JSONRPC_Error_object {
data?: {
stack?: string
type?:
| string
| 'Error'
| 'EvalError'
| 'RangeError'
| 'ReferenceError'
| 'SyntaxError'
| 'TypeError'
| 'URIError'
| 'DOMException'
}
}