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

@netless/synced-store

v2.0.9

Published

A white-web-sdk plugin for storing shared replayable states and sending/receiving replayable events.

Downloads

2,759

Readme

SyncedStore

A white-web-sdk plugin for storing shared replayable states and sending/receiving replayable events.

Install

npm add @netless/synced-store

Example

Init SyncedStore right after joining room:

import { createSyncedStorePlugin } from "@netless/synced-store";

const SyncedStorePlugin = createSyncedStorePlugin();

const whiteboard = new WhiteWebSdk({
  appIdentifier: "xxxxxxxxxxxxxx",
  useMobXState: true, // This is required to use SyncedStorePlugin
  deviceType: DeviceType.Surface,
});

const room = await whiteboard.joinRoom({
  uuid: roomUUID,
  roomToken: roomToken,
  uid: userID,
  invisiblePlugins: [SyncedStorePlugin],
  // Only writable users can modify states and dispatch events.
  // Set this to false for readonly users(audience) for better performance
  isWritable: true,
});

// Define typed event keys and payloads
type EventData = {
  "click-event": { id: string };
};

const syncedStore = await SyncedStorePlugin.init<EventData>(room);
interface State {
  count: number;
}

// connect to a namespaced storage
const storage = await syncedStore.connectStorage<State>("a-name", { count: 0 });

storage.state; // => { count: 0 }

if (storage.isWritable) {
  storage.setState({ count: 2 });
}

const stateChangedDisposer = storage.on("stateChanged", diff => {
  if (diff.count) {
    // count: 0 -> 2
    console.log("count:", diff.count.oldValue, "->", diff.count.newValue);
    console.log(diff.count.newValue === app.state.count);
  }
});

if (syncStore.isRoomWritable) {
  syncedStore.dispatchEvent("click-event", { id: "item1" });
}

const eventDisposer = syncedStore.addEventListener(
  "click-event",
  ({ payload }) => {
    console.log(payload.id); // item1
  }
);

Develop

Add .env at project root following the .env.example reference.

pnpm i
pnpm start

Testing

Unit Test:

pnpm t

End-to-end Test:

  1. Start hosting the testing resources
    pnpm e2e:page
  2. Then then open a new terminal to start e2e tests
    pnpm e2e

API

  • static SyncedStorePlugin.init(room)

    A static method that inits the SyncedStore. Should be called right after joining room.

    Returns: Promise<SyncedStore<EventData>>

  • SyncedStore.isRoomWritable

    Type: boolean

    Shortcut to whiteboard room writable state. When it is false, calling storage.setState() and dispatchEvent() will throw errors.

  • SyncedStore.setRoomWritable(isWritable)

    Shortcut to change whiteboard room writable state.

  • SyncedStore.addRoomWritableChangeListener(listener)

    It fires when whiteboard room writable state changes.

    Type: (isRoomWritable: boolean) => void

    Returns: () => void - a disposable function that can be called to remove the listener.

  • SyncedStore.isPluginWritable

    Type: boolean

    It is true if isRoomWritable === true and plugin finished initialization. When it is false, calling storage.setState() will throw errors.

  • SyncedStore.addPluginWritableChangeListener(listener)

    It fires when plugin writable state changes.

    Type: (isPluginWritable: boolean) => void

    Returns: () => void - a disposable function that can be called to remove the listener.

  • SyncedStore.dispatchEvent(event, payload)

    Broadcast an event message to other clients.

    syncedStore.dispatchEvent("click", { data: "data" });
  • SyncedStore.addEventListener(event, listener)

    It fires when receiving messages from other clients (when other clients called syncedStore.dispatchEvent()).

    Returns: () => void a disposer function.

    const disposer = syncedStore.addEventListener(
      "click-event",
      ({ payload }) => {
        console.log(payload.data);
        disposer();
      }
    );
    
    syncedStore.dispatchEvent("click-event", { data: "data" });
  • SyncedStore.connectStorage(namespace, defaultState)

    Connect to a namespaced storage. Each call returns an fresh storage instance with its own life-cycle. Calling multiple times with same namespace will result in different storage instances sharing the same data.

    namespace

    Name for the storage. Storages with the same namespace share the same state(but each storage instance keeps it own life-cycle).

    Type: string

    defaultState

    Type: State

    Returns: Storage<State>

    const storage = syncedStore.connectStorage("my-storage", { count: 0 });
  • Storage.state

    Type: State

    Default: initialState

    The synchronized state across all clients. To change it, call storage.setState().

  • Storage.setState(partialState)

    Works like React's setState, it updates storage.state and synchronize it to other clients.

    When some field's value is undefined, it will be removed from storage.state.

    Important: Do not rely on the order of state changes:

    • storage.setState() alters storage.state synchronously but onStateChanged will wait until the data is successfully synced.
    • State syncing time span varies due to network status and data size. It is recommended to store only necessary data in the store.

    partialState

    Type: Partial<State>

    storage.state; //=> { count: 0, a: 1 }
    storage.setState({ count: storage.state.count + 1, a: undefined, b: 2 });
    storage.state; //=> { count: 1, b: 2 }
  • Storage.on("stateChanged", listener)

    A state changed event that fires after someone called storage.setState() (including the current syncedStore itself).

    Returns: () => void - A disposable function that can be called to remove the listener.

    const disposer = storage.on("stateChanged", diff => {
      console.log("state changed", diff.oldValue, diff.newValue);
      disposer(); // remove listener by calling disposer
    });
  • Storage.on("disconnected", listener)

    An event that fires after the storage instance is disconnected.

    Returns: () => void - A disposable function that can be called to remove the listener.

    const disposer = storage.on("disconnected", () => {
      console.log(storage.disconnected); // true
    });
  • Stoarge.disconnect()

    Disconnect or dispose the storage, triggers disconnected event, and release all listeners.

License

MIT @ netless