svelte-adapter-uws 0.1.3 → 0.1.4

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 CHANGED
@@ -345,6 +345,9 @@ adapter({
345
345
  // Automatically send pings to keep the connection alive
346
346
  sendPingsAutomatically: true, // default: true
347
347
 
348
+ // Seconds before an async upgrade handler is rejected with 504
349
+ upgradeTimeout: 10, // default: 10
350
+
348
351
  // Allowed origins for WebSocket connections
349
352
  // 'same-origin' - only accept where Origin matches Host (default)
350
353
  // '*' - accept from any origin
@@ -836,18 +839,18 @@ The main function most users need. Returns a Svelte readable store that updates
836
839
 
837
840
  ### `on(topic, event)` - subscribe to a specific event
838
841
 
839
- Filters to a single event name and returns just the `data` payload (no envelope):
842
+ Filters to a single event name and wraps the payload in `{ data }`:
840
843
 
841
844
  ```svelte
842
845
  <script>
843
846
  import { on } from 'svelte-adapter-uws/client';
844
847
 
845
- // Only 'created' events, gives you just the data
848
+ // Only 'created' events, wrapped in { data }
846
849
  const newTodo = on('todos', 'created');
847
850
  </script>
848
851
 
849
852
  {#if $newTodo}
850
- <p>New todo: {$newTodo.text}</p>
853
+ <p>New todo: {$newTodo.data.text}</p>
851
854
  {/if}
852
855
  ```
853
856
 
@@ -1463,9 +1466,9 @@ The client store parses this automatically. When you use `on('todos')`, the stor
1463
1466
  { topic: 'todos', event: 'created', data: { id: 1, text: 'Buy milk', done: false } }
1464
1467
  ```
1465
1468
 
1466
- When you use `on('todos', 'created')`, you get just the `data`:
1469
+ When you use `on('todos', 'created')`, you get the payload wrapped in `{ data }`:
1467
1470
  ```js
1468
- { id: 1, text: 'Buy milk', done: false }
1471
+ { data: { id: 1, text: 'Buy milk', done: false } }
1469
1472
  ```
1470
1473
 
1471
1474
  ### "WebSocket works locally but not behind nginx/Caddy"
package/client.d.ts CHANGED
@@ -101,7 +101,7 @@ export interface TopicStore<T> extends Readable<T | null> {
101
101
  * {/if}
102
102
  * ```
103
103
  *
104
- * @example Event-level (filtered, data-only):
104
+ * @example Event-level (filtered, wrapped in `{ data }`):
105
105
  * ```svelte
106
106
  * <script>
107
107
  * import { on } from 'svelte-adapter-uws/client';
@@ -109,7 +109,7 @@ export interface TopicStore<T> extends Readable<T | null> {
109
109
  * </script>
110
110
  *
111
111
  * {#if $newTodo}
112
- * <p>New: {$newTodo.text}</p>
112
+ * <p>New: {$newTodo.data.text}</p>
113
113
  * {/if}
114
114
  * ```
115
115
  *
@@ -126,7 +126,7 @@ export interface TopicStore<T> extends Readable<T | null> {
126
126
  * ```
127
127
  */
128
128
  export function on<T = unknown>(topic: string): TopicStore<WSEvent<T>>;
129
- export function on<T = unknown>(topic: string, event: string): TopicStore<T>;
129
+ export function on<T = unknown>(topic: string, event: string): TopicStore<{ data: T }>;
130
130
 
131
131
  /**
132
132
  * Readable store - connection status: `'connecting'` | `'open'` | `'closed'`.
@@ -279,7 +279,7 @@ export function count(topic: string, initial?: number): Readable<number>;
279
279
  * ```
280
280
  */
281
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>;
282
+ export function once<T = unknown>(topic: string, event: string, options?: { timeout?: number }): Promise<{ data: T }>;
283
283
 
284
284
  /**
285
285
  * Returns a promise that resolves when the WebSocket connection is open.
package/client.js CHANGED
@@ -309,9 +309,9 @@ function createConnection(options) {
309
309
  const tStore = topicStores.get(msg.topic);
310
310
  if (tStore) tStore.set(wsEvent);
311
311
 
312
- // Update topic+event filtered stores (data only)
312
+ // Update topic+event filtered stores (wrapped for unique reference)
313
313
  const eStore = eventStores.get(`${msg.topic}\0${msg.event}`);
314
- if (eStore) eStore.set(msg.data);
314
+ if (eStore) eStore.set({ data: msg.data });
315
315
  }
316
316
  } catch {
317
317
  // Not a valid envelope - ignore
package/files/handler.js CHANGED
@@ -326,8 +326,9 @@ function readBody(res, limit, signal) {
326
326
  * @param {StaticEntry} entry
327
327
  * @param {string} acceptEncoding
328
328
  * @param {string} ifNoneMatch
329
+ * @param {boolean} headOnly
329
330
  */
330
- function serveStatic(res, entry, acceptEncoding, ifNoneMatch) {
331
+ function serveStatic(res, entry, acceptEncoding, ifNoneMatch, headOnly = false) {
331
332
  if (entry.etag && ifNoneMatch === entry.etag) {
332
333
  res.cork(() => {
333
334
  res.writeStatus('304 Not Modified').end();
@@ -356,7 +357,11 @@ function serveStatic(res, entry, acceptEncoding, ifNoneMatch) {
356
357
  for (let i = 0; i < entry.headers.length; i++) {
357
358
  res.writeHeader(entry.headers[i][0], entry.headers[i][1]);
358
359
  }
359
- res.end(body);
360
+ if (headOnly) {
361
+ res.endWithoutBody();
362
+ } else {
363
+ res.end(body);
364
+ }
360
365
  });
361
366
  }
362
367
 
@@ -626,7 +631,8 @@ function handleRequest(res, req) {
626
631
  return serveStatic(
627
632
  res, staticFile,
628
633
  req.getHeader('accept-encoding'),
629
- req.getHeader('if-none-match')
634
+ req.getHeader('if-none-match'),
635
+ method === 'head'
630
636
  );
631
637
  }
632
638
 
@@ -746,9 +752,23 @@ if (WS_ENABLED) {
746
752
 
747
753
  const cookies = parseCookies(headers['cookie']);
748
754
 
755
+ const upgradeTimeoutMs = (wsOptions.upgradeTimeout || 10) * 1000;
756
+ let timedOut = false;
757
+ const timer = setTimeout(() => {
758
+ timedOut = true;
759
+ if (!aborted) {
760
+ res.cork(() => {
761
+ res.writeStatus('504 Gateway Timeout');
762
+ res.writeHeader('content-type', 'text/plain');
763
+ res.end('Upgrade timed out');
764
+ });
765
+ }
766
+ }, upgradeTimeoutMs);
767
+
749
768
  Promise.resolve(wsModule.upgrade({ headers, cookies, url, remoteAddress }))
750
769
  .then((userData) => {
751
- if (aborted) return;
770
+ clearTimeout(timer);
771
+ if (aborted || timedOut) return;
752
772
  if (userData === false) {
753
773
  res.cork(() => {
754
774
  res.writeStatus('401 Unauthorized');
@@ -768,8 +788,9 @@ if (WS_ENABLED) {
768
788
  });
769
789
  })
770
790
  .catch((err) => {
791
+ clearTimeout(timer);
771
792
  console.error('WebSocket upgrade error:', err);
772
- if (!aborted) {
793
+ if (!aborted && !timedOut) {
773
794
  res.cork(() => {
774
795
  res.writeStatus('500 Internal Server Error');
775
796
  res.writeHeader('content-type', 'text/plain');
package/index.d.ts CHANGED
@@ -133,6 +133,14 @@ export interface WebSocketOptions {
133
133
  */
134
134
  sendPingsAutomatically?: boolean;
135
135
 
136
+ /**
137
+ * Timeout in seconds for async `upgrade` handlers.
138
+ * If the upgrade hook doesn't resolve within this time, the connection
139
+ * is rejected with 504 Gateway Timeout.
140
+ * @default 10
141
+ */
142
+ upgradeTimeout?: number;
143
+
136
144
  /**
137
145
  * Allowed origins for WebSocket connections.
138
146
  *
package/index.js CHANGED
@@ -158,7 +158,8 @@ export default function (opts = {}) {
158
158
  maxBackpressure: websocket?.maxBackpressure ?? 1024 * 1024,
159
159
  sendPingsAutomatically: websocket?.sendPingsAutomatically ?? true,
160
160
  compression: websocket?.compression ?? false,
161
- allowedOrigins: websocket?.allowedOrigins ?? 'same-origin'
161
+ allowedOrigins: websocket?.allowedOrigins ?? 'same-origin',
162
+ upgradeTimeout: websocket?.upgradeTimeout ?? 10
162
163
  };
163
164
 
164
165
  builder.copy(files, out, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-adapter-uws",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "SvelteKit adapter for uWebSockets.js - high-performance C++ HTTP server with built-in WebSocket support",
5
5
  "author": "Kevin Radziszewski",
6
6
  "license": "MIT",
package/vite.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { WebSocketServer } from 'ws';
2
2
  import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
3
4
  import { parseCookies } from './files/cookies.js';
4
5
 
5
6
  /**
@@ -164,7 +165,7 @@ export default function uwsDev(options = {}) {
164
165
  : null;
165
166
 
166
167
  if (handlerPath) {
167
- handlerReady = import(handlerPath).then((mod) => {
168
+ handlerReady = import(pathToFileURL(handlerPath).href).then((mod) => {
168
169
  userHandlers = {
169
170
  upgrade: mod.upgrade,
170
171
  open: mod.open,
@@ -182,7 +183,7 @@ export default function uwsDev(options = {}) {
182
183
  handlerReady = (async () => {
183
184
  for (const candidate of candidates) {
184
185
  try {
185
- const mod = await import(path.resolve(root, candidate));
186
+ const mod = await import(pathToFileURL(path.resolve(root, candidate)).href);
186
187
  userHandlers = {
187
188
  upgrade: mod.upgrade,
188
189
  open: mod.open,