
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Schema-free RPC over async iterables
Your RPC objects must follow a few rules:
.return/.throw methods invokedvoid, but if so they must not throwimport { 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'
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])
}
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
}
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
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'
$ npm i it-rpc
<script> tagLoading 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>
Licensed under either of
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.
FAQs
Schema-free RPC over async iterables
We found that it-rpc demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.