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 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 13 app directory</i><br>
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 **13** 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.
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
- // ^? WebSocket on the client, null on the server
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.send(value)}>
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 for server to send a message...'
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
- return () => instance?.close();
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.2.6",
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": "^13.4.6",
46
+ "next": "^14.0.1",
47
47
  "prettier": "^2.8.8",
48
48
  "react": "^18.2.0",
49
49
  "react-dom": "^18.2.0",
@@ -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
- // @ts-expect-error - hotReloader is private
78
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
79
- await nextServer.hotReloader?.ensurePage({
80
- page: filename,
81
- clientOnly: false,
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