@speedapi/driver v1.5.1
SpeedAPI wire protocol implementation
This library provides a SpeedAPI implementation for JS and TS in all major environments (browser, node, etc.). Install it with:
npm i @speedapi/driverAlso install a transport level library according to your environment:
npm i @speedapi/nodefor Node.JS- don't see an appropriate transport here? implement your own
You don't need additional libraries if you just use the Serializer class.
If you're using a BigInt polyfill, add this as close to entry as possible:
import * as speedapi from "@speedapi/driver";
// if using a polyfill that provides a BigInt(string, radix) constructor
// (e.g. 'big-integer', 'bigint-polyfill'):
speedapi.repr.BigInteger.polyfillMode = "radix";
// if using a polyfill that supports BigInt("0x<data>"):
speedapi.repr.BigInteger.polyfillMode = "0x";
// if not using a polyfill or using a polyfill that implements
// operators like native BigInts do (haven't seen one of those
// in the wild):
speedapi.repr.BigInteger.polyfillMode = "none";
// OR don't do anything, this is the default valueWhat is SpeedAPI?
It's a platform-agnostic API and serialization tool specifically geared towards high-throughput realtime applications. You can read more about its features here
How do I use it?
There's a complete tutorial over here.
Implementing a transport layer
You just have to write glue code between an implementation of your desired transport protocol and SpeedAPI. The requirements for the underlying protocol are as follows:
- for datagram/packet/frame-based protocols like UDP: reliable and unordered - packets must not get lost, but their order may be mixed up (note that UDP doesn't fit this description as it's unreliable)
- for stream-based protocols like TCP: reliable and ordered - bytes must not get lost and their order must not get mixed up
You will need to write 3 or 4 classes depending on the protocol nature:
- A
Link,ClientandServerif the protocol is two-partied in nature, meaning there are only two active devices on the bus/network (e.g. UART) - A
Link,Client,ServerandListenerif one server can serve multiple clients (the overwhelming majority of Internet protocols)
Note that for every client-to-server link there's a Client object on the client and a Server object on the server. There's only one Listener on the server.
Link
This class acts as the reader/writer for SpeedAPI and should extend Duplex. Its only job is to read and write Uint8Arrays. This class will be instantiated by you, therefore you can make the constructor of an arbitrary signature. Here's the skeleton for such a class:
import { Duplex } from "@speedapi/driver";
class MyLink extends Duplex {
constructor(/* pass what you need (e.g. a socket) */) { }
async write(data: Uint8Array): Promise<void> { }
async read(cnt: number): Promise<Uint8Array> { }
async close(): Promise<void> { }
}Alternatively, you can extend from BufferedLink if the underlying protocol implementation is event-driven, meaning it provides a "data has arrived" callback, but not a "read me N bytes" function.
import { BufferedLink } from "@speedapi/driver/transport/universal";
class MyLink extends BufferedLink {
constructor(/* again, pass what you need */) {
someProtocol.on("bytesArrived", (data: Uint8Array) => {
// feed BufferedLink data as it arrives
this.dataArrived(data);
});
}
protected async dataWrite(data: Uint8Array): Promise<void> { }
override async close(): Promise<void> { }
}Server and Client
Very thin wrappers that tell SpeedAPI about your Link while preserving type information.
Important: if you omit type information (e.g. if you're using plain JS to implement these two classes), your IDE will not be able to provide suggestions.
import { SpecSpaceGen, Session } from "@speedapi/driver";
class MyClient<Gen extends SpecSpaceGen> extends Session<Gen> {
constructor(specSpace: Gen /* any other arguments */) {
super(specSpace, new MyLink(/* your arguments */), "client");
}
}
class MyServer<Gen extends SpecSpaceGen> extends Session<Gen> {
constructor(specSpace: Gen /* any other arguments */) {
super(specSpace, new MyLink(/* your arguments */), "server");
}
}Listener (optional)
The notice from before applies here as well.
class MyListener<Gen extends SpecSpaceGen> {
constructor(specSpace: Gen, /* any other arguments */, callback: (server: MyServer<Gen>) => void /* optional too */) {
someProtocol.on("clientConnected", (socket) => {
const session = new MyServer(specSpace, socket /* or any other arguments per your definition */);
callback(session);
});
}
async close() { }
}Testing
Warning: this repository uses a pnpm lock file, hence you can't substitute it for npm below.
git clone https://github.com/speedapi/driver-ts
cd driver-ts
pnpm i
pip3 install susc
pnpm testTypeScript notice
This project relies heavily on typing. As such, the language server gets misled into thinking that an expression type is any even though it's not just because the type is deeply nested. If you see a type error in your IDE that you think shouldn't be there or you're missing some autocomplete entries, try restarting your IDE and/or language server.