@signe/sync
TypeScript icon, indicating that this package has built-in type declarations

2.3.1 • Public • Published

@signe/sync

A powerful synchronization library for real-time state management and persistence in TypeScript applications. This package is part of the Signe framework and provides decorators and utilities for seamless state synchronization between client and server.

Features

  • 🔄 Real-time state synchronization
  • 💾 State persistence
  • 🎯 Selective synchronization with fine-grained control
  • 🔌 WebSocket integration with PartySocket
  • 🎨 Decorator-based API for easy implementation
  • 🔍 Path-based value loading and retrieval
  • 📦 TypeScript support out of the box

Installation

npm install @signe/sync

Usage

Basic Synchronization

import { signal } from '@signe/reactive'
import { sync, syncClass } from '@signe/sync'

class MyClass {
  @sync()
  count = signal(0)

  @sync()
  text = signal('hello')
}

const instance = new MyClass()
syncClass(instance, {
  onSync: (cache) => console.log('Sync cache:', cache),
  onPersist: (cache) => console.log('Persist cache:', cache)
})

Property Decorators

@sync()

Synchronizes a property with optional settings:

class MyClass {
  // Basic sync with default options
  @sync()
  basicProp = signal(0)
}
Syncing Collections

You can synchronize collections of objects by specifying the class type:

class Player {
  @id() id = signal('player-1')
  @sync() name = signal('Player Name')
}

class MyClass {
  // Synchronize a collection of Player objects
  @sync(Player) players = signal<Record<string, Player>>({})
  
  addPlayer(playerId: string) {
    // Dynamic key synchronization
    // The Player instance automatically gets the id from the key
    this.players()[playerId] = new Player()
  }
}

In the example above, when you add a player with players.value['player-123'] = new Player(), the @id() decorator ensures the Player instance automatically takes 'player-123' as its ID.

Object Synchronization Options

There are two ways to synchronize objects:

  1. Entire object synchronization:
class MyClass {
  // The entire object is synchronized as one unit
  @sync() myObj = signal({ val: 1, count: 2 })
}
  1. Granular property synchronization:
class MyClass {
  // Individual properties with signals are synchronized separately
  @sync() myObject = { 
    val: signal(1), 
    count: signal(2) 
  }
}

The key difference:

  • In the first approach, changing any property triggers synchronization of the entire object
  • In the second approach, only the changed property is synchronized, providing finer-grained control

@id()

Marks a property as the unique identifier for an instance:

class Player {
  // Will automatically receive the key value when added to a collection
  @id() id = signal('')
  @sync() name = signal('Player Name')
}

The @id() decorator is especially useful for dynamic collections where the key in the collection should be reflected in the object's ID property.

@users()

Marks a property for special user collection synchronization:

class User {
  @id() id = signal('')
  @sync() name = signal('')
  @connected() isConnected = signal(false)
}

class Room {
  // Special collection that automatically populates based on user connections
  @users(User) connectedUsers = signal<Record<string, User>>({})
}

The @users() decorator creates a special collection that:

  • Automatically populates with user instances when they connect to the room
  • Automatically removes users when they disconnect
  • Links to the user's session information
  • Updates all clients in real-time with connection status

This is ideal for building features like user presence indicators, online user lists, or real-time collaboration tools.

@persist()

Marks a property for persistence only (no client sync):

class MyClass {
  @persist() myPersistentProp = signal(0)
}

@connected()

Marks a property for tracking user connection status:

class User {
  @id() id = signal('user-1')
  @connected() isConnected = signal(false)
  name = signal('User Name')
}

This decorator automatically tracks and synchronizes a user's connection state. When a user connects to a room, the property is automatically set to true. When they disconnect, it's set to false. This state is synchronized with all clients, allowing real-time connection status updates without manual management.

Benefits:

  • Automatically updated when users connect/disconnect
  • Synchronized to all clients in real-time
  • Can be used in UI to show online/offline indicators
  • No need to manually track connection status with custom events

Client Connection

Set up a WebSocket connection for real-time synchronization:

import { connectionRoom } from '@signe/sync/client'

const room = new Room()
const conn = connectionRoom({
  host: 'your-server-url',
  room: 'room-id'
}, room)

// Emit events
conn.emit('event-name', { data: 'value' })

// Listen for events
conn.on('event-name', (data) => {
  console.log('Received:', data)
})

Loading State

Load state from paths or objects:

import { load } from '@signe/sync'

// Load using paths
load(instance, {
  'position.x': 10,
  'position.y': 20
})

// Load using object
load(instance, {
  position: { x: 10, y: 20 }
}, true)

API Reference

syncClass(instance, options?)

Synchronizes an instance by adding state management methods.

Options:

  • onSync?: (value: Map<string, any>) => void - Callback for sync events
  • onPersist?: (value: Set<string>) => void - Callback for persistence events

Decorator Options

Common options for decorators:

  • classType?: Function - Specify a class type for complex objects
  • persist?: boolean - Enable/disable persistence (default: true)
  • syncToClient?: boolean - Enable/disable client synchronization (default: true)

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i @signe/sync

Weekly Downloads

73

Version

2.3.1

License

MIT

Unpacked Size

117 kB

Total Files

18

Last publish

Collaborators

  • webcreative5