@syncular/transport-ws 0.0.1 → 0.0.2-127

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/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @syncular/transport-ws
2
+
3
+ WebSocket transport for Syncular realtime wake-ups and presence.
4
+
5
+ WebSockets are used as a wake-up mechanism; data still flows over HTTP via `@syncular/transport-http` (clients pull after being notified).
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @syncular/transport-ws
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { createWebSocketTransport } from '@syncular/transport-ws';
17
+
18
+ const transport = createWebSocketTransport({
19
+ baseUrl: 'https://api.example.com',
20
+ getHeaders: () => ({ Authorization: `Bearer ${token}` }),
21
+ // In browsers, WebSocket auth is typically cookie-based (same-origin).
22
+ // If needed, use getRealtimeParams to add non-sensitive query params.
23
+ });
24
+ ```
25
+
26
+ ## Documentation
27
+
28
+ - Realtime wake-ups: https://syncular.dev/docs/build/realtime
29
+ - Presence: https://syncular.dev/docs/build/presence
30
+
31
+ ## Links
32
+
33
+ - GitHub: https://github.com/syncular/syncular
34
+ - Issues: https://github.com/syncular/syncular/issues
35
+
36
+ > Status: Alpha. APIs and storage layouts may change between releases.
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * - Browsers' `WebSocket` cannot attach custom headers.
9
9
  * - Use cookie auth (same-origin) or a query-param token for the realtime URL.
10
10
  */
11
- import type { SyncTransport } from '@syncular/core';
11
+ import type { SyncPushRequest, SyncPushResponse, SyncTransport } from '@syncular/core';
12
12
  import { type ClientOptions } from '@syncular/transport-http';
13
13
  /**
14
14
  * WebSocket connection state
@@ -30,15 +30,42 @@ export interface PresenceEventData {
30
30
  metadata?: Record<string, unknown>;
31
31
  }>;
32
32
  }
33
+ /**
34
+ * Push response data received from the server over WS
35
+ */
36
+ export interface WsPushResponseData {
37
+ requestId: string;
38
+ ok: boolean;
39
+ status: string;
40
+ commitSeq?: number;
41
+ results: Array<{
42
+ opIndex: number;
43
+ status: string;
44
+ [k: string]: unknown;
45
+ }>;
46
+ timestamp: number;
47
+ }
33
48
  /**
34
49
  * WebSocket event from the server
35
50
  */
36
51
  export interface WebSocketEvent {
37
- event: 'sync' | 'heartbeat' | 'error' | 'presence';
52
+ event: 'sync' | 'heartbeat' | 'error' | 'presence' | 'push-response';
38
53
  data: {
39
54
  cursor?: number;
55
+ /** Inline change data for small payloads (WS data delivery) */
56
+ changes?: unknown[];
40
57
  error?: string;
41
58
  presence?: PresenceEventData;
59
+ /** Push response fields (for push-response events) */
60
+ requestId?: string;
61
+ ok?: boolean;
62
+ status?: string;
63
+ commitSeq?: number;
64
+ results?: Array<{
65
+ opIndex: number;
66
+ status: string;
67
+ [k: string]: unknown;
68
+ }>;
42
69
  timestamp: number;
43
70
  };
44
71
  }
@@ -52,16 +79,17 @@ export type WebSocketEventCallback = (event: WebSocketEvent) => void;
52
79
  export type WebSocketStateCallback = (state: WebSocketConnectionState) => void;
53
80
  export interface WebSocketTransportOptions extends ClientOptions {
54
81
  /**
55
- * WebSocket endpoint URL. If not provided, uses `${baseUrl}/realtime` with
56
- * `http(s)` -> `ws(s)` conversion when possible.
82
+ * WebSocket endpoint URL. If not provided, uses `${baseUrl}/sync/realtime`
83
+ * with `http(s)` -> `ws(s)` conversion when possible.
57
84
  */
58
85
  wsUrl?: string;
59
86
  /**
60
87
  * Additional query params for the realtime URL (e.g. `{ token }`).
61
88
  *
62
89
  * ⚠️ SECURITY WARNING: Query parameters may be logged by proxies, CDNs, and
63
- * browser history. Do NOT pass sensitive tokens here. Use cookie-based auth
64
- * or the `authToken` option with a server that supports first-message auth.
90
+ * browser history. Do NOT pass sensitive tokens here. Prefer cookie-based
91
+ * auth. Use `authToken` only with servers that explicitly support first-message
92
+ * WebSocket authentication.
65
93
  */
66
94
  getRealtimeParams?: (args: {
67
95
  clientId: string;
@@ -103,6 +131,11 @@ export interface WebSocketTransportOptions extends ClientOptions {
103
131
  * Optional WebSocket implementation override (useful for non-browser runtimes).
104
132
  */
105
133
  WebSocketImpl?: typeof WebSocket;
134
+ /**
135
+ * Timeout for waiting on WS push responses before falling back to HTTP push.
136
+ * Default: 1500ms
137
+ */
138
+ wsPushTimeoutMs?: number;
106
139
  /**
107
140
  * Transport path telemetry sent to the server for push/pull and realtime.
108
141
  * Defaults to 'relay' for this transport.
@@ -126,6 +159,11 @@ export interface WebSocketTransport extends SyncTransport {
126
159
  sendPresenceLeave(scopeKey: string): void;
127
160
  sendPresenceUpdate(scopeKey: string, metadata: Record<string, unknown>): void;
128
161
  onPresenceEvent(callback: PresenceEventCallback): () => void;
162
+ /**
163
+ * Push a commit via WebSocket (bypasses HTTP).
164
+ * Returns `null` if WS is not connected or times out (caller should fall back to HTTP).
165
+ */
166
+ pushViaWs(request: SyncPushRequest): Promise<SyncPushResponse | null>;
129
167
  }
130
168
  export declare function createWebSocketTransport(options: WebSocketTransportOptions): WebSocketTransport;
131
169
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ function defaultWsUrl(baseUrl) {
16
16
  ? new URL(baseUrl)
17
17
  : new URL(baseUrl, location.origin);
18
18
  resolved.protocol = resolved.protocol === 'https:' ? 'wss:' : 'ws:';
19
- resolved.pathname = `${resolved.pathname.replace(/\/$/, '')}/realtime`;
19
+ resolved.pathname = `${resolved.pathname.replace(/\/$/, '')}/sync/realtime`;
20
20
  return resolved.toString();
21
21
  }
22
22
  catch {
@@ -32,10 +32,12 @@ export function createWebSocketTransport(options) {
32
32
  transportPath: telemetryTransportPath,
33
33
  });
34
34
  const { baseUrl, wsUrl = options.wsUrl ?? defaultWsUrl(baseUrl), getRealtimeParams, authToken, initialReconnectDelay = 1000, maxReconnectDelay = 30_000, reconnectBackoffFactor = 2, heartbeatTimeout = 60_000, WebSocketImpl = typeof WebSocket !== 'undefined' ? WebSocket : undefined, } = options;
35
- // Warn about security risk of using getRealtimeParams with sensitive data
36
- if (getRealtimeParams && !authToken) {
35
+ // Warn about security risk of using getRealtimeParams with sensitive data,
36
+ // but only if the consumer hasn't also provided getHeaders or authToken
37
+ // (which indicates intentional auth handling with query-param fallback).
38
+ if (getRealtimeParams && !authToken && !options.getHeaders) {
37
39
  console.warn('[transport-ws] getRealtimeParams sends data in URL query parameters, ' +
38
- 'which may be logged by proxies and CDNs. Consider using authToken instead.');
40
+ 'which may be logged by proxies and CDNs. Prefer cookie-based auth when possible.');
39
41
  }
40
42
  if (!wsUrl) {
41
43
  throw new Error('@syncular/transport-ws: wsUrl is required when baseUrl cannot be converted');
@@ -56,6 +58,9 @@ export function createWebSocketTransport(options) {
56
58
  // Presence state
57
59
  const activePresenceScopes = new Map();
58
60
  const presenceCallbacks = new Set();
61
+ // Pending WS push requests (requestId -> resolver)
62
+ const pendingPushRequests = new Map();
63
+ const wsPushTimeoutMs = Math.max(1, options.wsPushTimeoutMs ?? 1_500);
59
64
  function setConnectionState(state) {
60
65
  if (connectionState === state)
61
66
  return;
@@ -103,6 +108,12 @@ export function createWebSocketTransport(options) {
103
108
  function doDisconnect() {
104
109
  clearHeartbeatTimer();
105
110
  clearReconnectTimer();
111
+ // Resolve all pending WS push requests as null (triggers HTTP fallback)
112
+ for (const [, pending] of pendingPushRequests) {
113
+ clearTimeout(pending.timer);
114
+ pending.resolve(null);
115
+ }
116
+ pendingPushRequests.clear();
106
117
  if (ws) {
107
118
  try {
108
119
  ws.onopen = null;
@@ -128,6 +139,25 @@ export function createWebSocketTransport(options) {
128
139
  const data = raw.data;
129
140
  if (!data || typeof data !== 'object')
130
141
  return;
142
+ // Route push-response events to pending request resolvers
143
+ if (event === 'push-response') {
144
+ const d = data;
145
+ const requestId = typeof d.requestId === 'string' ? d.requestId : '';
146
+ const pending = pendingPushRequests.get(requestId);
147
+ if (pending) {
148
+ pendingPushRequests.delete(requestId);
149
+ clearTimeout(pending.timer);
150
+ pending.resolve({
151
+ ok: true,
152
+ status: d.status ?? 'rejected',
153
+ commitSeq: typeof d.commitSeq === 'number' ? d.commitSeq : undefined,
154
+ results: Array.isArray(d.results)
155
+ ? d.results
156
+ : [],
157
+ });
158
+ }
159
+ return;
160
+ }
131
161
  // Route presence events to dedicated callbacks
132
162
  if (event === 'presence') {
133
163
  const presenceData = data.presence;
@@ -146,10 +176,11 @@ export function createWebSocketTransport(options) {
146
176
  return;
147
177
  currentEventCallback?.({ event, data });
148
178
  }
149
- function sendPresenceMessage(msg) {
150
- if (!ws || ws.readyState !== WebSocketImpl.OPEN)
179
+ function sendPresenceMessage(msg, socketArg) {
180
+ const target = socketArg ?? ws;
181
+ if (!target || target.readyState !== WebSocketImpl.OPEN)
151
182
  return;
152
- ws.send(JSON.stringify({ type: 'presence', ...msg }));
183
+ target.send(JSON.stringify({ type: 'presence', ...msg }));
153
184
  }
154
185
  async function buildUrl(clientId) {
155
186
  if (!wsUrl)
@@ -195,23 +226,30 @@ export function createWebSocketTransport(options) {
195
226
  return;
196
227
  if (!WebSocketImpl)
197
228
  throw new Error('WebSocketImpl is required');
229
+ let socket;
198
230
  try {
199
- ws = new WebSocketImpl(url);
231
+ socket = new WebSocketImpl(url);
200
232
  }
201
233
  catch {
202
234
  doDisconnect();
203
235
  scheduleReconnect();
204
236
  return;
205
237
  }
206
- ws.onopen = async () => {
238
+ ws = socket;
239
+ socket.onopen = async () => {
207
240
  if (nonce !== connectNonce)
208
241
  return;
242
+ if (socket !== ws)
243
+ return;
209
244
  // Send auth token if provided (more secure than query params)
210
- if (authToken && ws) {
245
+ if (authToken) {
211
246
  try {
212
247
  const token = typeof authToken === 'function' ? await authToken() : authToken;
213
- if (token && nonce === connectNonce) {
214
- ws.send(JSON.stringify({ type: 'auth', token }));
248
+ if (token &&
249
+ nonce === connectNonce &&
250
+ socket === ws &&
251
+ socket.readyState === WebSocketImpl.OPEN) {
252
+ socket.send(JSON.stringify({ type: 'auth', token }));
215
253
  }
216
254
  }
217
255
  catch {
@@ -221,17 +259,23 @@ export function createWebSocketTransport(options) {
221
259
  }
222
260
  if (nonce !== connectNonce)
223
261
  return;
262
+ if (socket !== ws)
263
+ return;
264
+ if (socket.readyState !== WebSocketImpl.OPEN)
265
+ return;
224
266
  setConnectionState('connected');
225
267
  reconnectAttempts = 0;
226
268
  resetHeartbeatTimer();
227
269
  // Re-join all active presence scopes on reconnect
228
270
  for (const [scopeKey, metadata] of activePresenceScopes) {
229
- sendPresenceMessage({ action: 'join', scopeKey, metadata });
271
+ sendPresenceMessage({ action: 'join', scopeKey, metadata }, socket);
230
272
  }
231
273
  };
232
- ws.onmessage = (evt) => {
274
+ socket.onmessage = (evt) => {
233
275
  if (nonce !== connectNonce)
234
276
  return;
277
+ if (socket !== ws)
278
+ return;
235
279
  resetHeartbeatTimer();
236
280
  if (typeof evt.data === 'string') {
237
281
  try {
@@ -242,15 +286,19 @@ export function createWebSocketTransport(options) {
242
286
  }
243
287
  }
244
288
  };
245
- ws.onerror = () => {
289
+ socket.onerror = () => {
246
290
  if (nonce !== connectNonce)
247
291
  return;
292
+ if (socket !== ws)
293
+ return;
248
294
  doDisconnect();
249
295
  scheduleReconnect();
250
296
  };
251
- ws.onclose = () => {
297
+ socket.onclose = () => {
252
298
  if (nonce !== connectNonce)
253
299
  return;
300
+ if (socket !== ws)
301
+ return;
254
302
  doDisconnect();
255
303
  scheduleReconnect();
256
304
  };
@@ -303,6 +351,33 @@ export function createWebSocketTransport(options) {
303
351
  presenceCallbacks.delete(callback);
304
352
  };
305
353
  },
354
+ pushViaWs(request) {
355
+ if (!ws || ws.readyState !== WebSocketImpl.OPEN) {
356
+ return Promise.resolve(null);
357
+ }
358
+ const requestId = crypto.randomUUID();
359
+ return new Promise((resolve) => {
360
+ const timer = setTimeout(() => {
361
+ pendingPushRequests.delete(requestId);
362
+ resolve(null);
363
+ }, wsPushTimeoutMs);
364
+ pendingPushRequests.set(requestId, { resolve, timer });
365
+ try {
366
+ ws.send(JSON.stringify({
367
+ type: 'push',
368
+ requestId,
369
+ clientCommitId: request.clientCommitId,
370
+ operations: request.operations,
371
+ schemaVersion: request.schemaVersion,
372
+ }));
373
+ }
374
+ catch {
375
+ pendingPushRequests.delete(requestId);
376
+ clearTimeout(timer);
377
+ resolve(null);
378
+ }
379
+ });
380
+ },
306
381
  };
307
382
  }
308
383
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,EAEL,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAmKlC,SAAS,YAAY,CAAC,OAAe,EAAiB;IACpD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7D,MAAM,QAAQ,GACZ,UAAU,IAAI,OAAO,QAAQ,KAAK,WAAW;YAC3C,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC;YAClB,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAExC,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;QACpE,QAAQ,CAAC,QAAQ,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC;QAC5E,OAAO,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AAAA,CACF;AAED,MAAM,UAAU,wBAAwB,CACtC,OAAkC,EACd;IACpB,MAAM,sBAAsB,GAAG,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC;IAChE,MAAM,aAAa,GAAG,mBAAmB,CAAC;QACxC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,aAAa,EAAE,sBAAsB;KACtC,CAAC,CAAC;IAEH,MAAM,EACJ,OAAO,EACP,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,OAAO,CAAC,EAC9C,iBAAiB,EACjB,SAAS,EACT,qBAAqB,GAAG,IAAI,EAC5B,iBAAiB,GAAG,MAAM,EAC1B,sBAAsB,GAAG,CAAC,EAC1B,gBAAgB,GAAG,MAAM,EACzB,aAAa,GAAG,OAAO,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,GACzE,GAAG,OAAO,CAAC;IAEZ,2EAA2E;IAC3E,wEAAwE;IACxE,yEAAyE;IACzE,IAAI,iBAAiB,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC3D,OAAO,CAAC,IAAI,CACV,uEAAuE;YACrE,kFAAkF,CACrF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,4EAA4E,CAC7E,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,CAAC;IAED,IAAI,EAAE,GAAqB,IAAI,CAAC;IAChC,IAAI,eAAe,GAA6B,cAAc,CAAC;IAC/D,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,cAAc,GAAyC,IAAI,CAAC;IAChE,IAAI,cAAc,GAAyC,IAAI,CAAC;IAEhE,IAAI,oBAAoB,GAAkC,IAAI,CAAC;IAC/D,IAAI,oBAAoB,GAAkC,IAAI,CAAC;IAC/D,IAAI,sBAAsB,GAAG,KAAK,CAAC;IACnC,IAAI,eAAe,GAAkB,IAAI,CAAC;IAC1C,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,iBAAiB;IACjB,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAGjC,CAAC;IACJ,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE3D,mDAAmD;IACnD,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAMhC,CAAC;IACJ,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK,CAAC,CAAC;IAEtE,SAAS,kBAAkB,CAAC,KAA+B,EAAQ;QACjE,IAAI,eAAe,KAAK,KAAK;YAAE,OAAO;QACtC,eAAe,GAAG,KAAK,CAAC;QACxB,oBAAoB,EAAE,CAAC,KAAK,CAAC,CAAC;IAAA,CAC/B;IAED,SAAS,uBAAuB,GAAW;QACzC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,qBAAqB,GAAG,sBAAsB,IAAI,iBAAiB,EACnE,iBAAiB,CAClB,CAAC;QACF,uFAAuF;QACvF,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,IAAI,GAAG,CAAC;QACpD,MAAM,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB;QACtF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC;IAAA,CACpD;IAED,SAAS,mBAAmB,GAAS;QACnC,IAAI,CAAC,cAAc;YAAE,OAAO;QAC5B,YAAY,CAAC,cAAc,CAAC,CAAC;QAC7B,cAAc,GAAG,IAAI,CAAC;IAAA,CACvB;IAED,SAAS,mBAAmB,GAAS;QACnC,mBAAmB,EAAE,CAAC;QACtB,IAAI,gBAAgB,IAAI,CAAC;YAAE,OAAO;QAElC,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;YAChC,YAAY,EAAE,CAAC;YACf,iBAAiB,EAAE,CAAC;QAAA,CACrB,EAAE,gBAAgB,CAAC,CAAC;IAAA,CACtB;IAED,SAAS,mBAAmB,GAAS;QACnC,IAAI,CAAC,cAAc;YAAE,OAAO;QAC5B,YAAY,CAAC,cAAc,CAAC,CAAC;QAC7B,cAAc,GAAG,IAAI,CAAC;IAAA,CACvB;IAED,SAAS,iBAAiB,GAAS;QACjC,IAAI,sBAAsB;YAAE,OAAO;QAEnC,mBAAmB,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,uBAAuB,EAAE,CAAC;QACxC,iBAAiB,IAAI,CAAC,CAAC;QAEvB,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;YAChC,KAAK,SAAS,EAAE,CAAC;QAAA,CAClB,EAAE,KAAK,CAAC,CAAC;IAAA,CACX;IAED,SAAS,YAAY,GAAS;QAC5B,mBAAmB,EAAE,CAAC;QACtB,mBAAmB,EAAE,CAAC;QAEtB,wEAAwE;QACxE,KAAK,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,mBAAmB,EAAE,CAAC;YAC9C,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QACD,mBAAmB,CAAC,KAAK,EAAE,CAAC;QAE5B,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC;gBACH,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC;gBACjB,EAAE,CAAC,SAAS,GAAG,IAAI,CAAC;gBACpB,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC;gBAClB,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC;gBAClB,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,EAAE,GAAG,IAAI,CAAC;QACZ,CAAC;QAED,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAAA,CACpC;IAED,SAAS,aAAa,CAAC,GAAY,EAAQ;QACzC,mBAAmB,EAAE,CAAC;QAEtB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO;QAC5C,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC;YAAE,OAAO;QAClD,MAAM,KAAK,GAAI,GAA0B,CAAC,KAAK,CAAC;QAChD,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;QAC7C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO;QAE9C,0DAA0D;QAC1D,IAAI,KAAK,KAAK,eAAe,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,IAA+B,CAAC;YAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACnD,IAAI,OAAO,EAAE,CAAC;gBACZ,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACtC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC5B,OAAO,CAAC,OAAO,CAAC;oBACd,EAAE,EAAE,IAAa;oBACjB,MAAM,EAAG,CAAC,CAAC,MAA4C,IAAI,UAAU;oBACrE,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;oBACpE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;wBAC/B,CAAC,CAAE,CAAC,CAAC,OAAuC;wBAC5C,CAAC,CAAC,EAAE;iBACP,CAAC,CAAC;YACL,CAAC;YACD,OAAO;QACT,CAAC;QAED,+CAA+C;QAC/C,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;YACzB,MAAM,YAAY,GAAI,IAA+B,CAAC,QAAQ,CAAC;YAC/D,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;gBACrD,KAAK,MAAM,EAAE,IAAI,iBAAiB,EAAE,CAAC;oBACnC,EAAE,CAAC,YAAiC,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;YACD,sCAAsC;YACtC,IAAI,oBAAoB,EAAE,CAAC;gBACzB,oBAAoB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAoB,CAAC,CAAC;YAC1D,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,OAAO;YAAE,OAAO;QAC3E,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAoB,CAAC,CAAC;IAAA,CAC3D;IAED,SAAS,mBAAmB,CAC1B,GAA4B,EAC5B,SAAqB,EACf;QACN,MAAM,MAAM,GAAG,SAAS,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,aAAc,CAAC,IAAI;YAAE,OAAO;QACjE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;IAAA,CAC3D;IAED,KAAK,UAAU,QAAQ,CAAC,QAAgB,EAAmB;QACzD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACjD,wDAAwD;QACxD,MAAM,UAAU,GAAG,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3D,MAAM,GAAG,GACP,UAAU,IAAI,OAAO,QAAQ,KAAK,WAAW;YAC3C,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC;YAChB,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtC,qCAAqC;QACrC,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAAE,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC;QACrD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO;YAAE,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC;QACnD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE3C,IAAI,iBAAiB,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACrD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;oBAClD,IAAI,OAAO,CAAC,KAAK,QAAQ;wBAAE,SAAS;oBACpC,IAAI,CAAC,CAAC;wBAAE,SAAS;oBACjB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,kCAAkC;YACpC,CAAC;QACH,CAAC;QAED,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAAC;QAE9D,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IAAA,CACvB;IAED,KAAK,UAAU,SAAS,GAAkB;QACxC,IAAI,CAAC,eAAe;YAAE,OAAO;QAC7B,IAAI,sBAAsB;YAAE,OAAO;QAEnC,MAAM,KAAK,GAAG,EAAE,YAAY,CAAC;QAE7B,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAEjC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAC,CAAC;QAC5C,IAAI,KAAK,KAAK,YAAY;YAAE,OAAO;QAEnC,IAAI,CAAC,aAAa;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAEjE,IAAI,MAAiB,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,YAAY,EAAE,CAAC;YACf,iBAAiB,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QACD,EAAE,GAAG,MAAM,CAAC;QAEZ,MAAM,CAAC,MAAM,GAAG,KAAK,IAAI,EAAE,CAAC;YAC1B,IAAI,KAAK,KAAK,YAAY;gBAAE,OAAO;YACnC,IAAI,MAAM,KAAK,EAAE;gBAAE,OAAO;YAE1B,8DAA8D;YAC9D,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC;oBACH,MAAM,KAAK,GACT,OAAO,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;oBAClE,IACE,KAAK;wBACL,KAAK,KAAK,YAAY;wBACtB,MAAM,KAAK,EAAE;wBACb,MAAM,CAAC,UAAU,KAAK,aAAa,CAAC,IAAI,EACxC,CAAC;wBACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;oBACvD,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,kDAAkD;oBAClD,8DAA8D;gBAChE,CAAC;YACH,CAAC;YAED,IAAI,KAAK,KAAK,YAAY;gBAAE,OAAO;YACnC,IAAI,MAAM,KAAK,EAAE;gBAAE,OAAO;YAC1B,IAAI,MAAM,CAAC,UAAU,KAAK,aAAa,CAAC,IAAI;gBAAE,OAAO;YACrD,kBAAkB,CAAC,WAAW,CAAC,CAAC;YAChC,iBAAiB,GAAG,CAAC,CAAC;YACtB,mBAAmB,EAAE,CAAC;YAEtB,kDAAkD;YAClD,KAAK,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,oBAAoB,EAAE,CAAC;gBACxD,mBAAmB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;YACtE,CAAC;QAAA,CACF,CAAC;QAEF,MAAM,CAAC,SAAS,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC;YAC1B,IAAI,KAAK,KAAK,YAAY;gBAAE,OAAO;YACnC,IAAI,MAAM,KAAK,EAAE;gBAAE,OAAO;YAC1B,mBAAmB,EAAE,CAAC;YAEtB,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACjC,IAAI,CAAC;oBACH,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;gBACtC,CAAC;gBAAC,MAAM,CAAC;oBACP,4BAA4B;gBAC9B,CAAC;YACH,CAAC;QAAA,CACF,CAAC;QAEF,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;YACrB,IAAI,KAAK,KAAK,YAAY;gBAAE,OAAO;YACnC,IAAI,MAAM,KAAK,EAAE;gBAAE,OAAO;YAC1B,YAAY,EAAE,CAAC;YACf,iBAAiB,EAAE,CAAC;QAAA,CACrB,CAAC;QAEF,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;YACrB,IAAI,KAAK,KAAK,YAAY;gBAAE,OAAO;YACnC,IAAI,MAAM,KAAK,EAAE;gBAAE,OAAO;YAC1B,YAAY,EAAE,CAAC;YACf,iBAAiB,EAAE,CAAC;QAAA,CACrB,CAAC;IAAA,CACH;IAED,OAAO;QACL,GAAG,aAAa;QAChB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE;YACpC,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC;YAChC,oBAAoB,GAAG,OAAO,CAAC;YAC/B,oBAAoB,GAAG,aAAa,IAAI,IAAI,CAAC;YAC7C,sBAAsB,GAAG,KAAK,CAAC;YAC/B,iBAAiB,GAAG,CAAC,CAAC;YACtB,KAAK,SAAS,EAAE,CAAC;YAEjB,OAAO,GAAG,EAAE,CAAC;gBACX,sBAAsB,GAAG,IAAI,CAAC;gBAC9B,oBAAoB,GAAG,IAAI,CAAC;gBAC5B,oBAAoB,GAAG,IAAI,CAAC;gBAC5B,eAAe,GAAG,IAAI,CAAC;gBACvB,YAAY,IAAI,CAAC,CAAC;gBAClB,YAAY,EAAE,CAAC;YAAA,CAChB,CAAC;QAAA,CACH;QACD,kBAAkB,GAAG;YACnB,OAAO,eAAe,CAAC;QAAA,CACxB;QACD,SAAS,GAAG;YACV,IAAI,CAAC,eAAe;gBAAE,OAAO;YAC7B,IAAI,sBAAsB;gBAAE,OAAO;YACnC,YAAY,IAAI,CAAC,CAAC;YAClB,YAAY,EAAE,CAAC;YACf,KAAK,SAAS,EAAE,CAAC;QAAA,CAClB;QACD,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE;YACnC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC7C,mBAAmB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAAA,CAC7D;QACD,iBAAiB,CAAC,QAAQ,EAAE;YAC1B,oBAAoB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACtC,mBAAmB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QAAA,CACpD;QACD,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE;YACrC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC7C,mBAAmB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAAA,CAC/D;QACD,eAAe,CAAC,QAAQ,EAAE;YACxB,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAChC,OAAO,GAAG,EAAE,CAAC;gBACX,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAAA,CACpC,CAAC;QAAA,CACH;QACD,SAAS,CAAC,OAAwB,EAAoC;YACpE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,UAAU,KAAK,aAAc,CAAC,IAAI,EAAE,CAAC;gBACjD,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAEtC,OAAO,IAAI,OAAO,CAA0B,CAAC,OAAO,EAAE,EAAE,CAAC;gBACvD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;oBAC7B,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBACtC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAA,CACf,EAAE,eAAe,CAAC,CAAC;gBAEpB,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;gBAEvD,IAAI,CAAC;oBACH,EAAG,CAAC,IAAI,CACN,IAAI,CAAC,SAAS,CAAC;wBACb,IAAI,EAAE,MAAM;wBACZ,SAAS;wBACT,cAAc,EAAE,OAAO,CAAC,cAAc;wBACtC,UAAU,EAAE,OAAO,CAAC,UAAU;wBAC9B,aAAa,EAAE,OAAO,CAAC,aAAa;qBACrC,CAAC,CACH,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBACtC,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YAAA,CACF,CAAC,CAAC;QAAA,CACJ;KACF,CAAC;AAAA,CACH"}
package/package.json CHANGED
@@ -1,6 +1,27 @@
1
1
  {
2
2
  "name": "@syncular/transport-ws",
3
- "version": "0.0.1",
3
+ "version": "0.0.2-127",
4
+ "description": "WebSocket transport for Syncular real-time sync",
5
+ "license": "MIT",
6
+ "author": "Benjamin Kniffler",
7
+ "homepage": "https://syncular.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/syncular/syncular.git",
11
+ "directory": "packages/transport-ws"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/syncular/syncular/issues"
15
+ },
16
+ "keywords": [
17
+ "sync",
18
+ "offline-first",
19
+ "realtime",
20
+ "database",
21
+ "typescript",
22
+ "websocket",
23
+ "realtime"
24
+ ],
4
25
  "private": false,
5
26
  "publishConfig": {
6
27
  "access": "public"
@@ -17,14 +38,15 @@
17
38
  },
18
39
  "scripts": {
19
40
  "tsgo": "tsgo --noEmit",
20
- "build": "rm -rf dist && tsgo"
41
+ "build": "tsgo",
42
+ "release": "bunx syncular-publish"
21
43
  },
22
44
  "dependencies": {
23
- "@syncular/core": "workspace:*",
24
- "@syncular/transport-http": "workspace:*"
45
+ "@syncular/core": "0.0.2-127",
46
+ "@syncular/transport-http": "0.0.2-127"
25
47
  },
26
48
  "devDependencies": {
27
- "@syncular/config": "workspace:*"
49
+ "@syncular/config": "0.0.0"
28
50
  },
29
51
  "files": [
30
52
  "dist",
@@ -0,0 +1,183 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { createWebSocketTransport } from './index';
3
+
4
+ class MockWebSocket {
5
+ static readonly CONNECTING = 0;
6
+ static readonly OPEN = 1;
7
+ static readonly CLOSING = 2;
8
+ static readonly CLOSED = 3;
9
+ static instances: MockWebSocket[] = [];
10
+
11
+ readonly url: string;
12
+ readonly sent: string[] = [];
13
+ readyState = MockWebSocket.CONNECTING;
14
+ onopen: ((ev: Event) => unknown) | null = null;
15
+ onmessage: ((ev: MessageEvent<string>) => unknown) | null = null;
16
+ onerror: ((ev: Event) => unknown) | null = null;
17
+ onclose: ((ev: Event) => unknown) | null = null;
18
+
19
+ constructor(url: string | URL, _protocols?: string | string[]) {
20
+ this.url = String(url);
21
+ MockWebSocket.instances.push(this);
22
+ }
23
+
24
+ send(data: string): void {
25
+ if (this.readyState !== MockWebSocket.OPEN) {
26
+ throw new Error('Socket is not open');
27
+ }
28
+ this.sent.push(data);
29
+ }
30
+
31
+ close(): void {
32
+ this.readyState = MockWebSocket.CLOSED;
33
+ }
34
+
35
+ async triggerOpen(): Promise<void> {
36
+ this.readyState = MockWebSocket.OPEN;
37
+ const handler = this.onopen;
38
+ if (!handler) return;
39
+ await handler(new Event('open'));
40
+ }
41
+
42
+ triggerClose(): void {
43
+ this.readyState = MockWebSocket.CLOSED;
44
+ const handler = this.onclose;
45
+ if (!handler) return;
46
+ handler(new Event('close'));
47
+ }
48
+ }
49
+
50
+ function clearMockSockets(): void {
51
+ MockWebSocket.instances.length = 0;
52
+ }
53
+
54
+ async function waitForSocket(): Promise<MockWebSocket> {
55
+ for (let i = 0; i < 10; i++) {
56
+ const socket = MockWebSocket.instances[0];
57
+ if (socket) return socket;
58
+ await new Promise((resolve) => setTimeout(resolve, 0));
59
+ }
60
+ throw new Error('Expected a websocket instance to be created');
61
+ }
62
+
63
+ function createDeferred<T>(): {
64
+ promise: Promise<T>;
65
+ resolve: (value: T) => void;
66
+ } {
67
+ let resolve!: (value: T) => void;
68
+ const promise = new Promise<T>((r) => {
69
+ resolve = r;
70
+ });
71
+ return { promise, resolve };
72
+ }
73
+
74
+ describe('createWebSocketTransport auth flow', () => {
75
+ test('derives default wsUrl from baseUrl as /sync/realtime', async () => {
76
+ clearMockSockets();
77
+ const transport = createWebSocketTransport({
78
+ baseUrl: 'http://localhost:3000/api',
79
+ WebSocketImpl: MockWebSocket as typeof WebSocket,
80
+ reconnectJitter: 0,
81
+ });
82
+
83
+ const disconnect = transport.connect(
84
+ { clientId: 'client-default-url' },
85
+ () => {}
86
+ );
87
+ const socket = await waitForSocket();
88
+
89
+ const url = new URL(socket.url);
90
+ expect(url.protocol).toBe('ws:');
91
+ expect(url.pathname).toBe('/api/sync/realtime');
92
+ expect(url.searchParams.get('clientId')).toBe('client-default-url');
93
+ expect(url.searchParams.get('transportPath')).toBe('relay');
94
+
95
+ disconnect();
96
+ });
97
+
98
+ test('sends first-message auth token after open', async () => {
99
+ clearMockSockets();
100
+ const transport = createWebSocketTransport({
101
+ baseUrl: 'http://localhost:3000/api',
102
+ WebSocketImpl: MockWebSocket as typeof WebSocket,
103
+ authToken: 'token-1',
104
+ reconnectJitter: 0,
105
+ });
106
+
107
+ const disconnect = transport.connect({ clientId: 'client-1' }, () => {});
108
+ const socket = await waitForSocket();
109
+
110
+ await socket.triggerOpen();
111
+
112
+ expect(transport.getConnectionState()).toBe('connected');
113
+ expect(socket.sent).toContain(
114
+ JSON.stringify({ type: 'auth', token: 'token-1' })
115
+ );
116
+
117
+ disconnect();
118
+ });
119
+
120
+ test('does not become connected when socket closes while waiting for auth token', async () => {
121
+ clearMockSockets();
122
+ const token = createDeferred<string>();
123
+ const transport = createWebSocketTransport({
124
+ baseUrl: 'http://localhost:3000/api',
125
+ WebSocketImpl: MockWebSocket as typeof WebSocket,
126
+ authToken: () => token.promise,
127
+ reconnectJitter: 0,
128
+ initialReconnectDelay: 1_000_000,
129
+ maxReconnectDelay: 1_000_000,
130
+ });
131
+
132
+ const disconnect = transport.connect({ clientId: 'client-2' }, () => {});
133
+ const socket = await waitForSocket();
134
+
135
+ const openPromise = socket.triggerOpen();
136
+ expect(transport.getConnectionState()).toBe('connecting');
137
+
138
+ socket.triggerClose();
139
+ expect(transport.getConnectionState()).toBe('disconnected');
140
+
141
+ token.resolve('token-2');
142
+ await openPromise;
143
+
144
+ expect(transport.getConnectionState()).toBe('disconnected');
145
+ expect(socket.sent).toEqual([]);
146
+
147
+ disconnect();
148
+ });
149
+
150
+ test('times out ws push quickly when configured', async () => {
151
+ clearMockSockets();
152
+ const transport = createWebSocketTransport({
153
+ baseUrl: 'http://localhost:3000/api',
154
+ WebSocketImpl: MockWebSocket as typeof WebSocket,
155
+ reconnectJitter: 0,
156
+ wsPushTimeoutMs: 20,
157
+ });
158
+
159
+ const disconnect = transport.connect({ clientId: 'client-3' }, () => {});
160
+ const socket = await waitForSocket();
161
+ await socket.triggerOpen();
162
+
163
+ const startedAt = Date.now();
164
+ const response = await transport.pushViaWs({
165
+ clientId: 'client-3',
166
+ clientCommitId: 'commit-1',
167
+ operations: [],
168
+ schemaVersion: 1,
169
+ });
170
+ const elapsedMs = Date.now() - startedAt;
171
+
172
+ expect(response).toBeNull();
173
+ expect(elapsedMs).toBeGreaterThanOrEqual(15);
174
+ expect(elapsedMs).toBeLessThan(500);
175
+
176
+ const pushMessage = socket.sent.find((msg) =>
177
+ msg.includes('"type":"push"')
178
+ );
179
+ expect(pushMessage).toBeDefined();
180
+
181
+ disconnect();
182
+ });
183
+ });
package/src/index.ts CHANGED
@@ -9,7 +9,11 @@
9
9
  * - Use cookie auth (same-origin) or a query-param token for the realtime URL.
10
10
  */
11
11
 
12
- import type { SyncTransport } from '@syncular/core';
12
+ import type {
13
+ SyncPushRequest,
14
+ SyncPushResponse,
15
+ SyncTransport,
16
+ } from '@syncular/core';
13
17
  import {
14
18
  type ClientOptions,
15
19
  createHttpTransport,
@@ -40,15 +44,35 @@ export interface PresenceEventData {
40
44
  }>;
41
45
  }
42
46
 
47
+ /**
48
+ * Push response data received from the server over WS
49
+ */
50
+ export interface WsPushResponseData {
51
+ requestId: string;
52
+ ok: boolean;
53
+ status: string;
54
+ commitSeq?: number;
55
+ results: Array<{ opIndex: number; status: string; [k: string]: unknown }>;
56
+ timestamp: number;
57
+ }
58
+
43
59
  /**
44
60
  * WebSocket event from the server
45
61
  */
46
62
  export interface WebSocketEvent {
47
- event: 'sync' | 'heartbeat' | 'error' | 'presence';
63
+ event: 'sync' | 'heartbeat' | 'error' | 'presence' | 'push-response';
48
64
  data: {
49
65
  cursor?: number;
66
+ /** Inline change data for small payloads (WS data delivery) */
67
+ changes?: unknown[];
50
68
  error?: string;
51
69
  presence?: PresenceEventData;
70
+ /** Push response fields (for push-response events) */
71
+ requestId?: string;
72
+ ok?: boolean;
73
+ status?: string;
74
+ commitSeq?: number;
75
+ results?: Array<{ opIndex: number; status: string; [k: string]: unknown }>;
52
76
  timestamp: number;
53
77
  };
54
78
  }
@@ -65,16 +89,17 @@ export type WebSocketStateCallback = (state: WebSocketConnectionState) => void;
65
89
 
66
90
  export interface WebSocketTransportOptions extends ClientOptions {
67
91
  /**
68
- * WebSocket endpoint URL. If not provided, uses `${baseUrl}/realtime` with
69
- * `http(s)` -> `ws(s)` conversion when possible.
92
+ * WebSocket endpoint URL. If not provided, uses `${baseUrl}/sync/realtime`
93
+ * with `http(s)` -> `ws(s)` conversion when possible.
70
94
  */
71
95
  wsUrl?: string;
72
96
  /**
73
97
  * Additional query params for the realtime URL (e.g. `{ token }`).
74
98
  *
75
99
  * ⚠️ SECURITY WARNING: Query parameters may be logged by proxies, CDNs, and
76
- * browser history. Do NOT pass sensitive tokens here. Use cookie-based auth
77
- * or the `authToken` option with a server that supports first-message auth.
100
+ * browser history. Do NOT pass sensitive tokens here. Prefer cookie-based
101
+ * auth. Use `authToken` only with servers that explicitly support first-message
102
+ * WebSocket authentication.
78
103
  */
79
104
  getRealtimeParams?: (args: {
80
105
  clientId: string;
@@ -116,6 +141,11 @@ export interface WebSocketTransportOptions extends ClientOptions {
116
141
  * Optional WebSocket implementation override (useful for non-browser runtimes).
117
142
  */
118
143
  WebSocketImpl?: typeof WebSocket;
144
+ /**
145
+ * Timeout for waiting on WS push responses before falling back to HTTP push.
146
+ * Default: 1500ms
147
+ */
148
+ wsPushTimeoutMs?: number;
119
149
  /**
120
150
  * Transport path telemetry sent to the server for push/pull and realtime.
121
151
  * Defaults to 'relay' for this transport.
@@ -143,6 +173,11 @@ export interface WebSocketTransport extends SyncTransport {
143
173
  sendPresenceLeave(scopeKey: string): void;
144
174
  sendPresenceUpdate(scopeKey: string, metadata: Record<string, unknown>): void;
145
175
  onPresenceEvent(callback: PresenceEventCallback): () => void;
176
+ /**
177
+ * Push a commit via WebSocket (bypasses HTTP).
178
+ * Returns `null` if WS is not connected or times out (caller should fall back to HTTP).
179
+ */
180
+ pushViaWs(request: SyncPushRequest): Promise<SyncPushResponse | null>;
146
181
  }
147
182
 
148
183
  function defaultWsUrl(baseUrl: string): string | null {
@@ -154,7 +189,7 @@ function defaultWsUrl(baseUrl: string): string | null {
154
189
  : new URL(baseUrl, location.origin);
155
190
 
156
191
  resolved.protocol = resolved.protocol === 'https:' ? 'wss:' : 'ws:';
157
- resolved.pathname = `${resolved.pathname.replace(/\/$/, '')}/realtime`;
192
+ resolved.pathname = `${resolved.pathname.replace(/\/$/, '')}/sync/realtime`;
158
193
  return resolved.toString();
159
194
  } catch {
160
195
  return null;
@@ -184,11 +219,13 @@ export function createWebSocketTransport(
184
219
  WebSocketImpl = typeof WebSocket !== 'undefined' ? WebSocket : undefined,
185
220
  } = options;
186
221
 
187
- // Warn about security risk of using getRealtimeParams with sensitive data
188
- if (getRealtimeParams && !authToken) {
222
+ // Warn about security risk of using getRealtimeParams with sensitive data,
223
+ // but only if the consumer hasn't also provided getHeaders or authToken
224
+ // (which indicates intentional auth handling with query-param fallback).
225
+ if (getRealtimeParams && !authToken && !options.getHeaders) {
189
226
  console.warn(
190
227
  '[transport-ws] getRealtimeParams sends data in URL query parameters, ' +
191
- 'which may be logged by proxies and CDNs. Consider using authToken instead.'
228
+ 'which may be logged by proxies and CDNs. Prefer cookie-based auth when possible.'
192
229
  );
193
230
  }
194
231
 
@@ -223,6 +260,16 @@ export function createWebSocketTransport(
223
260
  >();
224
261
  const presenceCallbacks = new Set<PresenceEventCallback>();
225
262
 
263
+ // Pending WS push requests (requestId -> resolver)
264
+ const pendingPushRequests = new Map<
265
+ string,
266
+ {
267
+ resolve: (value: SyncPushResponse | null) => void;
268
+ timer: ReturnType<typeof setTimeout>;
269
+ }
270
+ >();
271
+ const wsPushTimeoutMs = Math.max(1, options.wsPushTimeoutMs ?? 1_500);
272
+
226
273
  function setConnectionState(state: WebSocketConnectionState): void {
227
274
  if (connectionState === state) return;
228
275
  connectionState = state;
@@ -278,6 +325,13 @@ export function createWebSocketTransport(
278
325
  clearHeartbeatTimer();
279
326
  clearReconnectTimer();
280
327
 
328
+ // Resolve all pending WS push requests as null (triggers HTTP fallback)
329
+ for (const [, pending] of pendingPushRequests) {
330
+ clearTimeout(pending.timer);
331
+ pending.resolve(null);
332
+ }
333
+ pendingPushRequests.clear();
334
+
281
335
  if (ws) {
282
336
  try {
283
337
  ws.onopen = null;
@@ -303,6 +357,26 @@ export function createWebSocketTransport(
303
357
  const data = (raw as { data: unknown }).data;
304
358
  if (!data || typeof data !== 'object') return;
305
359
 
360
+ // Route push-response events to pending request resolvers
361
+ if (event === 'push-response') {
362
+ const d = data as Record<string, unknown>;
363
+ const requestId = typeof d.requestId === 'string' ? d.requestId : '';
364
+ const pending = pendingPushRequests.get(requestId);
365
+ if (pending) {
366
+ pendingPushRequests.delete(requestId);
367
+ clearTimeout(pending.timer);
368
+ pending.resolve({
369
+ ok: true as const,
370
+ status: (d.status as 'applied' | 'cached' | 'rejected') ?? 'rejected',
371
+ commitSeq: typeof d.commitSeq === 'number' ? d.commitSeq : undefined,
372
+ results: Array.isArray(d.results)
373
+ ? (d.results as SyncPushResponse['results'])
374
+ : [],
375
+ });
376
+ }
377
+ return;
378
+ }
379
+
306
380
  // Route presence events to dedicated callbacks
307
381
  if (event === 'presence') {
308
382
  const presenceData = (data as { presence?: unknown }).presence;
@@ -322,9 +396,13 @@ export function createWebSocketTransport(
322
396
  currentEventCallback?.({ event, data } as WebSocketEvent);
323
397
  }
324
398
 
325
- function sendPresenceMessage(msg: Record<string, unknown>): void {
326
- if (!ws || ws.readyState !== WebSocketImpl!.OPEN) return;
327
- ws.send(JSON.stringify({ type: 'presence', ...msg }));
399
+ function sendPresenceMessage(
400
+ msg: Record<string, unknown>,
401
+ socketArg?: WebSocket
402
+ ): void {
403
+ const target = socketArg ?? ws;
404
+ if (!target || target.readyState !== WebSocketImpl!.OPEN) return;
405
+ target.send(JSON.stringify({ type: 'presence', ...msg }));
328
406
  }
329
407
 
330
408
  async function buildUrl(clientId: string): Promise<string> {
@@ -371,24 +449,32 @@ export function createWebSocketTransport(
371
449
 
372
450
  if (!WebSocketImpl) throw new Error('WebSocketImpl is required');
373
451
 
452
+ let socket: WebSocket;
374
453
  try {
375
- ws = new WebSocketImpl(url);
454
+ socket = new WebSocketImpl(url);
376
455
  } catch {
377
456
  doDisconnect();
378
457
  scheduleReconnect();
379
458
  return;
380
459
  }
460
+ ws = socket;
381
461
 
382
- ws.onopen = async () => {
462
+ socket.onopen = async () => {
383
463
  if (nonce !== connectNonce) return;
464
+ if (socket !== ws) return;
384
465
 
385
466
  // Send auth token if provided (more secure than query params)
386
- if (authToken && ws) {
467
+ if (authToken) {
387
468
  try {
388
469
  const token =
389
470
  typeof authToken === 'function' ? await authToken() : authToken;
390
- if (token && nonce === connectNonce) {
391
- ws.send(JSON.stringify({ type: 'auth', token }));
471
+ if (
472
+ token &&
473
+ nonce === connectNonce &&
474
+ socket === ws &&
475
+ socket.readyState === WebSocketImpl.OPEN
476
+ ) {
477
+ socket.send(JSON.stringify({ type: 'auth', token }));
392
478
  }
393
479
  } catch {
394
480
  // Auth token failed, but connection is still open
@@ -397,18 +483,21 @@ export function createWebSocketTransport(
397
483
  }
398
484
 
399
485
  if (nonce !== connectNonce) return;
486
+ if (socket !== ws) return;
487
+ if (socket.readyState !== WebSocketImpl.OPEN) return;
400
488
  setConnectionState('connected');
401
489
  reconnectAttempts = 0;
402
490
  resetHeartbeatTimer();
403
491
 
404
492
  // Re-join all active presence scopes on reconnect
405
493
  for (const [scopeKey, metadata] of activePresenceScopes) {
406
- sendPresenceMessage({ action: 'join', scopeKey, metadata });
494
+ sendPresenceMessage({ action: 'join', scopeKey, metadata }, socket);
407
495
  }
408
496
  };
409
497
 
410
- ws.onmessage = (evt) => {
498
+ socket.onmessage = (evt) => {
411
499
  if (nonce !== connectNonce) return;
500
+ if (socket !== ws) return;
412
501
  resetHeartbeatTimer();
413
502
 
414
503
  if (typeof evt.data === 'string') {
@@ -420,14 +509,16 @@ export function createWebSocketTransport(
420
509
  }
421
510
  };
422
511
 
423
- ws.onerror = () => {
512
+ socket.onerror = () => {
424
513
  if (nonce !== connectNonce) return;
514
+ if (socket !== ws) return;
425
515
  doDisconnect();
426
516
  scheduleReconnect();
427
517
  };
428
518
 
429
- ws.onclose = () => {
519
+ socket.onclose = () => {
430
520
  if (nonce !== connectNonce) return;
521
+ if (socket !== ws) return;
431
522
  doDisconnect();
432
523
  scheduleReconnect();
433
524
  };
@@ -480,5 +571,37 @@ export function createWebSocketTransport(
480
571
  presenceCallbacks.delete(callback);
481
572
  };
482
573
  },
574
+ pushViaWs(request: SyncPushRequest): Promise<SyncPushResponse | null> {
575
+ if (!ws || ws.readyState !== WebSocketImpl!.OPEN) {
576
+ return Promise.resolve(null);
577
+ }
578
+
579
+ const requestId = crypto.randomUUID();
580
+
581
+ return new Promise<SyncPushResponse | null>((resolve) => {
582
+ const timer = setTimeout(() => {
583
+ pendingPushRequests.delete(requestId);
584
+ resolve(null);
585
+ }, wsPushTimeoutMs);
586
+
587
+ pendingPushRequests.set(requestId, { resolve, timer });
588
+
589
+ try {
590
+ ws!.send(
591
+ JSON.stringify({
592
+ type: 'push',
593
+ requestId,
594
+ clientCommitId: request.clientCommitId,
595
+ operations: request.operations,
596
+ schemaVersion: request.schemaVersion,
597
+ })
598
+ );
599
+ } catch {
600
+ pendingPushRequests.delete(requestId);
601
+ clearTimeout(timer);
602
+ resolve(null);
603
+ }
604
+ });
605
+ },
483
606
  };
484
607
  }