next-ws 1.2.0 → 2.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.
@@ -1,139 +1,181 @@
1
- import { __name, __require } from '../chunk-WO25ABG2.js';
2
- import * as logger3 from 'next/dist/build/output/log';
3
- import { WebSocketServer } from 'ws';
1
+ import {
2
+ __require
3
+ } from "../chunk-3RG5ZIWI.js";
4
4
 
5
+ // src/server/setup.ts
6
+ import * as logger3 from "next/dist/build/output/log.js";
7
+ import { WebSocketServer } from "ws";
8
+
9
+ // src/server/helpers/persistent.ts
10
+ import * as logger from "next/dist/build/output/log.js";
5
11
  function getEnvironmentMeta() {
6
- let isCustomServer = !process.title.startsWith("next-"), isMainProcess = process.env.NEXT_WS_MAIN_PROCESS === "1", isDevelopment = process.env.NODE_ENV === "development";
12
+ const isCustomServer = !process.title.startsWith("next-");
13
+ const isMainProcess = process.env.NEXT_WS_MAIN_PROCESS === "1";
14
+ const isDevelopment = process.env.NODE_ENV === "development";
7
15
  return { isCustomServer, isMainProcess, isDevelopment };
8
16
  }
9
- __name(getEnvironmentMeta, "getEnvironmentMeta");
10
17
  function mainProcessOnly(fnName) {
11
18
  if (process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK === "1") return;
12
- let meta = getEnvironmentMeta();
13
- if (meta.isMainProcess)
14
- meta.isCustomServer || logger3.warnOnce(
19
+ const meta = getEnvironmentMeta();
20
+ if (!meta.isMainProcess) {
21
+ throw new Error(
22
+ `[next-ws] Attempt to invoke '${fnName}' outside the main process.
23
+ 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.
24
+ You can resolve this by using a custom server.`
25
+ );
26
+ } else if (!meta.isCustomServer) {
27
+ logger.warnOnce(
15
28
  `[next-ws] Caution: The function '${fnName}' was invoked without a custom server.
16
29
  This could lead to unintended behaviour, especially if you're attempting to interact with the WebSocket server outside of a SOCKET handler.
17
30
  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.
18
31
  You can resolve this by using a custom server.`
19
32
  );
20
- else throw new Error(
21
- `[next-ws] Attempt to invoke '${fnName}' outside the main process.
22
- 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.
23
- You can resolve this by using a custom server.`
24
- );
33
+ }
25
34
  }
26
- __name(mainProcessOnly, "mainProcessOnly");
27
35
  var NextWsHttpServer = Symbol.for("NextWs_HttpServer");
28
36
  function setHttpServer(server) {
29
37
  Reflect.set(globalThis, NextWsHttpServer, server);
30
38
  }
31
- __name(setHttpServer, "setHttpServer");
32
39
  function getHttpServer() {
33
- return mainProcessOnly("getHttpServer"), Reflect.get(globalThis, NextWsHttpServer);
40
+ mainProcessOnly("getHttpServer");
41
+ return Reflect.get(globalThis, NextWsHttpServer);
34
42
  }
35
- __name(getHttpServer, "getHttpServer");
36
43
  function useHttpServer(server) {
37
- let existing = getHttpServer();
38
- return existing || (server && setHttpServer(server), server);
44
+ const existing = getHttpServer();
45
+ if (existing) return existing;
46
+ if (server) setHttpServer(server);
47
+ return server;
39
48
  }
40
- __name(useHttpServer, "useHttpServer");
41
49
  var NextWsWebSocketServer = Symbol.for("NextWs_WebSocketServer");
42
50
  function setWebSocketServer(wsServer) {
43
51
  Reflect.set(globalThis, NextWsWebSocketServer, wsServer);
44
52
  }
45
- __name(setWebSocketServer, "setWebSocketServer");
46
53
  function getWebSocketServer() {
47
- return mainProcessOnly("getWebSocketServer"), Reflect.get(globalThis, NextWsWebSocketServer);
54
+ mainProcessOnly("getWebSocketServer");
55
+ return Reflect.get(globalThis, NextWsWebSocketServer);
48
56
  }
49
- __name(getWebSocketServer, "getWebSocketServer");
50
57
  function useWebSocketServer(wsServer) {
51
- let existing = getWebSocketServer();
52
- return existing || (wsServer && setWebSocketServer(wsServer), wsServer);
58
+ const existing = getWebSocketServer();
59
+ if (existing) return existing;
60
+ if (wsServer) setWebSocketServer(wsServer);
61
+ return wsServer;
53
62
  }
54
- __name(useWebSocketServer, "useWebSocketServer");
63
+
64
+ // src/server/helpers/route.ts
65
+ import { pathToFileURL } from "node:url";
66
+ import * as logger2 from "next/dist/build/output/log.js";
55
67
  function createRouteRegex(routePattern) {
56
- let paramRegex = routePattern.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&").replace(/\\\[([a-zA-Z0-9_]+)\\\]/g, "(?<$1>[^/]+)").replace(/\\\[(?:\\\.){3}([a-zA-Z0-9_]+)\\\]/g, "(?<rest_$1>.+)");
68
+ const escapedPattern = routePattern.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
69
+ const paramRegex = escapedPattern.replace(/\\\[([a-zA-Z0-9_]+)\\\]/g, "(?<$1>[^/]+)").replace(/\\\[(?:\\\.){3}([a-zA-Z0-9_]+)\\\]/g, "(?<rest_$1>.+)");
57
70
  return new RegExp(`^${paramRegex}$`);
58
71
  }
59
- __name(createRouteRegex, "createRouteRegex");
60
72
  function getRouteParams(routePattern, routePath) {
61
- let routeRegex = createRouteRegex(routePattern), match = routePath.match(routeRegex);
73
+ const routeRegex = createRouteRegex(routePattern);
74
+ const match = routePath.match(routeRegex);
62
75
  if (!match) return null;
63
76
  if (!match.groups) return {};
64
- let params = {};
65
- for (let [k, v] of Object.entries(match.groups))
66
- k.startsWith("rest_") && (k = k.slice(5), v = v.split("/")), Reflect.set(params, k, v);
77
+ const params = {};
78
+ for (let [k, v] of Object.entries(match.groups)) {
79
+ if (k.startsWith("rest_")) {
80
+ k = k.slice(5);
81
+ v = v.split("/");
82
+ }
83
+ Reflect.set(params, k, v);
84
+ }
67
85
  return params;
68
86
  }
69
- __name(getRouteParams, "getRouteParams");
70
87
  function resolvePathToRoute(nextServer, requestPath) {
71
- let routes = {
88
+ const routes = {
72
89
  // @ts-expect-error - appPathRoutes is protected
73
90
  ...nextServer.appPathRoutes,
74
91
  // @ts-expect-error - getAppPathRoutes is protected
75
92
  ...nextServer.getAppPathRoutes()
76
93
  };
77
- for (let [routePath, [filePath]] of Object.entries(routes)) {
78
- let routeParams = getRouteParams(routePath, requestPath);
94
+ for (const [routePath, [filePath]] of Object.entries(routes)) {
95
+ const routeParams = getRouteParams(routePath, requestPath);
79
96
  if (routeParams) return { filePath, routeParams };
80
97
  }
81
98
  return null;
82
99
  }
83
- __name(resolvePathToRoute, "resolvePathToRoute");
84
100
  async function importRouteModule(nextServer, filePath) {
85
101
  try {
86
- "hotReloader" in nextServer ? await nextServer.hotReloader?.ensurePage({
87
- page: filePath,
88
- clientOnly: !1
89
- }) : "ensurePage" in nextServer ? await nextServer.ensurePage({ page: filePath, clientOnly: !1 }) : logger3.warnOnce(
90
- "[next-ws] unable to ensure page, you may need to open the route in your browser first so Next.js compiles it"
91
- );
102
+ if ("hotReloader" in nextServer) {
103
+ await nextServer.hotReloader?.ensurePage({
104
+ page: filePath,
105
+ clientOnly: false
106
+ });
107
+ } else if ("ensurePage" in nextServer) {
108
+ await nextServer.ensurePage({ page: filePath, clientOnly: false });
109
+ } else {
110
+ logger2.warnOnce(
111
+ "[next-ws] unable to ensure page, you may need to open the route in your browser first so Next.js compiles it"
112
+ );
113
+ }
92
114
  } catch {
93
115
  }
94
- let buildPagePath = nextServer.getPagePath(filePath);
95
- return __require(buildPagePath);
116
+ const buildPagePath = nextServer.getPagePath(filePath);
117
+ return importModule(buildPagePath);
118
+ }
119
+ async function importModule(modulePath) {
120
+ const moduleUrl = pathToFileURL(modulePath).toString();
121
+ try {
122
+ return await import(moduleUrl).then((m) => m.default);
123
+ } catch (requireError) {
124
+ try {
125
+ return __require(modulePath);
126
+ } catch (requireError2) {
127
+ console.error(`Both import and require failed for ${modulePath}`);
128
+ throw requireError2;
129
+ }
130
+ }
131
+ }
132
+ function getSocketHandler(routeModule) {
133
+ return routeModule?.routeModule?.userland?.SOCKET ?? routeModule?.handlers?.SOCKET;
96
134
  }
97
- __name(importRouteModule, "importRouteModule");
98
135
 
99
136
  // src/server/setup.ts
100
137
  function setupWebSocketServer(nextServer) {
101
- process.env.NEXT_WS_MAIN_PROCESS = String(1), process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(1);
102
- let httpServer = useHttpServer(nextServer.serverOptions?.httpServer), wsServer = useWebSocketServer(new WebSocketServer({ noServer: !0 }));
103
- if (delete process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK, !httpServer)
138
+ process.env.NEXT_WS_MAIN_PROCESS = String(1);
139
+ process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(1);
140
+ const httpServer = useHttpServer(nextServer.serverOptions?.httpServer);
141
+ const wsServer = useWebSocketServer(new WebSocketServer({ noServer: true }));
142
+ process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(0);
143
+ if (!httpServer)
104
144
  return logger3.error("[next-ws] was not able to find the HTTP server");
105
145
  if (!wsServer)
106
146
  return logger3.error("[next-ws] was not able to find the WebSocket server");
107
- logger3.ready("[next-ws] has started the WebSocket server"), httpServer.on("upgrade", async (request, socket, head) => {
108
- let pathname = new URL(request.url ?? "", "ws://next").pathname;
147
+ logger3.ready("[next-ws] has started the WebSocket server");
148
+ httpServer.on("upgrade", async (request, socket, head) => {
149
+ const url = new URL(request.url ?? "", "ws://next");
150
+ const pathname = url.pathname;
109
151
  if (pathname.startsWith("/_next")) return;
110
- let routeInfo = resolvePathToRoute(nextServer, pathname);
111
- if (!routeInfo)
112
- return logger3.error(`[next-ws] could not find module for page ${pathname}`), socket.destroy();
113
- let routeModule = await importRouteModule(nextServer, routeInfo.filePath);
114
- if (!routeModule)
115
- return logger3.error(`[next-ws] could not find module for page ${pathname}`), socket.destroy();
116
- let socketHandler = routeModule?.routeModule?.userland?.SOCKET;
117
- return !socketHandler || typeof socketHandler != "function" ? (logger3.error(`[next-ws] ${pathname} does not export a SOCKET handler`), socket.destroy()) : wsServer.handleUpgrade(request, socket, head, async (c, r) => {
118
- let routeContext = { params: routeInfo.routeParams }, handleClose = await socketHandler(c, r, wsServer, routeContext);
119
- typeof handleClose == "function" && c.once("close", () => handleClose());
152
+ const routeInfo = resolvePathToRoute(nextServer, pathname);
153
+ if (!routeInfo) {
154
+ logger3.error(`[next-ws] could not find module for page ${pathname}`);
155
+ return socket.destroy();
156
+ }
157
+ const routeModule = await importRouteModule(nextServer, routeInfo.filePath);
158
+ if (!routeModule) {
159
+ logger3.error(`[next-ws] could not find module for page ${pathname}`);
160
+ return socket.destroy();
161
+ }
162
+ const socketHandler = getSocketHandler(routeModule);
163
+ if (!socketHandler || typeof socketHandler !== "function") {
164
+ logger3.error(`[next-ws] ${pathname} does not export a SOCKET handler`);
165
+ return socket.destroy();
166
+ }
167
+ return wsServer.handleUpgrade(request, socket, head, async (c, r) => {
168
+ const routeContext = { params: routeInfo.routeParams };
169
+ const handleClose = await socketHandler(c, r, wsServer, routeContext);
170
+ if (typeof handleClose === "function")
171
+ c.once("close", () => handleClose());
120
172
  });
121
173
  });
122
174
  }
123
- __name(setupWebSocketServer, "setupWebSocketServer");
124
- function hookNextNodeServer() {
125
- setupWebSocketServer(this);
126
- }
127
- __name(hookNextNodeServer, "hookNextNodeServer");
128
-
129
- // src/server/index.ts
130
- function verifyPatch() {
131
- throw new Error(
132
- "The 'verifyPatch' function has been deprecated in favour of the `npx next-ws-cli@latest verify` command."
133
- );
134
- }
135
- __name(verifyPatch, "verifyPatch");
136
-
137
- export { getHttpServer, getWebSocketServer, hookNextNodeServer, setHttpServer, setWebSocketServer, setupWebSocketServer, verifyPatch };
138
- //# sourceMappingURL=out.js.map
139
- //# sourceMappingURL=index.js.map
175
+ export {
176
+ getHttpServer,
177
+ getWebSocketServer,
178
+ setHttpServer,
179
+ setWebSocketServer,
180
+ setupWebSocketServer
181
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-ws",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "Add support for WebSockets in Next.js 13 app directory",
6
6
  "license": "MIT",
@@ -14,26 +14,27 @@
14
14
  "homepage": "https://github.com/apteryxxyz/next-ws#readme",
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "git+https://github.com/apteryxxyz/next-ws.git",
18
- "directory": "packages/core"
17
+ "url": "git+https://github.com/apteryxxyz/next-ws.git"
19
18
  },
20
19
  "bugs": {
21
20
  "url": "https://github.com/apteryxxyz/next-ws/issues"
22
21
  },
22
+ "bin": {
23
+ "next-ws": "./dist/cli.cjs"
24
+ },
23
25
  "files": [
24
26
  "dist"
25
27
  ],
26
28
  "exports": {
27
29
  "./client": {
28
- "import": "./dist/client/index.js",
29
30
  "require": "./dist/client/index.cjs",
30
- "types": "./dist/client/index.d.ts"
31
+ "import": "./dist/client/index.js"
31
32
  },
32
33
  "./server": {
33
- "import": "./dist/server/index.js",
34
34
  "require": "./dist/server/index.cjs",
35
- "types": "./dist/server/index.d.ts"
36
- }
35
+ "import": "./dist/server/index.js"
36
+ },
37
+ "./package.json": "./package.json"
37
38
  },
38
39
  "peerDependencies": {
39
40
  "next": ">=13.1.1",
@@ -41,15 +42,31 @@
41
42
  "ws": "*"
42
43
  },
43
44
  "devDependencies": {
44
- "@types/react": "^18.3.3",
45
- "@types/ws": "^8.5.10",
46
- "next": "^14.2.4",
47
- "react": "^18.3.1",
48
- "ws": "^8.17.1"
45
+ "@biomejs/biome": "^1.9.4",
46
+ "@changesets/changelog-git": "^0.2.0",
47
+ "@changesets/cli": "^2.27.12",
48
+ "@playwright/test": "^1.50.1",
49
+ "@types/node": "^22.13.1",
50
+ "@types/react": "^19.0.8",
51
+ "@types/semver": "^7.5.8",
52
+ "@types/ws": "^8.5.14",
53
+ "chalk": "^5.4.1",
54
+ "commander": "^13.1.0",
55
+ "husky": "^9.1.7",
56
+ "pinst": "^3.0.0",
57
+ "semver": "^7.7.1",
58
+ "tsup": "^8.3.6",
59
+ "typescript": "^5.7.3"
49
60
  },
50
61
  "scripts": {
62
+ "lint": "biome ci .",
63
+ "format": "biome check . --write",
51
64
  "check": "tsc --noEmit",
52
- "build": "cp ../../readme.md . && tsup",
53
- "dev": "pnpm build --watch"
65
+ "build": "tsup",
66
+ "dev": "tsup --watch",
67
+ "test": "playwright test",
68
+ "_postinstall": "biome format package.json --write && pnpm build",
69
+ "change": "changeset",
70
+ "release": "changeset version && biome format package.json --write && pnpm build && changeset publish"
54
71
  }
55
72
  }
@@ -1,14 +0,0 @@
1
- 'use strict';
2
-
3
- var __defProp = Object.defineProperty;
4
- var __name = (target, value) => __defProp(target, "name", { value, configurable: !0 }), __require = /* @__PURE__ */ ((x) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(x, {
5
- get: (a, b) => (typeof require < "u" ? require : a)[b]
6
- }) : x)(function(x) {
7
- if (typeof require < "u") return require.apply(this, arguments);
8
- throw Error('Dynamic require of "' + x + '" is not supported');
9
- });
10
-
11
- exports.__name = __name;
12
- exports.__require = __require;
13
- //# sourceMappingURL=out.js.map
14
- //# sourceMappingURL=chunk-PFW3KWBF.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","sourcesContent":[]}
@@ -1,11 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: !0 }), __require = /* @__PURE__ */ ((x) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(x, {
3
- get: (a, b) => (typeof require < "u" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require < "u") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
-
9
- export { __name, __require };
10
- //# sourceMappingURL=out.js.map
11
- //# sourceMappingURL=chunk-WO25ABG2.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","sourcesContent":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/client/context.tsx"],"names":["client"],"mappings":";;;;;AAGA,OAAO,WAAW;AAClB,SAAS,eAAe,YAAY,WAAW,eAAe;AAEvD,IAAM,mBAAmB,cAAgC,IAAI;AACpE,iBAAiB,cAAc;AACxB,IAAM,oBAAoB,iBAAiB;AAQ3C,SAAS,kBACd,GAQA;AACA,MAAM,SAAS,QAAQ,MAAM;AAC3B,QAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,QAAMA,UAAS,IAAI,UAAU,EAAE,KAAK,EAAE,SAAS;AAC/C,WAAI,EAAE,eAAYA,QAAO,aAAa,EAAE,aACjCA;AAAA,EACT,GAAG,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC;AAErC,mBAAU,MAAM;AACd,QAAI,QAAQ,eAAe,UAAU;AACrC,aAAO,MAAM,OAAO,MAAM;AAAA,EAC5B,GAAG,CAAC,MAAM,CAAC,GAGT,oCAAC,iBAAiB,UAAjB,EAA0B,OAAO,UAC/B,EAAE,QACL;AAEJ;AA3BgB;AAiCT,SAAS,eAAe;AAC7B,MAAM,UAAU,WAAW,gBAAgB;AAC3C,MAAI,YAAY;AACd,UAAM,IAAI,MAAM,sDAAsD;AACxE,SAAO;AACT;AALgB","sourcesContent":["'use client';\n\n// biome-ignore lint/style/useImportType: <explanation>\nimport React from 'react';\nimport { createContext, useContext, useEffect, useMemo } from 'react';\n\nexport const WebSocketContext = createContext<WebSocket | null>(null);\nWebSocketContext.displayName = 'WebSocketContext';\nexport const WebSocketConsumer = WebSocketContext.Consumer;\n\n/**\n * Provides a WebSocket client to its children via context,\n * allowing for easy access to the WebSocket from anywhere in the app.\n * @param props WebSocket parameters and children.\n * @returns JSX Element\n */\nexport function WebSocketProvider(\n p: React.PropsWithChildren<{\n /** The URL for the WebSocket to connect to. */\n url: string;\n /** The subprotocols to use. */\n protocols?: string[] | string;\n /** The binary type to use. */\n binaryType?: BinaryType;\n }>,\n) {\n const client = useMemo(() => {\n if (typeof window === 'undefined') return null;\n const client = new WebSocket(p.url, p.protocols);\n if (p.binaryType) client.binaryType = p.binaryType;\n return client;\n }, [p.url, p.protocols, p.binaryType]);\n\n useEffect(() => {\n if (client?.readyState !== WebSocket.OPEN) return;\n return () => client.close();\n }, [client]);\n\n return (\n <WebSocketContext.Provider value={client}>\n {p.children}\n </WebSocketContext.Provider>\n );\n}\n\n/**\n * Access the websocket from anywhere in the app, so long as it's wrapped in a WebSocketProvider.\n * @returns WebSocket client when connected, null when disconnected.\n */\nexport function useWebSocket() {\n const context = useContext(WebSocketContext);\n if (context === undefined)\n throw new Error('useWebSocket must be used within a WebSocketProvider');\n return context;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/client/context.tsx"],"names":["client"],"mappings":";;;;;AAGA,OAAO,WAAW;AAClB,SAAS,eAAe,YAAY,WAAW,eAAe;AAEvD,IAAM,mBAAmB,cAAgC,IAAI;AACpE,iBAAiB,cAAc;AACxB,IAAM,oBAAoB,iBAAiB;AAQ3C,SAAS,kBACd,GAQA;AACA,MAAM,SAAS,QAAQ,MAAM;AAC3B,QAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,QAAMA,UAAS,IAAI,UAAU,EAAE,KAAK,EAAE,SAAS;AAC/C,WAAI,EAAE,eAAYA,QAAO,aAAa,EAAE,aACjCA;AAAA,EACT,GAAG,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC;AAErC,mBAAU,MAAM;AACd,QAAI,QAAQ,eAAe,UAAU;AACrC,aAAO,MAAM,OAAO,MAAM;AAAA,EAC5B,GAAG,CAAC,MAAM,CAAC,GAGT,oCAAC,iBAAiB,UAAjB,EAA0B,OAAO,UAC/B,EAAE,QACL;AAEJ;AA3BgB;AAiCT,SAAS,eAAe;AAC7B,MAAM,UAAU,WAAW,gBAAgB;AAC3C,MAAI,YAAY;AACd,UAAM,IAAI,MAAM,sDAAsD;AACxE,SAAO;AACT;AALgB","sourcesContent":["'use client';\n\n// biome-ignore lint/style/useImportType: <explanation>\nimport React from 'react';\nimport { createContext, useContext, useEffect, useMemo } from 'react';\n\nexport const WebSocketContext = createContext<WebSocket | null>(null);\nWebSocketContext.displayName = 'WebSocketContext';\nexport const WebSocketConsumer = WebSocketContext.Consumer;\n\n/**\n * Provides a WebSocket client to its children via context,\n * allowing for easy access to the WebSocket from anywhere in the app.\n * @param props WebSocket parameters and children.\n * @returns JSX Element\n */\nexport function WebSocketProvider(\n p: React.PropsWithChildren<{\n /** The URL for the WebSocket to connect to. */\n url: string;\n /** The subprotocols to use. */\n protocols?: string[] | string;\n /** The binary type to use. */\n binaryType?: BinaryType;\n }>,\n) {\n const client = useMemo(() => {\n if (typeof window === 'undefined') return null;\n const client = new WebSocket(p.url, p.protocols);\n if (p.binaryType) client.binaryType = p.binaryType;\n return client;\n }, [p.url, p.protocols, p.binaryType]);\n\n useEffect(() => {\n if (client?.readyState !== WebSocket.OPEN) return;\n return () => client.close();\n }, [client]);\n\n return (\n <WebSocketContext.Provider value={client}>\n {p.children}\n </WebSocketContext.Provider>\n );\n}\n\n/**\n * Access the websocket from anywhere in the app, so long as it's wrapped in a WebSocketProvider.\n * @returns WebSocket client when connected, null when disconnected.\n */\nexport function useWebSocket() {\n const context = useContext(WebSocketContext);\n if (context === undefined)\n throw new Error('useWebSocket must be used within a WebSocketProvider');\n return context;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/server/setup.ts","../../src/server/helpers/persistent.ts","../../src/server/helpers/route.ts","../../src/server/index.ts"],"names":["logger"],"mappings":"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","sourcesContent":["import * as logger from 'next/dist/build/output/log';\nimport type NextNodeServer from 'next/dist/server/next-server';\nimport { WebSocketServer } from 'ws';\nimport { useHttpServer, useWebSocketServer } from './helpers/persistent';\nimport { importRouteModule, resolvePathToRoute } from './helpers/route';\n\nexport function setupWebSocketServer(nextServer: NextNodeServer) {\n process.env.NEXT_WS_MAIN_PROCESS = String(1);\n\n process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(1);\n // @ts-expect-error - serverOptions is protected\n const httpServer = useHttpServer(nextServer.serverOptions?.httpServer);\n const wsServer = useWebSocketServer(new WebSocketServer({ noServer: true }));\n // biome-ignore lint/performance/noDelete: <explanation>\n delete process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK;\n\n if (!httpServer)\n return logger.error('[next-ws] was not able to find the HTTP server');\n if (!wsServer)\n return logger.error('[next-ws] was not able to find the WebSocket server');\n\n logger.ready('[next-ws] has started the WebSocket server');\n\n httpServer.on('upgrade', async (request, socket, head) => {\n const url = new URL(request.url ?? '', 'ws://next');\n const pathname = url.pathname;\n if (pathname.startsWith('/_next')) return;\n\n const routeInfo = resolvePathToRoute(nextServer, pathname);\n if (!routeInfo) {\n logger.error(`[next-ws] could not find module for page ${pathname}`);\n return socket.destroy();\n }\n\n const routeModule = await importRouteModule(nextServer, routeInfo.filePath);\n if (!routeModule) {\n logger.error(`[next-ws] could not find module for page ${pathname}`);\n return socket.destroy();\n }\n\n const socketHandler = routeModule?.routeModule?.userland?.SOCKET;\n if (!socketHandler || typeof socketHandler !== 'function') {\n logger.error(`[next-ws] ${pathname} does not export a SOCKET handler`);\n return socket.destroy();\n }\n\n return wsServer.handleUpgrade(request, socket, head, async (c, r) => {\n const routeContext = { params: routeInfo.routeParams };\n const handleClose = await socketHandler(c, r, wsServer, routeContext);\n if (typeof handleClose === 'function')\n c.once('close', () => handleClose());\n });\n });\n}\n\n// Next WS versions below 0.2.0 used a different method of setup\n// This remains for backwards compatibility, but may be removed in a future version\nexport function hookNextNodeServer(this: NextNodeServer) {\n setupWebSocketServer(this);\n}\n","import * as logger from 'next/dist/build/output/log';\n\n/**\n * Get the environment metadata.\n * @returns The environment metadata.\n */\nfunction getEnvironmentMeta() {\n const isCustomServer = !process.title.startsWith('next-');\n const isMainProcess = process.env.NEXT_WS_MAIN_PROCESS === '1';\n const isDevelopment = process.env.NODE_ENV === 'development';\n return { isCustomServer, isMainProcess, isDevelopment };\n}\n\nfunction mainProcessOnly(fnName: string) {\n if (process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK === '1') return;\n\n const meta = getEnvironmentMeta();\n if (!meta.isMainProcess) {\n throw new Error(\n `[next-ws] Attempt to invoke '${fnName}' outside the main process.\nYou 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.\nYou can resolve this by using a custom server.`,\n );\n } else if (!meta.isCustomServer) {\n logger.warnOnce(\n `[next-ws] Caution: The function '${fnName}' was invoked without a custom server.\nThis could lead to unintended behaviour, especially if you're attempting to interact with the WebSocket server outside of a SOCKET handler.\nPlease 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.\nYou can resolve this by using a custom server.`,\n );\n }\n}\n\n// ========== HTTP Server ==========\n\nimport type { Server as HttpServer } from 'node:http';\nexport const NextWsHttpServer = Symbol.for('NextWs_HttpServer');\n\n/**\n * Set the HTTP server that the WebSocket server should listen on, must be called before the WebSocket server is created.\n * @param server The HTTP server.\n */\nexport function setHttpServer(server: HttpServer) {\n Reflect.set(globalThis, NextWsHttpServer, server);\n}\n\n/**\n * Get the HTTP server that the WebSocket server is listening on.\n * @remark If you want to access the HTTP server outside of a SOCKET handler, you must be using a custom server.\n * @returns The HTTP server.\n * @throws If attempting to access the HTTP server outside of the main process.\n */\nexport function getHttpServer() {\n mainProcessOnly('getHttpServer');\n return Reflect.get(globalThis, NextWsHttpServer) as HttpServer;\n}\n\nexport function useHttpServer(server?: HttpServer) {\n const existing = getHttpServer();\n if (existing) return existing;\n if (server) setHttpServer(server);\n return server;\n}\n\n// ========== WebSocket Server ==========\n\nimport type { WebSocketServer } from 'ws';\nexport const NextWsWebSocketServer = Symbol.for('NextWs_WebSocketServer');\n\n/**\n * Set the WebSocket server that the WebSocket server should listen on, must be called before the WebSocket server is created.\n * @param wsServer The WebSocket server.\n */\nexport function setWebSocketServer(wsServer: WebSocketServer) {\n Reflect.set(globalThis, NextWsWebSocketServer, wsServer);\n}\n\n/**\n * Get the WebSocket server that the WebSocket server is listening on.\n * @remark If you want to access the WebSocket server outside of a SOCKET handler, you must be using a custom server.\n * @returns The WebSocket server.\n * @throws If attempting to access the WebSocket server outside of the main process.\n */\nexport function getWebSocketServer() {\n mainProcessOnly('getWebSocketServer');\n return Reflect.get(globalThis, NextWsWebSocketServer) as WebSocketServer;\n}\n\nexport function useWebSocketServer(wsServer?: WebSocketServer) {\n const existing = getWebSocketServer();\n if (existing) return existing;\n if (wsServer) setWebSocketServer(wsServer);\n return wsServer;\n}\n","import * as logger from 'next/dist/build/output/log';\nimport type NextNodeServer from 'next/dist/server/next-server';\nimport type { SocketHandler } from './socket';\n\nfunction createRouteRegex(routePattern: string) {\n const escapedPattern = routePattern.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n const paramRegex = escapedPattern\n .replace(/\\\\\\[([a-zA-Z0-9_]+)\\\\\\]/g, '(?<$1>[^/]+)') // Match [param]\n .replace(/\\\\\\[(?:\\\\\\.){3}([a-zA-Z0-9_]+)\\\\\\]/g, '(?<rest_$1>.+)'); // Match [...param]\n return new RegExp(`^${paramRegex}$`);\n}\n\nfunction getRouteParams(routePattern: string, routePath: string) {\n const routeRegex = createRouteRegex(routePattern);\n const match = routePath.match(routeRegex);\n if (!match) return null;\n if (!match.groups) return {};\n\n const params: Record<string, string | string[]> = {};\n for (let [k, v] of Object.entries(match.groups)) {\n if (k.startsWith('rest_')) (k = k.slice(5)), (v = v.split('/') as never);\n Reflect.set(params, k, v);\n }\n return params;\n}\n\n/**\n * Resolve a request path to a route file path and route parameters.\n * @param nextServer The NextNodeServer instance.\n * @param requestPath The request path to resolve for.\n * @returns The resolved file path and route parameters, or null if the route could not be resolved.\n */\nexport function resolvePathToRoute(\n nextServer: NextNodeServer,\n requestPath: string,\n) {\n const routes = {\n // @ts-expect-error - appPathRoutes is protected\n ...nextServer.appPathRoutes,\n // @ts-expect-error - getAppPathRoutes is protected\n ...nextServer.getAppPathRoutes(),\n };\n\n for (const [routePath, [filePath]] of Object.entries(routes)) {\n const routeParams = getRouteParams(routePath, requestPath);\n if (routeParams) return { filePath: filePath!, routeParams };\n }\n\n return null;\n}\n\n/**\n * Import the route module for a route.\n * @param nextServer The NextNodeServer instance.\n * @param filePath The file path of the route.\n * @returns\n */\nexport async function importRouteModule(\n nextServer: NextNodeServer,\n filePath: string,\n) {\n try {\n // In Next.js 14, hotReloader was removed and ensurePage was moved to NextNodeServer\n if ('hotReloader' in nextServer) {\n // @ts-expect-error - hotReloader only exists in Next.js 13\n await nextServer.hotReloader?.ensurePage({\n page: filePath,\n clientOnly: false,\n });\n } else if ('ensurePage' in nextServer) {\n // ensurePage throws an error in production, so we need to catch it\n // @ts-expect-error - ensurePage is protected\n await nextServer.ensurePage({ page: filePath, clientOnly: false });\n } else {\n // Future-proofing\n logger.warnOnce(\n '[next-ws] unable to ensure page, you may need to open the route in your browser first so Next.js compiles it',\n );\n }\n } catch {}\n\n // @ts-expect-error - getPageModule is protected\n const buildPagePath = nextServer.getPagePath(filePath);\n return require(buildPagePath) as RouteModule;\n}\n\nexport interface RouteModule {\n routeModule?: {\n userland?: {\n SOCKET?: SocketHandler;\n };\n };\n}\n","export * from './setup';\nexport {\n setHttpServer,\n getHttpServer,\n setWebSocketServer,\n getWebSocketServer,\n} from './helpers/persistent';\n\n/**\n * @deprecated\n */\nexport function verifyPatch() {\n throw new Error(\n \"The 'verifyPatch' function has been deprecated in favour of the `npx next-ws-cli@latest verify` command.\",\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/server/setup.ts","../../src/server/helpers/persistent.ts","../../src/server/helpers/route.ts","../../src/server/index.ts"],"names":["logger"],"mappings":"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","sourcesContent":["import * as logger from 'next/dist/build/output/log';\nimport type NextNodeServer from 'next/dist/server/next-server';\nimport { WebSocketServer } from 'ws';\nimport { useHttpServer, useWebSocketServer } from './helpers/persistent';\nimport { importRouteModule, resolvePathToRoute } from './helpers/route';\n\nexport function setupWebSocketServer(nextServer: NextNodeServer) {\n process.env.NEXT_WS_MAIN_PROCESS = String(1);\n\n process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(1);\n // @ts-expect-error - serverOptions is protected\n const httpServer = useHttpServer(nextServer.serverOptions?.httpServer);\n const wsServer = useWebSocketServer(new WebSocketServer({ noServer: true }));\n // biome-ignore lint/performance/noDelete: <explanation>\n delete process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK;\n\n if (!httpServer)\n return logger.error('[next-ws] was not able to find the HTTP server');\n if (!wsServer)\n return logger.error('[next-ws] was not able to find the WebSocket server');\n\n logger.ready('[next-ws] has started the WebSocket server');\n\n httpServer.on('upgrade', async (request, socket, head) => {\n const url = new URL(request.url ?? '', 'ws://next');\n const pathname = url.pathname;\n if (pathname.startsWith('/_next')) return;\n\n const routeInfo = resolvePathToRoute(nextServer, pathname);\n if (!routeInfo) {\n logger.error(`[next-ws] could not find module for page ${pathname}`);\n return socket.destroy();\n }\n\n const routeModule = await importRouteModule(nextServer, routeInfo.filePath);\n if (!routeModule) {\n logger.error(`[next-ws] could not find module for page ${pathname}`);\n return socket.destroy();\n }\n\n const socketHandler = routeModule?.routeModule?.userland?.SOCKET;\n if (!socketHandler || typeof socketHandler !== 'function') {\n logger.error(`[next-ws] ${pathname} does not export a SOCKET handler`);\n return socket.destroy();\n }\n\n return wsServer.handleUpgrade(request, socket, head, async (c, r) => {\n const routeContext = { params: routeInfo.routeParams };\n const handleClose = await socketHandler(c, r, wsServer, routeContext);\n if (typeof handleClose === 'function')\n c.once('close', () => handleClose());\n });\n });\n}\n\n// Next WS versions below 0.2.0 used a different method of setup\n// This remains for backwards compatibility, but may be removed in a future version\nexport function hookNextNodeServer(this: NextNodeServer) {\n setupWebSocketServer(this);\n}\n","import * as logger from 'next/dist/build/output/log';\n\n/**\n * Get the environment metadata.\n * @returns The environment metadata.\n */\nfunction getEnvironmentMeta() {\n const isCustomServer = !process.title.startsWith('next-');\n const isMainProcess = process.env.NEXT_WS_MAIN_PROCESS === '1';\n const isDevelopment = process.env.NODE_ENV === 'development';\n return { isCustomServer, isMainProcess, isDevelopment };\n}\n\nfunction mainProcessOnly(fnName: string) {\n if (process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK === '1') return;\n\n const meta = getEnvironmentMeta();\n if (!meta.isMainProcess) {\n throw new Error(\n `[next-ws] Attempt to invoke '${fnName}' outside the main process.\nYou 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.\nYou can resolve this by using a custom server.`,\n );\n } else if (!meta.isCustomServer) {\n logger.warnOnce(\n `[next-ws] Caution: The function '${fnName}' was invoked without a custom server.\nThis could lead to unintended behaviour, especially if you're attempting to interact with the WebSocket server outside of a SOCKET handler.\nPlease 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.\nYou can resolve this by using a custom server.`,\n );\n }\n}\n\n// ========== HTTP Server ==========\n\nimport type { Server as HttpServer } from 'node:http';\nexport const NextWsHttpServer = Symbol.for('NextWs_HttpServer');\n\n/**\n * Set the HTTP server that the WebSocket server should listen on, must be called before the WebSocket server is created.\n * @param server The HTTP server.\n */\nexport function setHttpServer(server: HttpServer) {\n Reflect.set(globalThis, NextWsHttpServer, server);\n}\n\n/**\n * Get the HTTP server that the WebSocket server is listening on.\n * @remark If you want to access the HTTP server outside of a SOCKET handler, you must be using a custom server.\n * @returns The HTTP server.\n * @throws If attempting to access the HTTP server outside of the main process.\n */\nexport function getHttpServer() {\n mainProcessOnly('getHttpServer');\n return Reflect.get(globalThis, NextWsHttpServer) as HttpServer;\n}\n\nexport function useHttpServer(server?: HttpServer) {\n const existing = getHttpServer();\n if (existing) return existing;\n if (server) setHttpServer(server);\n return server;\n}\n\n// ========== WebSocket Server ==========\n\nimport type { WebSocketServer } from 'ws';\nexport const NextWsWebSocketServer = Symbol.for('NextWs_WebSocketServer');\n\n/**\n * Set the WebSocket server that the WebSocket server should listen on, must be called before the WebSocket server is created.\n * @param wsServer The WebSocket server.\n */\nexport function setWebSocketServer(wsServer: WebSocketServer) {\n Reflect.set(globalThis, NextWsWebSocketServer, wsServer);\n}\n\n/**\n * Get the WebSocket server that the WebSocket server is listening on.\n * @remark If you want to access the WebSocket server outside of a SOCKET handler, you must be using a custom server.\n * @returns The WebSocket server.\n * @throws If attempting to access the WebSocket server outside of the main process.\n */\nexport function getWebSocketServer() {\n mainProcessOnly('getWebSocketServer');\n return Reflect.get(globalThis, NextWsWebSocketServer) as WebSocketServer;\n}\n\nexport function useWebSocketServer(wsServer?: WebSocketServer) {\n const existing = getWebSocketServer();\n if (existing) return existing;\n if (wsServer) setWebSocketServer(wsServer);\n return wsServer;\n}\n","import * as logger from 'next/dist/build/output/log';\nimport type NextNodeServer from 'next/dist/server/next-server';\nimport type { SocketHandler } from './socket';\n\nfunction createRouteRegex(routePattern: string) {\n const escapedPattern = routePattern.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n const paramRegex = escapedPattern\n .replace(/\\\\\\[([a-zA-Z0-9_]+)\\\\\\]/g, '(?<$1>[^/]+)') // Match [param]\n .replace(/\\\\\\[(?:\\\\\\.){3}([a-zA-Z0-9_]+)\\\\\\]/g, '(?<rest_$1>.+)'); // Match [...param]\n return new RegExp(`^${paramRegex}$`);\n}\n\nfunction getRouteParams(routePattern: string, routePath: string) {\n const routeRegex = createRouteRegex(routePattern);\n const match = routePath.match(routeRegex);\n if (!match) return null;\n if (!match.groups) return {};\n\n const params: Record<string, string | string[]> = {};\n for (let [k, v] of Object.entries(match.groups)) {\n if (k.startsWith('rest_')) (k = k.slice(5)), (v = v.split('/') as never);\n Reflect.set(params, k, v);\n }\n return params;\n}\n\n/**\n * Resolve a request path to a route file path and route parameters.\n * @param nextServer The NextNodeServer instance.\n * @param requestPath The request path to resolve for.\n * @returns The resolved file path and route parameters, or null if the route could not be resolved.\n */\nexport function resolvePathToRoute(\n nextServer: NextNodeServer,\n requestPath: string,\n) {\n const routes = {\n // @ts-expect-error - appPathRoutes is protected\n ...nextServer.appPathRoutes,\n // @ts-expect-error - getAppPathRoutes is protected\n ...nextServer.getAppPathRoutes(),\n };\n\n for (const [routePath, [filePath]] of Object.entries(routes)) {\n const routeParams = getRouteParams(routePath, requestPath);\n if (routeParams) return { filePath: filePath!, routeParams };\n }\n\n return null;\n}\n\n/**\n * Import the route module for a route.\n * @param nextServer The NextNodeServer instance.\n * @param filePath The file path of the route.\n * @returns\n */\nexport async function importRouteModule(\n nextServer: NextNodeServer,\n filePath: string,\n) {\n try {\n // In Next.js 14, hotReloader was removed and ensurePage was moved to NextNodeServer\n if ('hotReloader' in nextServer) {\n // @ts-expect-error - hotReloader only exists in Next.js 13\n await nextServer.hotReloader?.ensurePage({\n page: filePath,\n clientOnly: false,\n });\n } else if ('ensurePage' in nextServer) {\n // ensurePage throws an error in production, so we need to catch it\n // @ts-expect-error - ensurePage is protected\n await nextServer.ensurePage({ page: filePath, clientOnly: false });\n } else {\n // Future-proofing\n logger.warnOnce(\n '[next-ws] unable to ensure page, you may need to open the route in your browser first so Next.js compiles it',\n );\n }\n } catch {}\n\n // @ts-expect-error - getPageModule is protected\n const buildPagePath = nextServer.getPagePath(filePath);\n return require(buildPagePath) as RouteModule;\n}\n\nexport interface RouteModule {\n routeModule?: {\n userland?: {\n SOCKET?: SocketHandler;\n };\n };\n}\n","export * from './setup';\nexport {\n setHttpServer,\n getHttpServer,\n setWebSocketServer,\n getWebSocketServer,\n} from './helpers/persistent';\n\n/**\n * @deprecated\n */\nexport function verifyPatch() {\n throw new Error(\n \"The 'verifyPatch' function has been deprecated in favour of the `npx next-ws-cli@latest verify` command.\",\n );\n}\n"]}