コンテンツにスキップ

配管を書かずに、リアルタイムな部屋を作る

本番稼働中のリアルタイムアプリから切り出した、小さなパッケージ7つ。extension を1つのヘッドレスな space に組み立てるエンジン、デプロイを生き延びる WebSocket トランスポート、サーバーなしでテストできる状態 store。
$npm install @insession/space

どの extension もそのまま渡せる記述子を持っているので、サーバー側の機能が1行で動きます。 返ってきた effect をどう扱うかははじめかたを参照してください。

import { createSpace } from '@insession/space';
import { pomodoroExtension } from '@insession/extension-pomodoro';
const space = createSpace({ extensions: [pomodoroExtension()] });
space.dispatch('pomodoro', 'start'); // -> [broadcast, schedule-timer]

@insession/space

ヘッドレスなリアルタイム space を、extension の集合として組み立てる親パッケージ。自分の WebSocket サーバーとストレージを持ち込めば、誰が接続しているか・どの extension がどの state のスライスを持つか・action がどの reducer に届くか・タイマーの再起動タイミングを このパッケージが引き受けます。すべての遷移は I/O を行わず、effect 記述子を返すだけです。

依存ゼロextension エンジン

@insession/ws-resilient-transport

本番デプロイの都合に合わせて再接続する WebSocket トランスポート。サービス再起動時は即座に、 それ以外はジッター付き指数バックオフで、terminal な close code なら再接続を完全に止める。

依存ゼロ約130行

@insession/space-state

共有ルームの状態(メンバー・チャット・プレゼンス・プラグイン)を持つ store。受信は純粋 reducer で畳み込み、副作用は実行せず記述子として返すだけ。

依存ゼロ純粋 reducer

@insession/extension-pomodoro

サーバーを正とするポモドーロタイマーの状態機械。start / pause / skip / configure と宣言・声援を 畳む純粋な reduce に、永続化の境界を扱う restore / persistState を加えたもの。

依存ゼロ純粋 reducer

@insession/extension-whiteboard

サーバーを正とするホワイトボードの状態機械。共有の自由描画 strokes/shapes に加えて、 独自のフェーズタイミングを持つ「お絵かき伝言ゲーム」の relay をオプションで同居させたもの。

依存ゼロ純粋 reducer

@insession/extension-watch-party

サーバーを正とする Watch Party の状態機械。キューと履歴を伴う動画/音声の同期再生。 reduce は I/O を行わず effect 記述子を返すだけなので、broadcast / 永続化 / タイトル解決はホスト側が自分で配線します。

依存ゼロ純粋 reducer

@insession/extension-chat

サーバーを正とするチャットの状態機械。メッセージの正規化・スタンプ検証・返信・メッセージ ごとの絵文字リアクション・入力中インジケーター・ピン留めを1つの純粋な reduce に畳んだもの。

依存ゼロ純粋 reducer

パッケージ間の依存は1本もありません。space / space-state / ws-resilient-transport / extension-pomodoro / extension-whiteboard / extension-watch-party / extension-chat はそれぞれ独立しています — space は既存4つの状態機械が もともと満たしている形(defaultState / reduce / restore / persistState 等)を契約として言語化しただけなので、状態機械の側から space を import する必要も、space の側から特定の状態機械を import する 必要もありません。バイトを運ぶことと状態を持つことを意図的に切り離してあるので、どれか1つだけを 採用できます。React で使う場合も、入れるバインディングのパッケージはありません。 自分で書く1行がフックの全部です。