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

remote-state-sync

v1.0.5

Published

A lightweight, fully type-safe unidirectional remote state synchronization library.

Downloads

566

Readme

Remote State Sync

轻量级、完全类型安全的单向远程状态同步库。轻松将复杂状态以细小粒度从 Provider(如:服务端 Server 或 Electron 主进程)同步到 Receiver(如:Web 客户端 Client 或 Electron 渲染进程)。

English | 中文

特性

  • 单向同步:通过高效的增量 Patch (补丁) 算法,将状态从 A 端安全地同步到 B 端。
  • 深度响应与代理拦截:内建支持对 ObjectMapSet 数据类型结构进行深层侦听、拦截与同步同步。
  • 补丁按需批处理:高频次、连续的状态变更会在底层自动批量打包合并为单次事件分发,最大化性能。
  • 多端传输解耦:不强绑定任何传输协议。可以灵活适配 WebSocket、Socket.io、Electron IPC、HTTP 等任意传输层网络!
  • 完美兼容 Vue:提供无缝的 @vue/reactivity 支持能力,开箱即用(支持 .toValue().toRef().toShallowRef())。

安装

npm install remote-state-sync

场景示例

1. 组合:Hono + Socket.IO (Server端与Web端通信)

服务端 Server (提供方 Provider)

import { SyncProvider } from 'remote-state-sync';
import { Hono } from 'hono';
import { Server } from 'socket.io';

const app = new Hono();
const io = new Server(3000);

const provider = new SyncProvider();
const usersNs = provider.register('users_space');

// 定义一个状态
type UserState = {
  connected: number;
  history: string[];
};

// 初始化这个状态
const userState = usersNs.sync<UserState>('data', {
  connected: 0,
  history: [],
});

// 第一步:利用 Hono 提供 HTTP 接口
// 向客户端暴露 Snapshot
app.get('/snapshot/:namespace/:key', (c) => {
  const ns = c.req.param('namespace');
  const key = c.req.param('key');
  return c.json(provider.getStateSnapshot(ns, key));
});

io.on('connection', (socket) => {
  // 修改复杂对象会自动生成补丁(得益于内部 Proxy)
  userState.set((state) => void state.connected++);

  socket.on('disconnect', () => {
    userState.set((state) => void state.connected--);
  });
});

// 第二步:通过 WebSocket 向外部广播由于变更生成的批量 Patches
provider.bus.on('update', (namespace, patches) => {
  io.emit('state-update', namespace, patches);
});

Web客户端 Client (接收方 Receiver)

import { SyncReceiver } from 'remote-state-sync';
import { io } from 'socket.io-client';

const socket = io('ws://localhost:3000');

const receiver = new SyncReceiver({
  // 第一步:通过 HTTP 拉取远端快照,作为初始化状态
  snapshotGetter: async (namespace, key) => {
    const res = await fetch(`http://localhost:3000/snapshot/${namespace}/${key}`);
    return res.json();
  },
});

// 第二步:接管增量 Patches,增量更新本地状态树
socket.on('state-update', (namespace, patches) => {
  receiver.applyPatches(namespace, patches);
});

async function main() {
  const usersNs = await receiver.register('users_space');

  type UserState = { connected: number; history: string[] };
  const userState = await usersNs.sync<UserState>('data');

  // Output: { connected: 1, history: [] }
  console.log(userState.toValue());

  userState.on('update', (newVal, oldVal, patches) => {
    console.log('状态更新', newVal.connected);
  });
}
main();

2.组合:Electron (ipcMain + ipcRenderer)

主进程 Main Process (提供方 Provider)

import { SyncProvider } from 'remote-state-sync';
import { ipcMain, BrowserWindow } from 'electron';

const provider = new SyncProvider();
const appNs = provider.register('app_ns');

type SettingsState = {
  theme: 'dark' | 'light';
  version: string;
};

// 初始化状态
const settings = appNs.sync<SettingsState>('settings', {
  theme: 'dark',
  version: '1.0.0',
});

// 第一步:通过 ipcMain.handle 暴露 snapshot 获取能力
ipcMain.handle('get-sync-snapshot', (_, namespace, key) => {
  return provider.getStateSnapshot(namespace, key);
});

// 第二步:将状态补丁通过 ipcEvent 主动投递给所有渲染进程
provider.bus.on('update', (namespace, patches) => {
  BrowserWindow.getAllWindows().forEach((win) => {
    win.webContents.send('sync-update', namespace, patches);
  });
});

// 修改示例
setTimeout(() => {
  settings.set((state) => void (state.theme = 'light'));
}, 5000);

渲染进程 Renderer Process (结合Vue使用的 Receiver)

import { SyncReceiver } from 'remote-state-sync';
import { ipcRenderer } from 'electron';
import { watch } from 'vue';

const receiver = new SyncReceiver({
  // 第一步:利用 ipcRenderer.invoke 异步请求 Snapshot
  snapshotGetter: (namespace, key) => ipcRenderer.invoke('get-sync-snapshot', namespace, key),
});

// 第二步:监听来自主进程投递的 Patches
ipcRenderer.on('sync-update', (_, namespace, patches) => {
  receiver.applyPatches(namespace, patches);
});

async function setup() {
  const appNs = await receiver.register('app_ns');
  const settings = await appNs.sync<SettingsState>('settings');

  // 第三步:将远端状态一键接入 Vue 的响应式生态内!
  const settingsRef = settings.toRef(); // 或 toShallowRef()

  watch(
    settingsRef,
    (newSettings) => {
      console.log('Renderer theme changed to:', newSettings.theme);
    },
    { deep: true },
  );
}
setup();