async-call-rpc
is aJSON RPCserver and client written in TypeScript for any ECMAScript environment.
CHANGELOG.md|Document of AsyncCall|Document of AsyncGeneratorCall|Playground
Chapters:
- Installation
channel
,how to communicateencoder
,how to use complex data types- Example
- Notifications and Batch requests
- Package entries
- Builtin
channels
(including WebSocket) - Implemented JSON RPC internal methods
- Non-standard extension to JSON RPC specification
- 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 supports4 JSON RPC internal methods listed belowand async generator exists in the environment)
- 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
npm i async-call-rpc
yarn add async-call-rpc
pnpm i async-call-rpc
Install viajsr
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
You can accesshttps:// jsdelivr /package/npm/async-call-rpc?path=outto get the latest URL andSRI.
Note: theutils/
entrypoint is not published on the jsr.
import{AsyncCall}from'https://cdn.jsdelivr.net/npm/async-call-rpc@latest/out/base.mjs'
<scriptsrc= "https://cdn.jsdelivr.net/npm/[email protected]/out/base.js"></script>
<script>
const{AsyncCall}=globalThis.AsyncCall
</script>
Load theout/base.mjs
(ES Module) orout/base.js
(UMD, CommonJS, or AMD) to your project.
To communicate with the server/client, you need to implement one of the following interfaces:
- CallbackBasedChannel,generally used in the server.Example: WebSocket on Node.
- EventBasedChannel,generally used in the client.Example: WebSocket on Web
There are somebuilt-in channelsyou can simplify the usage.
The following document will assume you have defined yourchannel
.
This library does not have any opinionated data transmitting format. You need to implement one of the following interfaces:
// server.ts
exportfunctionadd(x:number,y:number){
returnx+y
}
exportconstsleep=(ms:number)=>newPromise((resolve)=>setTimeout(resolve,ms))
// init.ts
import{AsyncCall}from'async-call-rpc'
import*asserverfrom'./server.ts'
// create a server
AsyncCall(server,{channel})
import{AsyncCall}from'async-call-rpc'
importtype*asserverfrom'./server.ts'
constserver=AsyncCall<typeofserver>({},{channel})
server.add(2,40).then(console.log)// 42
AsyncCall can sendnotifications.
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'
constserver=notify(AsyncCall<typeofserver>({},{channel}))
server.add(1,2).then(console.log)// undefined
AsyncCall can sendbatch requesttoo.
import{AsyncCall,batch}from'async-call-rpc'
const[server,emit,drop]=batch(AsyncCall<typeofserver>({},{channel}))
consta=server.req1()// pending
constb=server.req2()// pending
constc=server.req3()// pending
emit()// to send all pending requests
// request a, b, c sent
constd=server.req1()// pending
drop()// to drop all pending requests (and reject corresponding Promises)
// d rejected
This library has 2 entries.base
andfull
.base
is the default entry point. Thefull
version includes theAsyncGeneratorCall
but thebase
version doesn't.
Please check outhttps:// jsdelivr /package/npm/async-call-rpc?path=out
// Full version
require('async-rpc-call/full')// or
import{}from'async-rpc-call/full'
// Base version
require('async-rpc-call')// or
import{}from'async-rpc-call'
They're not part of the core library but are provided as utils to increase usability.
Server | Client | |
---|---|---|
Entry point | async-call-rpc/utils/node/websocket.server.js (Source code) |
TBD |
Entry point type | CommonJS | CommonJS |
Dependencies | ws | ws |
Example | ./examples/node.websocket.server.js | TBD |
Server | Client | |
---|---|---|
Entry point | https://cdn.jsdelivr.net/npm/async-call-rpc@latest/utils/deno/websocket.server.ts (Source code) |
TBD |
Entry point type | ES Module | ES Module |
Dependencies | Deno std | Deno std |
Example | ./examples/deno.websocket.server.ts | TBD |
Client | |
---|---|
Entry point | https://cdn.jsdelivr.net/npm/async-call-rpc@latest/utils/web/websocket.client.js (Source code) |
Entry point type | ES Module |
Dependencies | Nothing |
Example | ./examples/browser.websocket.client.js |
(Web)BroadcastChannel
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 |
(Web)Worker
Host & Worker | |
---|---|
Entry point | https://cdn.jsdelivr.net/npm/async-call-rpc@latest/utils/web/worker.js (Source code) |
Entry point type | ES Module |
Dependencies | Nothing |
Example | Main frame:./examples/browser.worker-main.js Worker:./examples/browser.worker-worker.js |
Main thread:new WorkerChannel(new Worker(...))
Worker:new WorkerChannel()
These four methods are used to implementAsyncGeneratorCall
support.
interfaceJSONRPC_Internal_Methods{
// These 4 methods represent the Async Iterator protocol in ECMAScript
//This method starts an async iterator, returns the id
'rpc.async-iterator.start'(method:string,params:unknown[]):Promise<string>
//This method executes the `next` method on the previous iterator started by `rpc.async-iterator.start`
'rpc.async-iterator.next'(id:string,value:unknown):Promise<IteratorResult<unknown>>
//This method executes the `return` method on the previous iterator started by `rpc.async-iterator.start`
'rpc.async-iterator.return'(id:string,value:unknown):Promise<IteratorResult<unknown>>
//This method executes the `throw` method on the previous iterator started by `rpc.async-iterator.start`
'rpc.async-iterator.throw'(id:string,value:unknown):Promise<IteratorResult<unknown>>
}
This library can send the client the call stack to the server to make the logger better.
Controlled byoption.log.sendLocalStack
.Default tofalse
.
interfaceJSONRPC_Request_object{
// This property includes the caller's stack.
remoteStack?:string
}
This is a non-standard property that appears when using the deprecated JSONSerialization due to JSON doesn't supportundefined
.It's a hint to the client, that the result isundefined
.
This behavior is controlled by the 3rd parameter ofJSONSerialization(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".
interfaceJSONRPC_Response_object{
// This property is a hint.
// If the client is run in JavaScript, it should treat "result: null" as "result: undefined"
undef?:boolean
}
In the JSON RPC specification, this is implementation-defined. This is controlled by the optionoptions.mapError
This library will try to "Recover" the Error object if there is enough information from another side.
interfaceJSONRPC_Error_object{
// This property will help the client to build a better Error object.
data?:{
stack?:string
// Supported value for "type" field (Defined in ECMAScript specification):
type?:
|string
|'Error'
|'EvalError'
|'RangeError'
|'ReferenceError'
|'SyntaxError'
|'TypeError'
|'URIError'
// Defined in HTML specification, only supported in Web
|'DOMException'
}
}