signalingserver.js 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nuzulul Zulkarnain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # signalingserver.js
2
+ Alternative signaling server that works in browser, no server required.
3
+
4
+ [>DEMO<](https://nuzulul.github.io/signalingserver.js/demo.html)
5
+
6
+ ## Benefit
7
+
8
+ * ✅ 0 Dependencies
9
+ * ✅ No server required
10
+ * ✅ Simple API
11
+
12
+ ## How does it works?
13
+
14
+ It use free public WebTorrent trackers as transport.
15
+
16
+ ## Ideas
17
+
18
+ * WebRTC signaling server
19
+ * Peer discovery
20
+ * Mesh network
21
+ * Chat
22
+ * Multiplayer
23
+
24
+ ## Install
25
+
26
+ ```
27
+ npm install signalingserver.js
28
+ ```
29
+
30
+ CDN
31
+
32
+ * [https://cdn.jsdelivr.net/npm/signalingserver.js/+esm](https://cdn.jsdelivr.net/npm/signalingserver.js/+esm)
33
+ * [https://unpkg.com/signalingserver.js](https://unpkg.com/signalingserver.js)
34
+ * [https://esm.sh/signalingserver.js](https://esm.sh/signalingserver.js)
35
+
36
+
37
+ ## Usage
38
+
39
+ ```
40
+ import {createSignalingServer} from 'signalingserver.js';
41
+
42
+ const config = {
43
+ appid : 'myApp'
44
+ }
45
+
46
+ //create new node
47
+ const node = createSignalingServer(config);
48
+
49
+ //signal handler
50
+ node.data((signal)=>{
51
+ console.log(`receive signal : ${signal}`);
52
+ });
53
+
54
+ //broadcast signal
55
+ const signal = 'test'; //example
56
+ node.send(signal);
57
+ console.log(`send signal : ${signal}`);
58
+ ```
59
+
60
+ ## API
61
+
62
+ ### createSignalingServer(config)
63
+
64
+ Create new signaling server node
65
+
66
+ Config : Parameter object
67
+
68
+ * appid = (string) Custom application name as identifier
69
+ * tracker = (Array) Custom WebTorrent trackers list
70
+
71
+ ### send
72
+
73
+ Broadcast signal to all node
74
+
75
+ ### data
76
+
77
+ On receive signal handler
78
+
79
+ ## Recomendation
80
+
81
+ Encrypt the signal before broadcast it to prevent mitm.
82
+
83
+ ## See Also
84
+
85
+ * [webConnect.js](https://github.com/nuzulul/webConnect.js) - Auto WebRTC Mesh P2P Network without signaling server.
86
+
87
+ ## License
88
+
89
+ * [MIT](https://github.com/nuzulul/signaling.js/blob/main/LICENSE) - [Nuzulul Zulkarnain](https://github.com/nuzulul)
package/demo.html ADDED
@@ -0,0 +1,38 @@
1
+ <html>
2
+ <header>
3
+ <title>signalingserver.js</title>
4
+ </header>
5
+ <body>
6
+ <h3>Demo <a href="https://github.com/nuzulul/signalingserver.js">signalingserver.js</a></h3>
7
+ <ul id="myList" style="overflow:scroll;height:70%;"></ul>
8
+ <script type="module">
9
+ import {createSignalingServer} from './signalingserver.js';
10
+
11
+ const write = (input) => {
12
+ const node = document.createElement("li")
13
+ const textnode = document.createTextNode(input)
14
+ node.appendChild(textnode)
15
+ document.getElementById("myList").appendChild(node)
16
+ var objDiv = document.getElementById("myList");
17
+ objDiv.scrollTop = objDiv.scrollHeight;
18
+ }
19
+
20
+ const config = {
21
+ appid : 'demo signalingserver.js'
22
+ }
23
+
24
+ const node = createSignalingServer(config);
25
+ node.data((signal)=>{
26
+ const status = `receive signal : ${signal}`;
27
+ write(status);
28
+ })
29
+
30
+ setInterval(()=>{
31
+ const signal = new Date().getTime(); //example signal
32
+ node.send(signal);
33
+ const status = `send signal : ${signal}`;
34
+ write(status);
35
+ },5000)
36
+ </script>
37
+ </body>
38
+ </html>
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "signalingserver.js",
3
+ "version": "0.0.1",
4
+ "description": "Alternative signaling server that works in browser, no server required.",
5
+ "main": "signalingserver.js",
6
+ "module": "signalingserver.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "signalingserver.js"
10
+ }
11
+ },
12
+ "scripts": {
13
+ "test": "echo \"Error: no test specified\" && exit 1"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/nuzulul/signalingserver.js.git"
18
+ },
19
+ "keywords": [
20
+ "signaling",
21
+ "signaling server"
22
+ ],
23
+ "author": "Nuzulul Zulkarnain",
24
+ "license": "MIT",
25
+ "bugs": {
26
+ "url": "https://github.com/nuzulul/signalingserver.js/issues"
27
+ },
28
+ "homepage": "https://github.com/nuzulul/signalingserver.js#readme"
29
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * signalingserver.js by Nuzulul Zulkarnain
3
+ * https://github.com/nuzulul/signalingserver.js
4
+ **/
5
+
6
+ const defaultTrackers = [
7
+ 'wss://tracker.webtorrent.dev',
8
+ 'wss://tracker.openwebtorrent.com',
9
+ 'wss://tracker.btorrent.xyz',
10
+ 'wss://tracker.files.fm:7073/announce'
11
+ ];
12
+ const defaultAppId = 'global';
13
+ const appName = 'signaling.js';
14
+ const hashLimit = 20;
15
+ const sockets = {};
16
+ const socketListeners = {};
17
+ const trackerAction = 'announce';
18
+ const intervalMs = 30000;
19
+ const {values} = Object;
20
+ const offerPoolSize = 10;
21
+ const charSet = '0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz';
22
+ const createId = () => new Array(20).fill().map(()=>charSet[Math.floor(Math.random() * charSet.length)]).join('');
23
+ const myid = createId();
24
+ const encodeBytes = txt => new TextEncoder().encode(txt);
25
+ let handledOffers = {}
26
+
27
+ const createSignalingServer = (config={}) => {
28
+
29
+ //use default appid if empty
30
+ if(!config.appid){
31
+ config.appid = defaultAppId;
32
+ }
33
+
34
+ //use default tracker if empty
35
+ if(!config.tracker){
36
+ config.tracker = defaultTrackers;
37
+ }
38
+
39
+ //validate tracker length is not empty
40
+ if(!config.tracker.length){
41
+ throw new Error(`Tracker list is empty`);
42
+ }
43
+
44
+ const createInfoHash = crypto.subtle
45
+ .digest('SHA-1', encodeBytes(`${appName}:${config.appid}`))
46
+ .then(buffer =>
47
+ Array.from(new Uint8Array(buffer))
48
+ .map(b => b.toString(36))
49
+ .join('')
50
+ .slice(0,hashLimit)
51
+ )
52
+
53
+ const send = async (content) => {
54
+ const infoHash = await createInfoHash;
55
+ const offer_id = createId()
56
+ config.tracker.forEach(async url => {
57
+ const socket = makeSocket(url,infoHash);
58
+ if(socket.readyState === WebSocket.OPEN){
59
+ announce(socket,infoHash,content,offer_id);
60
+ }else if(socket.readyState !== WebSocket.CONNECTING){
61
+ announce(await makeSocket(url,infoHash),infoHash,content,offer_id);
62
+ }
63
+ })
64
+ }
65
+
66
+ const makeSocket = (url,infoHash) => {
67
+ if(!sockets[url]){
68
+ socketListeners[url] = {[infoHash]: onSocketMessage};
69
+ sockets[url] = new Promise(res => {
70
+ const socket = new WebSocket(url);
71
+ socket.onopen = res.bind(null,socket);
72
+ socket.onmessage = e =>
73
+ values(socketListeners[url]).forEach(f => f(socket,e))
74
+ })
75
+ }else{
76
+ socketListeners[url][infoHash] = onSocketMessage;
77
+ }
78
+ return sockets[url];
79
+ }
80
+
81
+ const announce = async (socket,infoHash,content,offer_id) =>
82
+ socket.send(
83
+ JSON.stringify({
84
+ action : trackerAction,
85
+ info_hash: infoHash,
86
+ peer_id: myid,
87
+ numwant:offerPoolSize,
88
+ offers:[
89
+ {
90
+ offer:{
91
+ type: 'offer',
92
+ sdp:content
93
+ },
94
+ offer_id
95
+ }
96
+ ]
97
+ })
98
+ )
99
+
100
+ const onSocketMessage = async (socket,e) => {
101
+ const infoHash = await createInfoHash;
102
+ let val;
103
+
104
+ try{
105
+ val = JSON.parse(e.data);
106
+ }catch(e){
107
+ console.warn(e);
108
+ return;
109
+ }
110
+
111
+ if(val.info_hash !== infoHash){
112
+ return;
113
+ }
114
+
115
+ if(val.peer_id && val.peer_id === myid){
116
+ //got self message
117
+ return;
118
+ }
119
+
120
+ const failure = val['failure reason'];
121
+ if(failure){
122
+ console.warn(failure);
123
+ return;
124
+ }
125
+
126
+ if(handledOffers[val.offer_id]){
127
+ return;
128
+ }
129
+
130
+ handledOffers[val.offer_id] = true;
131
+
132
+ if(val.offer){
133
+ contentHandler(val.offer.sdp);
134
+ }
135
+
136
+ return;
137
+ }
138
+
139
+ let contentHandler = ()=>{};
140
+ const data = handle => (contentHandler = handle);
141
+
142
+ const auto = async () => {
143
+ const infoHash = await createInfoHash;
144
+ config.tracker.forEach(async url => {
145
+ const socket = makeSocket(url,infoHash);
146
+ })
147
+ }
148
+ const announceInterval = setInterval(auto, intervalMs);
149
+ auto();
150
+
151
+ return {send,data}
152
+
153
+ }
154
+
155
+ export default createSignalingServer;
156
+
157
+ export {createSignalingServer};