@luckystack/sync 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@luckystack/sync` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0]
11
+
12
+ ### Added
13
+
14
+ - Initial public release as part of the LuckyStack package split.
package/CLAUDE.md ADDED
@@ -0,0 +1,104 @@
1
+ # @luckystack/sync
2
+
3
+ > AI summary + function INDEX. For deep specs see `docs/` next to this file.
4
+
5
+ ## What this package does
6
+
7
+ Real-time sync transport for LuckyStack. Provides type-safe, room-based fanout over Socket.io with an HTTP/SSE fallback. Each sync event is a file-based route with a mandatory `_server_v{N}.ts` (runs once, validates, produces `serverOutput`) and an optional `_client_v{N}.ts` (runs once per recipient socket for per-target filtering or auth). Streaming primitives (`stream`, `broadcastStream`, `streamTo`) support live LLM tokens, collab-editor diffs, and per-recipient progress. An offline queue with configurable drop policy keeps optimistic sends from being lost when the socket reconnects.
8
+
9
+ ## When to USE this package
10
+
11
+ - Real-time room-based fanout (collab editors, chat, multiplayer state)
12
+ - Streaming AI/LLM tokens with throttled chunking
13
+ - Per-recipient customization (filter / translate / brand) without a second round-trip
14
+ - Optimistic offline sends that should replay when the socket reconnects
15
+ - Server-validated mutations that must be broadcast to every peer in a room
16
+
17
+ ## When to NOT suggest this (yet)
18
+
19
+ - Request/response with no fanout — use `@luckystack/api` (`apiRequest`)
20
+ - Pure presence/online-status broadcasts — use `@luckystack/presence`
21
+ - Cross-instance fanout without a Redis adapter — wire the adapter first (see `/docs/ARCHITECTURE_SOCKET.md`)
22
+ - One-off background jobs unrelated to a client room
23
+ - HTTP-only environments without a long-lived connection AND no SSE — the package needs at minimum the HTTP/SSE fallback
24
+
25
+ ## Function Index
26
+
27
+ ### Server entry (`@luckystack/sync`)
28
+
29
+ | Export | One-liner | Deep doc |
30
+ |---|---|---|
31
+ | `handleSyncRequest({ msg, socket, token })` | Socket.io sync entry — auth, rate-limit, validate, run `_server`, fanout, run `_client` per recipient, emit results, dispatch hooks. | → `docs/server-vs-client-handlers.md` |
32
+ | `handleHttpSyncRequest(req, res)` | HTTP/SSE fallback for sync requests when websockets are blocked. | → `docs/sync-request.md` |
33
+ | `createStreamThrottle({ flushEveryMs?, flushAtChars?, field? })` | Coalesce tiny stream pieces (LLM tokens) into bigger chunks before emit. | → `docs/streaming.md` |
34
+ | Type: `HttpSyncStreamEvent` | SSE event shape emitted by the HTTP fallback. | → `docs/sync-request.md` |
35
+ | Type: `StreamThrottle` / `CreateStreamThrottleOptions` | Throttle handle + options. | → `docs/streaming.md` |
36
+
37
+ ### Client entry (`@luckystack/sync/client`)
38
+
39
+ | Export | One-liner | Deep doc |
40
+ |---|---|---|
41
+ | `syncRequest({ name, version, data?, receiver, ignoreSelf?, onStream?, offlineDropPolicy? })` | Fire a typed sync event into a room; resolves with the server result envelope. | → `docs/sync-request.md` |
42
+ | `useSyncEvents()` | React hook returning `{ upsertSyncEventCallback, upsertSyncEventStreamCallback }` scoped to the component lifetime. | → `docs/callback-registration.md` |
43
+ | `useSyncEventTrigger()` | React hook returning `{ triggerSyncEvent, triggerSyncStreamEvent }` for manually invoking registered callbacks (testing / local echo). | → `docs/callback-registration.md` |
44
+ | `initSyncRequest({ setSocketStatus, sessionRef })` | One-time wiring of socket lifecycle handlers (connect/disconnect/reconnectAttempt/userAfk/userBack/connectError) into the socket-status provider. | → `docs/callback-registration.md` |
45
+ | Type: `SyncRequestStreamEvent<T>` | Payload shape passed to `onStream` on the originator side. | → `docs/streaming.md` |
46
+ | Type: `SyncRouteStreamEvent<T>` | Payload shape passed to `upsertSyncEventStreamCallback` on recipients. | → `docs/streaming.md` |
47
+
48
+ ### Streaming primitives (received inside `_server_v{N}.ts` params)
49
+
50
+ | Primitive | Audience | Deep doc |
51
+ |---|---|---|
52
+ | `stream(payload)` | Originator socket only (cheapest). | → `docs/streaming.md` |
53
+ | `broadcastStream(payload)` | Every socket in `roomCode`, across all instances (`io.to(room).emit` via the Redis adapter). | → `docs/streaming.md` |
54
+ | `streamTo(tokens, payload)` | Selective fanout to specific session tokens. | → `docs/streaming.md` |
55
+ | `stream(payload)` in `_client_v{N}.ts` | Per-recipient, runs after `_server` finishes. | → `docs/streaming.md` |
56
+
57
+ ### Hooks dispatched by the server handler
58
+
59
+ | Hook | When | Deep doc |
60
+ |---|---|---|
61
+ | `preSyncAuthorize` | After basic `AuthProps` check, before rate-limit + input validation. Stop to reject. | → `docs/server-vs-client-handlers.md` |
62
+ | `preSyncFanout` | After `_server` runs, before any recipient receives the payload. Stop to abort fanout. | → `docs/room-fanout.md` |
63
+ | `postSyncFanout` | After all recipients have been emitted to. Receives `recipientCount`. | → `docs/room-fanout.md` |
64
+ | `rateLimitExceeded` | When the per-route or per-IP bucket rejects a sync. | → `docs/error-states.md` |
65
+
66
+ ## Config keys
67
+
68
+ ### `registerProjectConfig({ sync, offlineQueue })`
69
+
70
+ - `sync.streamThrottle.flushAtChars` — char threshold before a buffered throttle flushes. Default `32`.
71
+ - `sync.streamThrottle.flushEveryMs` — timer-based flush interval; `false` disables the timer. Default `50`.
72
+ - `sync.streamThrottle.field` — payload key carrying the buffered text. Default `'chunk'`.
73
+ - `sync.fanoutYieldEvery` — yield to the event loop every N recipients during a giant fanout.
74
+ - `sync.fanoutYieldMs` — duration of the yield `setTimeout`.
75
+ - `offlineQueue.maxSize` — cap on the client-side offline queue.
76
+ - `offlineQueue.dropPolicy` — `'reject'` (default — overflow returns `offline.queueFull`), `'drop-oldest'`, or `'drop-newest'`. Per-request override via `syncRequest({ offlineDropPolicy })`.
77
+
78
+ ### Logging toggles
79
+
80
+ - `logging.devLogs`, `logging.devNotifications`, `logging.socketStatus`, `logging.stream` — drive the dev-only log lines in this package.
81
+
82
+ ### Env vars
83
+
84
+ None directly. Inherits socket transport config from `@luckystack/server` and Redis adapter config from `@luckystack/core`.
85
+
86
+ ## Peer dependencies
87
+
88
+ - **Required**: `@luckystack/core`, `@luckystack/login`, `@luckystack/error-tracking`
89
+ - **Peer (canonical ranges, 2026-05-07)**:
90
+ - `@prisma/client@^6.19.0` (transitively required via `@luckystack/core`)
91
+ - `react@^19.2.0` (only the `/client` subpath)
92
+ - `socket.io@^4.8.0` (server entry)
93
+ - `socket.io-client@^4.8.0` (client entry)
94
+ - Redis adapter (required for cross-instance fanout) wired by `@luckystack/server`; see `/docs/ARCHITECTURE_SOCKET.md`.
95
+
96
+ ## Related
97
+
98
+ - Architecture deep-dive: `/docs/ARCHITECTURE_SYNC.md`
99
+ - Socket setup + Redis adapter: `/docs/ARCHITECTURE_SOCKET.md`
100
+ - Multi-instance model + pitfalls (regular `syncRequest` fan-out reaches across instances via `io.in(room).fetchSockets()` + `RemoteSocket.emit()`; streaming via `broadcastStream`/`streamTo`): `/docs/ARCHITECTURE_MULTI_INSTANCE.md`
101
+ - File-based `_sync/` routing: `/docs/ARCHITECTURE_ROUTING.md`
102
+ - Streaming page reconstruction: `/docs/STREAMING_RECONSTRUCTION.md`
103
+ - README (consumer quickstart): `./README.md`
104
+ - Sibling packages: `@luckystack/api`, `@luckystack/presence`, `@luckystack/server`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mathijs van Melick
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # @luckystack/sync
2
+
3
+ > Real-time sync transport for [LuckyStack](https://github.com/ItsLucky23/LuckyStack-v2). Type-safe room-based fanout, server + per-client validation, streaming, optimistic offline queue. Server entry plus a browser-safe `./client` subpath for React.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @luckystack/sync @luckystack/core @luckystack/login @luckystack/error-tracking react socket.io socket.io-client
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ A sync event is two files (one mandatory, one optional):
14
+
15
+ ```ts
16
+ // src/board/_sync/moveCard_server_v1.ts — runs ONCE per request, validates and produces serverOutput
17
+ export const auth = { login: true };
18
+
19
+ export interface SyncParams {
20
+ data: { cardId: string; toLane: string };
21
+ user: SessionLayout;
22
+ receiver: string; // room code
23
+ functions: Functions;
24
+ }
25
+
26
+ export const main = async ({ data, user, receiver }: SyncParams) => {
27
+ await prisma.card.update({ where: { id: data.cardId }, data: { laneId: data.toLane } });
28
+ return { status: 'success', serverOutput: { cardId: data.cardId, movedBy: user.id } };
29
+ };
30
+ ```
31
+
32
+ Add a `_client_v1.ts` only when you need per-client filtering, per-target auth, or a custom `clientOutput`. If it would just `return { status: 'success' }`, leave it out.
33
+
34
+ ### Client side
35
+
36
+ ```tsx
37
+ import { syncRequest, upsertSyncEventCallback } from '@luckystack/sync/client';
38
+
39
+ upsertSyncEventCallback({
40
+ name: 'board/moveCard',
41
+ version: 'v1',
42
+ callback: ({ serverOutput, clientOutput }) => {
43
+ if (serverOutput.status !== 'success') return;
44
+ setCards(prev => moveCardLocally(prev, serverOutput.cardId));
45
+ },
46
+ });
47
+
48
+ await syncRequest({
49
+ name: 'board/moveCard',
50
+ version: 'v1',
51
+ data: { cardId, toLane },
52
+ receiver: roomCode,
53
+ ignoreSelf: true,
54
+ });
55
+ ```
56
+
57
+ ## Subpaths
58
+
59
+ - `@luckystack/sync` — server-only transport adapters (`handleSyncRequest`, `handleHttpSyncRequest`). Wired by `@luckystack/server`.
60
+ - `@luckystack/sync/client` — browser-safe hooks (`syncRequest`, `useSyncEvents`, `upsertSyncEventCallback`). React 19 required.
61
+
62
+ ## How it integrates
63
+
64
+ 1. **Validates** server payload, then runs `_server_v{N}.ts` once.
65
+ 2. **Dispatches** the `preSyncFanout` hook (may abort).
66
+ 3. **Resolves** the room receiver list, optionally running `_client_v{N}.ts` once per recipient socket for per-client filtering or auth.
67
+ 4. **Emits** the merged `{ serverOutput, clientOutput }` payload to each socket.
68
+ 5. **Dispatches** `postSyncFanout` with the recipient count.
69
+
70
+ ## Streaming
71
+
72
+ Sync handlers receive **four** stream primitives in their `_server` params, each picking a different audience and cost profile:
73
+
74
+ | Primitive | Audience | Use when |
75
+ | --- | --- | --- |
76
+ | `stream(payload)` | Originator only (cheapest) | Per-user progress nobody else cares about |
77
+ | `broadcastStream(payload)` | Everyone in `roomCode`, across all instances (Redis adapter) | Live AI chat tokens, collab editor diffs |
78
+ | `streamTo(tokens, payload)` | Specific session tokens | Selective subscribers (admin viewers, etc.) |
79
+ | `_client_v{N}.ts` `stream(...)` | Per-recipient (after `_server` finishes) | Per-target customization (filter / translate / brand) |
80
+
81
+ Plus `createStreamThrottle({ flushEveryMs, flushAtChars })` for coalescing tiny LLM tokens into bigger chunks — cuts message count by 10–100× without losing the "live" feel.
82
+
83
+ ```ts
84
+ // src/chat/_sync/sendMessage_server_v1.ts — AI chat with live broadcast
85
+ import { createStreamThrottle } from '@luckystack/sync';
86
+
87
+ export const main = async ({ clientInput, broadcastStream }: SyncParams) => {
88
+ const throttle = createStreamThrottle({ flushEveryMs: 50, flushAtChars: 32 });
89
+
90
+ let full = '';
91
+ for await (const piece of openaiStream) {
92
+ full += piece.text;
93
+ throttle.push(piece.text, broadcastStream);
94
+ }
95
+ throttle.flush(broadcastStream);
96
+
97
+ return { status: 'success', message: full };
98
+ };
99
+ ```
100
+
101
+ Recipients consume both `broadcastStream` and `streamTo` chunks via the same `upsertSyncEventCallback` they already use:
102
+
103
+ ```ts
104
+ upsertSyncEventCallback({
105
+ name: 'chat/sendMessage',
106
+ version: 'v1',
107
+ callback: ({ stream, status }) => {
108
+ if (status === 'stream' && stream?.chunk) appendToken(stream.chunk);
109
+ },
110
+ });
111
+ ```
112
+
113
+ Full decision tree, performance notes, and additional examples live in [`docs/ARCHITECTURE_SYNC.md`](../../docs/ARCHITECTURE_SYNC.md#streaming).
114
+
115
+ ## Public API
116
+
117
+ Server entry (`@luckystack/sync`):
118
+
119
+ | Export | Purpose |
120
+ | --- | --- |
121
+ | `handleSyncRequest(socket, msg, ack)` | Socket.io sync handler (default export). |
122
+ | `handleHttpSyncRequest(req, res)` | HTTP/SSE fallback. |
123
+ | `createStreamThrottle(options)` | Coalesce small stream pieces into bigger chunks (LLM-token-friendly). |
124
+ | Type: `HttpSyncStreamEvent` | SSE event shape. |
125
+ | Type: `StreamThrottle` / `CreateStreamThrottleOptions` | Throttle helper types. |
126
+
127
+ Configure stream throttling and offline-queue policy via `registerProjectConfig({ sync, offlineQueue })`. The shapes are exported from `@luckystack/core` as **`SyncConfig`** (with nested `SyncStreamThrottleConfig`) and **`OfflineQueueConfig`** — they cover the throttle defaults, fanout iteration tuning, and the queue's max-size + drop policy (`'reject'` triggers the `offline.queueFull` error code on overflow).
128
+
129
+ Client entry (`@luckystack/sync/client`):
130
+
131
+ | Export | Purpose |
132
+ | --- | --- |
133
+ | `syncRequest(opts)` | Fire a typed sync event, optionally with `ignoreSelf` and `receiver`. |
134
+ | `upsertSyncEventCallback({ name, version, callback })` | Subscribe to inbound sync payloads. |
135
+ | `useSyncEvents(...)` | React hook for component-scoped subscriptions. |
136
+
137
+ ## Related architecture docs
138
+
139
+ - [`docs/ARCHITECTURE_SYNC.md`](../../docs/ARCHITECTURE_SYNC.md) — full sync lifecycle, streaming decision tree, performance notes.
140
+ - [`docs/ARCHITECTURE_SOCKET.md`](../../docs/ARCHITECTURE_SOCKET.md) — Socket.io + Redis adapter (required for cross-instance fanout).
141
+ - [`docs/ARCHITECTURE_ROUTING.md`](../../docs/ARCHITECTURE_ROUTING.md) — `_sync/` file conventions and `_server` / `_client` split.
142
+ - [`docs/STREAMING_RECONSTRUCTION.md`](../../docs/STREAMING_RECONSTRUCTION.md) — recreating the streaming demo page.
143
+
144
+ ## Dependencies
145
+
146
+ - Runtime: `@luckystack/core`, `@luckystack/login`, `@luckystack/error-tracking`
147
+ - Peer (canonical ranges, standardized 2026-05-07):
148
+ - `@prisma/client@^6.19.0` (transitively required via `@luckystack/core`)
149
+ - `react@^19.2.0` (`/client` entry only)
150
+ - `socket.io@^4.8.0`
151
+ - `socket.io-client@^4.8.0`
152
+
153
+ ## License
154
+
155
+ MIT — see [LICENSE](../../LICENSE).
@@ -0,0 +1,126 @@
1
+ import { StreamPayload, statusContent, BaseSessionLayout, SyncTypeMap } from '@luckystack/core/client';
2
+ import { Dispatch, SetStateAction, RefObject } from 'react';
3
+
4
+ type SyncRequestStreamEvent<T extends StreamPayload = StreamPayload> = T;
5
+ type SyncRouteStreamEvent<T extends StreamPayload = StreamPayload> = T;
6
+ type DataRequired<T> = Record<string, never> extends T ? false : true;
7
+ type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I : never;
8
+ type Prettify<T> = {
9
+ [K in keyof T]: T[K];
10
+ } & {};
11
+ type SyncRouteRecord = UnionToIntersection<{
12
+ [P in keyof SyncTypeMap]: {
13
+ [N in keyof SyncTypeMap[P] as P extends 'root' ? `system/${Extract<N, string>}` : `${Extract<P, string>}/${Extract<N, string>}`]: SyncTypeMap[P][N];
14
+ };
15
+ }[keyof SyncTypeMap]>;
16
+ type SyncFullName = Extract<keyof SyncRouteRecord, string>;
17
+ type VersionsForFullName<F extends SyncFullName> = Extract<keyof SyncRouteRecord[F], string>;
18
+ type ClientInputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends {
19
+ clientInput: infer I;
20
+ } ? I : never;
21
+ type ServerOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends {
22
+ serverOutput: infer O;
23
+ } ? O : never;
24
+ type ClientOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends {
25
+ clientOutput: infer O;
26
+ } ? O : never;
27
+ type ServerStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends {
28
+ serverStream: infer O;
29
+ } ? O : never;
30
+ type ClientStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends {
31
+ clientStream: infer O;
32
+ } ? O : never;
33
+ type SyncRequestStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = [
34
+ ServerStreamForFullName<F, V>
35
+ ] extends [never] ? never : (event: SyncRequestStreamEvent<Prettify<ServerStreamForFullName<F, V> extends StreamPayload ? ServerStreamForFullName<F, V> : StreamPayload>>) => void;
36
+ type CombinedRouteStream<F extends SyncFullName, V extends VersionsForFullName<F>> = (ClientStreamForFullName<F, V> extends never ? never : ClientStreamForFullName<F, V>) | (ServerStreamForFullName<F, V> extends never ? never : ServerStreamForFullName<F, V>);
37
+ type SyncRouteStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = [
38
+ CombinedRouteStream<F, V>
39
+ ] extends [never] ? never : (params: {
40
+ stream: SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>>;
41
+ }) => void;
42
+ type SyncParamsForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = DataRequired<ClientInputForFullName<F, V>> extends true ? {
43
+ name: F;
44
+ version: V;
45
+ data: ClientInputForFullName<F, V>;
46
+ receiver: string;
47
+ ignoreSelf?: boolean;
48
+ onStream?: SyncRequestStreamCallbackForFullName<F, V>;
49
+ /**
50
+ * Per-request override of `projectConfig.offlineQueue.dropPolicy`. Lets a
51
+ * specific sync ("editor cursor move") pick `'drop-oldest'` while the
52
+ * app default stays `'reject'` for safer sends. When omitted, falls back
53
+ * to the global config.
54
+ */
55
+ offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';
56
+ /**
57
+ * Optional AbortSignal. When aborted the client emits `syncCancel { cb }`
58
+ * to the server and resolves locally with
59
+ * `{ status: 'error', errorCode: 'request.aborted' }`.
60
+ */
61
+ signal?: AbortSignal;
62
+ } : {
63
+ name: F;
64
+ version: V;
65
+ data?: ClientInputForFullName<F, V>;
66
+ receiver: string;
67
+ ignoreSelf?: boolean;
68
+ onStream?: SyncRequestStreamCallbackForFullName<F, V>;
69
+ /** Per-request override (see typed branch). */
70
+ offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';
71
+ /** Optional AbortSignal — see typed branch. */
72
+ signal?: AbortSignal;
73
+ };
74
+ interface SyncErrorParam {
75
+ key: string;
76
+ value: string | number | boolean;
77
+ }
78
+ interface SyncResponseError {
79
+ status: 'error';
80
+ message: string;
81
+ errorCode: string;
82
+ errorParams?: SyncErrorParam[];
83
+ httpStatus?: number;
84
+ }
85
+ type SyncResultForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = [
86
+ ServerOutputForFullName<F, V>
87
+ ] extends [never] ? Record<string, never> : ServerOutputForFullName<F, V>;
88
+ type SyncRequestResponseForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncResponseError | {
89
+ status: 'success';
90
+ message: string;
91
+ result: SyncResultForFullName<F, V>;
92
+ };
93
+ type SyncRequestParamsWithOptions<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncParamsForFullName<F, V>;
94
+ declare function syncRequest<F extends SyncFullName, V extends VersionsForFullName<F>>(params: SyncRequestParamsWithOptions<F, V>): Promise<Prettify<SyncRequestResponseForFullName<F, V>>>;
95
+ interface TypedCallbackParams<F extends SyncFullName, V extends VersionsForFullName<F>> {
96
+ clientOutput: ClientOutputForFullName<F, V>;
97
+ serverOutput: ServerOutputForFullName<F, V>;
98
+ }
99
+ interface UpsertParams<F extends SyncFullName, V extends VersionsForFullName<F>> {
100
+ name: F;
101
+ version: V;
102
+ callback: (params: TypedCallbackParams<F, V>) => void;
103
+ }
104
+ interface UpsertStreamParams<F extends SyncFullName, V extends VersionsForFullName<F>> {
105
+ name: F;
106
+ version: V;
107
+ callback: SyncRouteStreamCallbackForFullName<F, V>;
108
+ }
109
+ declare const useSyncEvents: () => {
110
+ upsertSyncEventCallback: <F extends SyncFullName, V extends VersionsForFullName<F>>(params: UpsertParams<F, V>) => (() => void);
111
+ upsertSyncEventStreamCallback: <F extends SyncFullName, V extends VersionsForFullName<F>>(params: UpsertStreamParams<F, V>) => (() => void);
112
+ };
113
+ declare const useSyncEventTrigger: () => {
114
+ triggerSyncEvent: (name: string, clientOutput?: unknown, serverOutput?: unknown) => void;
115
+ triggerSyncStreamEvent: (name: string, stream: SyncRouteStreamEvent) => void;
116
+ };
117
+ type SocketStatusSetter = Dispatch<SetStateAction<{
118
+ self: statusContent;
119
+ [userId: string]: statusContent;
120
+ }>>;
121
+ declare const initSyncRequest: ({ setSocketStatus, sessionRef }: {
122
+ setSocketStatus: SocketStatusSetter;
123
+ sessionRef: RefObject<BaseSessionLayout | null> | null;
124
+ }) => Promise<void>;
125
+
126
+ export { type SyncRequestStreamEvent, type SyncRouteStreamEvent, initSyncRequest, syncRequest, useSyncEventTrigger, useSyncEvents };