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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@x-oasis/async-call-rpc-web

v0.13.0

Published

Web transport adapters (MessagePort, Worker, WebSocket) for @x-oasis/async-call-rpc

Readme

@x-oasis/async-call-rpc-web

Web 平台传输层适配器,为 @x-oasis/async-call-rpc 提供 MessagePort、Web Worker、WebSocket 三种通道实现。

安装

pnpm add @x-oasis/async-call-rpc-web @x-oasis/async-call-rpc

核心概念

本包提供三个 Channel 类,均继承自 AbstractChannelProtocol

| 类 | 传输层 | 使用场景 | | ------------------ | --------------------------------------- | --------------------- | | MessageChannel | MessagePortnew MessageChannel()) | iframe / 跨窗口通信 | | WorkerChannel | Worker / self | Web Worker 双向通信 | | WebSocketChannel | WebSocket(浏览器和 Node.js ws) | 客户端-服务端实时通信 |

快速开始

1. Web Worker

// === 主线程 ===
import { WorkerChannel } from '@x-oasis/async-call-rpc-web';
import { serviceHost, clientHost } from '@x-oasis/async-call-rpc';

const worker = new Worker(new URL('./worker.ts', import.meta.url), {
  type: 'module',
});
const channel = new WorkerChannel(worker, { name: 'main-thread' });

const proxy = clientHost.registerClient('compute', { channel }).createProxy<{
  fibonacci(n: number): Promise<number>;
}>();

const result = await proxy.fibonacci(10); // 55
// === Worker 线程 ===
import { WorkerChannel } from '@x-oasis/async-call-rpc-web';
import { serviceHost } from '@x-oasis/async-call-rpc';

const channel = new WorkerChannel(self, { name: 'worker' });

const service = serviceHost.registerService('compute', {
  fibonacci: (n: number): number => {
    if (n <= 1) return n;
    let a = 0,
      b = 1;
    for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
    return b;
  },
});
service.setChannel(channel);

2. WebSocket

// === 客户端(浏览器)===
import { WebSocketChannel } from '@x-oasis/async-call-rpc-web';
import { clientHost } from '@x-oasis/async-call-rpc';

const ws = new WebSocket('ws://localhost:3456');
const channel = new WebSocketChannel(ws, { name: 'client' });

const api = clientHost.registerClient('api', { channel }).createProxy<{
  echo(msg: string): Promise<string>;
  now(): Promise<number>;
}>();

const result = await api.echo('hello'); // 'hello'
// === 服务端(Node.js + ws 库)===
import { WebSocketServer } from 'ws';
import { WebSocketChannel } from '@x-oasis/async-call-rpc-web';
import { serviceHost } from '@x-oasis/async-call-rpc';

const wss = new WebSocketServer({ port: 3456 });

wss.on('connection', (ws) => {
  const channel = new WebSocketChannel(ws as any, {
    name: 'server',
    connected: true,
  });
  channel.activate();

  const service = serviceHost.registerService('api', {
    echo: (x: string) => x,
    now: () => Date.now(),
  });
  service.setChannel(channel);

  ws.on('close', () => channel.disconnect());
});

3. MessageChannel(iframe / 跨窗口)

import { MessageChannel as RPCMessageChannel } from '@x-oasis/async-call-rpc-web';

const { port1, port2 } = new MessageChannel();

// 主窗口用 port1
const channel = new RPCMessageChannel({ port: port1 });

// 将 port2 传给 iframe
iframe.contentWindow.postMessage('init', '*', [port2]);

API 参考

WorkerChannel

new WorkerChannel(worker: Worker | DedicatedWorkerGlobalScope, options?: {
  name?: string;
} & AbstractChannelProtocolProps)
  • worker — 主线程传 Worker 实例,Worker 内传 self
  • name — 可选的通道名称(用于调试日志)

WebSocketChannel

new WebSocketChannel(socket: WebSocket, options?: {
  name?: string;
  maxReconnectAttempts?: number;  // 默认 5
  reconnectDelay?: number;        // 默认 1000ms
  connected?: boolean;            // 默认 false
} & AbstractChannelProtocolProps)
  • socket — 浏览器原生 WebSocket 或 Node.js ws 实例
  • connected — 服务端场景下(已 open)设为 true
  • WebSocket open 时自动 activate(),close 时自动 disconnect()

实用方法:

| 方法 / 属性 | 说明 | | -------------- | ----------------------------- | | readyState | 返回底层 WebSocket readyState | | isOpen() | 是否处于 OPEN 状态 | | disconnect() | 关闭 WebSocket 并断开通道 |

RPCMessageChannel

new RPCMessageChannel(options?: {
  port?: MessagePort;     // 可省略,之后用 bindPort 绑定
  sender?: any;           // 默认 window
  targetOrigin?: string;  // 默认 '*'
} & AbstractChannelProtocolProps)
  • portMessagePort 实例。可选:省略时通道以"未连接"状态创建,send() 会暂存到 pendingSendEntriesbindPort 触发后自动 flush
  • 构造时(或 bindPort 时)自动调用 port.start()
  • send() 支持 transfer 参数传递 Transferable 对象
  • 同时导出别名:MessageChannelRPCMessageChannel 指向同一类(前者保持向后兼容,后者避免与 DOM 同名 MessageChannel 混淆)

延迟端口绑定(bindPort)

当 port 通过后续 MessageEvent 的 transfer list 才到达时使用此模式:先把通道挂到服务/客户端上,等 port 到了再绑。

import { RPCMessageChannel } from '@x-oasis/async-call-rpc-web';
import { serviceHost } from '@x-oasis/async-call-rpc';

// 1. 创建未绑定 port 的通道
const channel = new RPCMessageChannel();
channel.setServiceHost(serviceHost); // 注册路由

// 2. 等待 port 通过 message 事件到达
window.addEventListener('message', (event) => {
  if (event.data === 'rpc-port' && event.ports[0]) {
    channel.bindPort(event.ports[0]);
    // bindPort 调用后,channel 进入连接态,此前 queued 的 send 自动 flush
  }
});

bindPort 是幂等的:重复调用或对已绑定通道再调用都是 no-op。在绑定前调用 send() 会打 warn 并丢弃数据(业务一般不会触发,因为正常路径都是通过 pendingSendEntries 排队)。

运行测试

pnpm test

License

ISC