@device-portal/server 0.0.1

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.
Files changed (3) hide show
  1. package/README.md +28 -0
  2. package/dist/main.js +113 -0
  3. package/package.json +29 -0
package/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # @device-portal/server
2
+
3
+ [![NPM](https://img.shields.io/npm/v/@device-portal/server.svg)](https://www.npmjs.com/package/@device-portal/server)
4
+
5
+ Signaling server for WebRTC used by `@device-portal/react`.
6
+
7
+ ## Usage
8
+
9
+ You can run the server with the following command:
10
+
11
+ ```sh
12
+ npx @device-portal/server
13
+ ```
14
+
15
+ The server will run on `ws://localhost:8080` by default.
16
+
17
+ ## Development
18
+
19
+ ```sh
20
+ npm ci
21
+ npm run dev
22
+ ```
23
+
24
+ Build the server with:
25
+
26
+ ```sh
27
+ npm run build
28
+ ```
package/dist/main.js ADDED
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_server_1 = require("@hono/node-server");
4
+ const serve_static_1 = require("@hono/node-server/serve-static");
5
+ const node_ws_1 = require("@hono/node-ws");
6
+ const fs_1 = require("fs");
7
+ const hono_1 = require("hono");
8
+ const path_1 = require("path");
9
+ const app = new hono_1.Hono();
10
+ const { injectWebSocket, upgradeWebSocket } = (0, node_ws_1.createNodeWebSocket)({ app });
11
+ const rooms = new Map();
12
+ const webSocketToPeerId = new Map();
13
+ const webSocketToRoom = new Map();
14
+ app.get('/v0/', upgradeWebSocket((context) => {
15
+ return {
16
+ onOpen(event, webSocket) {
17
+ const peerId = crypto.randomUUID();
18
+ webSocketToPeerId.set(webSocket.raw, peerId);
19
+ console.log(`WebSocket connection opened: ${peerId}`);
20
+ webSocket.send(JSON.stringify({ type: 'identity', data: { peerId } }));
21
+ },
22
+ onMessage(event, webSocket) {
23
+ const peerId = webSocketToPeerId.get(webSocket.raw);
24
+ const message = JSON.parse(event.data);
25
+ switch (message.type) {
26
+ case 'join-room': {
27
+ const room = message.room;
28
+ webSocketToRoom.set(webSocket.raw, room);
29
+ if (!rooms.has(room)) {
30
+ rooms.set(room, new Set());
31
+ }
32
+ const roomPeers = rooms.get(room);
33
+ roomPeers.add(webSocket.raw);
34
+ console.log(`Peer ${peerId} joined room: ${room}`);
35
+ // Notify other peers in the room that a new peer has joined
36
+ for (const client of roomPeers) {
37
+ if (client !== webSocket.raw &&
38
+ client.readyState === 1 /* WebSocket.OPEN */) {
39
+ client.send(JSON.stringify({ type: 'peer-joined', data: { peerId } }));
40
+ }
41
+ }
42
+ break;
43
+ }
44
+ case 'offer':
45
+ case 'answer':
46
+ case 'ice-candidate': {
47
+ const room = webSocketToRoom.get(webSocket.raw);
48
+ console.log(`Forwarding ${message.type} from ${peerId} in room: ${room}`);
49
+ if (room && rooms.has(room)) {
50
+ for (const client of rooms.get(room)) {
51
+ if (client !== webSocket.raw &&
52
+ client.readyState === 1 /* WebSocket.OPEN */) {
53
+ // If message has a target 'to', only send to that client
54
+ const clientPeerId = webSocketToPeerId.get(client);
55
+ if (message.to && clientPeerId !== message.to) {
56
+ continue;
57
+ }
58
+ client.send(JSON.stringify({
59
+ type: message.type,
60
+ from: peerId,
61
+ data: message.data,
62
+ }));
63
+ }
64
+ }
65
+ }
66
+ break;
67
+ }
68
+ }
69
+ },
70
+ onClose(event, webSocket) {
71
+ const peerId = webSocketToPeerId.get(webSocket.raw);
72
+ const room = webSocketToRoom.get(webSocket.raw);
73
+ if (room && rooms.has(room)) {
74
+ const roomPeers = rooms.get(room);
75
+ roomPeers.delete(webSocket.raw);
76
+ // Notify other peers in the room that a peer has left
77
+ for (const client of roomPeers) {
78
+ if (client.readyState === 1 /* WebSocket.OPEN */) {
79
+ client.send(JSON.stringify({ type: 'peer-left', data: { peerId } }));
80
+ }
81
+ }
82
+ if (roomPeers.size === 0) {
83
+ rooms.delete(room);
84
+ }
85
+ }
86
+ webSocketToPeerId.delete(webSocket.raw);
87
+ webSocketToRoom.delete(webSocket.raw);
88
+ console.log(`WebSocket connection closed: ${peerId}`);
89
+ },
90
+ onError(event, webSocket) {
91
+ const peerId = webSocketToPeerId.get(webSocket.raw);
92
+ console.error(`WebSocket error for ${peerId}:`, event);
93
+ },
94
+ };
95
+ }));
96
+ const storybookPath = (0, path_1.resolve)(__dirname, '../frontend/storybook-static');
97
+ if ((0, fs_1.existsSync)(storybookPath)) {
98
+ app.use('/*', (0, serve_static_1.serveStatic)({ root: '../frontend/storybook-static' }));
99
+ }
100
+ const portString = process.env.PORT;
101
+ let port = 8080;
102
+ if (portString) {
103
+ const parsedPort = parseInt(portString, 10);
104
+ if (!isNaN(parsedPort)) {
105
+ port = parsedPort;
106
+ }
107
+ }
108
+ console.log(`Starting signaling server on 0.0.0.0:${port}…`);
109
+ const server = (0, node_server_1.serve)({
110
+ fetch: app.fetch,
111
+ port,
112
+ });
113
+ injectWebSocket(server);
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@device-portal/server",
3
+ "version": "0.0.1",
4
+ "main": "dist/main.js",
5
+ "bin": {
6
+ "device-portal-server": "dist/main.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "start": "node dist/main.js",
11
+ "dev": "node --watch -r ts-node/register src/main.ts",
12
+ "test": "tsc --build --dry",
13
+ "prepare": "npm run build"
14
+ },
15
+ "files": [
16
+ "/dist/"
17
+ ],
18
+ "dependencies": {
19
+ "hono": "^4.1.0",
20
+ "@hono/node-server": "^1.8.0",
21
+ "@hono/node-ws": "^1.3.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.0.0",
25
+ "ts-node": "^10.9.1",
26
+ "@types/node": "^20.0.0",
27
+ "@types/ws": "^8.5.3"
28
+ }
29
+ }