svelte-adapter-uws 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/LICENSE +21 -0
- package/README.md +1543 -0
- package/client.d.ts +356 -0
- package/client.js +571 -0
- package/files/cookies.js +25 -0
- package/files/env.js +41 -0
- package/files/handler.js +898 -0
- package/files/index.js +116 -0
- package/files/shims.js +21 -0
- package/files/utils.js +136 -0
- package/index.d.ts +396 -0
- package/index.js +224 -0
- package/package.json +81 -0
- package/vite.d.ts +48 -0
- package/vite.js +310 -0
package/client.d.ts
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import type { Readable } from 'svelte/store';
|
|
2
|
+
|
|
3
|
+
export interface ConnectOptions {
|
|
4
|
+
/**
|
|
5
|
+
* WebSocket endpoint path. Must match the adapter config.
|
|
6
|
+
* @default '/ws'
|
|
7
|
+
*/
|
|
8
|
+
path?: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Base delay in ms before reconnecting after a disconnect.
|
|
12
|
+
* Uses exponential backoff with jitter.
|
|
13
|
+
* @default 3000
|
|
14
|
+
*/
|
|
15
|
+
reconnectInterval?: number;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Maximum delay in ms between reconnection attempts.
|
|
19
|
+
* @default 30000
|
|
20
|
+
*/
|
|
21
|
+
maxReconnectInterval?: number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of reconnection attempts before giving up.
|
|
25
|
+
* @default Infinity
|
|
26
|
+
*/
|
|
27
|
+
maxReconnectAttempts?: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Log all WebSocket events to the console.
|
|
31
|
+
* Useful during development to see exactly what's happening.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
debug?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A message received from the server via `platform.publish(topic, event, data)`.
|
|
39
|
+
*/
|
|
40
|
+
export interface WSEvent<T = unknown> {
|
|
41
|
+
/** The topic this message was published to. */
|
|
42
|
+
topic: string;
|
|
43
|
+
/** The event name (e.g. `'created'`, `'updated'`, `'deleted'`). */
|
|
44
|
+
event: string;
|
|
45
|
+
/** The event payload. */
|
|
46
|
+
data: T;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Scannable store ──────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A readable store with an additional `.scan()` method for accumulating state.
|
|
53
|
+
*/
|
|
54
|
+
export interface TopicStore<T> extends Readable<T | null> {
|
|
55
|
+
/**
|
|
56
|
+
* Create a derived store that accumulates events using a reducer.
|
|
57
|
+
*
|
|
58
|
+
* Like `Array.reduce` but reactive - each new event feeds through
|
|
59
|
+
* the reducer and the store updates with the new accumulated value.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```svelte
|
|
63
|
+
* <script>
|
|
64
|
+
* import { on } from 'svelte-adapter-uws/client';
|
|
65
|
+
*
|
|
66
|
+
* const todos = on('todos').scan([], (list, { event, data }) => {
|
|
67
|
+
* if (event === 'created') return [...list, data];
|
|
68
|
+
* if (event === 'deleted') return list.filter(t => t.id !== data.id);
|
|
69
|
+
* if (event === 'updated') return list.map(t => t.id === data.id ? data : t);
|
|
70
|
+
* return list;
|
|
71
|
+
* });
|
|
72
|
+
* </script>
|
|
73
|
+
*
|
|
74
|
+
* {#each $todos as todo}
|
|
75
|
+
* <p>{todo.text}</p>
|
|
76
|
+
* {/each}
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @param initial - Starting value (e.g. `[]`, `{}`, `0`)
|
|
80
|
+
* @param reducer - Called with `(accumulator, event)` on each new event
|
|
81
|
+
*/
|
|
82
|
+
scan<A>(initial: A, reducer: (acc: A, value: T) => A): Readable<A>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Direct exports (recommended) ────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get a reactive Svelte store for a topic. Auto-connects and auto-subscribes.
|
|
89
|
+
*
|
|
90
|
+
* **This is the only function most users need.**
|
|
91
|
+
*
|
|
92
|
+
* @example Topic-level (all events):
|
|
93
|
+
* ```svelte
|
|
94
|
+
* <script>
|
|
95
|
+
* import { on } from 'svelte-adapter-uws/client';
|
|
96
|
+
* const todos = on('todos');
|
|
97
|
+
* </script>
|
|
98
|
+
*
|
|
99
|
+
* {#if $todos}
|
|
100
|
+
* <p>{$todos.event}: {JSON.stringify($todos.data)}</p>
|
|
101
|
+
* {/if}
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @example Event-level (filtered, data-only):
|
|
105
|
+
* ```svelte
|
|
106
|
+
* <script>
|
|
107
|
+
* import { on } from 'svelte-adapter-uws/client';
|
|
108
|
+
* const newTodo = on('todos', 'created');
|
|
109
|
+
* </script>
|
|
110
|
+
*
|
|
111
|
+
* {#if $newTodo}
|
|
112
|
+
* <p>New: {$newTodo.text}</p>
|
|
113
|
+
* {/if}
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @example Accumulate state with .scan() (Svelte 5):
|
|
117
|
+
* ```svelte
|
|
118
|
+
* <script>
|
|
119
|
+
* import { on } from 'svelte-adapter-uws/client';
|
|
120
|
+
* const todos = on('todos').scan([], (list, { event, data }) => {
|
|
121
|
+
* if (event === 'created') return [...list, data];
|
|
122
|
+
* if (event === 'deleted') return list.filter(t => t.id !== data.id);
|
|
123
|
+
* return list;
|
|
124
|
+
* });
|
|
125
|
+
* </script>
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function on<T = unknown>(topic: string): TopicStore<WSEvent<T>>;
|
|
129
|
+
export function on<T = unknown>(topic: string, event: string): TopicStore<T>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Readable store - connection status: `'connecting'` | `'open'` | `'closed'`.
|
|
133
|
+
* Auto-connects on first access.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```svelte
|
|
137
|
+
* <script>
|
|
138
|
+
* import { status } from 'svelte-adapter-uws/client';
|
|
139
|
+
* </script>
|
|
140
|
+
*
|
|
141
|
+
* {#if $status === 'open'}
|
|
142
|
+
* <span class="badge">Live</span>
|
|
143
|
+
* {/if}
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export const status: Readable<'connecting' | 'open' | 'closed'>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Live CRUD list - one line for real-time collections.
|
|
150
|
+
*
|
|
151
|
+
* Subscribes to a topic and automatically handles `created`, `updated`,
|
|
152
|
+
* and `deleted` events. Pair with `platform.topic('...').created(item)`
|
|
153
|
+
* on the server for zero-boilerplate real-time lists.
|
|
154
|
+
*
|
|
155
|
+
* @param topic - Topic to subscribe to
|
|
156
|
+
* @param initial - Starting data (e.g. from a load function)
|
|
157
|
+
* @param options - Options (default key: `'id'`)
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```svelte
|
|
161
|
+
* <script>
|
|
162
|
+
* import { crud } from 'svelte-adapter-uws/client';
|
|
163
|
+
* let { data } = $props(); // { todos: [...] } from +page.server.js load()
|
|
164
|
+
*
|
|
165
|
+
* const todos = crud('todos', data.todos);
|
|
166
|
+
* // $todos auto-updates when server publishes created/updated/deleted
|
|
167
|
+
* </script>
|
|
168
|
+
*
|
|
169
|
+
* {#each $todos as todo (todo.id)}
|
|
170
|
+
* <p>{todo.text}</p>
|
|
171
|
+
* {/each}
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @example Notification feed (newest first):
|
|
175
|
+
* ```js
|
|
176
|
+
* const notifications = crud('notifications', [], { prepend: true });
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export function crud<T extends Record<string, any>>(
|
|
180
|
+
topic: string,
|
|
181
|
+
initial?: T[],
|
|
182
|
+
options?: { key?: keyof T & string; prepend?: boolean }
|
|
183
|
+
): Readable<T[]>;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Live keyed object - like `crud()` but returns a `Record` keyed by ID.
|
|
187
|
+
* Better for dashboards and fast lookups where you need `$users['abc']`.
|
|
188
|
+
*
|
|
189
|
+
* @param topic - Topic to subscribe to
|
|
190
|
+
* @param initial - Starting data (e.g. from a load function)
|
|
191
|
+
* @param options - Options (default key: `'id'`)
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```svelte
|
|
195
|
+
* <script>
|
|
196
|
+
* import { lookup } from 'svelte-adapter-uws/client';
|
|
197
|
+
* let { data } = $props();
|
|
198
|
+
*
|
|
199
|
+
* const users = lookup('users', data.users);
|
|
200
|
+
* </script>
|
|
201
|
+
*
|
|
202
|
+
* {#if $users[selectedId]}
|
|
203
|
+
* <UserCard user={$users[selectedId]} />
|
|
204
|
+
* {/if}
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
export function lookup<T extends Record<string, any>>(
|
|
208
|
+
topic: string,
|
|
209
|
+
initial?: T[],
|
|
210
|
+
options?: { key?: keyof T & string }
|
|
211
|
+
): Readable<Record<string, T>>;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Ring buffer of the last N events on a topic.
|
|
215
|
+
* Perfect for chat messages, activity feeds, and notification lists.
|
|
216
|
+
*
|
|
217
|
+
* @param topic - Topic to subscribe to
|
|
218
|
+
* @param max - Maximum number of events to keep (default: 50)
|
|
219
|
+
* @param initial - Starting data
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```svelte
|
|
223
|
+
* <script>
|
|
224
|
+
* import { latest } from 'svelte-adapter-uws/client';
|
|
225
|
+
*
|
|
226
|
+
* const messages = latest('chat', 100);
|
|
227
|
+
* </script>
|
|
228
|
+
*
|
|
229
|
+
* {#each $messages as msg}
|
|
230
|
+
* <p><b>{msg.event}:</b> {msg.data.text}</p>
|
|
231
|
+
* {/each}
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
export function latest<T = unknown>(
|
|
235
|
+
topic: string,
|
|
236
|
+
max?: number,
|
|
237
|
+
initial?: WSEvent<T>[]
|
|
238
|
+
): Readable<WSEvent<T>[]>;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Live counter store -- handles `set`, `increment`, and `decrement` events.
|
|
242
|
+
*
|
|
243
|
+
* Pair with `platform.topic('metric').publish('increment', 1)` on the server.
|
|
244
|
+
*
|
|
245
|
+
* @param topic - Topic to subscribe to
|
|
246
|
+
* @param initial - Starting value (default: 0)
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```svelte
|
|
250
|
+
* <script>
|
|
251
|
+
* import { count } from 'svelte-adapter-uws/client';
|
|
252
|
+
* const online = count('online-users');
|
|
253
|
+
* </script>
|
|
254
|
+
*
|
|
255
|
+
* <p>{$online} users online</p>
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export function count(topic: string, initial?: number): Readable<number>;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Wait for a specific event on a topic. Resolves once and unsubscribes.
|
|
262
|
+
*
|
|
263
|
+
* @param topic - Topic to listen on
|
|
264
|
+
* @param event - Optional event name to filter on
|
|
265
|
+
* @param options - Options (e.g. `{ timeout: 5000 }`)
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```js
|
|
269
|
+
* import { once } from 'svelte-adapter-uws/client';
|
|
270
|
+
*
|
|
271
|
+
* // Wait for server confirmation after a form submit
|
|
272
|
+
* const result = await once('jobs', 'completed');
|
|
273
|
+
*
|
|
274
|
+
* // With a timeout (rejects if no event within 5s)
|
|
275
|
+
* const result = await once('jobs', 'completed', { timeout: 5000 });
|
|
276
|
+
*
|
|
277
|
+
* // Timeout without event filter
|
|
278
|
+
* const event = await once('jobs', { timeout: 5000 });
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
export function once<T = unknown>(topic: string, options?: { timeout?: number }): Promise<WSEvent<T>>;
|
|
282
|
+
export function once<T = unknown>(topic: string, event: string, options?: { timeout?: number }): Promise<T>;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Returns a promise that resolves when the WebSocket connection is open.
|
|
286
|
+
* Auto-connects if not already connected.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```js
|
|
290
|
+
* import { ready } from 'svelte-adapter-uws/client';
|
|
291
|
+
* await ready();
|
|
292
|
+
* // connection is now open
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
export function ready(): Promise<void>;
|
|
296
|
+
|
|
297
|
+
// ── Power-user API ──────────────────────────────────────────────────────────
|
|
298
|
+
|
|
299
|
+
export interface WSConnection {
|
|
300
|
+
/** Readable store - the latest event from any subscribed topic. */
|
|
301
|
+
events: Readable<WSEvent | null>;
|
|
302
|
+
|
|
303
|
+
/** Readable store - connection status. */
|
|
304
|
+
status: Readable<'connecting' | 'open' | 'closed'>;
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get a reactive store for a specific topic.
|
|
308
|
+
* Auto-subscribes to the topic.
|
|
309
|
+
*/
|
|
310
|
+
on<T = unknown>(topic: string): TopicStore<WSEvent<T>>;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Subscribe to a topic. Duplicate calls are ignored.
|
|
314
|
+
* Subscriptions persist across reconnects.
|
|
315
|
+
*
|
|
316
|
+
* **Tip:** `on()` calls this automatically.
|
|
317
|
+
*/
|
|
318
|
+
subscribe(topic: string): void;
|
|
319
|
+
|
|
320
|
+
/** Unsubscribe from a topic. */
|
|
321
|
+
unsubscribe(topic: string): void;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Send a custom message to the server.
|
|
325
|
+
* Dropped silently if not connected.
|
|
326
|
+
*/
|
|
327
|
+
send(data: unknown): void;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Send a message, queuing it if not currently connected.
|
|
331
|
+
* Queued messages flush automatically on reconnect (FIFO order).
|
|
332
|
+
*/
|
|
333
|
+
sendQueued(data: unknown): void;
|
|
334
|
+
|
|
335
|
+
/** Close the connection permanently. Will not auto-reconnect. */
|
|
336
|
+
close(): void;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Connect to the adapter's WebSocket server.
|
|
341
|
+
*
|
|
342
|
+
* Returns a **singleton**. Auto-connects and auto-reconnects.
|
|
343
|
+
*
|
|
344
|
+
* Most users should use `on()` and `status` directly - they auto-connect
|
|
345
|
+
* behind the scenes. Use `connect()` when you need `close()`, `send()`,
|
|
346
|
+
* or custom `ConnectOptions`.
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```js
|
|
350
|
+
* import { connect } from 'svelte-adapter-uws/client';
|
|
351
|
+
* const ws = connect();
|
|
352
|
+
* ws.subscribe('notifications');
|
|
353
|
+
* ws.close(); // when done
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
export function connect(options?: ConnectOptions): WSConnection;
|