next-ws 0.1.5 → 0.2.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 +67 -21
- package/client/context.d.ts +17 -13
- package/client/context.js +20 -18
- package/client/index.js +0 -1
- package/index.js +3 -0
- package/package.json +46 -66
- package/server/index.d.ts +3 -1
- package/server/index.js +2 -2
- package/server/setup.d.ts +3 -0
- package/server/setup.js +38 -0
- package/server/utilities/next.d.ts +31 -0
- package/server/utilities/next.js +61 -0
- package/server/utilities/patch.d.ts +13 -0
- package/server/utilities/patch.js +36 -0
- package/server/utilities/workspace.d.ts +1 -0
- package/server/utilities/workspace.js +38 -0
- package/client/context.js.map +0 -1
- package/client/index.js.map +0 -1
- package/common/patch.js +0 -50
- package/common/patch.js.map +0 -1
- package/server/hook.d.ts +0 -3
- package/server/hook.js +0 -69
- package/server/hook.js.map +0 -1
- package/server/index.js.map +0 -1
- /package/{common/patch.d.ts → index.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ Next WS (`next-ws`) is an advanced Next.js **13** plugin designed to seamlessly
|
|
|
21
21
|
|
|
22
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.
|
|
23
23
|
|
|
24
|
+
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.
|
|
25
|
+
|
|
24
26
|
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.
|
|
25
27
|
|
|
26
28
|
---
|
|
@@ -30,36 +32,37 @@ This module is inspired by the now outdated `next-plugin-websocket`, if you are
|
|
|
30
32
|
- [🤔 About](#-about)
|
|
31
33
|
- [🏓 Table of Contents](#-table-of-contents)
|
|
32
34
|
- [📦 Installation](#-installation)
|
|
33
|
-
- [🚀 Usage](#-
|
|
35
|
+
- [🚀 Usage](#-usage)
|
|
36
|
+
- [🚓 Verify Patch](#-verify-patch)
|
|
34
37
|
- [🌀 Example](#-example)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
- [📁 Server](#-server)
|
|
39
|
+
- [📁 Client](#-client)
|
|
40
|
+
|
|
38
41
|
---
|
|
39
42
|
|
|
40
43
|
## 📦 Installation
|
|
41
44
|
|
|
45
|
+
In order to setup a WebSocket server, Next WS needs to patch your local Next.js installation. Next WS provides a CLI command to do this for you, it will automatically detect your Next.js version and patch it accordingly, however a minimum version of Next.js 13.1.1 is required.
|
|
46
|
+
|
|
42
47
|
```sh
|
|
43
|
-
|
|
44
|
-
yarn add next-ws
|
|
45
|
-
pnpm add next-ws
|
|
48
|
+
npx next-ws-cli patch
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
> If at any point your local Next.js installation is changed or updated you will need to re-run the patch command.
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
<summary><strong>Caveats</strong></summary>
|
|
53
|
+
Once the patch is complete, you will need to install the Next WS package into your project.
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
```sh
|
|
56
|
+
npm install next-ws
|
|
57
|
+
```
|
|
55
58
|
|
|
56
59
|
---
|
|
57
60
|
|
|
58
61
|
## 🚀 Usage
|
|
59
62
|
|
|
60
|
-
Using Next WS is a breeze, requiring zero configuration. Simply export a `SOCKET` function from
|
|
63
|
+
Using Next WS is a breeze, requiring zero configuration. Simply export a `SOCKET` function from any API route. This function gets called whenever a client connects to the WebSocket server at the respective API path.
|
|
61
64
|
|
|
62
|
-
The `SOCKET` function receives
|
|
65
|
+
The `SOCKET` function receives three arguments: the WebSocket client, the HTTP request - which you can use to get the URL path, query parameters, and headers - and the WebSocket server that `next-ws` created.
|
|
63
66
|
|
|
64
67
|
```ts
|
|
65
68
|
export function SOCKET(
|
|
@@ -73,6 +76,16 @@ export function SOCKET(
|
|
|
73
76
|
|
|
74
77
|
With this straightforward setup, you can fully leverage the capabilities of Next WS and efficiently handle WebSocket connections within your Next.js application.
|
|
75
78
|
|
|
79
|
+
### 🚓 Verify Patch
|
|
80
|
+
|
|
81
|
+
It is recommended to add the following code to the top level of your `next.config.js`.
|
|
82
|
+
|
|
83
|
+
This will verify that Next WS has been patched correctly, and throw an error if it has not. Preventing you from accidentally deploying a broken setup.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
require('next-ws/server').verifyPatch();
|
|
87
|
+
```
|
|
88
|
+
|
|
76
89
|
---
|
|
77
90
|
|
|
78
91
|
## 🌀 Example
|
|
@@ -86,6 +99,7 @@ Create an API route anywhere within the app directory, and export a `SOCKET` fun
|
|
|
86
99
|
export function SOCKET(
|
|
87
100
|
client: import('ws').WebSocket,
|
|
88
101
|
request: import('http').IncomingMessage,
|
|
102
|
+
server: import('ws').WebSocketServer,
|
|
89
103
|
) {
|
|
90
104
|
console.log('A client connected!');
|
|
91
105
|
|
|
@@ -101,11 +115,9 @@ export function SOCKET(
|
|
|
101
115
|
|
|
102
116
|
You are pretty much done at this point, you can now connect to the WebSocket server using the native WebSocket API in the browser.
|
|
103
117
|
|
|
104
|
-
---
|
|
105
|
-
|
|
106
118
|
### 📁 Client
|
|
107
119
|
|
|
108
|
-
To make it easier to connect to your new WebSocker server, Next WS also provides some client-side utilities. These are
|
|
120
|
+
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.
|
|
109
121
|
|
|
110
122
|
```tsx
|
|
111
123
|
// layout.tsx
|
|
@@ -114,12 +126,45 @@ To make it easier to connect to your new WebSocker server, Next WS also provides
|
|
|
114
126
|
import { WebSocketProvider } from 'next-ws/client';
|
|
115
127
|
|
|
116
128
|
export default function Layout() {
|
|
117
|
-
return <WebSocketProvider
|
|
129
|
+
return <WebSocketProvider
|
|
130
|
+
url="ws://localhost:3000/api/ws"
|
|
131
|
+
// ... other props
|
|
132
|
+
>
|
|
118
133
|
{...}
|
|
119
134
|
</WebSocketProvider>;
|
|
120
135
|
}
|
|
121
136
|
```
|
|
122
137
|
|
|
138
|
+
The following is the props interface for the `WebSocketProvider` component, containing all the available options.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
interface WebSocketProviderProps {
|
|
142
|
+
children: React.ReactNode;
|
|
143
|
+
|
|
144
|
+
/** The URL for the WebSocket to connect to. */
|
|
145
|
+
url: string;
|
|
146
|
+
/** The subprotocols to use. */
|
|
147
|
+
protocols?: string[] | string;
|
|
148
|
+
/** The binary type to use. */
|
|
149
|
+
binaryType?: BinaryType;
|
|
150
|
+
|
|
151
|
+
/** Whether to reconnect when the WebSocket closes. */
|
|
152
|
+
reconnect?: boolean;
|
|
153
|
+
/** The maximum number of times to reconnect. */
|
|
154
|
+
maxReconnectAttempts?: number;
|
|
155
|
+
/** The delay between reconnect attempts. */
|
|
156
|
+
reconnectDelay?: number;
|
|
157
|
+
|
|
158
|
+
// NOTE: You do not need to use these, they are only here for convenience
|
|
159
|
+
/** Event listener that is triggered when the WebSocket connection opens. */
|
|
160
|
+
onOpen?(this: WebSocket, event: Event): any;
|
|
161
|
+
/** Event listener that is triggered when the WebSocket connection closes. */
|
|
162
|
+
onClose?(this: WebSocket, event: CloseEvent): any;
|
|
163
|
+
/** Event listener that is triggered when an error occurs. */
|
|
164
|
+
onError?(this: WebSocket, event: Event): any;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
123
168
|
```tsx
|
|
124
169
|
// page.tsx
|
|
125
170
|
'use client';
|
|
@@ -129,7 +174,8 @@ import { useCallback, useEffect, useState } from 'react';
|
|
|
129
174
|
|
|
130
175
|
export default function Page() {
|
|
131
176
|
const ws = useWebSocket();
|
|
132
|
-
//
|
|
177
|
+
// WebSocket instance only exists when connected
|
|
178
|
+
// It will be null when state is closed or connecting
|
|
133
179
|
|
|
134
180
|
const [value, setValue] = useState('');
|
|
135
181
|
const [message, setMessage] = useState<string | null>('');
|
|
@@ -142,7 +188,7 @@ export default function Page() {
|
|
|
142
188
|
useEffect(() => {
|
|
143
189
|
ws?.addEventListener('message', onMessage);
|
|
144
190
|
return () => ws?.removeEventListener('message', onMessage);
|
|
145
|
-
}, []);
|
|
191
|
+
}, [ws]);
|
|
146
192
|
|
|
147
193
|
return <>
|
|
148
194
|
<input
|
|
@@ -160,6 +206,6 @@ export default function Page() {
|
|
|
160
206
|
? 'Waiting for server to send a message...'
|
|
161
207
|
: `Got message: ${message}`}
|
|
162
208
|
</p>
|
|
163
|
-
|
|
209
|
+
</>;
|
|
164
210
|
}
|
|
165
211
|
```
|
package/client/context.d.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
declare const WebSocketContext: import("react").Context<WebSocket | null>;
|
|
2
|
+
export declare const WebSocketContext: import("react").Context<WebSocket | null>;
|
|
3
|
+
export interface WebSocketProviderProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
/** The URL for the WebSocket to connect to. */
|
|
6
|
+
url: string;
|
|
7
|
+
/** The subprotocols to use. */
|
|
8
|
+
protocols?: string[] | string;
|
|
9
|
+
/** The binary type to use. */
|
|
10
|
+
binaryType?: BinaryType;
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
13
|
* Provides a WebSocket instance to its children via context,
|
|
5
|
-
* allowing for easy access to the
|
|
6
|
-
* @param props WebSocket parameters and children
|
|
14
|
+
* allowing for easy access to the WebSocket from anywhere in the app.
|
|
15
|
+
* @param props WebSocket parameters and children.
|
|
7
16
|
* @returns JSX Element
|
|
8
17
|
*/
|
|
9
|
-
declare function WebSocketProvider({ children, url, protocols, }:
|
|
10
|
-
|
|
11
|
-
url: string;
|
|
12
|
-
protocols?: string[] | string;
|
|
13
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
-
declare const WebSocketConsumer: import("react").Consumer<WebSocket | null>;
|
|
18
|
+
export declare function WebSocketProvider({ children, url, protocols, binaryType, }: WebSocketProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare const WebSocketConsumer: import("react").Consumer<WebSocket | null>;
|
|
15
20
|
/**
|
|
16
|
-
* Access the websocket from anywhere in the app, so long as it's wrapped in a WebSocketProvider
|
|
17
|
-
* @returns WebSocket
|
|
21
|
+
* Access the websocket from anywhere in the app, so long as it's wrapped in a WebSocketProvider.
|
|
22
|
+
* @returns WebSocket instance when connected, null when disconnected.
|
|
18
23
|
*/
|
|
19
|
-
declare function useWebSocket(): WebSocket | null;
|
|
20
|
-
export { WebSocketContext, WebSocketProvider, WebSocketConsumer, useWebSocket };
|
|
24
|
+
export declare function useWebSocket(): WebSocket | null;
|
package/client/context.js
CHANGED
|
@@ -3,38 +3,40 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.useWebSocket = exports.WebSocketConsumer = exports.WebSocketProvider = exports.WebSocketContext = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
|
-
|
|
7
|
-
exports.WebSocketContext = WebSocketContext;
|
|
8
|
-
WebSocketContext.displayName = 'WebSocketContext';
|
|
6
|
+
exports.WebSocketContext = (0, react_1.createContext)(null);
|
|
7
|
+
exports.WebSocketContext.displayName = 'WebSocketContext';
|
|
9
8
|
/**
|
|
10
9
|
* Provides a WebSocket instance to its children via context,
|
|
11
|
-
* allowing for easy access to the
|
|
12
|
-
* @param props WebSocket parameters and children
|
|
10
|
+
* allowing for easy access to the WebSocket from anywhere in the app.
|
|
11
|
+
* @param props WebSocket parameters and children.
|
|
13
12
|
* @returns JSX Element
|
|
14
13
|
*/
|
|
15
|
-
function WebSocketProvider({ children, url, protocols, }) {
|
|
14
|
+
function WebSocketProvider({ children, url, protocols, binaryType, }) {
|
|
16
15
|
const isBrowser = typeof window !== 'undefined';
|
|
17
|
-
const
|
|
16
|
+
const setupClient = () => {
|
|
17
|
+
if (!isBrowser)
|
|
18
|
+
return null;
|
|
19
|
+
const client = new WebSocket(url, protocols);
|
|
20
|
+
if (binaryType)
|
|
21
|
+
client.binaryType = binaryType;
|
|
22
|
+
return client;
|
|
23
|
+
};
|
|
24
|
+
const instance = (0, react_1.useMemo)(setupClient, [isBrowser, url, protocols]);
|
|
18
25
|
(0, react_1.useEffect)(() => {
|
|
19
|
-
return () =>
|
|
20
|
-
if (ws && ws.readyState !== WebSocket.CLOSED)
|
|
21
|
-
ws.close();
|
|
22
|
-
};
|
|
26
|
+
return () => instance?.close();
|
|
23
27
|
}, []);
|
|
24
|
-
return (0, jsx_runtime_1.jsx)(WebSocketContext.Provider, { value:
|
|
28
|
+
return (0, jsx_runtime_1.jsx)(exports.WebSocketContext.Provider, { value: instance, children: children });
|
|
25
29
|
}
|
|
26
30
|
exports.WebSocketProvider = WebSocketProvider;
|
|
27
|
-
|
|
28
|
-
exports.WebSocketConsumer = WebSocketConsumer;
|
|
31
|
+
exports.WebSocketConsumer = exports.WebSocketContext.Consumer;
|
|
29
32
|
/**
|
|
30
|
-
* Access the websocket from anywhere in the app, so long as it's wrapped in a WebSocketProvider
|
|
31
|
-
* @returns WebSocket
|
|
33
|
+
* Access the websocket from anywhere in the app, so long as it's wrapped in a WebSocketProvider.
|
|
34
|
+
* @returns WebSocket instance when connected, null when disconnected.
|
|
32
35
|
*/
|
|
33
36
|
function useWebSocket() {
|
|
34
|
-
const context = (0, react_1.useContext)(WebSocketContext);
|
|
37
|
+
const context = (0, react_1.useContext)(exports.WebSocketContext);
|
|
35
38
|
if (context === undefined)
|
|
36
39
|
throw new Error('useWebSocket must be used within a WebSocketProvider');
|
|
37
40
|
return context;
|
|
38
41
|
}
|
|
39
42
|
exports.useWebSocket = useWebSocket;
|
|
40
|
-
//# sourceMappingURL=context.js.map
|
package/client/index.js
CHANGED
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,66 +1,46 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "next-ws",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Add support for WebSockets in Next.js 13 app directory",
|
|
5
|
-
"
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
},
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/apteryxxyz/next-ws/issues"
|
|
15
|
-
},
|
|
16
|
-
"files": ["
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"@
|
|
36
|
-
"@
|
|
37
|
-
"@types/
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"eslint-config-apteryx": "^2.1.7",
|
|
48
|
-
"eslint-config-prettier": "^8.8.0",
|
|
49
|
-
"eslint-plugin-import": "^2.27.5",
|
|
50
|
-
"eslint-plugin-jsdoc": "^46.1.0",
|
|
51
|
-
"eslint-plugin-n": "^16.0.0",
|
|
52
|
-
"eslint-plugin-prettier": "^4.2.1",
|
|
53
|
-
"eslint-plugin-promise": "^6.1.1",
|
|
54
|
-
"eslint-plugin-sonarjs": "^0.19.0",
|
|
55
|
-
"eslint-plugin-unicorn": "^47.0.0",
|
|
56
|
-
"next": "^13.4.4",
|
|
57
|
-
"prettier": "^2.8.8",
|
|
58
|
-
"prettier-config-apteryx": "^2.1.0",
|
|
59
|
-
"react": "^18.2.0",
|
|
60
|
-
"rimraf": "^5.0.1",
|
|
61
|
-
"ts-config-apteryx": "^2.1.0",
|
|
62
|
-
"typescript": "<5.1.0",
|
|
63
|
-
"ws": "^8.13.0"
|
|
64
|
-
},
|
|
65
|
-
"prettier": "prettier-config-apteryx"
|
|
66
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "next-ws",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Add support for WebSockets in Next.js 13 app directory",
|
|
5
|
+
"keywords": ["next", "websocket", "ws", "server", "client"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/apteryxxyz/next-ws#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/apteryxxyz/next-ws.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/apteryxxyz/next-ws/issues"
|
|
15
|
+
},
|
|
16
|
+
"files": ["index.js", "index.d.ts", "client", "server"],
|
|
17
|
+
"main": "./index.js",
|
|
18
|
+
"types": "./index.d.ts",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "cp ../../README.md . && tsc",
|
|
21
|
+
"clean": "rimraf index.js index.d.ts client server README.md",
|
|
22
|
+
"lint": "eslint --ext .ts,.tsx src",
|
|
23
|
+
"format": "prettier --write src/**/*.{ts,tsx} && eslint --fix --ext .ts,.tsx src/"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"ws": "^8.13.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"next": ">=13.1.1",
|
|
30
|
+
"react": "*"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/eslint": "^8",
|
|
34
|
+
"@types/prettier": "^2",
|
|
35
|
+
"@types/react": "^18",
|
|
36
|
+
"@types/react-dom": "^18",
|
|
37
|
+
"@types/ws": "^8",
|
|
38
|
+
"eslint": "^8.43.0",
|
|
39
|
+
"next": "^13.4.6",
|
|
40
|
+
"prettier": "^2.8.8",
|
|
41
|
+
"react": "^18.2.0",
|
|
42
|
+
"react-dom": "^18.2.0",
|
|
43
|
+
"rimraf": "^5.0.1",
|
|
44
|
+
"typescript": "<5.1.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/server/index.d.ts
CHANGED
package/server/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
-
tslib_1.__exportStar(require("./
|
|
5
|
-
|
|
4
|
+
tslib_1.__exportStar(require("./utilities/patch"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./setup"), exports);
|
package/server/setup.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hookNextNodeServer = exports.setupWebSocketServer = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
6
|
+
const Log = tslib_1.__importStar(require("next/dist/build/output/log"));
|
|
7
|
+
const ws_1 = require("ws");
|
|
8
|
+
const next_1 = require("./utilities/next");
|
|
9
|
+
function setupWebSocketServer(nextServer) {
|
|
10
|
+
const httpServer = (0, next_1.getHttpServer)(nextServer);
|
|
11
|
+
const wsServer = new ws_1.WebSocketServer({ noServer: true });
|
|
12
|
+
Log.ready('[next-ws] websocket server started successfully');
|
|
13
|
+
httpServer.on('upgrade', async (request, socket, head) => {
|
|
14
|
+
const url = new URL(request.url ?? '', 'http://next-ws');
|
|
15
|
+
const pathname = await (0, next_1.resolvePathname)(nextServer, url.pathname);
|
|
16
|
+
if (!pathname)
|
|
17
|
+
return;
|
|
18
|
+
const fsPathname = node_path_1.default
|
|
19
|
+
.join(pathname, 'route')
|
|
20
|
+
.replaceAll(node_path_1.default.sep, '/');
|
|
21
|
+
const pageModule = await (0, next_1.getPageModule)(nextServer, fsPathname);
|
|
22
|
+
if (!pageModule)
|
|
23
|
+
return Log.error(`[next-ws] could not find module for page ${pathname}`);
|
|
24
|
+
const socketHandler = pageModule?.routeModule?.userland?.SOCKET;
|
|
25
|
+
if (!socketHandler || typeof socketHandler !== 'function')
|
|
26
|
+
return Log.error(`[next-ws] could not find SOCKET handler for page ${pathname}`);
|
|
27
|
+
wsServer.handleUpgrade(request, socket, head, (client, request) => {
|
|
28
|
+
socketHandler(client, request, wsServer);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
exports.setupWebSocketServer = setupWebSocketServer;
|
|
33
|
+
// Next WS versions below 0.2.0 used a different method of setup
|
|
34
|
+
// This remains for backwards compatibility, but may be removed in a future version
|
|
35
|
+
function hookNextNodeServer() {
|
|
36
|
+
setupWebSocketServer(this);
|
|
37
|
+
}
|
|
38
|
+
exports.hookNextNodeServer = hookNextNodeServer;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Server } from 'node:http';
|
|
3
|
+
import NextNodeServer from 'next/dist/server/next-server';
|
|
4
|
+
/** A function that handles a WebSocket connection. */
|
|
5
|
+
export type SocketHandler = (
|
|
6
|
+
/** The WebSocket client that connected. */
|
|
7
|
+
client: import('ws').WebSocket,
|
|
8
|
+
/** The HTTP request that initiated the WebSocket connection. */
|
|
9
|
+
request: import('http').IncomingMessage,
|
|
10
|
+
/** The WebSocket server. */
|
|
11
|
+
server: import('ws').WebSocketServer) => any;
|
|
12
|
+
/**
|
|
13
|
+
* Get the http.Server instance from the NextNodeServer.
|
|
14
|
+
* @param nextServer The NextNodeServer instance.
|
|
15
|
+
* @returns The http.Server instance.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getHttpServer(nextServer: NextNodeServer): Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a pathname to a page.
|
|
20
|
+
* @param nextServer The NextNodeServer instance.
|
|
21
|
+
* @param pathname The pathname to resolve.
|
|
22
|
+
* @returns The resolved page, or null if the page could not be resolved.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolvePathname(nextServer: NextNodeServer, pathname: string): Promise<string | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Get the page module for a page.
|
|
27
|
+
* @param nextServer The NextNodeServer instance.
|
|
28
|
+
* @param filename The filename of the page.
|
|
29
|
+
* @returns The page module.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getPageModule(nextServer: NextNodeServer, filename: string): Promise<any>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPageModule = exports.resolvePathname = exports.getHttpServer = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const node_http_1 = require("node:http");
|
|
6
|
+
const next_server_1 = tslib_1.__importDefault(require("next/dist/server/next-server"));
|
|
7
|
+
const is_dynamic_1 = require("next/dist/shared/lib/router/utils/is-dynamic");
|
|
8
|
+
/**
|
|
9
|
+
* Get the http.Server instance from the NextNodeServer.
|
|
10
|
+
* @param nextServer The NextNodeServer instance.
|
|
11
|
+
* @returns The http.Server instance.
|
|
12
|
+
*/
|
|
13
|
+
// prettier-ignore
|
|
14
|
+
function getHttpServer(nextServer) {
|
|
15
|
+
if (!nextServer || !(nextServer instanceof next_server_1.default))
|
|
16
|
+
throw new Error('Next WS is missing access to the NextNodeServer');
|
|
17
|
+
// @ts-expect-error serverOptions is protected
|
|
18
|
+
const httpServer = nextServer.serverOptions?.httpServer;
|
|
19
|
+
if (!httpServer || !(httpServer instanceof node_http_1.Server))
|
|
20
|
+
throw new Error('Next WS is missing access to the http.Server');
|
|
21
|
+
return httpServer;
|
|
22
|
+
}
|
|
23
|
+
exports.getHttpServer = getHttpServer;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a pathname to a page.
|
|
26
|
+
* @param nextServer The NextNodeServer instance.
|
|
27
|
+
* @param pathname The pathname to resolve.
|
|
28
|
+
* @returns The resolved page, or null if the page could not be resolved.
|
|
29
|
+
*/
|
|
30
|
+
async function resolvePathname(nextServer, pathname) {
|
|
31
|
+
if (pathname.startsWith('/_next'))
|
|
32
|
+
return null;
|
|
33
|
+
// @ts-expect-error hasPage is protected
|
|
34
|
+
if (!(0, is_dynamic_1.isDynamicRoute)(pathname) && (await nextServer.hasPage(pathname)))
|
|
35
|
+
return pathname;
|
|
36
|
+
// @ts-expect-error dynamicRoutes is protected
|
|
37
|
+
for (const route of nextServer.dynamicRoutes ?? []) {
|
|
38
|
+
const params = route.match(pathname) || undefined;
|
|
39
|
+
if (params)
|
|
40
|
+
return route.page;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
exports.resolvePathname = resolvePathname;
|
|
45
|
+
/**
|
|
46
|
+
* Get the page module for a page.
|
|
47
|
+
* @param nextServer The NextNodeServer instance.
|
|
48
|
+
* @param filename The filename of the page.
|
|
49
|
+
* @returns The page module.
|
|
50
|
+
*/
|
|
51
|
+
async function getPageModule(nextServer, filename) {
|
|
52
|
+
// @ts-expect-error HotReloader is private
|
|
53
|
+
await nextServer.hotReloader.ensurePage({
|
|
54
|
+
page: filename,
|
|
55
|
+
clientOnly: false,
|
|
56
|
+
});
|
|
57
|
+
// @ts-expect-error getPagePath is protected
|
|
58
|
+
const builtPagePath = nextServer.getPagePath(filename);
|
|
59
|
+
return require(builtPagePath);
|
|
60
|
+
}
|
|
61
|
+
exports.getPageModule = getPageModule;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the Next WS patch version and Next.js version.
|
|
3
|
+
* @returns The patch version and Next.js version if the patch has been applied, otherwise null.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getPatch(): {
|
|
6
|
+
patch: string;
|
|
7
|
+
version: string;
|
|
8
|
+
} | null;
|
|
9
|
+
/**
|
|
10
|
+
* Verify that the Next WS patch has been applied to Next.js.
|
|
11
|
+
* @returns The patch version and Next.js version if the patch has been applied, otherwise null.
|
|
12
|
+
*/
|
|
13
|
+
export declare function verifyPatch(): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyPatch = exports.getPatch = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("node:fs"));
|
|
6
|
+
const path = tslib_1.__importStar(require("node:path"));
|
|
7
|
+
const workspace_1 = require("./workspace");
|
|
8
|
+
/**
|
|
9
|
+
* Get the Next WS patch version and Next.js version.
|
|
10
|
+
* @returns The patch version and Next.js version if the patch has been applied, otherwise null.
|
|
11
|
+
*/
|
|
12
|
+
function getPatch() {
|
|
13
|
+
const location = path.join((0, workspace_1.findWorkspaceRoot)(), 'node_modules/next/.next-ws-trace.json');
|
|
14
|
+
try {
|
|
15
|
+
const content = fs.readFileSync(location, 'utf8');
|
|
16
|
+
return JSON.parse(content);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.getPatch = getPatch;
|
|
23
|
+
/**
|
|
24
|
+
* Verify that the Next WS patch has been applied to Next.js.
|
|
25
|
+
* @returns The patch version and Next.js version if the patch has been applied, otherwise null.
|
|
26
|
+
*/
|
|
27
|
+
// prettier-ignore
|
|
28
|
+
function verifyPatch() {
|
|
29
|
+
const patch = getPatch();
|
|
30
|
+
if (!patch)
|
|
31
|
+
throw new Error('Next.js has not been patched to support Next WS, please run `npx next-ws-cli patch`');
|
|
32
|
+
const version = require('next/package.json').version.split('-')[0];
|
|
33
|
+
if (patch.version !== version)
|
|
34
|
+
throw new Error(`Next.js version mismatch, expected ${patch.version} but found ${version}`);
|
|
35
|
+
}
|
|
36
|
+
exports.verifyPatch = verifyPatch;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function findWorkspaceRoot(initalPath?: string): string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findWorkspaceRoot = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("node:fs"));
|
|
6
|
+
const path = tslib_1.__importStar(require("node:path"));
|
|
7
|
+
function findWorkspaceRoot(initalPath = process.cwd()) {
|
|
8
|
+
let currentPath = initalPath;
|
|
9
|
+
let lastPossiblePath = currentPath;
|
|
10
|
+
let isRoot = 'maybe';
|
|
11
|
+
const isCurrentPathRoot = () => {
|
|
12
|
+
const files = fs.readdirSync(currentPath);
|
|
13
|
+
const lockFiles = ['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json'];
|
|
14
|
+
const hasLockFile = files.some(file => lockFiles.includes(file));
|
|
15
|
+
if (hasLockFile)
|
|
16
|
+
return 'true';
|
|
17
|
+
const packageJson = files.find(file => file === 'package.json');
|
|
18
|
+
if (packageJson) {
|
|
19
|
+
const packageContent = fs.readFileSync(path.resolve(currentPath, packageJson), 'utf8');
|
|
20
|
+
const packageObject = JSON.parse(packageContent);
|
|
21
|
+
if (packageObject.packageManager)
|
|
22
|
+
return 'true';
|
|
23
|
+
return 'maybe';
|
|
24
|
+
}
|
|
25
|
+
return 'false';
|
|
26
|
+
};
|
|
27
|
+
while (true) {
|
|
28
|
+
isRoot = isCurrentPathRoot();
|
|
29
|
+
const nextPath = path.resolve(currentPath, '..');
|
|
30
|
+
if (isRoot === 'true' || nextPath === currentPath)
|
|
31
|
+
break;
|
|
32
|
+
else if (isRoot === 'maybe')
|
|
33
|
+
lastPossiblePath = currentPath;
|
|
34
|
+
currentPath = nextPath;
|
|
35
|
+
}
|
|
36
|
+
return isRoot === 'true' ? currentPath : lastPossiblePath;
|
|
37
|
+
}
|
|
38
|
+
exports.findWorkspaceRoot = findWorkspaceRoot;
|
package/client/context.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/client/context.tsx"],"names":[],"mappings":";;;;AAAA,iCAAsE;AAEtE,MAAM,gBAAgB,GAAG,IAAA,qBAAa,EAAmB,IAAI,CAAC,CAAC;AAgDtD,4CAAgB;AA/CzB,gBAAgB,CAAC,WAAW,GAAG,kBAAkB,CAAC;AAElD;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,EACvB,QAAQ,EACR,GAAG,EACH,SAAS,GAKZ;IACG,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;IAChD,MAAM,EAAE,GAAG,IAAA,eAAO,EACd,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EACxD,CAAC,SAAS,EAAE,GAAG,EAAE,SAAS,CAAC,CAC9B,CAAC;IAEF,IAAA,iBAAS,EAAC,GAAG,EAAE;QACX,OAAO,GAAG,EAAE;YACR,IAAI,EAAE,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,MAAM;gBAAE,EAAE,CAAC,KAAK,EAAE,CAAC;QAC7D,CAAC,CAAC;IACN,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,uBAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,YACtC,QAAQ,GACe,CAAC;AACjC,CAAC;AAe0B,8CAAiB;AAb5C,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,QAAQ,CAAC;AAaN,8CAAiB;AAX/D;;;GAGG;AACH,SAAS,YAAY;IACjB,MAAM,OAAO,GAAG,IAAA,kBAAU,EAAC,gBAAgB,CAAC,CAAC;IAC7C,IAAI,OAAO,KAAK,SAAS;QACrB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC5E,OAAO,OAAO,CAAC;AACnB,CAAC;AAEgE,oCAAY"}
|
package/client/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/client/index.ts"],"names":[],"mappings":";;;AAAA,oDAA0B"}
|
package/common/patch.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
/* eslint-disable no-template-curly-in-string */
|
|
5
|
-
const fs = tslib_1.__importStar(require("node:fs/promises"));
|
|
6
|
-
const generator_1 = tslib_1.__importDefault(require("@babel/generator"));
|
|
7
|
-
const parser = tslib_1.__importStar(require("@babel/parser"));
|
|
8
|
-
const template_1 = tslib_1.__importDefault(require("@babel/template"));
|
|
9
|
-
void main();
|
|
10
|
-
async function main() {
|
|
11
|
-
await patchNextNodeServer();
|
|
12
|
-
await patchNextTypesPlugin();
|
|
13
|
-
}
|
|
14
|
-
const mod = template_1.default.expression.ast `require("next-ws/server")`;
|
|
15
|
-
// Add `require('next-ws/server').hookNextNodeServer.call(this)` to the
|
|
16
|
-
// constructor of `NextNodeServer` in `next/dist/server/next-server.js`
|
|
17
|
-
async function patchNextNodeServer() {
|
|
18
|
-
const filePath = require.resolve('next/dist/server/next-server');
|
|
19
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
20
|
-
const ast = parser.parse(content);
|
|
21
|
-
const classDeclaration = ast.program.body.find(node => node.type === 'ClassDeclaration' &&
|
|
22
|
-
node.id.name === 'NextNodeServer');
|
|
23
|
-
if (!classDeclaration)
|
|
24
|
-
return;
|
|
25
|
-
const constructorMethod = classDeclaration.body.body.find(node => node.type === 'ClassMethod' && node.kind === 'constructor');
|
|
26
|
-
if (!constructorMethod)
|
|
27
|
-
return;
|
|
28
|
-
const statement = template_1.default.statement
|
|
29
|
-
.ast `${mod}.hookNextNodeServer.call(this)`;
|
|
30
|
-
const expression = (0, generator_1.default)(statement).code;
|
|
31
|
-
// Ensure the statement is not already in the constructor
|
|
32
|
-
const existingStatement = constructorMethod.body.body //
|
|
33
|
-
.some(state => (0, generator_1.default)(state).code === expression);
|
|
34
|
-
if (!existingStatement)
|
|
35
|
-
constructorMethod.body.body.push(statement);
|
|
36
|
-
await fs.writeFile(filePath, (0, generator_1.default)(ast).code);
|
|
37
|
-
}
|
|
38
|
-
// Add `SOCKET?: Function` to the page module interface check field thing in
|
|
39
|
-
// `next/dist/build/webpack/plugins/next-types-plugin.js`
|
|
40
|
-
async function patchNextTypesPlugin() {
|
|
41
|
-
const filePath = require.resolve('next/dist/build/webpack/plugins/next-types-plugin.js');
|
|
42
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
43
|
-
if (content.includes('SOCKET?: Function'))
|
|
44
|
-
return;
|
|
45
|
-
const toFind = '.map((method)=>`${method}?: Function`).join("\\n ")';
|
|
46
|
-
const replaceWith = `${toFind} + "; SOCKET?: Function"`;
|
|
47
|
-
const newContent = content.replace(toFind, replaceWith);
|
|
48
|
-
await fs.writeFile(filePath, newContent);
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=patch.js.map
|
package/common/patch.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/common/patch.ts"],"names":[],"mappings":";;;AAAA,gDAAgD;AAChD,6DAAuC;AACvC,yEAAwC;AACxC,8DAAwC;AACxC,uEAAuC;AAGvC,KAAK,IAAI,EAAE,CAAC;AACZ,KAAK,UAAU,IAAI;IACf,MAAM,mBAAmB,EAAE,CAAC;IAC5B,MAAM,oBAAoB,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,GAAG,GAAG,kBAAQ,CAAC,UAAU,CAAC,GAAG,CAAA,2BAA2B,CAAC;AAE/D,uEAAuE;AACvE,uEAAuE;AACvE,KAAK,UAAU,mBAAmB;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAElC,MAAM,gBAAgB,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAC1C,IAAI,CAAC,EAAE,CACH,IAAI,CAAC,IAAI,KAAK,kBAAkB;QAChC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,gBAAgB,CACR,CAAC;IAClC,IAAI,CAAC,gBAAgB;QAAE,OAAO;IAE9B,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CACrD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,CAC1C,CAAC;IAC7B,IAAI,CAAC,iBAAiB;QAAE,OAAO;IAE/B,MAAM,SAAS,GAAG,kBAAQ,CAAC,SAAS;SAC/B,GAAG,CAAA,GAAG,GAAG,gCAAgC,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAA,mBAAQ,EAAC,SAAS,CAAC,CAAC,IAAI,CAAC;IAE5C,yDAAyD;IACzD,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SACnD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAA,mBAAQ,EAAC,KAAK,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACxD,IAAI,CAAC,iBAAiB;QAAE,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAEpE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAA,mBAAQ,EAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,4EAA4E;AAC5E,yDAAyD;AACzD,KAAK,UAAU,oBAAoB;IAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAC5B,sDAAsD,CACzD,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACpD,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAAE,OAAO;IAElD,MAAM,MAAM,GAAG,sDAAsD,CAAC;IACtE,MAAM,WAAW,GAAG,GAAG,MAAM,0BAA0B,CAAC;IAExD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACxD,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AAC7C,CAAC"}
|
package/server/hook.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import NextNodeServer from 'next/dist/server/next-server';
|
|
2
|
-
export type SocketHandler = (client: import('ws').WebSocket, request: import('http').IncomingMessage, server: import('ws').WebSocketServer) => any;
|
|
3
|
-
export declare function hookNextNodeServer(this: NextNodeServer): void;
|
package/server/hook.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hookNextNodeServer = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const node_http_1 = require("node:http");
|
|
6
|
-
const path = tslib_1.__importStar(require("node:path"));
|
|
7
|
-
const Log = tslib_1.__importStar(require("next/dist/build/output/log"));
|
|
8
|
-
const next_server_1 = tslib_1.__importDefault(require("next/dist/server/next-server"));
|
|
9
|
-
const is_dynamic_1 = require("next/dist/shared/lib/router/utils/is-dynamic");
|
|
10
|
-
const ws_1 = require("ws");
|
|
11
|
-
let existingWebSocketServer;
|
|
12
|
-
function hookNextNodeServer() {
|
|
13
|
-
if (!this || !(this instanceof next_server_1.default))
|
|
14
|
-
throw new Error("[next-ws] 'this' of hookNextNodeServer is not a NextNodeServer");
|
|
15
|
-
const server = this.serverOptions?.httpServer;
|
|
16
|
-
if (!server || !(server instanceof node_http_1.Server))
|
|
17
|
-
throw new Error("[next-ws] Failed to find NextNodeServer's HTTP server");
|
|
18
|
-
if (existingWebSocketServer)
|
|
19
|
-
return;
|
|
20
|
-
const wss = new ws_1.WebSocketServer({ noServer: true });
|
|
21
|
-
Log.ready('[next-ws] websocket server started successfully');
|
|
22
|
-
existingWebSocketServer = wss;
|
|
23
|
-
server.on('upgrade', async (request, socket, head) => {
|
|
24
|
-
const url = new URL(request.url ?? '', 'http://next-ws');
|
|
25
|
-
// Ignore requests to Next.js' own internal files
|
|
26
|
-
if (url.pathname.startsWith('/_next'))
|
|
27
|
-
return;
|
|
28
|
-
// Attempt to find a matching page
|
|
29
|
-
const pathname = await isPageFound.call(this, url.pathname);
|
|
30
|
-
if (!pathname)
|
|
31
|
-
return;
|
|
32
|
-
const internalPathname = path
|
|
33
|
-
.join(pathname, 'route')
|
|
34
|
-
.replaceAll(path.sep, '/');
|
|
35
|
-
// Ensure the page is built
|
|
36
|
-
// @ts-expect-error HotReloader is private
|
|
37
|
-
await this.hotReloader.ensurePage({
|
|
38
|
-
page: internalPathname,
|
|
39
|
-
clientOnly: false,
|
|
40
|
-
});
|
|
41
|
-
let builtPagePath;
|
|
42
|
-
try {
|
|
43
|
-
builtPagePath = this.getPagePath(internalPathname);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
return Log.error(`[next-ws] failed to get page ${pathname}`);
|
|
47
|
-
}
|
|
48
|
-
const pageModule = await require(builtPagePath);
|
|
49
|
-
// Equates to the exported "SOCKET" function in the route file
|
|
50
|
-
const socketHandler = pageModule.routeModule.userland.SOCKET;
|
|
51
|
-
if (!socketHandler || typeof socketHandler !== 'function')
|
|
52
|
-
return Log.error(`[next-ws] failed to find SOCKET handler for page ${pathname}`);
|
|
53
|
-
wss.handleUpgrade(request, socket, head, (client, request) => {
|
|
54
|
-
socketHandler(client, request, wss);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
exports.hookNextNodeServer = hookNextNodeServer;
|
|
59
|
-
async function isPageFound(pathname) {
|
|
60
|
-
if (!(0, is_dynamic_1.isDynamicRoute)(pathname) && (await this.hasPage(pathname)))
|
|
61
|
-
return pathname;
|
|
62
|
-
for (const route of this.dynamicRoutes ?? []) {
|
|
63
|
-
const params = route.match(pathname) || undefined;
|
|
64
|
-
if (params)
|
|
65
|
-
return route.page;
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
//# sourceMappingURL=hook.js.map
|
package/server/hook.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../src/server/hook.ts"],"names":[],"mappings":"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"}
|
package/server/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/server/index.ts"],"names":[],"mappings":";;;AAAA,iDAAuB"}
|
|
File without changes
|