next-ws 0.2.6 → 1.0.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/README.md +16 -18
- package/client/context.js +4 -2
- package/package.json +2 -2
- package/server/utilities/next.js +24 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1><strong>Next WS</strong></h1>
|
|
3
|
-
<i>Add support for WebSockets in Next.js
|
|
3
|
+
<i>Add support for WebSockets in Next.js app directory</i><br>
|
|
4
4
|
<code>npm install next-ws ws</code>
|
|
5
5
|
</div>
|
|
6
6
|
|
|
@@ -17,14 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
## 🤔 About
|
|
19
19
|
|
|
20
|
-
Next WS (`next-ws`) is an advanced Next.js
|
|
21
|
-
|
|
22
|
-
> The last supported version of Next.js is 13.4.12, read more [here](https://github.com/apteryxxyz/next-ws/issues/6).
|
|
20
|
+
Next WS (`next-ws`) is an advanced Next.js plugin designed to seamlessly integrate WebSocket server functionality into API routes within the **app directory**. With Next WS, you no longer require a separate server for WebSocket functionality.
|
|
23
21
|
|
|
24
22
|
It's **important** to note that this module can only be used when working with a server. Unfortunately, in serverless environments like Vercel, WebSocket servers cannot be used. Additionally, this module was built for the app directory and is incompatible with the older pages directory.
|
|
25
23
|
|
|
26
|
-
Next WS is still pre its 1.0 release, and as such, things may change. If you find any bugs or have any suggestions, please open an issue on the GitHub repository.
|
|
27
|
-
|
|
28
24
|
This module is inspired by the now outdated `next-plugin-websocket`, if you are using an older version of Next.js, that module may work for you.
|
|
29
25
|
|
|
30
26
|
---
|
|
@@ -138,8 +134,6 @@ export default function Layout() {
|
|
|
138
134
|
}
|
|
139
135
|
```
|
|
140
136
|
|
|
141
|
-
To make it easier to connect to your new WebSocker server, Next WS also provides some client-side utilities. These are completely optional, you can use your own implementation if you wish.
|
|
142
|
-
|
|
143
137
|
The following is the props interface for the `WebSocketProvider` component, containing all the available options.
|
|
144
138
|
|
|
145
139
|
```ts
|
|
@@ -155,6 +149,8 @@ interface WebSocketProviderProps {
|
|
|
155
149
|
}
|
|
156
150
|
```
|
|
157
151
|
|
|
152
|
+
Now you can use the `useWebSocket` hook to get the WebSocket instance, and send and receive messages.
|
|
153
|
+
|
|
158
154
|
```tsx
|
|
159
155
|
// page.tsx
|
|
160
156
|
'use client';
|
|
@@ -164,20 +160,21 @@ import { useCallback, useEffect, useState } from 'react';
|
|
|
164
160
|
|
|
165
161
|
export default function Page() {
|
|
166
162
|
const ws = useWebSocket();
|
|
167
|
-
//
|
|
163
|
+
// ^? WebSocket on the client, null on the server
|
|
168
164
|
|
|
169
165
|
const [value, setValue] = useState('');
|
|
170
|
-
const [message, setMessage] = useState<string | null>(
|
|
171
|
-
|
|
172
|
-
const onMessage = useCallback((event: MessageEvent) => {
|
|
173
|
-
const text = await event.data.text();
|
|
174
|
-
setMessage(text);
|
|
175
|
-
}, []);
|
|
166
|
+
const [message, setMessage] = useState<string | null>(null);
|
|
176
167
|
|
|
168
|
+
const onMessage = useCallback(
|
|
169
|
+
(event: MessageEvent<Blob>) =>
|
|
170
|
+
void event.data.text().then(setMessage),
|
|
171
|
+
[],
|
|
172
|
+
);
|
|
173
|
+
|
|
177
174
|
useEffect(() => {
|
|
178
175
|
ws?.addEventListener('message', onMessage);
|
|
179
176
|
return () => ws?.removeEventListener('message', onMessage);
|
|
180
|
-
}, [ws]);
|
|
177
|
+
}, [onMessage, ws]);
|
|
181
178
|
|
|
182
179
|
return <>
|
|
183
180
|
<input
|
|
@@ -186,15 +183,16 @@ export default function Page() {
|
|
|
186
183
|
onChange={event => setValue(event.target.value)}
|
|
187
184
|
/>
|
|
188
185
|
|
|
189
|
-
<button onClick={() => ws
|
|
186
|
+
<button onClick={() => ws?.send(value)}>
|
|
190
187
|
Send message to server
|
|
191
188
|
</button>
|
|
192
189
|
|
|
193
190
|
<p>
|
|
194
191
|
{message === null
|
|
195
|
-
? 'Waiting
|
|
192
|
+
? 'Waiting to receive message...'
|
|
196
193
|
: `Got message: ${message}`}
|
|
197
194
|
</p>
|
|
198
195
|
</>;
|
|
199
196
|
}
|
|
197
|
+
|
|
200
198
|
```
|
package/client/context.js
CHANGED
|
@@ -22,9 +22,11 @@ function WebSocketProvider({ children, url, protocols, binaryType, }) {
|
|
|
22
22
|
return client;
|
|
23
23
|
}, [isBrowser, url, protocols]);
|
|
24
24
|
(0, react_1.useEffect)(() => {
|
|
25
|
-
|
|
25
|
+
if (instance?.readyState !== WebSocket.OPEN)
|
|
26
|
+
return;
|
|
27
|
+
return () => instance.close();
|
|
26
28
|
}, []);
|
|
27
|
-
return (0, jsx_runtime_1.jsx)(exports.WebSocketContext.Provider, { value: instance, children: children });
|
|
29
|
+
return ((0, jsx_runtime_1.jsx)(exports.WebSocketContext.Provider, { value: instance, children: children }));
|
|
28
30
|
}
|
|
29
31
|
exports.WebSocketProvider = WebSocketProvider;
|
|
30
32
|
exports.WebSocketConsumer = exports.WebSocketContext.Consumer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-ws",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Add support for WebSockets in Next.js 13 app directory",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"next",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@types/react-dom": "^18",
|
|
44
44
|
"@types/ws": "^8",
|
|
45
45
|
"eslint": "^8.43.0",
|
|
46
|
-
"next": "^
|
|
46
|
+
"next": "^14.0.1",
|
|
47
47
|
"prettier": "^2.8.8",
|
|
48
48
|
"react": "^18.2.0",
|
|
49
49
|
"react-dom": "^18.2.0",
|
package/server/utilities/next.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getPageModule = exports.resolvePathname = exports.getHttpServer = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const node_http_1 = require("node:http");
|
|
6
|
+
const Log = tslib_1.__importStar(require("next/dist/build/output/log"));
|
|
6
7
|
const next_server_1 = tslib_1.__importDefault(require("next/dist/server/next-server"));
|
|
7
8
|
/**
|
|
8
9
|
* Get the http.Server instance from the NextNodeServer.
|
|
@@ -74,12 +75,29 @@ exports.resolvePathname = resolvePathname;
|
|
|
74
75
|
* @returns The page module.
|
|
75
76
|
*/
|
|
76
77
|
async function getPageModule(nextServer, filename) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
try {
|
|
79
|
+
// In Next.js 14, hotReloader was removed and ensurePage was moved to NextNodeServer
|
|
80
|
+
if ('hotReloader' in nextServer) {
|
|
81
|
+
// @ts-expect-error - hotReloader only exists in Next.js 13
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
83
|
+
await nextServer.hotReloader?.ensurePage({
|
|
84
|
+
page: filename,
|
|
85
|
+
clientOnly: false,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else if ('ensurePage' in nextServer) {
|
|
89
|
+
// ensurePage throws an error in production, so we need to catch it
|
|
90
|
+
// @ts-expect-error - ensurePage is protected
|
|
91
|
+
await nextServer.ensurePage({ page: filename, clientOnly: false });
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Future-proofing
|
|
95
|
+
Log.warnOnce('[next-ws] was unable to ensure page, you may need to open the route in your browser first so Next.js compiles it');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
void 0;
|
|
100
|
+
}
|
|
83
101
|
// @ts-expect-error - getPagePath is protected
|
|
84
102
|
const builtPagePath = nextServer.getPagePath(filename);
|
|
85
103
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|