npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

it-rpc

v1.0.2

Published

Schema-free RPC over async iterables

Downloads

3,643

Readme

it-rpc

codecov CI

Schema-free RPC over async iterables

About

Your RPC objects must follow a few rules:

  1. All RPC methods must return a promise or an async generator
  2. Property access on the RPC object is not supported
  3. RPC Arguments must not be promises (though may be functions that return promises)
  4. The values resolved/yielded from an RPC function must be serializable (e.g. contain no functions) unless custom types are used (see Custom Types below)
  5. AsyncGenerators returned from RPC methods must be either read to completion, or their .return/.throw methods invoked
  6. Callback functions (e.g. functions passed as arguments) should return promises or async generators
  7. Callback functions may return void, but if so they must not throw

Example - Getting started

import { rpc } from 'it-rpc'

// the invocation target interface - used by the client and the server
interface Target {
  sayHello(): Promise<string>
}

// the target implementation, lives on the server side
const target: Target = {
  async sayHello () {
    return 'hello'
  }
}

// create client and server
const server = rpc()
const client = rpc()

// pipe the streams together
void server.sink(client.source)
void client.sink(server.source)

// a string that is the same on both the server and the client
const objectName = 'target'

// expose target implementation to RPC calls on the server side
server.createTarget(objectName, target)

// create a client-side version of target
const clientTarget = client.createClient<Target>(objectName)

// invoke a remote method
await clientTarget.sayHello() // 'hello'

Example - Streaming data from the server to the client

import { rpc } from 'it-rpc'

interface Target {
  streamingMethod(): AsyncGenerator<Uint8Array>
}

const target: Target = {
  async * streamingMethod () {
    yield Uint8Array.from([0, 1, 2, 3])
    yield Uint8Array.from([4, 5, 6, 7])
  }
}

const server = rpc()
const client = rpc()
void server.sink(client.source)
void client.sink(server.source)

const objectName = 'target'
server.createTarget(objectName, target)

const clientTarget = client.createClient<Target>(objectName)

for await (const buf of clientTarget.streamingMethod()) {
  console.info(buf)
  // Uint8Array([0, 1, 2, 3])
  // Uint8Array([4, 5, 6, 7])
}

Example - Aborting remote method invocations

Any abort signals passed as arguments will have equivalents passed on to the remote method invocation and these will fire their abort event when the client side signal fires.

import { rpc } from 'it-rpc'

interface Target {
  slowStream(arg: { signal: AbortSignal }): AsyncGenerator<Uint8Array>
}

const target: Target = {
  async * slowStream () {
    await new Promise<void>((resolve) => {
      setTimeout(() => {
        resolve()
      }, 5000)
    })
    yield Uint8Array.from([0, 1, 2, 3])
    yield Uint8Array.from([4, 5, 6, 7])
  }
}

const server = rpc()
const client = rpc()
void server.sink(client.source)
void client.sink(server.source)

const objectName = 'target'
server.createTarget(objectName, target)

const clientTarget = client.createClient<Target>(objectName)

const signal = AbortSignal.timeout(1000)

for await (const buf of clientTarget.slowStream({ signal })) {
  console.info(buf)
  // explodes after 1s
}

Custom types

It is possible to extend it-rpc to support serializing/deserializing custom types by passing ValueCodecs to the constructor.

Each ValueCodec needs a unique type field which identifies the value type on the wire.

it-rpc uses value types starting at 1024 and has a catch-all 2147483647 type which resolves to plain objects.

You should define your type values higher than the max value it-rpc uses (2048 is a safe value) but lower than the catch-all type value.

Matching codecs are searched for in type order so you can override the built-in codecs by specifying a type field lower than 1024.

[!IMPORTANT] Both the server and the client must be configured with the same set of custom ValueCodecs

Example - Custom Types

import { encode, decode } from 'cborg'
import { rpc } from 'it-rpc'
import type { ValueCodec } from 'it-rpc'

// a custom type we want to encode
class MyClass {
  field: string

  constructor (val: string) {
    this.field = val
  }

  getField () {
    return this.field
  }
}

// our custom codec
const codec: ValueCodec<MyClass> = {
  type: 2048,
  canEncode: (val) => val instanceof MyClass,
  encode: (val) => encode({ field: val.getField() }),
  decode: (buf) => {
    const decoded = decode(buf)

    return new MyClass(decoded.field)
  }
}

// configure the server/client with the custom codec
const server = rpc({
  valueCodecs: [
    codec
  ]
})
const client = rpc({
  valueCodecs: [
    codec
  ]
})
void server.sink(client.source)
void client.sink(server.source)

interface Target {
  getFieldFromArg(arg: MyClass): Promise<string>
}

const target: Target = {
  async getFieldFromArg (arg) {
    return arg.getField()
  }
}

const objectName = 'target'
server.createTarget(objectName, target)

const clientTarget = client.createClient<Target>(objectName)

const val = new MyClass('hello')

await clientTarget.getFieldFromArg(val) // 'hello'

Install

$ npm i it-rpc

Browser <script> tag

Loading this module through a script tag will make its exports available as ItRpc in the global namespace.

<script src="https://unpkg.com/it-rpc/dist/index.min.js"></script>

API Docs

License

Licensed under either of

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.