Skip to content

Commit 30be438

Browse files
authored
Remove environment checks from persistent variables (#115)
1 parent 806c873 commit 30be438

File tree

4 files changed

+37
-90
lines changed

4 files changed

+37
-90
lines changed

.changeset/pretty-candles-knock.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"next-ws": minor
3+
---
4+
5+
Remove environment checks for getting persistent variables

examples/custom-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"scripts": {
55
"build": "next build",
6-
"start": "NODE_ENV=production tsx server.ts",
6+
"start": "NODE_ENV=production tsx --require=\"./global.js\" server.ts",
77
"dev": "tsx --require=\"./global.js\" server.ts",
88
"prepare": "next-ws patch"
99
},

src/server/persistent.ts

Lines changed: 30 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,30 @@
1-
import * as logger from 'next/dist/build/output/log.js';
2-
3-
function getEnvironmentMeta() {
4-
const isCustomServer = !process.title.startsWith('next-');
5-
const isMainProcess = process.env.NEXT_WS_MAIN_PROCESS === '1';
6-
const isDevelopment = process.env.NODE_ENV === 'development';
7-
return { isCustomServer, isMainProcess, isDevelopment };
8-
}
9-
10-
function mainProcessOnly(callerName: string) {
11-
if (process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK === '1') return;
12-
13-
const meta = getEnvironmentMeta();
14-
if (!meta.isCustomServer && !meta.isMainProcess) {
15-
throw new Error(
16-
`[next-ws] Attempt to call '${callerName}' outside the main process.
17-
You may be attempting to interact with the WebSocket server outside of an UPGRADE 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.
18-
You can resolve this by using a custom server.`,
19-
);
20-
} else if (!meta.isCustomServer) {
21-
logger.warnOnce(
22-
`[next-ws] The function '${callerName}' was called without a custom server.
23-
This could lead to unintended behaviour, especially if you're attempting to interact with the WebSocket server outside of an UPGRADE handler.
24-
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.
25-
You can resolve this by using a custom server.`,
26-
);
27-
}
28-
}
29-
30-
//
31-
32-
import type { Server as HttpServer } from 'node:http';
33-
export const kHttpServer = Symbol.for('kHttpServer');
34-
35-
export function getHttpServer() {
36-
mainProcessOnly('getHttpServer');
37-
return Reflect.get(globalThis, kHttpServer) as HttpServer | undefined;
38-
}
39-
40-
export function setHttpServer<T extends HttpServer | undefined>(server: T) {
41-
mainProcessOnly('setHttpServer');
42-
Reflect.set(globalThis, kHttpServer, server);
43-
return getHttpServer() as T;
44-
}
45-
46-
export function useHttpServer<T extends HttpServer | undefined>(
47-
server: () => T,
48-
): T {
49-
mainProcessOnly('useHttpServer');
50-
const existing = getHttpServer();
51-
if (existing) return existing as T;
52-
return setHttpServer(server());
53-
}
54-
55-
//
56-
57-
import type { WebSocketServer } from 'ws';
58-
export const kWebSocketServer = Symbol.for('kWebSocketServer');
59-
60-
export function getWebSocketServer() {
61-
mainProcessOnly('getWebSocketServer');
62-
return Reflect.get(globalThis, kWebSocketServer) as
63-
| WebSocketServer
64-
| undefined;
65-
}
66-
67-
export function setWebSocketServer<T extends WebSocketServer | undefined>(
68-
server: T,
69-
) {
70-
mainProcessOnly('setWebSocketServer');
71-
Reflect.set(globalThis, kWebSocketServer, server);
72-
return getWebSocketServer() as T;
73-
}
74-
75-
export function useWebSocketServer<T extends WebSocketServer | undefined>(
76-
server: () => T,
77-
): T {
78-
mainProcessOnly('useWebSocketServer');
79-
const existing = getWebSocketServer();
80-
if (existing) return existing as T;
81-
return setWebSocketServer(server()) as T;
82-
}
1+
function useGlobal<T>(key: PropertyKey) {
2+
return [
3+
function get() {
4+
return Reflect.get(globalThis, key) as T | undefined;
5+
},
6+
function set(value: T) {
7+
return Reflect.set(globalThis, key, value);
8+
},
9+
function use(getter: () => T) {
10+
const existing = Reflect.get(globalThis, key);
11+
if (existing) return existing as T;
12+
Reflect.set(globalThis, key, getter());
13+
return Reflect.get(globalThis, key) as T;
14+
},
15+
] as const;
16+
}
17+
18+
// ===== HTTP Server ===== //
19+
20+
export const [getHttpServer, setHttpServer, useHttpServer] = //
21+
useGlobal<import('node:http').Server>(
22+
Symbol.for('next-ws.http-server'), //
23+
);
24+
25+
// ===== WebSocket Server ===== //
26+
27+
export const [getWebSocketServer, setWebSocketServer, useWebSocketServer] =
28+
useGlobal<import('ws').WebSocketServer>(
29+
Symbol.for('next-ws.websocket-server'),
30+
);

src/server/setup.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,18 @@ import { toNextRequest } from './helpers/request.js';
77
import { useHttpServer, useWebSocketServer } from './persistent.js';
88

99
export function setupWebSocketServer(nextServer: NextNodeServer) {
10-
process.env.NEXT_WS_MAIN_PROCESS = String(1);
11-
12-
process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(1);
1310
const httpServer = //
1411
// @ts-expect-error - serverOptions is protected
1512
useHttpServer(() => nextServer.serverOptions?.httpServer);
1613
if (!httpServer)
1714
return logger.error('[next-ws] was not able to find the HTTP server');
1815
const wsServer = //
1916
useWebSocketServer(() => new WebSocketServer({ noServer: true }));
20-
if (!wsServer)
21-
return logger.error('[next-ws] was not able to find the WebSocket server');
22-
process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(0);
2317

2418
logger.ready('[next-ws] has started the WebSocket server');
2519

2620
// Prevent double-attaching
27-
const kInstalled = Symbol.for('kInstalled');
21+
const kInstalled = Symbol.for('next-ws.http-server.attached');
2822
if (Reflect.has(httpServer, kInstalled)) return;
2923
Reflect.set(httpServer, kInstalled, true);
3024

0 commit comments

Comments
 (0)