next-ws 2.0.13 → 2.1.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 +13 -14
- package/dist/cli.cjs +946 -19
- package/dist/client/index.cjs +4 -5
- package/dist/server/index.cjs +184 -126
- package/dist/server/index.d.ts +43 -21
- package/package.json +25 -20
- package/dist/chunk-3RG5ZIWI.js +0 -10
- package/dist/client/index.d.cts +0 -27
- package/dist/client/index.js +0 -38
- package/dist/server/index.d.cts +0 -32
- package/dist/server/index.js +0 -186
package/dist/client/index.cjs
CHANGED
|
@@ -38,14 +38,13 @@ __export(client_exports, {
|
|
|
38
38
|
module.exports = __toCommonJS(client_exports);
|
|
39
39
|
|
|
40
40
|
// src/client/context.tsx
|
|
41
|
-
var import_react = __toESM(require("react")
|
|
42
|
-
var
|
|
43
|
-
var WebSocketContext = (0, import_react2.createContext)(null);
|
|
41
|
+
var import_react = __toESM(require("react"));
|
|
42
|
+
var WebSocketContext = (0, import_react.createContext)(null);
|
|
44
43
|
WebSocketContext.displayName = "WebSocketContext";
|
|
45
44
|
var WebSocketConsumer = WebSocketContext.Consumer;
|
|
46
45
|
function WebSocketProvider(p) {
|
|
47
46
|
const clientRef = (0, import_react.useRef)(null);
|
|
48
|
-
(0,
|
|
47
|
+
(0, import_react.useEffect)(() => {
|
|
49
48
|
if (typeof window === "undefined") return;
|
|
50
49
|
if (clientRef.current) {
|
|
51
50
|
clientRef.current.close();
|
|
@@ -62,7 +61,7 @@ function WebSocketProvider(p) {
|
|
|
62
61
|
return /* @__PURE__ */ import_react.default.createElement(WebSocketContext.Provider, { value: clientRef.current }, p.children);
|
|
63
62
|
}
|
|
64
63
|
function useWebSocket() {
|
|
65
|
-
const context = (0,
|
|
64
|
+
const context = (0, import_react.useContext)(WebSocketContext);
|
|
66
65
|
if (context === void 0)
|
|
67
66
|
throw new Error("useWebSocket must be used within a WebSocketProvider");
|
|
68
67
|
return context;
|
package/dist/server/index.cjs
CHANGED
|
@@ -38,179 +38,237 @@ __export(server_exports, {
|
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(server_exports);
|
|
40
40
|
|
|
41
|
+
// src/server/persistent.ts
|
|
42
|
+
function useGlobal(key) {
|
|
43
|
+
return [
|
|
44
|
+
function get() {
|
|
45
|
+
return Reflect.get(globalThis, key);
|
|
46
|
+
},
|
|
47
|
+
function set(value) {
|
|
48
|
+
return Reflect.set(globalThis, key, value);
|
|
49
|
+
},
|
|
50
|
+
function use(getter) {
|
|
51
|
+
const existing = Reflect.get(globalThis, key);
|
|
52
|
+
if (existing) return existing;
|
|
53
|
+
Reflect.set(globalThis, key, getter());
|
|
54
|
+
return Reflect.get(globalThis, key);
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
var [getHttpServer, setHttpServer, useHttpServer] = (
|
|
59
|
+
//
|
|
60
|
+
useGlobal(
|
|
61
|
+
Symbol.for("next-ws.http-server")
|
|
62
|
+
//
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
var [getWebSocketServer, setWebSocketServer, useWebSocketServer] = (
|
|
66
|
+
//
|
|
67
|
+
useGlobal(
|
|
68
|
+
Symbol.for("next-ws.websocket-server")
|
|
69
|
+
//
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
var [getRequestStorage, setRequestStorage, useRequestStorage] = (
|
|
73
|
+
//
|
|
74
|
+
useGlobal(
|
|
75
|
+
Symbol.for("next-ws.request-store")
|
|
76
|
+
//
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
|
|
41
80
|
// src/server/setup.ts
|
|
42
|
-
var
|
|
81
|
+
var import_node_async_hooks = require("async_hooks");
|
|
82
|
+
var logger2 = __toESM(require("next/dist/build/output/log.js"));
|
|
43
83
|
var import_ws = require("ws");
|
|
44
84
|
|
|
45
|
-
// src/server/helpers/
|
|
46
|
-
|
|
47
|
-
function getEnvironmentMeta() {
|
|
48
|
-
const isCustomServer = !process.title.startsWith("next-");
|
|
49
|
-
const isMainProcess = process.env.NEXT_WS_MAIN_PROCESS === "1";
|
|
50
|
-
const isDevelopment = process.env.NODE_ENV === "development";
|
|
51
|
-
return { isCustomServer, isMainProcess, isDevelopment };
|
|
52
|
-
}
|
|
53
|
-
function mainProcessOnly(fnName) {
|
|
54
|
-
if (process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK === "1") return;
|
|
55
|
-
const meta = getEnvironmentMeta();
|
|
56
|
-
if (!meta.isMainProcess) {
|
|
57
|
-
throw new Error(
|
|
58
|
-
`[next-ws] Attempt to invoke '${fnName}' outside the main process.
|
|
59
|
-
You may be attempting to interact with the WebSocket server outside of a SOCKET handler. This will fail in production, as Next.js employs a worker process for routing, which do not have access to the WebSocket server on the main process.
|
|
60
|
-
You can resolve this by using a custom server.`
|
|
61
|
-
);
|
|
62
|
-
} else if (!meta.isCustomServer) {
|
|
63
|
-
logger.warnOnce(
|
|
64
|
-
`[next-ws] Caution: The function '${fnName}' was invoked without a custom server.
|
|
65
|
-
This could lead to unintended behaviour, especially if you're attempting to interact with the WebSocket server outside of a SOCKET handler.
|
|
66
|
-
Please note, while such configurations might function during development, they will fail in production. This is because Next.js employs a worker process for routing in production, which do not have access to the WebSocket server on the main process.
|
|
67
|
-
You can resolve this by using a custom server.`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
var NextWsHttpServer = Symbol.for("NextWs_HttpServer");
|
|
72
|
-
function setHttpServer(server) {
|
|
73
|
-
Reflect.set(globalThis, NextWsHttpServer, server);
|
|
74
|
-
}
|
|
75
|
-
function getHttpServer() {
|
|
76
|
-
mainProcessOnly("getHttpServer");
|
|
77
|
-
return Reflect.get(globalThis, NextWsHttpServer);
|
|
78
|
-
}
|
|
79
|
-
function useHttpServer(server) {
|
|
80
|
-
const existing = getHttpServer();
|
|
81
|
-
if (existing) return existing;
|
|
82
|
-
if (server) setHttpServer(server);
|
|
83
|
-
return server;
|
|
84
|
-
}
|
|
85
|
-
var NextWsWebSocketServer = Symbol.for("NextWs_WebSocketServer");
|
|
86
|
-
function setWebSocketServer(wsServer) {
|
|
87
|
-
Reflect.set(globalThis, NextWsWebSocketServer, wsServer);
|
|
88
|
-
}
|
|
89
|
-
function getWebSocketServer() {
|
|
90
|
-
mainProcessOnly("getWebSocketServer");
|
|
91
|
-
return Reflect.get(globalThis, NextWsWebSocketServer);
|
|
92
|
-
}
|
|
93
|
-
function useWebSocketServer(wsServer) {
|
|
94
|
-
const existing = getWebSocketServer();
|
|
95
|
-
if (existing) return existing;
|
|
96
|
-
if (wsServer) setWebSocketServer(wsServer);
|
|
97
|
-
return wsServer;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// src/server/helpers/route.ts
|
|
101
|
-
var import_node_url = require("url");
|
|
102
|
-
var logger2 = __toESM(require("next/dist/build/output/log.js"), 1);
|
|
103
|
-
function createRouteRegex(routePattern) {
|
|
85
|
+
// src/server/helpers/match.ts
|
|
86
|
+
function compileRoutePattern(routePattern) {
|
|
104
87
|
const escapedPattern = routePattern.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
105
|
-
const paramRegex = escapedPattern.replace(/\\\[([a-
|
|
88
|
+
const paramRegex = escapedPattern.replace(/\\\[\\\[(?:\\\.){3}([a-z0-9_]+)\\\]\\\]/gi, "?(?<r_o_$1>.+)?").replace(/\\\[(?:\\\.){3}([a-z0-9_]+)\\\]/gi, "(?<r_$1>.+)").replace(/\\\[([a-z0-9_]+)\\\]/gi, "(?<$1>[^/]+)");
|
|
106
89
|
return new RegExp(`^${paramRegex}$`);
|
|
107
90
|
}
|
|
108
|
-
function getRouteParams(routePattern,
|
|
109
|
-
const routeRegex =
|
|
110
|
-
const match =
|
|
91
|
+
function getRouteParams(routePattern, requestPathname) {
|
|
92
|
+
const routeRegex = compileRoutePattern(routePattern);
|
|
93
|
+
const match = requestPathname.replace(/\/+$/, "").match(routeRegex);
|
|
111
94
|
if (!match) return null;
|
|
112
95
|
if (!match.groups) return {};
|
|
113
96
|
const params = {};
|
|
114
97
|
for (let [k, v] of Object.entries(match.groups)) {
|
|
115
|
-
if (k.startsWith("
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
if (k.startsWith("r_")) {
|
|
99
|
+
const optional = k.startsWith("r_o_");
|
|
100
|
+
k = k.slice(optional ? 4 : 2);
|
|
101
|
+
v = v?.split("/");
|
|
118
102
|
}
|
|
119
|
-
Reflect.set(params, k, v);
|
|
103
|
+
if (v) Reflect.set(params, k, v);
|
|
120
104
|
}
|
|
121
105
|
return params;
|
|
122
106
|
}
|
|
123
|
-
function
|
|
124
|
-
const basePath = nextServer.serverOptions
|
|
125
|
-
const
|
|
107
|
+
function findMatchingRoute(nextServer, requestPathname) {
|
|
108
|
+
const basePath = nextServer.serverOptions?.conf.basePath || "";
|
|
109
|
+
const appPathRoutes = {
|
|
126
110
|
// @ts-expect-error - appPathRoutes is protected
|
|
127
111
|
...nextServer.appPathRoutes,
|
|
128
112
|
// @ts-expect-error - getAppPathRoutes is protected
|
|
129
113
|
...nextServer.getAppPathRoutes()
|
|
130
114
|
};
|
|
131
|
-
let
|
|
132
|
-
for (const [routePath, [filePath]] of Object.entries(
|
|
115
|
+
let matchedRoute = void 0;
|
|
116
|
+
for (const [routePath, [filePath]] of Object.entries(appPathRoutes)) {
|
|
117
|
+
if (!routePath || !filePath) continue;
|
|
133
118
|
const realPath = `${basePath}${routePath}`;
|
|
134
|
-
const routeParams = getRouteParams(realPath,
|
|
135
|
-
if (routeParams)
|
|
119
|
+
const routeParams = getRouteParams(realPath, requestPathname);
|
|
120
|
+
if (routeParams) matchedRoute = { filename: filePath, params: routeParams };
|
|
136
121
|
}
|
|
137
|
-
return
|
|
122
|
+
return matchedRoute;
|
|
138
123
|
}
|
|
139
|
-
|
|
124
|
+
|
|
125
|
+
// src/server/helpers/module.ts
|
|
126
|
+
var logger = __toESM(require("next/dist/build/output/log.js"));
|
|
127
|
+
async function importRouteModule(nextServer, filePathname) {
|
|
140
128
|
try {
|
|
141
129
|
if ("hotReloader" in nextServer) {
|
|
142
130
|
await nextServer.hotReloader?.ensurePage({
|
|
143
|
-
page:
|
|
131
|
+
page: filePathname,
|
|
144
132
|
clientOnly: false
|
|
145
133
|
});
|
|
146
134
|
} else if ("ensurePage" in nextServer) {
|
|
147
|
-
await nextServer.ensurePage({ page:
|
|
135
|
+
await nextServer.ensurePage({ page: filePathname, clientOnly: false });
|
|
148
136
|
} else {
|
|
149
|
-
|
|
137
|
+
logger.warnOnce(
|
|
150
138
|
"[next-ws] unable to ensure page, you may need to open the route in your browser first so Next.js compiles it"
|
|
151
139
|
);
|
|
152
140
|
}
|
|
153
141
|
} catch {
|
|
154
142
|
}
|
|
155
|
-
const buildPagePath = nextServer.getPagePath(filePath);
|
|
156
|
-
return importModule(buildPagePath);
|
|
157
|
-
}
|
|
158
|
-
async function importModule(modulePath) {
|
|
159
|
-
const moduleUrl = (0, import_node_url.pathToFileURL)(modulePath).toString();
|
|
160
143
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
console.error(`Both import and require failed for ${modulePath}`);
|
|
167
|
-
throw requireError2;
|
|
168
|
-
}
|
|
144
|
+
const buildPagePath = nextServer.getPagePath(filePathname);
|
|
145
|
+
return (await require(buildPagePath)).routeModule;
|
|
146
|
+
} catch (cause) {
|
|
147
|
+
console.error(cause);
|
|
148
|
+
return void 0;
|
|
169
149
|
}
|
|
170
150
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
151
|
+
|
|
152
|
+
// src/server/helpers/request.ts
|
|
153
|
+
var import_server = require("next/server");
|
|
154
|
+
function toNextRequest(message) {
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
const headers = new Headers(message.headers);
|
|
157
|
+
const protocol = "encrypted" in message.socket ? "https" : "http";
|
|
158
|
+
const url = `${protocol}://${headers.get("host")}${message.url}`;
|
|
159
|
+
message.once("aborted", () => controller.abort());
|
|
160
|
+
return new import_server.NextRequest(url, {
|
|
161
|
+
method: message.method,
|
|
162
|
+
headers,
|
|
163
|
+
body: message.method === "GET" || message.method === "HEAD" ? void 0 : message,
|
|
164
|
+
signal: controller.signal,
|
|
165
|
+
referrer: headers.get("referer") || void 0
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/server/helpers/store.ts
|
|
170
|
+
var import_cookies = require("next/dist/compiled/@edge-runtime/cookies");
|
|
171
|
+
var ReadonlyHeaders = class extends Headers {
|
|
172
|
+
append() {
|
|
173
|
+
throw new Error("Headers are read-only");
|
|
174
|
+
}
|
|
175
|
+
set() {
|
|
176
|
+
throw new Error("Headers are read-only");
|
|
177
|
+
}
|
|
178
|
+
delete() {
|
|
179
|
+
throw new Error("Headers are read-only");
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var ReadonlyRequestsCookies = class extends import_cookies.RequestCookies {
|
|
183
|
+
set() {
|
|
184
|
+
throw new Error("Cookies are read-only");
|
|
185
|
+
}
|
|
186
|
+
delete() {
|
|
187
|
+
throw new Error("Cookies are read-only");
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
function createRequestStore(request) {
|
|
191
|
+
return {
|
|
192
|
+
headers: new ReadonlyHeaders(request.headers),
|
|
193
|
+
cookies: new ReadonlyRequestsCookies(request.headers)
|
|
194
|
+
};
|
|
175
195
|
}
|
|
176
196
|
|
|
177
197
|
// src/server/setup.ts
|
|
178
198
|
function setupWebSocketServer(nextServer) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
199
|
+
const httpServer = (
|
|
200
|
+
//
|
|
201
|
+
// @ts-expect-error - serverOptions is protected
|
|
202
|
+
useHttpServer(() => nextServer.serverOptions?.httpServer)
|
|
203
|
+
);
|
|
184
204
|
if (!httpServer)
|
|
185
|
-
return
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
205
|
+
return logger2.error("[next-ws] was not able to find the HTTP server");
|
|
206
|
+
const wsServer = (
|
|
207
|
+
//
|
|
208
|
+
useWebSocketServer(() => new import_ws.WebSocketServer({ noServer: true }))
|
|
209
|
+
);
|
|
210
|
+
const requestStorage = (
|
|
211
|
+
//
|
|
212
|
+
useRequestStorage(() => new import_node_async_hooks.AsyncLocalStorage())
|
|
213
|
+
);
|
|
214
|
+
logger2.ready("[next-ws] has started the WebSocket server");
|
|
215
|
+
const kAttached = Symbol.for("next-ws.http-server.attached");
|
|
216
|
+
if (Reflect.has(httpServer, kAttached)) return;
|
|
217
|
+
Reflect.set(httpServer, kAttached, true);
|
|
218
|
+
httpServer.on("upgrade", async (message, socket, head) => {
|
|
219
|
+
const request = toNextRequest(message);
|
|
220
|
+
const pathname = request.nextUrl.pathname;
|
|
192
221
|
if (pathname.includes("/_next")) return;
|
|
193
|
-
const
|
|
194
|
-
if (!
|
|
195
|
-
|
|
196
|
-
return socket.
|
|
222
|
+
const route = findMatchingRoute(nextServer, pathname);
|
|
223
|
+
if (!route) {
|
|
224
|
+
logger2.error(`[next-ws] could not find route for page ${pathname}`);
|
|
225
|
+
return socket.end();
|
|
197
226
|
}
|
|
198
|
-
const
|
|
199
|
-
if (!
|
|
200
|
-
|
|
201
|
-
return socket.
|
|
227
|
+
const module2 = await importRouteModule(nextServer, route.filename);
|
|
228
|
+
if (!module2) {
|
|
229
|
+
logger2.error(`[next-ws] could not import module for page ${pathname}`);
|
|
230
|
+
return socket.end();
|
|
202
231
|
}
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
232
|
+
const handleUpgrade = module2.userland.UPGRADE;
|
|
233
|
+
const handleSocket = module2.userland.SOCKET;
|
|
234
|
+
if ((!handleUpgrade || typeof handleUpgrade !== "function") && (!handleSocket || typeof handleSocket !== "function")) {
|
|
235
|
+
logger2.error(`[next-ws] route '${pathname}' does not export a handler`);
|
|
236
|
+
return socket.end();
|
|
207
237
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
238
|
+
if (handleSocket)
|
|
239
|
+
logger2.warnOnce(
|
|
240
|
+
"DeprecationWarning: [next-ws] SOCKET is deprecated, prefer UPGRADE instead, see https://github.com/apteryxxyz/next-ws#-usage"
|
|
241
|
+
);
|
|
242
|
+
wsServer.handleUpgrade(message, socket, head, async (client) => {
|
|
243
|
+
wsServer.emit("connection", client, message);
|
|
244
|
+
try {
|
|
245
|
+
const context = { params: route.params };
|
|
246
|
+
if (handleUpgrade) {
|
|
247
|
+
await requestStorage.run(
|
|
248
|
+
createRequestStore(request),
|
|
249
|
+
//
|
|
250
|
+
() => handleUpgrade(client, wsServer, request, context)
|
|
251
|
+
);
|
|
252
|
+
} else if (handleSocket) {
|
|
253
|
+
const handleClose = (
|
|
254
|
+
//
|
|
255
|
+
await handleSocket(client, message, wsServer, context)
|
|
256
|
+
);
|
|
257
|
+
if (typeof handleClose === "function")
|
|
258
|
+
client.once("close", () => handleClose());
|
|
259
|
+
}
|
|
260
|
+
} catch (cause) {
|
|
261
|
+
logger2.error(
|
|
262
|
+
`[next-ws] error in socket handler for '${pathname}'`,
|
|
263
|
+
cause
|
|
264
|
+
);
|
|
265
|
+
try {
|
|
266
|
+
client.close(1011, "Internal Server Error");
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
213
270
|
});
|
|
271
|
+
return;
|
|
214
272
|
});
|
|
215
273
|
}
|
|
216
274
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,32 +1,54 @@
|
|
|
1
|
+
import * as next_server from 'next/server';
|
|
2
|
+
import * as http from 'http';
|
|
3
|
+
import * as ws from 'ws';
|
|
1
4
|
import NextNodeServer from 'next/dist/server/next-server.js';
|
|
2
|
-
import { Server } from 'node:http';
|
|
3
|
-
import { WebSocketServer } from 'ws';
|
|
4
|
-
|
|
5
|
-
declare function setupWebSocketServer(nextServer: NextNodeServer): void;
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @param
|
|
7
|
+
* WebSocket socket handler.
|
|
8
|
+
* @param client WebSocket client instance
|
|
9
|
+
* @param request Node.js HTTP incoming message instance
|
|
10
|
+
* @param server WebSocket server instance
|
|
11
|
+
* @param context Route context
|
|
12
|
+
* @deprecated Prefer UPGRADE and {@link UpgradeHandler}
|
|
13
|
+
*/
|
|
14
|
+
type SocketHandler = (client: ws.WebSocket, request: http.IncomingMessage, server: ws.WebSocketServer, context: {
|
|
15
|
+
params: Record<string, string | string[]>;
|
|
16
|
+
}) => unknown;
|
|
17
|
+
/**
|
|
18
|
+
* WebSocket upgrade handler.
|
|
19
|
+
* @param client WebSocket client instance
|
|
20
|
+
* @param server WebSocket server instance
|
|
21
|
+
* @param request Next.js request instance
|
|
22
|
+
* @param context Route context
|
|
10
23
|
*/
|
|
11
|
-
|
|
24
|
+
type UpgradeHandler = (client: ws.WebSocket, server: ws.WebSocketServer, request: next_server.NextRequest, context: RouteContext<string>) => unknown;
|
|
25
|
+
|
|
12
26
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @remark If you want to access the HTTP server outside of a SOCKET handler, you must be using a custom server.
|
|
15
|
-
* @returns The HTTP server.
|
|
16
|
-
* @throws If attempting to access the HTTP server outside of the main process.
|
|
27
|
+
* Extract the parameters from a route pattern.
|
|
17
28
|
*/
|
|
18
|
-
|
|
29
|
+
type RouteParams<Pattern extends string> = Pattern extends `${infer Before}[[...${infer Param}]]${infer After}` ? RouteParams<Before> & {
|
|
30
|
+
[K in Param]?: string[];
|
|
31
|
+
} & RouteParams<After> : Pattern extends `${infer Before}[...${infer Param}]${infer After}` ? RouteParams<Before> & {
|
|
32
|
+
[K in Param]: string[];
|
|
33
|
+
} & RouteParams<After> : Pattern extends `${infer Before}[${infer Param}]${infer After}` ? RouteParams<Before> & {
|
|
34
|
+
[K in Param]: string;
|
|
35
|
+
} & RouteParams<After> : {};
|
|
19
36
|
/**
|
|
20
|
-
*
|
|
21
|
-
* @param wsServer The WebSocket server.
|
|
37
|
+
* Route context object containing the parameters.
|
|
22
38
|
*/
|
|
23
|
-
|
|
39
|
+
type RouteContext<Path extends string> = {
|
|
40
|
+
params: Record<string, string | string[] | undefined> & RouteParams<Path> & RouteParams<Path>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
declare const getHttpServer: () => http.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | undefined;
|
|
44
|
+
declare const setHttpServer: (value: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>) => boolean;
|
|
45
|
+
declare const getWebSocketServer: () => ws.WebSocketServer | undefined;
|
|
46
|
+
declare const setWebSocketServer: (value: ws.WebSocketServer) => boolean;
|
|
47
|
+
|
|
24
48
|
/**
|
|
25
|
-
*
|
|
26
|
-
* @
|
|
27
|
-
* @returns The WebSocket server.
|
|
28
|
-
* @throws If attempting to access the WebSocket server outside of the main process.
|
|
49
|
+
* Attach the WebSocket server to the HTTP server.
|
|
50
|
+
* @param nextServer Next.js Node server instance
|
|
29
51
|
*/
|
|
30
|
-
declare function
|
|
52
|
+
declare function setupWebSocketServer(nextServer: NextNodeServer): void;
|
|
31
53
|
|
|
32
|
-
export { getHttpServer, getWebSocketServer, setHttpServer, setWebSocketServer, setupWebSocketServer };
|
|
54
|
+
export { type RouteContext, type SocketHandler, type UpgradeHandler, getHttpServer, getWebSocketServer, setHttpServer, setWebSocketServer, setupWebSocketServer };
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-ws",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "2.1.0",
|
|
5
4
|
"description": "Add support for WebSockets in the Next.js app directory",
|
|
6
5
|
"license": "MIT",
|
|
7
6
|
"keywords": [
|
|
@@ -28,43 +27,49 @@
|
|
|
28
27
|
"exports": {
|
|
29
28
|
"./client": {
|
|
30
29
|
"types": "./dist/client/index.d.ts",
|
|
31
|
-
"
|
|
32
|
-
"require": "./dist/client/index.cjs"
|
|
30
|
+
"default": "./dist/client/index.cjs"
|
|
33
31
|
},
|
|
34
32
|
"./server": {
|
|
35
33
|
"types": "./dist/server/index.d.ts",
|
|
36
|
-
"
|
|
37
|
-
"require": "./dist/server/index.cjs"
|
|
34
|
+
"default": "./dist/server/index.cjs"
|
|
38
35
|
},
|
|
39
36
|
"./package.json": "./package.json"
|
|
40
37
|
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"jscodeshift": "^17.3.0",
|
|
40
|
+
"minimist": "^1.2.8",
|
|
41
|
+
"semver": "^7.7.2"
|
|
42
|
+
},
|
|
41
43
|
"peerDependencies": {
|
|
42
|
-
"next": ">=13.
|
|
44
|
+
"next": ">=13.5.1",
|
|
43
45
|
"react": "*",
|
|
44
46
|
"ws": "*"
|
|
45
47
|
},
|
|
46
48
|
"devDependencies": {
|
|
47
|
-
"@biomejs/biome": "^
|
|
48
|
-
"@changesets/changelog-git": "^0.2.
|
|
49
|
+
"@biomejs/biome": "^2.2.0",
|
|
50
|
+
"@changesets/changelog-git": "^0.2.1",
|
|
49
51
|
"@changesets/cli": "^2.27.12",
|
|
50
|
-
"@
|
|
52
|
+
"@favware/npm-deprecate": "^2.0.0",
|
|
53
|
+
"@playwright/test": "^1.55.0",
|
|
54
|
+
"@types/jscodeshift": "^17.3.0",
|
|
51
55
|
"@types/minimist": "^1.2.5",
|
|
52
|
-
"@types/node": "^
|
|
53
|
-
"@types/react": "
|
|
54
|
-
"@types/semver": "^7.
|
|
55
|
-
"@types/ws": "^8.
|
|
56
|
-
"chalk": "^5.
|
|
56
|
+
"@types/node": "^24.3.0",
|
|
57
|
+
"@types/react": "19.1.10",
|
|
58
|
+
"@types/semver": "^7.7.0",
|
|
59
|
+
"@types/ws": "^8.18.1",
|
|
60
|
+
"chalk": "^5.6.0",
|
|
57
61
|
"husky": "^9.1.7",
|
|
58
|
-
"
|
|
62
|
+
"next": "15.5.0",
|
|
59
63
|
"pinst": "^3.0.0",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
64
|
+
"react": "19.1.1",
|
|
65
|
+
"react-dom": "19.1.1",
|
|
66
|
+
"tsup": "^8.5.0",
|
|
67
|
+
"typescript": "^5.9.2"
|
|
63
68
|
},
|
|
64
69
|
"scripts": {
|
|
70
|
+
"check": "tsc --noEmit",
|
|
65
71
|
"lint": "biome ci .",
|
|
66
72
|
"format": "biome check . --write",
|
|
67
|
-
"check": "tsc --noEmit",
|
|
68
73
|
"build": "tsup",
|
|
69
74
|
"dev": "tsup --watch",
|
|
70
75
|
"test": "playwright test",
|
package/dist/chunk-3RG5ZIWI.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
__require
|
|
10
|
-
};
|
package/dist/client/index.d.cts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
declare const WebSocketContext: React.Context<WebSocket | null>;
|
|
4
|
-
declare const WebSocketConsumer: React.Consumer<WebSocket | null>;
|
|
5
|
-
/**
|
|
6
|
-
* Provides a WebSocket client to its children via context,
|
|
7
|
-
* allowing for easy access to the WebSocket from anywhere in the app.
|
|
8
|
-
* @param props WebSocket parameters and children.
|
|
9
|
-
* @returns JSX Element
|
|
10
|
-
* @deprecated `WebSocketProvider` is deprecated, use your own implementation instead.
|
|
11
|
-
*/
|
|
12
|
-
declare function WebSocketProvider(p: React.PropsWithChildren<{
|
|
13
|
-
/** The URL for the WebSocket to connect to. */
|
|
14
|
-
url: string;
|
|
15
|
-
/** The subprotocols to use. */
|
|
16
|
-
protocols?: string[] | string;
|
|
17
|
-
/** The binary type to use. */
|
|
18
|
-
binaryType?: BinaryType;
|
|
19
|
-
}>): React.JSX.Element;
|
|
20
|
-
/**
|
|
21
|
-
* Access the websocket from anywhere in the app, so long as it's wrapped in a WebSocketProvider.
|
|
22
|
-
* @returns WebSocket client when connected, null when disconnected.
|
|
23
|
-
* @deprecated `useWebSocket` is deprecated, use your own implementation instead.
|
|
24
|
-
*/
|
|
25
|
-
declare function useWebSocket(): WebSocket | null;
|
|
26
|
-
|
|
27
|
-
export { WebSocketConsumer, WebSocketContext, WebSocketProvider, useWebSocket };
|
package/dist/client/index.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import "../chunk-3RG5ZIWI.js";
|
|
2
|
-
|
|
3
|
-
// src/client/context.tsx
|
|
4
|
-
import React, { useRef } from "react";
|
|
5
|
-
import { createContext, useContext, useEffect } from "react";
|
|
6
|
-
var WebSocketContext = createContext(null);
|
|
7
|
-
WebSocketContext.displayName = "WebSocketContext";
|
|
8
|
-
var WebSocketConsumer = WebSocketContext.Consumer;
|
|
9
|
-
function WebSocketProvider(p) {
|
|
10
|
-
const clientRef = useRef(null);
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
if (typeof window === "undefined") return;
|
|
13
|
-
if (clientRef.current) {
|
|
14
|
-
clientRef.current.close();
|
|
15
|
-
clientRef.current = null;
|
|
16
|
-
}
|
|
17
|
-
const client = new WebSocket(p.url, p.protocols);
|
|
18
|
-
if (p.binaryType) client.binaryType = p.binaryType;
|
|
19
|
-
clientRef.current = client;
|
|
20
|
-
return () => {
|
|
21
|
-
client.close();
|
|
22
|
-
clientRef.current = null;
|
|
23
|
-
};
|
|
24
|
-
}, [p.url, p.protocols, p.binaryType]);
|
|
25
|
-
return /* @__PURE__ */ React.createElement(WebSocketContext.Provider, { value: clientRef.current }, p.children);
|
|
26
|
-
}
|
|
27
|
-
function useWebSocket() {
|
|
28
|
-
const context = useContext(WebSocketContext);
|
|
29
|
-
if (context === void 0)
|
|
30
|
-
throw new Error("useWebSocket must be used within a WebSocketProvider");
|
|
31
|
-
return context;
|
|
32
|
-
}
|
|
33
|
-
export {
|
|
34
|
-
WebSocketConsumer,
|
|
35
|
-
WebSocketContext,
|
|
36
|
-
WebSocketProvider,
|
|
37
|
-
useWebSocket
|
|
38
|
-
};
|
package/dist/server/index.d.cts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import NextNodeServer from 'next/dist/server/next-server.js';
|
|
2
|
-
import { Server } from 'node:http';
|
|
3
|
-
import { WebSocketServer } from 'ws';
|
|
4
|
-
|
|
5
|
-
declare function setupWebSocketServer(nextServer: NextNodeServer): void;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Set the HTTP server that the WebSocket server should listen on, must be called before the WebSocket server is created.
|
|
9
|
-
* @param server The HTTP server.
|
|
10
|
-
*/
|
|
11
|
-
declare function setHttpServer(server: Server): void;
|
|
12
|
-
/**
|
|
13
|
-
* Get the HTTP server that the WebSocket server is listening on.
|
|
14
|
-
* @remark If you want to access the HTTP server outside of a SOCKET handler, you must be using a custom server.
|
|
15
|
-
* @returns The HTTP server.
|
|
16
|
-
* @throws If attempting to access the HTTP server outside of the main process.
|
|
17
|
-
*/
|
|
18
|
-
declare function getHttpServer(): Server;
|
|
19
|
-
/**
|
|
20
|
-
* Set the WebSocket server that the WebSocket server should listen on, must be called before the WebSocket server is created.
|
|
21
|
-
* @param wsServer The WebSocket server.
|
|
22
|
-
*/
|
|
23
|
-
declare function setWebSocketServer(wsServer: WebSocketServer): void;
|
|
24
|
-
/**
|
|
25
|
-
* Get the WebSocket server that the WebSocket server is listening on.
|
|
26
|
-
* @remark If you want to access the WebSocket server outside of a SOCKET handler, you must be using a custom server.
|
|
27
|
-
* @returns The WebSocket server.
|
|
28
|
-
* @throws If attempting to access the WebSocket server outside of the main process.
|
|
29
|
-
*/
|
|
30
|
-
declare function getWebSocketServer(): WebSocketServer;
|
|
31
|
-
|
|
32
|
-
export { getHttpServer, getWebSocketServer, setHttpServer, setWebSocketServer, setupWebSocketServer };
|