3.1.0 • Published 2 years ago

triangulum v3.1.0

Weekly downloads
-
License
LGPL-2.1
Repository
github
Last release
2 years ago

Triangulum

Triangulum is a lightweight, reactive, type safe, typescript API that makes the transfer of data as easy as possible by removing the need to manually encode and decode and send data to where it's needed in your application.

How it works

Triangulum uses some simple concepts to make it as flexible as possible

A Registry is a class that stores the prototypes of various classes, so object's prototypes can be changed to that of the target class. It also stores the channel of each class. The channel is used to differentiate different incoming data types. It also stores the type checkers for that class, which are used to ensure arbitrary unknown data is the correct type. It can also store any arbitrary data you like for each class, typically functions that do extra decoding steps, but it can really be anything.

A ListenerManager is an interface that provides an API for sending classes around. You must implement the interface yourself, and the easiest way to do that is to extend AbstractListenerManager, which does much of the hard work that comes with implementing the APIs. You could also extend JSONListenerManager which includes the logic for encoding/decoding JSON, or InternalListenerManager which doesn't encode anything at all and can be used for sending data to web workers, or to other parts of your program, however it doesn't do type checking.

@MakeSendable is a decorator that registers a class to a registry, using it is much more convenient than registering the class yourself. It's usually a good idea to make your own decorator that calls @MakeSendable internally to add the extra convenience of not having to specify the registry, and also if you have your own class you want to make classes extend. You can do this easily with the makeCustomSendableDecorator function.

The strats object tries to make writing type checkers for all kinds of data as easy and convenient as possible by providing a bunch of default type checkers for all kinds of data, which can be combined to type check almost anything. If the options in strats are insufficient, you can still always just input your own predicates

Example

Here's an example using websockets:

import { Sendable, JSONListenerManager, MakeSendable, strats } from "triangulum"

// Create the registry that will be used to store all types that can be sent
const websiteRegistry = new Registry<Sendable, [(data: any) => boolean]>()

class ClientConnectionManager extends JSONListenerManager {
    constructor() {
        super(websiteRegistry)

        this.ws = new WebSocket(`ws://${location.host}/ws`)

        this.ws.onmessage = e => {
            if (!(typeof e.data === "string")) return

            // Tell the JSONListenerManager that some data has been received
            this.onData(e.data as string)
        }

        this.ws.onopen = e => {
            // Tell the JSONListenerManager that this instance is ready to send messages
            // This is neccessary because the parent class caches messages to allow for instantiating the class and sending stuff immediately after, which wouldn't be allowed otherwise
            this.ready()
        }
    }

    // Defines how the data should be sent over the network
    transmit(data: string) {
        this.ws.send(data)
    }

    ws
}

const clientConnectionManager = new ClientConnectionManager()

// @MakeSendable registers the class to the registry, it takes the channel the class should be sent through, as well as how the data should be type checked.
@MakeSendable(
    websiteRegistry,
    "Thingy",
    // The strats object contains helper methods to make writing type checkers easier.
    strats.class({
        value: strats.isString,
    })
)
class Thingy extends Sendable {
    constructor(value: string) {
        this.value = value
    }

    value: string
}

// Send some data
clientConnectionManager.send(new Thingy("Hello world"))

// Listen for some data
clientConnectionManager.listen(Thingy, data => {
    alert(data.value)
})
3.1.0

2 years ago

3.0.4

3 years ago

3.0.3

3 years ago

3.0.2

3 years ago

3.0.1

3 years ago

3.0.0

3 years ago

2.0.0

3 years ago

1.0.0

3 years ago