next-ws 0.0.0-beta-20250821061718 → 0.0.0-beta-20250822123253
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/dist/cli.cjs +64 -73
- package/dist/client/index.cjs +3 -4
- package/dist/server/index.cjs +108 -72
- package/dist/server/index.d.ts +6 -26
- package/package.json +14 -14
package/dist/cli.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
var import_minimist = __toESM(require("minimist"));
|
|
28
28
|
|
|
29
29
|
// package.json
|
|
30
|
-
var version = "0.0.0-beta-
|
|
30
|
+
var version = "0.0.0-beta-20250822123253";
|
|
31
31
|
|
|
32
32
|
// src/commands/helpers/define.ts
|
|
33
33
|
function defineCommandGroup(definition) {
|
|
@@ -79,7 +79,39 @@ Options:
|
|
|
79
79
|
`;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// src/patches/helpers/next.ts
|
|
83
|
+
var import_promises = require("fs/promises");
|
|
84
|
+
var import_node_path = require("path");
|
|
85
|
+
function resolveNextWsDirectory() {
|
|
86
|
+
const id = (
|
|
87
|
+
//
|
|
88
|
+
require.resolve("next-ws/package.json", { paths: [process.cwd()] })
|
|
89
|
+
);
|
|
90
|
+
return (0, import_node_path.dirname)(id);
|
|
91
|
+
}
|
|
92
|
+
function resolveNextDirectory() {
|
|
93
|
+
const id = (
|
|
94
|
+
//
|
|
95
|
+
require.resolve("next/package.json", { paths: [process.cwd()] })
|
|
96
|
+
);
|
|
97
|
+
return (0, import_node_path.dirname)(id);
|
|
98
|
+
}
|
|
99
|
+
async function getInstalledNextVersion() {
|
|
100
|
+
const id = (0, import_node_path.join)(resolveNextDirectory(), "package.json");
|
|
101
|
+
const pkg = await (0, import_promises.readFile)(id, "utf8").then(JSON.parse);
|
|
102
|
+
return String(pkg.version.split("-")[0]);
|
|
103
|
+
}
|
|
104
|
+
async function readTrace() {
|
|
105
|
+
const id = (0, import_node_path.join)(resolveNextDirectory(), ".next-ws-trace.json");
|
|
106
|
+
return (0, import_promises.readFile)(id, "utf-8").then(JSON.parse).catch(() => null);
|
|
107
|
+
}
|
|
108
|
+
async function writeTrace(trace) {
|
|
109
|
+
const id = (0, import_node_path.join)(resolveNextDirectory(), ".next-ws-trace.json");
|
|
110
|
+
await (0, import_promises.writeFile)(id, JSON.stringify(trace, null, 2));
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
// src/patches/patch-1.ts
|
|
114
|
+
var import_node_path3 = require("path");
|
|
83
115
|
var import_jscodeshift = __toESM(require("jscodeshift"));
|
|
84
116
|
|
|
85
117
|
// src/patches/helpers/define.ts
|
|
@@ -627,7 +659,7 @@ async function task(promise, ...message) {
|
|
|
627
659
|
let spinnerIndex = 0;
|
|
628
660
|
const spinnerInterval = setInterval(() => {
|
|
629
661
|
import_node_readline.default.cursorTo(process.stdout, 0);
|
|
630
|
-
const spinnerChar = spinnerChars[spinnerIndex++ % spinnerChars.length];
|
|
662
|
+
const spinnerChar = spinnerChars[spinnerIndex++ % spinnerChars.length] ?? " ";
|
|
631
663
|
process.stdout.write(source_default.cyan("[next-ws]", spinnerChar, ...message));
|
|
632
664
|
}, 100);
|
|
633
665
|
return promise.then((value) => {
|
|
@@ -647,40 +679,6 @@ async function task(promise, ...message) {
|
|
|
647
679
|
});
|
|
648
680
|
}
|
|
649
681
|
|
|
650
|
-
// src/patches/helpers/next.ts
|
|
651
|
-
var import_promises = require("fs/promises");
|
|
652
|
-
var import_node_path = require("path");
|
|
653
|
-
function getDistDirname() {
|
|
654
|
-
const resolveOptions = { paths: [process.cwd()] };
|
|
655
|
-
const nextWsPackagePath = (
|
|
656
|
-
//
|
|
657
|
-
require.resolve("next-ws/package.json", resolveOptions)
|
|
658
|
-
);
|
|
659
|
-
const nextWsDirName = (0, import_node_path.dirname)(nextWsPackagePath);
|
|
660
|
-
return `${nextWsDirName}/dist`.replace(/\\/g, "/").replace(/'/g, "\\'");
|
|
661
|
-
}
|
|
662
|
-
function findNextDirectory() {
|
|
663
|
-
const resolveOptions = { paths: [process.cwd()] };
|
|
664
|
-
const nextPackagePath = require.resolve("next/package.json", resolveOptions);
|
|
665
|
-
return (0, import_node_path.dirname)(nextPackagePath);
|
|
666
|
-
}
|
|
667
|
-
async function getNextVersion() {
|
|
668
|
-
const nextDirectory = findNextDirectory();
|
|
669
|
-
const nextPackagePath = (0, import_node_path.join)(nextDirectory, "package.json");
|
|
670
|
-
const nextPackage = await (0, import_promises.readFile)(nextPackagePath, "utf-8").then(JSON.parse);
|
|
671
|
-
return String(nextPackage.version.split("-")[0]);
|
|
672
|
-
}
|
|
673
|
-
async function getTrace() {
|
|
674
|
-
const nextDirectory = findNextDirectory();
|
|
675
|
-
const tracePath = (0, import_node_path.join)(nextDirectory, ".next-ws-trace.json");
|
|
676
|
-
return (0, import_promises.readFile)(tracePath, "utf-8").then(JSON.parse).catch(() => null);
|
|
677
|
-
}
|
|
678
|
-
async function setTrace(trace) {
|
|
679
|
-
const nextDirectory = findNextDirectory();
|
|
680
|
-
const tracePath = (0, import_node_path.join)(nextDirectory, ".next-ws-trace.json");
|
|
681
|
-
await (0, import_promises.writeFile)(tracePath, JSON.stringify(trace, null, 2));
|
|
682
|
-
}
|
|
683
|
-
|
|
684
682
|
// src/patches/helpers/define.ts
|
|
685
683
|
function definePatch(definition) {
|
|
686
684
|
return {
|
|
@@ -707,7 +705,7 @@ function definePatchStep(definition) {
|
|
|
707
705
|
function resolvePath(path) {
|
|
708
706
|
switch (path.split(":")[0]) {
|
|
709
707
|
case "next": {
|
|
710
|
-
const nextDirectory =
|
|
708
|
+
const nextDirectory = resolveNextDirectory();
|
|
711
709
|
const realPath = path.slice(5);
|
|
712
710
|
return (0, import_node_path2.resolve)(nextDirectory, realPath);
|
|
713
711
|
}
|
|
@@ -718,38 +716,27 @@ function resolvePath(path) {
|
|
|
718
716
|
}
|
|
719
717
|
|
|
720
718
|
// src/patches/patch-1.ts
|
|
719
|
+
var CommentLine = import_jscodeshift.default.Comment;
|
|
721
720
|
var patchNextNodeServer = definePatchStep({
|
|
722
721
|
title: "Add WebSocket server setup script to NextNodeServer constructor",
|
|
723
722
|
path: "next:dist/server/next-server.js",
|
|
724
723
|
async transform(code) {
|
|
725
|
-
const
|
|
726
|
-
const
|
|
727
|
-
// ${
|
|
724
|
+
const marker = "@patch attach-websocket-server";
|
|
725
|
+
const snippet = (0, import_jscodeshift.default)(`
|
|
726
|
+
// ${marker}
|
|
728
727
|
let nextWs;
|
|
729
|
-
try { nextWs ??= require('next-ws/
|
|
730
|
-
try { nextWs ??= require('
|
|
728
|
+
try { nextWs ??= require('next-ws/server') } catch {}
|
|
729
|
+
try { nextWs ??= require(require.resolve('next-ws/server', { paths: [process.cwd()] }) )} catch {}
|
|
730
|
+
try { nextWs ??= require('${resolveNextWsDirectory().replaceAll(import_node_path3.sep, "/").replaceAll("'", "\\'")}/dist/server/index.cjs') } catch {}
|
|
731
731
|
nextWs?.setupWebSocketServer(this);
|
|
732
732
|
`);
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
kind: "constructor",
|
|
741
|
-
value: { body: { type: "BlockStatement" } }
|
|
742
|
-
}).forEach(({ node: method }) => {
|
|
743
|
-
const bodyStatements = method.value.body.body;
|
|
744
|
-
const existingPatchPath = (0, import_jscodeshift.default)(bodyStatements).find(import_jscodeshift.default.Comment, {
|
|
745
|
-
value: ` ${PATCH_MARKER}`
|
|
746
|
-
}).paths()[0];
|
|
747
|
-
const existingPatchIndex = bodyStatements.findIndex(
|
|
748
|
-
(s) => s === existingPatchPath?.parent.node
|
|
749
|
-
);
|
|
750
|
-
if (existingPatchIndex > -1)
|
|
751
|
-
bodyStatements[existingPatchIndex] = patchBlock;
|
|
752
|
-
else bodyStatements.push(patchBlock);
|
|
733
|
+
const block = import_jscodeshift.default.blockStatement(snippet.nodes()[0].program.body);
|
|
734
|
+
return (0, import_jscodeshift.default)(code).find(import_jscodeshift.default.ClassDeclaration, { id: { name: "NextNodeServer" } }).find(import_jscodeshift.default.MethodDefinition, { kind: "constructor" }).forEach(({ node }) => {
|
|
735
|
+
const body = node.value.body.body;
|
|
736
|
+
const existing = (0, import_jscodeshift.default)(body).find(CommentLine, { value: ` ${marker}` }).paths()[0];
|
|
737
|
+
const idx = body.indexOf(existing?.parent.node);
|
|
738
|
+
if (existing && idx > -1) body[idx] = block;
|
|
739
|
+
else body.push(block);
|
|
753
740
|
}).toSource();
|
|
754
741
|
}
|
|
755
742
|
});
|
|
@@ -763,7 +750,11 @@ var patchRouterServer = definePatchStep({
|
|
|
763
750
|
object: { type: "Identifier", name: "socket" },
|
|
764
751
|
property: { type: "Identifier", name: "end" }
|
|
765
752
|
}
|
|
766
|
-
}).replaceWith((path) =>
|
|
753
|
+
}).replaceWith((path) => {
|
|
754
|
+
const expr = import_jscodeshift.default.unaryExpression("void", import_jscodeshift.default.literal(0));
|
|
755
|
+
expr.comments = [import_jscodeshift.default.commentLine(` ${(0, import_jscodeshift.default)(path.node).toSource()}`)];
|
|
756
|
+
return expr;
|
|
757
|
+
}).toSource();
|
|
767
758
|
}
|
|
768
759
|
});
|
|
769
760
|
var patch_1_default = definePatch({
|
|
@@ -776,13 +767,13 @@ var patch_1_default = definePatch({
|
|
|
776
767
|
var patches_default = [patch_1_default];
|
|
777
768
|
|
|
778
769
|
// src/commands/helpers/semver.ts
|
|
779
|
-
var import_range = __toESM(require("semver/classes/range"));
|
|
780
|
-
var import_semver = __toESM(require("semver/classes/semver"));
|
|
781
|
-
var import_gt = __toESM(require("semver/functions/gt"));
|
|
782
|
-
var import_min_version = __toESM(require("semver/ranges/min-version"));
|
|
783
|
-
var import_ltr = __toESM(require("semver/ranges/ltr"));
|
|
784
|
-
var import_gtr = __toESM(require("semver/ranges/gtr"));
|
|
770
|
+
var import_range = __toESM(require("semver/classes/range.js"));
|
|
771
|
+
var import_semver = __toESM(require("semver/classes/semver.js"));
|
|
772
|
+
var import_gt = __toESM(require("semver/functions/gt.js"));
|
|
785
773
|
var import_satisfies = __toESM(require("semver/functions/satisfies"));
|
|
774
|
+
var import_gtr = __toESM(require("semver/ranges/gtr"));
|
|
775
|
+
var import_ltr = __toESM(require("semver/ranges/ltr"));
|
|
776
|
+
var import_min_version = __toESM(require("semver/ranges/min-version"));
|
|
786
777
|
function maxVersion(range, loose) {
|
|
787
778
|
range = new import_range.default(range, loose);
|
|
788
779
|
let maximumVersion = null;
|
|
@@ -819,7 +810,7 @@ var patch_default = defineCommand({
|
|
|
819
810
|
const supported = patches_default.map((p) => p.versions).join(" || ");
|
|
820
811
|
const minimum = import_min_version.default(supported)?.version ?? supported;
|
|
821
812
|
const maximum = maxVersion(supported)?.version ?? supported;
|
|
822
|
-
const current = await
|
|
813
|
+
const current = await getInstalledNextVersion();
|
|
823
814
|
if (import_ltr.default(current, minimum)) {
|
|
824
815
|
error(
|
|
825
816
|
`Next.js v${current} is not supported, a minimum of v${minimum} is required`
|
|
@@ -852,7 +843,7 @@ var patch_default = defineCommand({
|
|
|
852
843
|
info(`Patching Next.js v${current} with '${patch.versions}'`);
|
|
853
844
|
await patch.execute();
|
|
854
845
|
info("Saving patch trace file...");
|
|
855
|
-
await
|
|
846
|
+
await writeTrace({ patch: patch.versions, version: current });
|
|
856
847
|
info("All done!");
|
|
857
848
|
}
|
|
858
849
|
});
|
|
@@ -869,7 +860,7 @@ var verify_default = defineCommand({
|
|
|
869
860
|
}
|
|
870
861
|
],
|
|
871
862
|
async action(options) {
|
|
872
|
-
const trace = await
|
|
863
|
+
const trace = await readTrace();
|
|
873
864
|
if (!trace) {
|
|
874
865
|
if (options.ensure) {
|
|
875
866
|
warn("Next.js has not been patched, running the patch command");
|
|
@@ -881,7 +872,7 @@ var verify_default = defineCommand({
|
|
|
881
872
|
process.exit(1);
|
|
882
873
|
}
|
|
883
874
|
}
|
|
884
|
-
const current = await
|
|
875
|
+
const current = await getInstalledNextVersion();
|
|
885
876
|
if (current !== trace.version) {
|
|
886
877
|
error(
|
|
887
878
|
"Next.js has been patched with a different version, you'll need to run the patch command"
|
package/dist/client/index.cjs
CHANGED
|
@@ -39,13 +39,12 @@ module.exports = __toCommonJS(client_exports);
|
|
|
39
39
|
|
|
40
40
|
// src/client/context.tsx
|
|
41
41
|
var import_react = __toESM(require("react"));
|
|
42
|
-
var
|
|
43
|
-
var WebSocketContext = (0, import_react2.createContext)(null);
|
|
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,11 +38,7 @@ __export(server_exports, {
|
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(server_exports);
|
|
40
40
|
|
|
41
|
-
// src/server/
|
|
42
|
-
var logger3 = __toESM(require("next/dist/build/output/log.js"));
|
|
43
|
-
var import_ws = require("ws");
|
|
44
|
-
|
|
45
|
-
// src/server/helpers/persistent.ts
|
|
41
|
+
// src/server/persistent.ts
|
|
46
42
|
var logger = __toESM(require("next/dist/build/output/log.js"));
|
|
47
43
|
function getEnvironmentMeta() {
|
|
48
44
|
const isCustomServer = !process.title.startsWith("next-");
|
|
@@ -50,91 +46,103 @@ function getEnvironmentMeta() {
|
|
|
50
46
|
const isDevelopment = process.env.NODE_ENV === "development";
|
|
51
47
|
return { isCustomServer, isMainProcess, isDevelopment };
|
|
52
48
|
}
|
|
53
|
-
function mainProcessOnly(
|
|
49
|
+
function mainProcessOnly(callerName) {
|
|
54
50
|
if (process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK === "1") return;
|
|
55
51
|
const meta = getEnvironmentMeta();
|
|
56
|
-
if (!meta.isMainProcess) {
|
|
52
|
+
if (!meta.isCustomServer && !meta.isMainProcess) {
|
|
57
53
|
throw new Error(
|
|
58
|
-
`[next-ws] Attempt to
|
|
54
|
+
`[next-ws] Attempt to call '${callerName}' outside the main process.
|
|
59
55
|
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
56
|
You can resolve this by using a custom server.`
|
|
61
57
|
);
|
|
62
58
|
} else if (!meta.isCustomServer) {
|
|
63
59
|
logger.warnOnce(
|
|
64
|
-
`[next-ws]
|
|
60
|
+
`[next-ws] The function '${callerName}' was called without a custom server.
|
|
65
61
|
This could lead to unintended behaviour, especially if you're attempting to interact with the WebSocket server outside of a SOCKET handler.
|
|
66
62
|
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
63
|
You can resolve this by using a custom server.`
|
|
68
64
|
);
|
|
69
65
|
}
|
|
70
66
|
}
|
|
71
|
-
var
|
|
72
|
-
function setHttpServer(server) {
|
|
73
|
-
Reflect.set(globalThis, NextWsHttpServer, server);
|
|
74
|
-
}
|
|
67
|
+
var kHttpServer = Symbol.for("kHttpServer");
|
|
75
68
|
function getHttpServer() {
|
|
76
69
|
mainProcessOnly("getHttpServer");
|
|
77
|
-
return Reflect.get(globalThis,
|
|
70
|
+
return Reflect.get(globalThis, kHttpServer);
|
|
71
|
+
}
|
|
72
|
+
function setHttpServer(server) {
|
|
73
|
+
mainProcessOnly("setHttpServer");
|
|
74
|
+
Reflect.set(globalThis, kHttpServer, server);
|
|
75
|
+
return getHttpServer();
|
|
78
76
|
}
|
|
79
77
|
function useHttpServer(server) {
|
|
78
|
+
mainProcessOnly("useHttpServer");
|
|
80
79
|
const existing = getHttpServer();
|
|
81
80
|
if (existing) return existing;
|
|
82
|
-
|
|
83
|
-
return server;
|
|
84
|
-
}
|
|
85
|
-
var NextWsWebSocketServer = Symbol.for("NextWs_WebSocketServer");
|
|
86
|
-
function setWebSocketServer(wsServer) {
|
|
87
|
-
Reflect.set(globalThis, NextWsWebSocketServer, wsServer);
|
|
81
|
+
return setHttpServer(server());
|
|
88
82
|
}
|
|
83
|
+
var kWebSocketServer = Symbol.for("kWebSocketServer");
|
|
89
84
|
function getWebSocketServer() {
|
|
90
85
|
mainProcessOnly("getWebSocketServer");
|
|
91
|
-
return Reflect.get(globalThis,
|
|
86
|
+
return Reflect.get(globalThis, kWebSocketServer);
|
|
92
87
|
}
|
|
93
|
-
function
|
|
88
|
+
function setWebSocketServer(server) {
|
|
89
|
+
mainProcessOnly("setWebSocketServer");
|
|
90
|
+
Reflect.set(globalThis, kWebSocketServer, server);
|
|
91
|
+
return getWebSocketServer();
|
|
92
|
+
}
|
|
93
|
+
function useWebSocketServer(server) {
|
|
94
|
+
mainProcessOnly("useWebSocketServer");
|
|
94
95
|
const existing = getWebSocketServer();
|
|
95
96
|
if (existing) return existing;
|
|
96
|
-
|
|
97
|
-
return wsServer;
|
|
97
|
+
return setWebSocketServer(server());
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
// src/server/
|
|
101
|
-
var
|
|
102
|
-
|
|
100
|
+
// src/server/setup.ts
|
|
101
|
+
var logger3 = __toESM(require("next/dist/build/output/log.js"));
|
|
102
|
+
var import_ws = require("ws");
|
|
103
|
+
|
|
104
|
+
// src/server/helpers/match.ts
|
|
105
|
+
function compileRoutePattern(routePattern) {
|
|
103
106
|
const escapedPattern = routePattern.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
104
|
-
const paramRegex = escapedPattern.replace(/\\\[([a-
|
|
107
|
+
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>[^/]+)");
|
|
105
108
|
return new RegExp(`^${paramRegex}$`);
|
|
106
109
|
}
|
|
107
|
-
function getRouteParams(routePattern,
|
|
108
|
-
const routeRegex =
|
|
109
|
-
const match =
|
|
110
|
+
function getRouteParams(routePattern, requestPathname) {
|
|
111
|
+
const routeRegex = compileRoutePattern(routePattern);
|
|
112
|
+
const match = requestPathname.replace(/\/+$/, "").match(routeRegex);
|
|
110
113
|
if (!match) return null;
|
|
111
114
|
if (!match.groups) return {};
|
|
112
115
|
const params = {};
|
|
113
116
|
for (let [k, v] of Object.entries(match.groups)) {
|
|
114
|
-
if (k.startsWith("
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
if (k.startsWith("r_")) {
|
|
118
|
+
const optional = k.startsWith("r_o_");
|
|
119
|
+
k = k.slice(optional ? 4 : 2);
|
|
120
|
+
v = v?.split("/");
|
|
117
121
|
}
|
|
118
|
-
Reflect.set(params, k, v);
|
|
122
|
+
if (v) Reflect.set(params, k, v);
|
|
119
123
|
}
|
|
120
124
|
return params;
|
|
121
125
|
}
|
|
122
|
-
function
|
|
123
|
-
const basePath = nextServer.serverOptions
|
|
124
|
-
const
|
|
126
|
+
function findMatchingRoute(nextServer, requestPathname) {
|
|
127
|
+
const basePath = nextServer.serverOptions?.conf.basePath || "";
|
|
128
|
+
const appPathRoutes = {
|
|
125
129
|
// @ts-expect-error - appPathRoutes is protected
|
|
126
130
|
...nextServer.appPathRoutes,
|
|
127
131
|
// @ts-expect-error - getAppPathRoutes is protected
|
|
128
132
|
...nextServer.getAppPathRoutes()
|
|
129
133
|
};
|
|
130
|
-
let
|
|
131
|
-
for (const [routePath, [filePath]] of Object.entries(
|
|
134
|
+
let matchedRoute = void 0;
|
|
135
|
+
for (const [routePath, [filePath]] of Object.entries(appPathRoutes)) {
|
|
136
|
+
if (!routePath || !filePath) continue;
|
|
132
137
|
const realPath = `${basePath}${routePath}`;
|
|
133
|
-
const routeParams = getRouteParams(realPath,
|
|
134
|
-
if (routeParams)
|
|
138
|
+
const routeParams = getRouteParams(realPath, requestPathname);
|
|
139
|
+
if (routeParams) matchedRoute = { filename: filePath, params: routeParams };
|
|
135
140
|
}
|
|
136
|
-
return
|
|
141
|
+
return matchedRoute;
|
|
137
142
|
}
|
|
143
|
+
|
|
144
|
+
// src/server/helpers/module.ts
|
|
145
|
+
var logger2 = __toESM(require("next/dist/build/output/log.js"));
|
|
138
146
|
async function importRouteModule(nextServer, filePath) {
|
|
139
147
|
try {
|
|
140
148
|
if ("hotReloader" in nextServer) {
|
|
@@ -151,52 +159,80 @@ async function importRouteModule(nextServer, filePath) {
|
|
|
151
159
|
}
|
|
152
160
|
} catch {
|
|
153
161
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return
|
|
160
|
-
|
|
162
|
+
try {
|
|
163
|
+
const buildPagePath = nextServer.getPagePath(filePath);
|
|
164
|
+
return require(buildPagePath).routeModule;
|
|
165
|
+
} catch (cause) {
|
|
166
|
+
console.error(cause);
|
|
167
|
+
return void 0;
|
|
168
|
+
}
|
|
161
169
|
}
|
|
162
170
|
|
|
163
171
|
// src/server/setup.ts
|
|
164
172
|
function setupWebSocketServer(nextServer) {
|
|
165
173
|
process.env.NEXT_WS_MAIN_PROCESS = String(1);
|
|
166
174
|
process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(1);
|
|
167
|
-
const httpServer =
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
const httpServer = (
|
|
176
|
+
//
|
|
177
|
+
// @ts-expect-error - serverOptions is protected
|
|
178
|
+
useHttpServer(() => nextServer.serverOptions?.httpServer)
|
|
179
|
+
);
|
|
170
180
|
if (!httpServer)
|
|
171
181
|
return logger3.error("[next-ws] was not able to find the HTTP server");
|
|
182
|
+
const wsServer = (
|
|
183
|
+
//
|
|
184
|
+
useWebSocketServer(() => new import_ws.WebSocketServer({ noServer: true }))
|
|
185
|
+
);
|
|
172
186
|
if (!wsServer)
|
|
173
187
|
return logger3.error("[next-ws] was not able to find the WebSocket server");
|
|
188
|
+
process.env.NEXT_WS_SKIP_ENVIRONMENT_CHECK = String(0);
|
|
174
189
|
logger3.ready("[next-ws] has started the WebSocket server");
|
|
175
|
-
|
|
176
|
-
|
|
190
|
+
const kInstalled = Symbol.for("kInstalled");
|
|
191
|
+
if (Reflect.has(httpServer, kInstalled)) return;
|
|
192
|
+
Reflect.set(httpServer, kInstalled, true);
|
|
193
|
+
httpServer.on("upgrade", async (message, socket, head) => {
|
|
194
|
+
const url = new URL(message.url ?? "", "ws://next");
|
|
177
195
|
const pathname = url.pathname;
|
|
178
196
|
if (pathname.includes("/_next")) return;
|
|
179
|
-
const
|
|
180
|
-
if (!
|
|
181
|
-
logger3.error(`[next-ws] could not find
|
|
182
|
-
return socket.
|
|
197
|
+
const route = findMatchingRoute(nextServer, pathname);
|
|
198
|
+
if (!route) {
|
|
199
|
+
logger3.error(`[next-ws] could not find route for page ${pathname}`);
|
|
200
|
+
return socket.end();
|
|
183
201
|
}
|
|
184
|
-
const
|
|
185
|
-
if (!
|
|
186
|
-
logger3.error(`[next-ws] could not
|
|
187
|
-
return socket.
|
|
202
|
+
const module2 = await importRouteModule(nextServer, route.filename);
|
|
203
|
+
if (!module2) {
|
|
204
|
+
logger3.error(`[next-ws] could not import module for page ${pathname}`);
|
|
205
|
+
return socket.end();
|
|
188
206
|
}
|
|
189
|
-
const
|
|
190
|
-
if (!
|
|
191
|
-
logger3.error(
|
|
192
|
-
|
|
207
|
+
const handleSocket = module2.userland.SOCKET;
|
|
208
|
+
if (!handleSocket || typeof handleSocket !== "function") {
|
|
209
|
+
logger3.error(
|
|
210
|
+
`[next-ws] route '${pathname}' does not export a valid 'SOCKET' handler`
|
|
211
|
+
);
|
|
212
|
+
return socket.end();
|
|
193
213
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
214
|
+
wsServer.handleUpgrade(message, socket, head, async (client) => {
|
|
215
|
+
wsServer.emit("connection", client, message);
|
|
216
|
+
try {
|
|
217
|
+
const context = { params: route.params };
|
|
218
|
+
const handleClose = (
|
|
219
|
+
//
|
|
220
|
+
await handleSocket(client, message, wsServer, context)
|
|
221
|
+
);
|
|
222
|
+
if (typeof handleClose === "function")
|
|
223
|
+
client.once("close", () => handleClose());
|
|
224
|
+
} catch (cause) {
|
|
225
|
+
logger3.error(
|
|
226
|
+
`[next-ws] error in socket handler for '${pathname}'`,
|
|
227
|
+
cause
|
|
228
|
+
);
|
|
229
|
+
try {
|
|
230
|
+
client.close(1011, "Internal Server Error");
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
199
234
|
});
|
|
235
|
+
return;
|
|
200
236
|
});
|
|
201
237
|
}
|
|
202
238
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,32 +1,12 @@
|
|
|
1
|
-
import NextNodeServer from 'next/dist/server/next-server.js';
|
|
2
1
|
import { Server } from 'node:http';
|
|
3
2
|
import { WebSocketServer } from 'ws';
|
|
3
|
+
import NextNodeServer from 'next/dist/server/next-server.js';
|
|
4
4
|
|
|
5
|
-
declare function
|
|
5
|
+
declare function getHttpServer(): Server | undefined;
|
|
6
|
+
declare function setHttpServer<T extends Server | undefined>(server: T): T;
|
|
7
|
+
declare function getWebSocketServer(): WebSocketServer | undefined;
|
|
8
|
+
declare function setWebSocketServer<T extends WebSocketServer | undefined>(server: T): T;
|
|
6
9
|
|
|
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;
|
|
10
|
+
declare function setupWebSocketServer(nextServer: NextNodeServer): void;
|
|
31
11
|
|
|
32
12
|
export { getHttpServer, getWebSocketServer, setHttpServer, setWebSocketServer, setupWebSocketServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-ws",
|
|
3
|
-
"version": "0.0.0-beta-
|
|
3
|
+
"version": "0.0.0-beta-20250822123253",
|
|
4
4
|
"description": "Add support for WebSockets in the Next.js app directory",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"jscodeshift": "^17.3.0",
|
|
40
40
|
"minimist": "^1.2.8",
|
|
41
|
-
"semver": "^7.7.
|
|
41
|
+
"semver": "^7.7.2"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"next": ">=13.5.1",
|
|
@@ -46,24 +46,24 @@
|
|
|
46
46
|
"ws": "*"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@biomejs/biome": "^
|
|
50
|
-
"@changesets/changelog-git": "^0.2.
|
|
49
|
+
"@biomejs/biome": "^2.2.0",
|
|
50
|
+
"@changesets/changelog-git": "^0.2.1",
|
|
51
51
|
"@changesets/cli": "^2.27.12",
|
|
52
|
-
"@playwright/test": "^1.
|
|
52
|
+
"@playwright/test": "^1.55.0",
|
|
53
53
|
"@types/jscodeshift": "^17.3.0",
|
|
54
54
|
"@types/minimist": "^1.2.5",
|
|
55
|
-
"@types/node": "^
|
|
56
|
-
"@types/react": "19.
|
|
57
|
-
"@types/semver": "^7.
|
|
58
|
-
"@types/ws": "^8.
|
|
59
|
-
"chalk": "^5.
|
|
55
|
+
"@types/node": "^24.3.0",
|
|
56
|
+
"@types/react": "19.1.10",
|
|
57
|
+
"@types/semver": "^7.7.0",
|
|
58
|
+
"@types/ws": "^8.18.1",
|
|
59
|
+
"chalk": "^5.6.0",
|
|
60
60
|
"husky": "^9.1.7",
|
|
61
61
|
"next": "15.5.0",
|
|
62
62
|
"pinst": "^3.0.0",
|
|
63
|
-
"react": "19.
|
|
64
|
-
"react-dom": "19.
|
|
65
|
-
"tsup": "^8.
|
|
66
|
-
"typescript": "^5.
|
|
63
|
+
"react": "19.1.1",
|
|
64
|
+
"react-dom": "19.1.1",
|
|
65
|
+
"tsup": "^8.5.0",
|
|
66
|
+
"typescript": "^5.9.2"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"check": "tsc --noEmit",
|