next-ws 1.0.1-experimental.2 → 1.0.1
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/package.json +2 -3
- package/server/index.d.ts +2 -2
- package/server/index.js +1 -5
- package/server/setup.d.ts +1 -1
- package/server/setup.js +10 -14
- package/server/utilities/next.d.ts +11 -4
- package/server/utilities/next.js +43 -25
- package/server/utilities/ws.d.ts +15 -0
- package/server/utilities/ws.js +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-ws",
|
|
3
|
-
"version": "1.0.1
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Add support for WebSockets in Next.js 13 app directory",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"next",
|
|
@@ -48,8 +48,7 @@
|
|
|
48
48
|
"react": "^18.2.0",
|
|
49
49
|
"react-dom": "^18.2.0",
|
|
50
50
|
"rimraf": "^5.0.1",
|
|
51
|
-
"typescript": "<5.1.0"
|
|
52
|
-
"ws": "^8.14.2"
|
|
51
|
+
"typescript": "<5.1.0"
|
|
53
52
|
},
|
|
54
53
|
"eslintConfig": {
|
|
55
54
|
"root": true,
|
package/server/index.d.ts
CHANGED
package/server/index.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CustomWsServer = exports.CustomHttpServer = void 0;
|
|
4
3
|
const tslib_1 = require("tslib");
|
|
5
|
-
tslib_1.__exportStar(require("./setup"), exports);
|
|
6
4
|
tslib_1.__exportStar(require("./utilities/patch"), exports);
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(exports, "CustomHttpServer", { enumerable: true, get: function () { return server_1.CustomHttpServer; } });
|
|
9
|
-
Object.defineProperty(exports, "CustomWsServer", { enumerable: true, get: function () { return server_1.CustomWsServer; } });
|
|
5
|
+
tslib_1.__exportStar(require("./setup"), exports);
|
package/server/setup.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import NextNodeServer from 'next/dist/server/next-server';
|
|
1
|
+
import type NextNodeServer from 'next/dist/server/next-server';
|
|
2
2
|
export declare function setupWebSocketServer(nextServer: NextNodeServer): void;
|
|
3
3
|
export declare function hookNextNodeServer(this: NextNodeServer): void;
|
package/server/setup.js
CHANGED
|
@@ -4,34 +4,31 @@ exports.hookNextNodeServer = exports.setupWebSocketServer = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const Log = tslib_1.__importStar(require("next/dist/build/output/log"));
|
|
6
6
|
const next_1 = require("./utilities/next");
|
|
7
|
-
const
|
|
7
|
+
const ws_1 = require("./utilities/ws");
|
|
8
8
|
function setupWebSocketServer(nextServer) {
|
|
9
|
-
const httpServer = (0,
|
|
10
|
-
const wsServer = (0,
|
|
11
|
-
Log.ready('[next-ws]
|
|
9
|
+
const httpServer = (0, next_1.getHttpServer)(nextServer);
|
|
10
|
+
const wsServer = (0, ws_1.getWsServer)();
|
|
11
|
+
Log.ready('[next-ws] websocket server started successfully');
|
|
12
12
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
13
13
|
httpServer.on('upgrade', async (request, socket, head) => {
|
|
14
14
|
const url = new URL(request.url ?? '', 'ws://next');
|
|
15
15
|
const pathname = url.pathname;
|
|
16
|
-
// Ignore Next.js internal requests (aka HMR)
|
|
17
16
|
if (pathname.startsWith('/_next'))
|
|
18
17
|
return;
|
|
19
|
-
// Resolve the pathname to a file system path (eg /about -> /about/route)
|
|
20
18
|
const fsPathname = (0, next_1.resolvePathname)(nextServer, pathname);
|
|
21
19
|
if (!fsPathname) {
|
|
22
|
-
Log.error(
|
|
23
|
-
return socket.
|
|
20
|
+
Log.error(`[next-ws] could not find module for page ${pathname}`);
|
|
21
|
+
return socket.destroy();
|
|
24
22
|
}
|
|
25
|
-
// Get the page module for the pathname (aka require('/about/route'))
|
|
26
23
|
const pageModule = await (0, next_1.getPageModule)(nextServer, fsPathname);
|
|
27
24
|
if (!pageModule) {
|
|
28
|
-
Log.error(
|
|
29
|
-
return socket.
|
|
25
|
+
Log.error(`[next-ws] could not find module for page ${pathname}`);
|
|
26
|
+
return socket.destroy();
|
|
30
27
|
}
|
|
31
28
|
const socketHandler = pageModule?.routeModule?.userland?.SOCKET;
|
|
32
29
|
if (!socketHandler || typeof socketHandler !== 'function') {
|
|
33
|
-
Log.error(
|
|
34
|
-
return socket.
|
|
30
|
+
Log.error(`[next-ws] ${pathname} does not export a SOCKET handler`);
|
|
31
|
+
return socket.destroy();
|
|
35
32
|
}
|
|
36
33
|
return wsServer.handleUpgrade(request, socket, head, (client, request) => void socketHandler(client, request, wsServer));
|
|
37
34
|
});
|
|
@@ -40,7 +37,6 @@ exports.setupWebSocketServer = setupWebSocketServer;
|
|
|
40
37
|
// Next WS versions below 0.2.0 used a different method of setup
|
|
41
38
|
// This remains for backwards compatibility, but may be removed in a future version
|
|
42
39
|
function hookNextNodeServer() {
|
|
43
|
-
Log.warnOnce('[next-ws] is using a deprecated method of hooking into Next.js, this may break in future versions');
|
|
44
40
|
setupWebSocketServer(this);
|
|
45
41
|
}
|
|
46
42
|
exports.hookNextNodeServer = hookNextNodeServer;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Server } from 'node:http';
|
|
1
3
|
import NextNodeServer from 'next/dist/server/next-server';
|
|
4
|
+
import type { SocketHandler } from './ws';
|
|
2
5
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @
|
|
5
|
-
* @
|
|
6
|
+
* Get the http.Server instance from the NextNodeServer.
|
|
7
|
+
* @param nextServer The NextNodeServer instance.
|
|
8
|
+
* @returns The http.Server instance.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getHttpServer(nextServer: NextNodeServer): Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve a pathname to a page.
|
|
6
13
|
* @param nextServer The NextNodeServer instance.
|
|
7
14
|
* @param pathname The pathname to resolve.
|
|
8
15
|
* @returns The resolved page, or null if the page could not be resolved.
|
|
@@ -18,7 +25,7 @@ export declare function getPageModule(nextServer: NextNodeServer, filename: stri
|
|
|
18
25
|
export interface PageModule {
|
|
19
26
|
routeModule?: {
|
|
20
27
|
userland?: {
|
|
21
|
-
SOCKET?:
|
|
28
|
+
SOCKET?: SocketHandler;
|
|
22
29
|
};
|
|
23
30
|
};
|
|
24
31
|
}
|
package/server/utilities/next.js
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPageModule = exports.resolvePathname = void 0;
|
|
3
|
+
exports.getPageModule = exports.resolvePathname = exports.getHttpServer = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const node_http_1 = require("node:http");
|
|
5
6
|
const Log = tslib_1.__importStar(require("next/dist/build/output/log"));
|
|
7
|
+
const next_server_1 = tslib_1.__importDefault(require("next/dist/server/next-server"));
|
|
6
8
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
9
|
-
* @
|
|
9
|
+
* Get the http.Server instance from the NextNodeServer.
|
|
10
|
+
* @param nextServer The NextNodeServer instance.
|
|
11
|
+
* @returns The http.Server instance.
|
|
12
|
+
*/
|
|
13
|
+
function getHttpServer(nextServer) {
|
|
14
|
+
if (!nextServer || !(nextServer instanceof next_server_1.default))
|
|
15
|
+
throw new Error('Next WS is missing access to the NextNodeServer');
|
|
16
|
+
// @ts-expect-error - serverOptions is protected
|
|
17
|
+
const httpServer = nextServer.serverOptions?.httpServer;
|
|
18
|
+
if (!httpServer || !(httpServer instanceof node_http_1.Server))
|
|
19
|
+
throw new Error('Next WS is missing access to the http.Server');
|
|
20
|
+
return httpServer;
|
|
21
|
+
}
|
|
22
|
+
exports.getHttpServer = getHttpServer;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a pathname to a page.
|
|
10
25
|
* @param nextServer The NextNodeServer instance.
|
|
11
26
|
* @param pathname The pathname to resolve.
|
|
12
27
|
* @returns The resolved page, or null if the page could not be resolved.
|
|
13
28
|
*/
|
|
14
29
|
function resolvePathname(nextServer, pathname) {
|
|
30
|
+
if (pathname.startsWith('/_next'))
|
|
31
|
+
return null;
|
|
15
32
|
const pathParts = pathname.split('/');
|
|
16
33
|
const appRoutes = {
|
|
17
34
|
// @ts-expect-error - appPathRoutes is protected
|
|
@@ -19,7 +36,7 @@ function resolvePathname(nextServer, pathname) {
|
|
|
19
36
|
// @ts-expect-error - getAppPathRoutes is protected
|
|
20
37
|
...nextServer.getAppPathRoutes(),
|
|
21
38
|
};
|
|
22
|
-
for (const [key, [path]] of Object.entries(appRoutes)) {
|
|
39
|
+
outer: for (const [key, [path]] of Object.entries(appRoutes)) {
|
|
23
40
|
const hasDynamic = key.includes('[') && key.includes(']');
|
|
24
41
|
if (hasDynamic) {
|
|
25
42
|
const keyParts = key.split('/');
|
|
@@ -35,7 +52,7 @@ function resolvePathname(nextServer, pathname) {
|
|
|
35
52
|
break;
|
|
36
53
|
if (i === keyParts.length - 1) {
|
|
37
54
|
if (!path?.endsWith('/route'))
|
|
38
|
-
|
|
55
|
+
continue outer;
|
|
39
56
|
return path;
|
|
40
57
|
}
|
|
41
58
|
}
|
|
@@ -58,27 +75,28 @@ exports.resolvePathname = resolvePathname;
|
|
|
58
75
|
* @returns The page module.
|
|
59
76
|
*/
|
|
60
77
|
async function getPageModule(nextServer, filename) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
else if ('ensurePage' in nextServer) {
|
|
72
|
-
// @ts-expect-error - ensurePage is protected
|
|
73
|
-
await nextServer.ensurePage({ page: filename, clientOnly: false });
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
Log.warnOnce('[next-ws] cannot find a way to ensure page, you may need to open routes in your browser first so Next.js compiles them');
|
|
77
|
-
}
|
|
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
|
+
});
|
|
78
87
|
}
|
|
79
|
-
|
|
80
|
-
|
|
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 });
|
|
81
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;
|
|
82
100
|
}
|
|
83
101
|
// @ts-expect-error - getPagePath is protected
|
|
84
102
|
const builtPagePath = nextServer.getPagePath(filename);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="ws" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/** A function that handles a WebSocket connection. */
|
|
4
|
+
export type SocketHandler = (
|
|
5
|
+
/** The WebSocket client that connected. */
|
|
6
|
+
client: import('ws').WebSocket,
|
|
7
|
+
/** The HTTP request that initiated the WebSocket connection. */
|
|
8
|
+
request: import('http').IncomingMessage,
|
|
9
|
+
/** The WebSocket server. */
|
|
10
|
+
server: import('ws').WebSocketServer) => unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Get the WebSocketServer instance.
|
|
13
|
+
* @returns The WebSocketServer instance.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getWsServer(): import("ws").Server<typeof import("ws"), typeof import("http").IncomingMessage>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getWsServer = void 0;
|
|
4
|
+
/* eslint-disable @typescript-eslint/consistent-type-imports */
|
|
5
|
+
const ws_1 = require("ws");
|
|
6
|
+
/**
|
|
7
|
+
* Get the WebSocketServer instance.
|
|
8
|
+
* @returns The WebSocketServer instance.
|
|
9
|
+
*/
|
|
10
|
+
function getWsServer() {
|
|
11
|
+
return new ws_1.WebSocketServer({ noServer: true });
|
|
12
|
+
}
|
|
13
|
+
exports.getWsServer = getWsServer;
|