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 +8 -5
- package/client.d.ts +4 -4
- package/client.js +2 -2
- package/files/handler.js +26 -5
- package/index.d.ts +8 -0
- package/index.js +2 -1
- package/package.json +1 -1
- package/vite.js +3 -2
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
|
|
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,
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
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,
|