next-ws 0.2.5-next.b46e532 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  <div align="center">
2
- <h1><strong>Next WS</strong></h1>
3
- <i>Add support for WebSockets in Next.js 13 app directory</i><br>
4
- <code>npm install next-ws</code>
2
+ <h1><strong>Next WS</strong></h1>
3
+ <i>Add support for WebSockets in Next.js 13 app directory</i><br>
4
+ <code>npm install next-ws</code>
5
5
  </div>
6
6
 
7
7
  <div align="center">
8
- <img alt="package version" src="https://img.shields.io/npm/v/next-ws?label=version">
9
- <img alt="total downloads" src="https://img.shields.io/npm/dt/next-ws">
10
- <br>
11
- <a href="https://github.com/apteryxxyz/next-ws"><img alt="next-ws repo stars" src="https://img.shields.io/github/stars/apteryxxyz/next-ws?style=social"></a>
12
- <a href="https://github.com/apteryxxyz"><img alt="apteryxxyz followers" src="https://img.shields.io/github/followers/apteryxxyz?style=social"></a>
13
- <a href="https://discord.gg/vZQbMhwsKY"><img src="https://discordapp.com/api/guilds/829836158007115806/widget.png?style=shield" alt="discord shield"/></a>
8
+ <img alt="package version" src="https://img.shields.io/npm/v/next-ws?label=version">
9
+ <img alt="total downloads" src="https://img.shields.io/npm/dt/next-ws">
10
+ <br>
11
+ <a href="https://github.com/apteryxxyz/next-ws"><img alt="next-ws repo stars" src="https://img.shields.io/github/stars/apteryxxyz/next-ws?style=social"></a>
12
+ <a href="https://github.com/apteryxxyz"><img alt="apteryxxyz followers" src="https://img.shields.io/github/followers/apteryxxyz?style=social"></a>
13
+ <a href="https://discord.gg/vZQbMhwsKY"><img src="https://discordapp.com/api/guilds/829836158007115806/widget.png?style=shield" alt="discord shield"/></a>
14
14
  </div>
15
15
 
16
16
  ---
@@ -33,10 +33,10 @@ This module is inspired by the now outdated `next-plugin-websocket`, if you are
33
33
  - [🏓 Table of Contents](#-table-of-contents)
34
34
  - [📦 Installation](#-installation)
35
35
  - [🚀 Usage](#-usage)
36
- - [🚓 Verify Patch](#-verify-patch)
36
+ - [🚓 Verify Patch](#-verify-patch)
37
37
  - [🌀 Example](#-example)
38
- - [📁 Server](#-server)
39
- - [📁 Client](#-client)
38
+ - [📁 Server](#-server)
39
+ - [📁 Client](#-client)
40
40
 
41
41
  ---
42
42
 
@@ -45,7 +45,7 @@ This module is inspired by the now outdated `next-plugin-websocket`, if you are
45
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
46
 
47
47
  ```sh
48
- npx next-ws-cli patch
48
+ npx next-ws-cli@latest patch
49
49
  ```
50
50
 
51
51
  > If at any point your local Next.js installation is changed or updated you will need to re-run the patch command.
@@ -56,6 +56,16 @@ Once the patch is complete, you will need to install the Next WS package into yo
56
56
  npm install next-ws
57
57
  ```
58
58
 
59
+ ### 🚓 Verify Patch (Optional)
60
+
61
+ It is recommended to add the following code to the top level of your `next.config.js`.
62
+
63
+ 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.
64
+
65
+ ```ts
66
+ require('next-ws/server').verifyPatch();
67
+ ```
68
+
59
69
  ---
60
70
 
61
71
  ## 🚀 Usage
@@ -66,26 +76,16 @@ The `SOCKET` function receives three arguments: the WebSocket client, the HTTP r
66
76
 
67
77
  ```ts
68
78
  export function SOCKET(
69
- client: import('ws').WebSocket,
70
- request: import('http').IncomingMessage,
71
- server: import('ws').WebSocketServer,
79
+ client: import('ws').WebSocket,
80
+ request: import('http').IncomingMessage,
81
+ server: import('ws').WebSocketServer,
72
82
  ) {
73
- // ...
83
+ // ...
74
84
  }
75
85
  ```
76
86
 
77
87
  With this straightforward setup, you can fully leverage the capabilities of Next WS and efficiently handle WebSocket connections within your Next.js application.
78
88
 
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
-
89
89
  ---
90
90
 
91
91
  ## 🌀 Example
@@ -97,19 +97,19 @@ Create an API route anywhere within the app directory, and export a `SOCKET` fun
97
97
  ```ts
98
98
  // app/api/ws/route.ts (can be any route file in the app directory)
99
99
  export function SOCKET(
100
- client: import('ws').WebSocket,
101
- request: import('http').IncomingMessage,
102
- server: import('ws').WebSocketServer,
100
+ client: import('ws').WebSocket,
101
+ request: import('http').IncomingMessage,
102
+ server: import('ws').WebSocketServer,
103
103
  ) {
104
- console.log('A client connected!');
105
-
106
- client.on('message', message => {
107
- client.send(message);
108
- });
109
-
110
- client.on('close', () => {
111
- console.log('A client disconnected!');
112
- });
104
+ console.log('A client connected!');
105
+
106
+ client.on('message', message => {
107
+ client.send(message);
108
+ });
109
+
110
+ client.on('close', () => {
111
+ console.log('A client disconnected!');
112
+ });
113
113
  }
114
114
  ```
115
115
 
@@ -126,27 +126,29 @@ To make it easier to connect to your new WebSocker server, Next WS also provides
126
126
  import { WebSocketProvider } from 'next-ws/client';
127
127
 
128
128
  export default function Layout() {
129
- return <WebSocketProvider
130
- url="ws://localhost:3000/api/ws"
131
- // ... other props
132
- >
133
- {...}
134
- </WebSocketProvider>;
129
+ return <WebSocketProvider
130
+ url="ws://localhost:3000/api/ws"
131
+ // ... other props
132
+ >
133
+ {...}
134
+ </WebSocketProvider>;
135
135
  }
136
136
  ```
137
137
 
138
+ 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.
139
+
138
140
  The following is the props interface for the `WebSocketProvider` component, containing all the available options.
139
141
 
140
142
  ```ts
141
143
  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;
144
+ children: React.ReactNode;
145
+
146
+ /** The URL for the WebSocket to connect to. */
147
+ url: string;
148
+ /** The subprotocols to use. */
149
+ protocols?: string[] | string;
150
+ /** The binary type to use. */
151
+ binaryType?: BinaryType;
150
152
  }
151
153
  ```
152
154
 
@@ -158,38 +160,38 @@ import { useWebSocket } from 'next-ws/client';
158
160
  import { useCallback, useEffect, useState } from 'react';
159
161
 
160
162
  export default function Page() {
161
- const ws = useWebSocket();
162
- // ^? WebSocket on the client, null on the server
163
-
164
- const [value, setValue] = useState('');
165
- const [message, setMessage] = useState<string | null>('');
166
-
167
- const onMessage = useCallback((event: MessageEvent) => {
168
- const text = await event.data.text();
169
- setMessage(text);
170
- }, []);
171
-
172
- useEffect(() => {
173
- ws?.addEventListener('message', onMessage);
174
- return () => ws?.removeEventListener('message', onMessage);
175
- }, [ws]);
176
-
177
- return <>
178
- <input
179
- type="text"
180
- value={value}
181
- onChange={event => setValue(event.target.value)}
182
- />
183
-
184
- <button onClick={() => ws.send(value)}>
185
- Send message to server
186
- </button>
187
-
188
- <p>
189
- {message === null
190
- ? 'Waiting for server to send a message...'
191
- : `Got message: ${message}`}
192
- </p>
193
- </>;
163
+ const ws = useWebSocket();
164
+ // ^? WebSocket on the client, null on the server
165
+
166
+ const [value, setValue] = useState('');
167
+ const [message, setMessage] = useState<string | null>('');
168
+
169
+ const onMessage = useCallback((event: MessageEvent) => {
170
+ const text = await event.data.text();
171
+ setMessage(text);
172
+ }, []);
173
+
174
+ useEffect(() => {
175
+ ws?.addEventListener('message', onMessage);
176
+ return () => ws?.removeEventListener('message', onMessage);
177
+ }, [ws]);
178
+
179
+ return <>
180
+ <input
181
+ type="text"
182
+ value={value}
183
+ onChange={event => setValue(event.target.value)}
184
+ />
185
+
186
+ <button onClick={() => ws.send(value)}>
187
+ Send message to server
188
+ </button>
189
+
190
+ <p>
191
+ {message === null
192
+ ? 'Waiting for server to send a message...'
193
+ : `Got message: ${message}`}
194
+ </p>
195
+ </>;
194
196
  }
195
197
  ```
package/client/context.js CHANGED
@@ -13,15 +13,14 @@ exports.WebSocketContext.displayName = 'WebSocketContext';
13
13
  */
14
14
  function WebSocketProvider({ children, url, protocols, binaryType, }) {
15
15
  const isBrowser = typeof window !== 'undefined';
16
- const setupClient = () => {
16
+ const instance = (0, react_1.useMemo)(() => {
17
17
  if (!isBrowser)
18
18
  return null;
19
19
  const client = new WebSocket(url, protocols);
20
20
  if (binaryType)
21
21
  client.binaryType = binaryType;
22
22
  return client;
23
- };
24
- const instance = (0, react_1.useMemo)(setupClient, [isBrowser, url, protocols]);
23
+ }, [isBrowser, url, protocols]);
25
24
  (0, react_1.useEffect)(() => {
26
25
  return () => instance?.close();
27
26
  }, []);
package/package.json CHANGED
@@ -1,57 +1,59 @@
1
1
  {
2
- "name": "next-ws",
3
- "version": "0.2.5-next.b46e532",
4
- "description": "Add support for WebSockets in Next.js 13 app directory",
5
- "keywords": [
6
- "next",
7
- "websocket",
8
- "ws",
9
- "server",
10
- "client"
11
- ],
12
- "license": "MIT",
13
- "homepage": "https://github.com/apteryxxyz/next-ws#readme",
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/apteryxxyz/next-ws.git",
17
- "directory": "packages/core"
18
- },
19
- "bugs": {
20
- "url": "https://github.com/apteryxxyz/next-ws/issues"
21
- },
22
- "files": [
23
- "index.js",
24
- "index.d.ts",
25
- "client",
26
- "server"
27
- ],
28
- "main": "./index.js",
29
- "types": "./index.d.ts",
30
- "scripts": {
31
- "build": "cp ../../README.md . && tsc",
32
- "clean": "rimraf index.js index.d.ts client server README.md",
33
- "lint": "eslint --ext .ts,.tsx src",
34
- "format": "prettier --write src/**/*.{ts,tsx} && eslint --fix --ext .ts,.tsx src/"
35
- },
36
- "dependencies": {
37
- "ws": "^8.13.0"
38
- },
39
- "peerDependencies": {
40
- "next": ">=13.1.1",
41
- "react": "*"
42
- },
43
- "devDependencies": {
44
- "@types/eslint": "^8",
45
- "@types/prettier": "^2",
46
- "@types/react": "^18",
47
- "@types/react-dom": "^18",
48
- "@types/ws": "^8",
49
- "eslint": "^8.43.0",
50
- "next": "^13.4.6",
51
- "prettier": "^2.8.8",
52
- "react": "^18.2.0",
53
- "react-dom": "^18.2.0",
54
- "rimraf": "^5.0.1",
55
- "typescript": "<5.1.0"
56
- }
57
- }
2
+ "name": "next-ws",
3
+ "version": "0.2.5",
4
+ "description": "Add support for WebSockets in Next.js 13 app directory",
5
+ "keywords": [
6
+ "next",
7
+ "websocket",
8
+ "ws",
9
+ "server",
10
+ "client"
11
+ ],
12
+ "license": "MIT",
13
+ "homepage": "https://github.com/apteryxxyz/next-ws#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/apteryxxyz/next-ws.git",
17
+ "directory": "packages/core"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/apteryxxyz/next-ws/issues"
21
+ },
22
+ "files": [
23
+ "index.js",
24
+ "index.d.ts",
25
+ "client",
26
+ "server"
27
+ ],
28
+ "main": "./index.js",
29
+ "types": "./index.d.ts",
30
+ "scripts": {
31
+ "build": "cp ../../README.md . && tsc",
32
+ "clean": "rimraf index.js index.d.ts client server README.md",
33
+ "lint": "eslint --ext .ts,.tsx src",
34
+ "format": "prettier --write src/**/*.{ts,tsx} && eslint --fix --ext .ts,.tsx src/"
35
+ },
36
+ "peerDependencies": {
37
+ "next": ">=13.1.1",
38
+ "react": "*",
39
+ "ws": "*"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^18",
43
+ "@types/react-dom": "^18",
44
+ "@types/ws": "^8",
45
+ "eslint": "^8.43.0",
46
+ "next": "^13.4.6",
47
+ "prettier": "^2.8.8",
48
+ "react": "^18.2.0",
49
+ "react-dom": "^18.2.0",
50
+ "rimraf": "^5.0.1",
51
+ "typescript": "<5.1.0"
52
+ },
53
+ "eslintConfig": {
54
+ "root": true,
55
+ "extends": [
56
+ "../../.eslintrc.js"
57
+ ]
58
+ }
59
+ }
package/server/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from './utilities/patch';
2
- export type { SocketHandler } from './utilities/next';
2
+ export type { SocketHandler } from './utilities/ws';
3
3
  export * from './setup';
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
@@ -3,18 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  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
- const ws_1 = require("ws");
7
6
  const next_1 = require("./utilities/next");
7
+ const ws_1 = require("./utilities/ws");
8
8
  function setupWebSocketServer(nextServer) {
9
9
  const httpServer = (0, next_1.getHttpServer)(nextServer);
10
- const wsServer = new ws_1.WebSocketServer({ noServer: true });
10
+ const wsServer = (0, ws_1.getWsServer)();
11
11
  Log.ready('[next-ws] websocket server started successfully');
12
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
12
13
  httpServer.on('upgrade', async (request, socket, head) => {
13
- const url = new URL(request.url ?? '', 'http://next-ws');
14
+ const url = new URL(request.url ?? '', 'ws://next');
14
15
  const pathname = url.pathname;
15
16
  if (pathname.startsWith('/_next'))
16
17
  return;
17
- const fsPathname = await (0, next_1.resolvePathname)(nextServer, pathname);
18
+ const fsPathname = (0, next_1.resolvePathname)(nextServer, pathname);
18
19
  if (!fsPathname) {
19
20
  Log.error(`[next-ws] could not find module for page ${pathname}`);
20
21
  return socket.destroy();
@@ -24,13 +25,12 @@ function setupWebSocketServer(nextServer) {
24
25
  Log.error(`[next-ws] could not find module for page ${pathname}`);
25
26
  return socket.destroy();
26
27
  }
27
- const socketHandler = pageModule?.routeModule?.userland?.SOCKET;
28
+ const socketHandler = pageModule?.routeModule?.userload?.SOCKET;
28
29
  if (!socketHandler || typeof socketHandler !== 'function') {
29
30
  Log.error(`[next-ws] ${pathname} does not export a SOCKET handler`);
30
31
  return socket.destroy();
31
32
  }
32
- // prettier-ignore
33
- return wsServer.handleUpgrade(request, socket, head, (client, request) => socketHandler(client, request, wsServer));
33
+ return wsServer.handleUpgrade(request, socket, head, (client, request) => void socketHandler(client, request, wsServer));
34
34
  });
35
35
  }
36
36
  exports.setupWebSocketServer = setupWebSocketServer;
@@ -1,14 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { Server } from 'node:http';
3
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;
4
+ import type { SocketHandler } from './ws';
12
5
  /**
13
6
  * Get the http.Server instance from the NextNodeServer.
14
7
  * @param nextServer The NextNodeServer instance.
@@ -21,11 +14,18 @@ export declare function getHttpServer(nextServer: NextNodeServer): Server<typeof
21
14
  * @param pathname The pathname to resolve.
22
15
  * @returns The resolved page, or null if the page could not be resolved.
23
16
  */
24
- export declare function resolvePathname(nextServer: NextNodeServer, pathname: string): Promise<string | null>;
17
+ export declare function resolvePathname(nextServer: NextNodeServer, pathname: string): string | null;
25
18
  /**
26
19
  * Get the page module for a page.
27
20
  * @param nextServer The NextNodeServer instance.
28
21
  * @param filename The filename of the page.
29
22
  * @returns The page module.
30
23
  */
31
- export declare function getPageModule(nextServer: NextNodeServer, filename: string): Promise<any>;
24
+ export declare function getPageModule(nextServer: NextNodeServer, filename: string): Promise<PageModule>;
25
+ export interface PageModule {
26
+ routeModule?: {
27
+ userload?: {
28
+ SOCKET?: SocketHandler;
29
+ };
30
+ };
31
+ }
@@ -9,11 +9,10 @@ const next_server_1 = tslib_1.__importDefault(require("next/dist/server/next-ser
9
9
  * @param nextServer The NextNodeServer instance.
10
10
  * @returns The http.Server instance.
11
11
  */
12
- // prettier-ignore
13
12
  function getHttpServer(nextServer) {
14
13
  if (!nextServer || !(nextServer instanceof next_server_1.default))
15
14
  throw new Error('Next WS is missing access to the NextNodeServer');
16
- // @ts-expect-error serverOptions is protected
15
+ // @ts-expect-error - serverOptions is protected
17
16
  const httpServer = nextServer.serverOptions?.httpServer;
18
17
  if (!httpServer || !(httpServer instanceof node_http_1.Server))
19
18
  throw new Error('Next WS is missing access to the http.Server');
@@ -26,21 +25,44 @@ exports.getHttpServer = getHttpServer;
26
25
  * @param pathname The pathname to resolve.
27
26
  * @returns The resolved page, or null if the page could not be resolved.
28
27
  */
29
- async function resolvePathname(nextServer, pathname) {
28
+ function resolvePathname(nextServer, pathname) {
30
29
  if (pathname.startsWith('/_next'))
31
30
  return null;
31
+ const pathParts = pathname.split('/');
32
32
  const appRoutes = {
33
- // @ts-expect-error appPathRoutes is protected
33
+ // @ts-expect-error - appPathRoutes is protected
34
34
  ...nextServer.appPathRoutes,
35
- // @ts-expect-error getAppPathRoutes is protected
35
+ // @ts-expect-error - getAppPathRoutes is protected
36
36
  ...nextServer.getAppPathRoutes(),
37
37
  };
38
- // TODO: 'appRoutes[pathname]' is an array of routes, need to investigate in which case that array has more than one item
39
- if (pathname in appRoutes) {
40
- const route = appRoutes[pathname][0];
41
- if (!route?.endsWith('/route'))
42
- return null;
43
- return route;
38
+ for (const [key, [path]] of Object.entries(appRoutes)) {
39
+ const hasDynamic = key.includes('[') && key.includes(']');
40
+ if (hasDynamic) {
41
+ const keyParts = key.split('/');
42
+ if (keyParts.length !== pathParts.length)
43
+ continue;
44
+ for (let i = 0; i < keyParts.length; i++) {
45
+ const keyPart = keyParts[i];
46
+ const pathPart = pathParts[i];
47
+ const isDynamic = keyPart.includes('[') && keyPart.includes(']');
48
+ if (isDynamic)
49
+ keyParts[i] = pathPart;
50
+ if (keyParts[i] !== pathParts[i])
51
+ break;
52
+ if (i === keyParts.length - 1) {
53
+ if (!path?.endsWith('/route'))
54
+ return null;
55
+ return path;
56
+ }
57
+ }
58
+ }
59
+ else {
60
+ if (key !== pathname)
61
+ continue;
62
+ if (!path?.endsWith('/route'))
63
+ return null;
64
+ return path;
65
+ }
44
66
  }
45
67
  return null;
46
68
  }
@@ -52,13 +74,15 @@ exports.resolvePathname = resolvePathname;
52
74
  * @returns The page module.
53
75
  */
54
76
  async function getPageModule(nextServer, filename) {
55
- // @ts-expect-error HotReloader is private
77
+ // @ts-expect-error - hotReloader is private
78
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
56
79
  await nextServer.hotReloader?.ensurePage({
57
80
  page: filename,
58
81
  clientOnly: false,
59
82
  });
60
- // @ts-expect-error getPagePath is protected
83
+ // @ts-expect-error - getPagePath is protected
61
84
  const builtPagePath = nextServer.getPagePath(filename);
85
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
62
86
  return require(builtPagePath);
63
87
  }
64
88
  exports.getPageModule = getPageModule;
@@ -24,12 +24,13 @@ exports.getPatch = getPatch;
24
24
  * Verify that the Next WS patch has been applied to Next.js.
25
25
  * @returns The patch version and Next.js version if the patch has been applied, otherwise null.
26
26
  */
27
- // prettier-ignore
28
27
  function verifyPatch() {
29
28
  const patch = getPatch();
30
29
  if (!patch)
31
30
  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];
31
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
32
+ const packageJson = require('next/package.json');
33
+ const version = packageJson.version.split('-')[0];
33
34
  if (patch.version !== version)
34
35
  throw new Error(`Next.js version mismatch, expected ${patch.version} but found ${version}, try running \`npx next-ws-cli patch\``);
35
36
  }
@@ -4,6 +4,7 @@ exports.findWorkspaceRoot = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const fs = tslib_1.__importStar(require("node:fs"));
6
6
  const path = tslib_1.__importStar(require("node:path"));
7
+ const cache = new Map();
7
8
  function findWorkspaceRoot(initalPath = process.cwd()) {
8
9
  let currentPath = initalPath;
9
10
  let lastPossiblePath = currentPath;
@@ -11,10 +12,10 @@ function findWorkspaceRoot(initalPath = process.cwd()) {
11
12
  const isCurrentPathRoot = () => {
12
13
  const files = fs.readdirSync(currentPath);
13
14
  const lockFiles = ['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json'];
14
- const hasLockFile = files.some(file => lockFiles.includes(file));
15
+ const hasLockFile = files.some((file) => lockFiles.includes(file));
15
16
  if (hasLockFile)
16
17
  return 'true';
17
- const packageJson = files.find(file => file === 'package.json');
18
+ const packageJson = files.find((file) => file === 'package.json');
18
19
  if (packageJson) {
19
20
  const packageContent = fs.readFileSync(path.resolve(currentPath, packageJson), 'utf8');
20
21
  const packageObject = JSON.parse(packageContent);
@@ -24,7 +25,10 @@ function findWorkspaceRoot(initalPath = process.cwd()) {
24
25
  }
25
26
  return 'false';
26
27
  };
27
- while (true) {
28
+ const shouldContinue = true;
29
+ while (shouldContinue) {
30
+ if (cache.has(currentPath))
31
+ return cache.get(currentPath);
28
32
  isRoot = isCurrentPathRoot();
29
33
  const nextPath = path.resolve(currentPath, '..');
30
34
  if (isRoot === 'true' || nextPath === currentPath)
@@ -33,6 +37,8 @@ function findWorkspaceRoot(initalPath = process.cwd()) {
33
37
  lastPossiblePath = currentPath;
34
38
  currentPath = nextPath;
35
39
  }
36
- return isRoot === 'true' ? currentPath : lastPossiblePath;
40
+ const finalPath = isRoot === 'true' ? currentPath : lastPossiblePath;
41
+ cache.set(initalPath, finalPath);
42
+ return finalPath;
37
43
  }
38
44
  exports.findWorkspaceRoot = findWorkspaceRoot;
@@ -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;