mirror of
https://github.com/LukeHagar/crossws.git
synced 2025-12-10 20:37:45 +00:00
feat: dynamic resolver
This commit is contained in:
@@ -66,12 +66,13 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "unbuild",
|
"build": "unbuild",
|
||||||
|
"dev": "pnpm play:node",
|
||||||
"lint": "eslint --cache --ext .ts,.js,.mjs,.cjs . && prettier -c src",
|
"lint": "eslint --cache --ext .ts,.js,.mjs,.cjs . && prettier -c src",
|
||||||
"lint:fix": "eslint --cache --ext .ts,.js,.mjs,.cjs . --fix && prettier -c src -w",
|
"lint:fix": "eslint --cache --ext .ts,.js,.mjs,.cjs . --fix && prettier -c src -w",
|
||||||
"prepack": "pnpm run build",
|
"prepack": "pnpm run build",
|
||||||
"play:bun": "bun playground/bun.ts",
|
"play:bun": "bun playground/bun.ts",
|
||||||
"play:cf": "wrangler dev --port 3001",
|
"play:cf": "wrangler dev --port 3001",
|
||||||
"play:deno": "deno run -A playground/deno.ts",
|
"play:deno": "deno run --unstable-sloppy-imports -A playground/deno.ts",
|
||||||
"play:node": "jiti playground/node.ts",
|
"play:node": "jiti playground/node.ts",
|
||||||
"play:uws": "jiti playground/uws.ts",
|
"play:uws": "jiti playground/uws.ts",
|
||||||
"release": "pnpm test && changelogen --release && npm publish && git push --follow-tags",
|
"release": "pnpm test && changelogen --release && npm publish && git push --follow-tags",
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import type { WebSocketHooks, WebSocketAdapter } from "../src";
|
|
||||||
|
|
||||||
export const getIndexHTML = (params) =>
|
|
||||||
import("../examples/h3/index.html.ts").then((r) => r.html(params));
|
|
||||||
|
|
||||||
export function createDemo<T extends WebSocketAdapter>(
|
|
||||||
adapter: T,
|
|
||||||
opts?: Parameters<T>[1],
|
|
||||||
): ReturnType<T> {
|
|
||||||
const hooks = createWebSocketDebugHooks({
|
|
||||||
open(peer) {
|
|
||||||
peer.send(`Hello!`);
|
|
||||||
peer.send(
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
url: peer.url,
|
|
||||||
headers: peer.headers && Object.fromEntries(peer.headers),
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
message(peer, message) {
|
|
||||||
if (message.text() === "ping") {
|
|
||||||
peer.send("pong");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return adapter(hooks, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWebSocketDebugHooks(
|
|
||||||
hooks: Partial<WebSocketHooks>,
|
|
||||||
): Partial<WebSocketHooks> {
|
|
||||||
const createDebugHook =
|
|
||||||
(name: keyof WebSocketHooks) =>
|
|
||||||
(peer, ...args: any[]) => {
|
|
||||||
console.log(
|
|
||||||
`[ws] [${name}]`,
|
|
||||||
peer,
|
|
||||||
[...args].map((arg, i) => `\n - arg#${i} ${arg}`).join(""),
|
|
||||||
);
|
|
||||||
hooks[name]?.(peer, ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Proxy(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
get(_, prop) {
|
|
||||||
return createDebugHook(prop as keyof WebSocketHooks);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
59
playground/_shared.ts
Normal file
59
playground/_shared.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import {
|
||||||
|
CrossWSOptions,
|
||||||
|
WebSocketAdapter,
|
||||||
|
defineWebSocketHooks,
|
||||||
|
} from "../src/index.ts";
|
||||||
|
|
||||||
|
export const getIndexHTML = (params) =>
|
||||||
|
import("../examples/h3/index.html.ts").then((r) => r.html(params));
|
||||||
|
|
||||||
|
export function createDemo<T extends WebSocketAdapter>(
|
||||||
|
adapter: T,
|
||||||
|
opts?: Parameters<T>[1],
|
||||||
|
): ReturnType<T> {
|
||||||
|
const hooks = defineWebSocketHooks({
|
||||||
|
$(name, peer, ...args) {
|
||||||
|
console.log(
|
||||||
|
`$ ${peer} ${name} (${args.map((arg) => stringify(arg)).join(", ")})`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
open(peer) {
|
||||||
|
peer.send(`Hello ${peer}`);
|
||||||
|
},
|
||||||
|
message(peer, message) {
|
||||||
|
if (message.text() === "ping") {
|
||||||
|
peer.send("pong");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolve: CrossWSOptions["resolve"] = (peer) => {
|
||||||
|
return {
|
||||||
|
open: () => {
|
||||||
|
peer.send(
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
url: peer.url,
|
||||||
|
headers: peer.headers && Object.fromEntries(peer.headers),
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return adapter(hooks, {
|
||||||
|
resolve,
|
||||||
|
...opts,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringify(val) {
|
||||||
|
const str = val.toString();
|
||||||
|
if (str === "[object Object]") {
|
||||||
|
return val.constructor?.name || "??";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// You can run this demo using `bun --bun ./bun.ts` or `npm run play:bun` in repo
|
// You can run this demo using `bun --bun ./bun.ts` or `npm run play:bun` in repo
|
||||||
|
|
||||||
import bunAdapter from "../src/adapters/bun";
|
import bunAdapter from "../src/adapters/bun";
|
||||||
import { createDemo, getIndexHTML } from "./_common";
|
import { createDemo, getIndexHTML } from "./_shared";
|
||||||
|
|
||||||
const adapter = createDemo(bunAdapter);
|
const adapter = createDemo(bunAdapter);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// You can run this demo using `npm run play:cf` in repo
|
// You can run this demo using `npm run play:cf` in repo
|
||||||
import type { Request, ExecutionContext } from "@cloudflare/workers-types";
|
import type { Request, ExecutionContext } from "@cloudflare/workers-types";
|
||||||
import cloudflareAdapter from "../src/adapters/cloudflare";
|
import cloudflareAdapter from "../src/adapters/cloudflare";
|
||||||
import { createDemo, getIndexHTML } from "./_common.ts";
|
import { createDemo, getIndexHTML } from "./_shared.ts";
|
||||||
|
|
||||||
const { handleUpgrade } = createDemo(cloudflareAdapter);
|
const { handleUpgrade } = createDemo(cloudflareAdapter);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// You can run this demo using `deno run -A ./deno.ts` or `npm run play:deno` in repo
|
// You can run this demo using `deno run -A ./deno.ts` or `npm run play:deno` in repo
|
||||||
|
|
||||||
import denoAdapter from "../dist/adapters/deno.mjs";
|
import denoAdapter from "../src/adapters/deno.ts";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import type * as _Deno from "../types/lib.deno.d.ts";
|
import type * as _Deno from "../types/lib.deno.d.ts";
|
||||||
|
|
||||||
import { createDemo, getIndexHTML } from "./_common.ts";
|
import { createDemo, getIndexHTML } from "./_shared.ts";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
const Deno: typeof import("@deno/types").Deno;
|
const Deno: typeof import("@deno/types").Deno;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { createServer } from "node:http";
|
import { createServer } from "node:http";
|
||||||
import nodeAdapter from "../src/adapters/node";
|
import nodeAdapter from "../src/adapters/node";
|
||||||
import { createDemo, getIndexHTML } from "./_common";
|
import { createDemo, getIndexHTML } from "./_shared";
|
||||||
|
|
||||||
const adapter = createDemo(nodeAdapter);
|
const adapter = createDemo(nodeAdapter);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// You can run this demo using `npm run play:node-uws` in repo
|
// You can run this demo using `npm run play:node-uws` in repo
|
||||||
|
|
||||||
import { App } from "uWebSockets.js";
|
import { App } from "uWebSockets.js";
|
||||||
import uwsAdapter from "../src/adapters/uws.ts";
|
import uwsAdapter from "../src/adapters/uws";
|
||||||
import { createDemo, getIndexHTML } from "./_common.ts";
|
import { createDemo, getIndexHTML } from "./_shared.ts";
|
||||||
|
|
||||||
const adapter = createDemo(uwsAdapter);
|
const adapter = createDemo(uwsAdapter);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { WebSocketHandler, ServerWebSocket, Server } from "bun";
|
|||||||
|
|
||||||
import { WebSocketMessage } from "../message";
|
import { WebSocketMessage } from "../message";
|
||||||
import { WebSocketError } from "../error";
|
import { WebSocketError } from "../error";
|
||||||
import { WebSocketPeerBase } from "../peer";
|
import { WebSocketPeer } from "../peer";
|
||||||
import { defineWebSocketAdapter } from "../adapter";
|
import { defineWebSocketAdapter } from "../adapter";
|
||||||
import { CrossWSOptions, createCrossWS } from "../crossws";
|
import { CrossWSOptions, createCrossWS } from "../crossws";
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
if (ws.data?._peer) {
|
if (ws.data?._peer) {
|
||||||
return ws.data._peer;
|
return ws.data._peer;
|
||||||
}
|
}
|
||||||
const peer = new WebSocketPeer({ bun: { ws } });
|
const peer = new BunPeer({ bun: { ws } });
|
||||||
ws.data = ws.data || {};
|
ws.data = ws.data || {};
|
||||||
ws.data._peer = peer;
|
ws.data._peer = peer;
|
||||||
return peer;
|
return peer;
|
||||||
@@ -79,7 +79,7 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
class WebSocketPeer extends WebSocketPeerBase<{
|
class BunPeer extends WebSocketPeer<{
|
||||||
bun: { ws: ServerWebSocket<ContextData> };
|
bun: { ws: ServerWebSocket<ContextData> };
|
||||||
}> {
|
}> {
|
||||||
get id() {
|
get id() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import type * as _cf from "@cloudflare/workers-types";
|
import type * as _cf from "@cloudflare/workers-types";
|
||||||
|
|
||||||
import { WebSocketPeerBase } from "../peer";
|
import { WebSocketPeer } from "../peer";
|
||||||
import { defineWebSocketAdapter } from "../adapter.js";
|
import { defineWebSocketAdapter } from "../adapter.js";
|
||||||
import { WebSocketMessage } from "../message";
|
import { WebSocketMessage } from "../message";
|
||||||
import { WebSocketError } from "../error";
|
import { WebSocketError } from "../error";
|
||||||
@@ -36,14 +36,14 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
const client = pair[0];
|
const client = pair[0];
|
||||||
const server = pair[1];
|
const server = pair[1];
|
||||||
|
|
||||||
const peer = new CloudflareWebSocketPeer({
|
const peer = new CloudflarePeer({
|
||||||
cloudflare: { client, server, req, env, context },
|
cloudflare: { client, server, req, env, context },
|
||||||
});
|
});
|
||||||
|
|
||||||
server.accept();
|
server.accept();
|
||||||
|
|
||||||
crossws.$("cloudflare:accept", peer);
|
crossws.$("cloudflare:accept", peer);
|
||||||
hooks.open?.(peer);
|
crossws.open(peer);
|
||||||
|
|
||||||
server.addEventListener("message", (event) => {
|
server.addEventListener("message", (event) => {
|
||||||
crossws.$("cloudflare:message", peer, event);
|
crossws.$("cloudflare:message", peer, event);
|
||||||
@@ -73,7 +73,7 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
class CloudflareWebSocketPeer extends WebSocketPeerBase<{
|
class CloudflarePeer extends WebSocketPeer<{
|
||||||
cloudflare: {
|
cloudflare: {
|
||||||
client: _cf.WebSocket;
|
client: _cf.WebSocket;
|
||||||
server: _cf.WebSocket;
|
server: _cf.WebSocket;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import { WebSocketMessage } from "../message";
|
import { WebSocketMessage } from "../message";
|
||||||
import { WebSocketError } from "../error";
|
import { WebSocketError } from "../error";
|
||||||
import { WebSocketPeerBase } from "../peer";
|
import { WebSocketPeer } from "../peer";
|
||||||
import { defineWebSocketAdapter } from "../adapter.js";
|
import { defineWebSocketAdapter } from "../adapter.js";
|
||||||
import { CrossWSOptions, createCrossWS } from "../crossws";
|
import { CrossWSOptions, createCrossWS } from "../crossws";
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
|
|
||||||
const handleUpgrade = (req: Request) => {
|
const handleUpgrade = (req: Request) => {
|
||||||
const upgrade = Deno.upgradeWebSocket(req);
|
const upgrade = Deno.upgradeWebSocket(req);
|
||||||
const peer = new DenoWebSocketPeer({
|
const peer = new DenoPeer({
|
||||||
deno: { ws: upgrade.socket, req },
|
deno: { ws: upgrade.socket, req },
|
||||||
});
|
});
|
||||||
upgrade.socket.addEventListener("open", () => {
|
upgrade.socket.addEventListener("open", () => {
|
||||||
crossws.$("deno:open", peer);
|
crossws.$("deno:open", peer);
|
||||||
hooks.open?.(peer);
|
crossws.open(peer);
|
||||||
});
|
});
|
||||||
upgrade.socket.addEventListener("message", (event) => {
|
upgrade.socket.addEventListener("message", (event) => {
|
||||||
crossws.$("deno:message", peer, event);
|
crossws.$("deno:message", peer, event);
|
||||||
@@ -52,7 +52,7 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
class DenoWebSocketPeer extends WebSocketPeerBase<{
|
class DenoPeer extends WebSocketPeer<{
|
||||||
deno: { ws: any; req: Request };
|
deno: { ws: any; req: Request };
|
||||||
}> {
|
}> {
|
||||||
get id() {
|
get id() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
WebSocketServer,
|
WebSocketServer,
|
||||||
WebSocket as WebSocketT,
|
WebSocket as WebSocketT,
|
||||||
} from "../../types/ws";
|
} from "../../types/ws";
|
||||||
import { WebSocketPeerBase } from "../peer";
|
import { WebSocketPeer } from "../peer";
|
||||||
import { WebSocketMessage } from "../message";
|
import { WebSocketMessage } from "../message";
|
||||||
import { WebSocketError } from "../error";
|
import { WebSocketError } from "../error";
|
||||||
import { defineWebSocketAdapter } from "../adapter";
|
import { defineWebSocketAdapter } from "../adapter";
|
||||||
@@ -37,22 +37,23 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
}) as WebSocketServer);
|
}) as WebSocketServer);
|
||||||
|
|
||||||
// Unmanaged server-level events
|
// Unmanaged server-level events
|
||||||
wss.on("error", (error) => {
|
// TODO: Expose with new API
|
||||||
crossws.$("node:server-error", error);
|
// wss.on("error", (error) => {
|
||||||
});
|
// crossws.$("node:server-error", error);
|
||||||
wss.on("headers", (headers, request) => {
|
// });
|
||||||
crossws.$("node:server-headers", headers, request);
|
// wss.on("headers", (headers, request) => {
|
||||||
});
|
// crossws.$("node:server-headers", headers, request);
|
||||||
wss.on("listening", () => {
|
// });
|
||||||
crossws.$("node:server-listening");
|
// wss.on("listening", () => {
|
||||||
});
|
// crossws.$("node:server-listening");
|
||||||
wss.on("close", () => {
|
// });
|
||||||
crossws.$("node:server-close");
|
// wss.on("close", () => {
|
||||||
});
|
// crossws.$("node:server-close");
|
||||||
|
// });
|
||||||
|
|
||||||
wss.on("connection", (ws, req) => {
|
wss.on("connection", (ws, req) => {
|
||||||
const peer = new NodeWebSocketPeer({ node: { ws, req, server: wss } });
|
const peer = new NodePeer({ node: { ws, req, server: wss } });
|
||||||
hooks.open?.(peer);
|
crossws.open(peer);
|
||||||
|
|
||||||
// Managed socket-level events
|
// Managed socket-level events
|
||||||
ws.on("message", (data: RawData, isBinary: boolean) => {
|
ws.on("message", (data: RawData, isBinary: boolean) => {
|
||||||
@@ -105,7 +106,7 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
class NodeWebSocketPeer extends WebSocketPeerBase<{
|
class NodePeer extends WebSocketPeer<{
|
||||||
node: {
|
node: {
|
||||||
server: WebSocketServer;
|
server: WebSocketServer;
|
||||||
req: IncomingMessage;
|
req: IncomingMessage;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
HttpRequest,
|
HttpRequest,
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
} from "uWebSockets.js";
|
} from "uWebSockets.js";
|
||||||
import { WebSocketPeerBase } from "../peer";
|
import { WebSocketPeer } from "../peer";
|
||||||
import { WebSocketMessage } from "../message";
|
import { WebSocketMessage } from "../message";
|
||||||
import { defineWebSocketAdapter } from "../adapter";
|
import { defineWebSocketAdapter } from "../adapter";
|
||||||
import { CrossWSOptions, createCrossWS } from "../crossws";
|
import { CrossWSOptions, createCrossWS } from "../crossws";
|
||||||
@@ -48,7 +48,7 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
if (userData._peer) {
|
if (userData._peer) {
|
||||||
return userData._peer as WebSocketPeer;
|
return userData._peer as WebSocketPeer;
|
||||||
}
|
}
|
||||||
const peer = new WebSocketPeer({ uws: { ws, userData } });
|
const peer = new UWSPeer({ uws: { ws, userData } });
|
||||||
userData._peer = peer;
|
userData._peer = peer;
|
||||||
return peer;
|
return peer;
|
||||||
};
|
};
|
||||||
@@ -73,7 +73,7 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
open(ws) {
|
open(ws) {
|
||||||
const peer = getPeer(ws);
|
const peer = getPeer(ws);
|
||||||
crossws.$("uws:open", peer, ws);
|
crossws.$("uws:open", peer, ws);
|
||||||
hooks.open?.(peer);
|
crossws.open(peer);
|
||||||
},
|
},
|
||||||
ping(ws, message) {
|
ping(ws, message) {
|
||||||
const peer = getPeer(ws);
|
const peer = getPeer(ws);
|
||||||
@@ -110,18 +110,21 @@ export default defineWebSocketAdapter<Adapter, AdapterOptions>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
class WebSocketPeer extends WebSocketPeerBase<{
|
class UWSPeer extends WebSocketPeer<{
|
||||||
uws: {
|
uws: {
|
||||||
ws: WebSocket<UserData>;
|
ws: WebSocket<UserData>;
|
||||||
userData: UserData;
|
userData: UserData;
|
||||||
};
|
};
|
||||||
}> {
|
}> {
|
||||||
_headers: Headers | undefined;
|
_headers: Headers | undefined;
|
||||||
|
_decoder = new TextDecoder();
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
try {
|
try {
|
||||||
const addr = this.ctx.uws.ws?.getRemoteAddressAsText();
|
const addr = this._decoder.decode(
|
||||||
return new TextDecoder().decode(addr);
|
this.ctx.uws.ws?.getRemoteAddressAsText(),
|
||||||
|
);
|
||||||
|
return addr.replace(/(0000:)+/, "");
|
||||||
} catch {
|
} catch {
|
||||||
// Error: Invalid access of closed uWS.WebSocket/SSLWebSocket.
|
// Error: Invalid access of closed uWS.WebSocket/SSLWebSocket.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,46 @@
|
|||||||
import type { WebSocketHooks, AdapterHooks, UserHooks } from "./hooks";
|
import type { WebSocketHooks, AdapterHooks, UserHooks } from "./hooks";
|
||||||
|
import { WebSocketPeer } from "./peer";
|
||||||
|
|
||||||
export interface CrossWSOptions {}
|
export interface CrossWS extends WebSocketHooks {}
|
||||||
|
|
||||||
type AdapterHook = <NAME extends keyof AdapterHooks>(
|
export interface CrossWSOptions {
|
||||||
name: NAME,
|
resolve?: (
|
||||||
...args: Parameters<AdapterHooks[NAME]>
|
peer: WebSocketPeer,
|
||||||
) => ReturnType<AdapterHooks[NAME]>;
|
) => UserHooks | void | Promise<UserHooks | void>;
|
||||||
|
|
||||||
export interface CrossWS extends WebSocketHooks {
|
|
||||||
$: AdapterHook;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCrossWS(
|
export function createCrossWS(
|
||||||
_hooks: UserHooks,
|
_hooks: UserHooks,
|
||||||
options: CrossWSOptions,
|
options: CrossWSOptions,
|
||||||
): CrossWS {
|
): CrossWS {
|
||||||
|
const _callHook = options.resolve
|
||||||
|
? async (name: keyof UserHooks, peer: WebSocketPeer, ...args: any[]) => {
|
||||||
|
const hooks = await options.resolve?.(peer);
|
||||||
|
// @ts-expect-error
|
||||||
|
return hooks?.[name]?.(peer, ...args);
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// @ts-expect-error TODO
|
$(name, peer, ...args) {
|
||||||
$(name, ...args) {
|
_hooks.$?.(name, peer, ...args);
|
||||||
// @ts-expect-error TODO
|
_callHook?.(name, peer, ...args);
|
||||||
return _hooks[name]?.(...args);
|
|
||||||
},
|
},
|
||||||
message(peer, message) {
|
message(peer, message) {
|
||||||
return _hooks.message?.(peer, message);
|
_hooks.message?.(peer, message);
|
||||||
|
_callHook?.("message", peer, message);
|
||||||
},
|
},
|
||||||
open(peer) {
|
open(peer) {
|
||||||
return _hooks.open?.(peer);
|
_hooks.open?.(peer);
|
||||||
|
_callHook?.("open", peer);
|
||||||
},
|
},
|
||||||
close(peer, { code, reason }) {
|
close(peer, { code, reason }) {
|
||||||
return _hooks.close?.(peer, { code, reason });
|
_hooks.close?.(peer, { code, reason });
|
||||||
|
_callHook?.("close", peer, { code, reason });
|
||||||
},
|
},
|
||||||
error(peer, error) {
|
error(peer, error) {
|
||||||
return _hooks.error?.(peer, error);
|
_hooks.error?.(peer, error);
|
||||||
|
_callHook?.("error", peer, error);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/hooks.ts
21
src/hooks.ts
@@ -1,15 +1,15 @@
|
|||||||
import { WebSocketError } from "./error";
|
import { WebSocketError } from "./error";
|
||||||
import type { WebSocketMessage } from "./message";
|
import type { WebSocketMessage } from "./message";
|
||||||
import type { WebSocketPeerBase } from "./peer";
|
import type { WebSocketPeer } from "./peer";
|
||||||
|
|
||||||
type WSHook<ArgsT extends Array<any> = []> = (
|
type WSHook<ArgsT extends Array<any> = []> = (
|
||||||
peer: WebSocketPeerBase,
|
peer: WebSocketPeer,
|
||||||
...args: ArgsT
|
...args: ArgsT
|
||||||
) => void | Promise<void>;
|
) => void | Promise<void>;
|
||||||
|
|
||||||
type WSGlobalHook<ArgsT extends Array<any> = []> = (
|
// type WSGlobalHook<ArgsT extends Array<any> = []> = (
|
||||||
...args: ArgsT
|
// ...args: ArgsT
|
||||||
) => void | Promise<void>;
|
// ) => void | Promise<void>;
|
||||||
|
|
||||||
export type UserHooks = Partial<WebSocketHooks & AdapterHooks>;
|
export type UserHooks = Partial<WebSocketHooks & AdapterHooks>;
|
||||||
|
|
||||||
@@ -20,6 +20,9 @@ export function defineWebSocketHooks<T extends UserHooks = UserHooks>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface WebSocketHooks {
|
export interface WebSocketHooks {
|
||||||
|
/** Catch-all handler */
|
||||||
|
$: (name: keyof UserHooks, peer: WebSocketPeer, ...args: any[]) => void;
|
||||||
|
|
||||||
/** A message is received */
|
/** A message is received */
|
||||||
message: WSHook<[WebSocketMessage]>;
|
message: WSHook<[WebSocketMessage]>;
|
||||||
|
|
||||||
@@ -64,10 +67,10 @@ export interface AdapterHooks {
|
|||||||
"node:pong": WSHook<[data: Buffer]>;
|
"node:pong": WSHook<[data: Buffer]>;
|
||||||
"node:unexpected-response": WSHook<[req: any, res: any]>;
|
"node:unexpected-response": WSHook<[req: any, res: any]>;
|
||||||
"node:upgrade": WSHook<[req: any]>;
|
"node:upgrade": WSHook<[req: any]>;
|
||||||
"node:server-error": WSGlobalHook<[error: any]>;
|
// "node:server-error": WSGlobalHook<[error: any]>;
|
||||||
"node:server-listening": WSGlobalHook<[]>;
|
// "node:server-listening": WSGlobalHook<[]>;
|
||||||
"node:server-close": WSGlobalHook<[]>;
|
// "node:server-close": WSGlobalHook<[]>;
|
||||||
"node:server-headers": WSGlobalHook<[headers: any, request: any]>;
|
// "node:server-headers": WSGlobalHook<[headers: any, request: any]>;
|
||||||
|
|
||||||
// uws (Node)
|
// uws (Node)
|
||||||
"uws:open": WSHook<[ws: any]>;
|
"uws:open": WSHook<[ws: any]>;
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ const ReadyStateMap = {
|
|||||||
3: "closed",
|
3: "closed",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export abstract class WebSocketPeerBase<AdapterContext = any> {
|
export abstract class WebSocketPeer<AdapterContext = any> {
|
||||||
constructor(public ctx: AdapterContext) {}
|
constructor(public ctx: AdapterContext) {}
|
||||||
|
|
||||||
get id(): string | undefined {
|
get id(): string | undefined {
|
||||||
return undefined;
|
return "??";
|
||||||
}
|
}
|
||||||
|
|
||||||
get url(): string {
|
get url(): string {
|
||||||
@@ -33,8 +33,7 @@ export abstract class WebSocketPeerBase<AdapterContext = any> {
|
|||||||
): number;
|
): number;
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
const readyState = ReadyStateMap[this.readyState];
|
return `${this.id || ""}${this.readyState === 1 ? "" : ` [${ReadyStateMap[this.readyState]}]`}`;
|
||||||
return `<WebSocketPeer${this.id ? ` ${this.id}` : ""} (${readyState})>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.for("nodejs.util.inspect.custom")]() {
|
[Symbol.for("nodejs.util.inspect.custom")]() {
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { expect, it, describe } from "vitest";
|
|
||||||
import {} from "../src";
|
|
||||||
|
|
||||||
describe("crossws", () => {
|
|
||||||
it.todo("pass", () => {
|
|
||||||
expect(true).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user