iosignal 2.2.1 → 3.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/README.md +248 -114
- package/dist/io.d.ts +481 -0
- package/dist/io.js +4 -5
- package/dist/io.js.map +1 -1
- package/dist/io.min.js +4 -5
- package/dist/io.min.js.map +1 -1
- package/dist/iosignal.js +9559 -0
- package/dist/types/client/IOCore.d.ts +408 -0
- package/dist/types/client/browser/IOWebSocket.d.ts +75 -0
- package/dist/types/common/constants.d.ts +169 -0
- package/dist/types/common/payload.d.ts +6 -0
- package/dist/types/common/quotaTable.d.ts +47 -0
- package/docs/README.ko.md +371 -0
- package/docs/iosignal_architecture.png +0 -0
- package/examples/react-chat-js/dist/assets/index-Cy4qaVcJ.js +56 -0
- package/examples/react-chat-js/dist/assets/index-NETrjYhz.css +1 -0
- package/examples/react-chat-js/dist/index.html +14 -0
- package/examples/react-chat-js/index.html +13 -0
- package/examples/react-chat-js/package-lock.json +1628 -0
- package/examples/react-chat-js/package.json +20 -0
- package/examples/react-chat-js/src/App.css +61 -0
- package/examples/react-chat-js/src/App.jsx +125 -0
- package/examples/react-chat-js/src/index.css +13 -0
- package/examples/react-chat-js/src/main.jsx +13 -0
- package/examples/react-chat-js/src/shared_io.js +12 -0
- package/examples/react-chat-js/vite.config.js +7 -0
- package/examples/server/index.js +12 -0
- package/examples/server/package.json +12 -0
- package/package.json +24 -12
- package/rollup.config.js +13 -8
- package/src/auth/Auth_File.js +1 -1
- package/src/client/IOCongSocket.js +20 -9
- package/src/client/IOCore.js +422 -77
- package/src/client/IOWS.js +12 -15
- package/src/client/browser/IOWebSocket.js +217 -0
- package/src/common/constants.js +91 -1
- package/test-nodejs/server-Auth_File.js +1 -1
- package/tsconfig.json +24 -0
- package/dist/iosignal.cjs +0 -23
- package/dist/iosignal.mjs +0 -23
- package/img/iosignal_stack.png +0 -0
- package/src/client/IOWebSocket.js +0 -118
- package/test-nodejs/client.cjs +0 -17
- package/test-nodejs/server-commonjs.cjs +0 -9
- /package/{auth_file.mjs → auth_file.js} +0 -0
- /package/test-nodejs/{client.mjs → client.js} +0 -0
- /package/test-nodejs/{client_api_reply.mjs → client_api_reply.js} +0 -0
- /package/test-nodejs/{server-esm.mjs → server.js} +0 -0
- /package/test-nodejs/{simple-server.mjs → simple-server.js} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-chat-js",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"react": "^18.2.0",
|
|
13
|
+
"react-dom": "^18.2.0",
|
|
14
|
+
"iosignal": "file:../.."
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
18
|
+
"vite": "^5.0.8"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
html, body, #root, .App {
|
|
2
|
+
height: 100%;
|
|
3
|
+
margin: 0;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.App {
|
|
9
|
+
padding: 20px;
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
h1 {
|
|
14
|
+
margin-top: 0;
|
|
15
|
+
color: #333;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.messages {
|
|
19
|
+
flex-grow: 1; /* 남은 공간을 모두 차지하도록 설정 */
|
|
20
|
+
border: 1px solid #ccc;
|
|
21
|
+
padding: 10px;
|
|
22
|
+
overflow-y: auto; /* 내용이 넘치면 스크롤바 표시 */
|
|
23
|
+
margin-bottom: 10px;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column; /* 메시지를 아래에서 위로 쌓이도록 */
|
|
26
|
+
justify-content: flex-end; /* 메시지를 아래로 정렬 */
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.messages div {
|
|
30
|
+
background-color: #f0f0f0;
|
|
31
|
+
padding: 8px;
|
|
32
|
+
margin-bottom: 5px;
|
|
33
|
+
border-radius: 5px;
|
|
34
|
+
text-align: left;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.input-area {
|
|
38
|
+
display: flex;
|
|
39
|
+
gap: 10px;
|
|
40
|
+
margin-top: auto; /* 항상 하단에 위치하도록 */
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.input-area input {
|
|
44
|
+
flex-grow: 1;
|
|
45
|
+
padding: 10px;
|
|
46
|
+
border: 1px solid #ccc;
|
|
47
|
+
border-radius: 5px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.input-area button {
|
|
51
|
+
padding: 10px 15px;
|
|
52
|
+
background-color: #007bff;
|
|
53
|
+
color: white;
|
|
54
|
+
border: none;
|
|
55
|
+
border-radius: 5px;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.input-area button:hover {
|
|
60
|
+
background-color: #0056b3;
|
|
61
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import './App.css';
|
|
3
|
+
import IO from 'iosignal/io.js';
|
|
4
|
+
|
|
5
|
+
const url = 'ws://localhost:7777';
|
|
6
|
+
const channel_tag = 'channel#topic';
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
const [messages, setMessages] = useState([]);
|
|
10
|
+
const [input, setInput] = useState('Hello, World!');
|
|
11
|
+
const [ioState, setIoState] = useState(null); // ioState is updated by io.on('change')
|
|
12
|
+
const [cid, setCid] = useState(null); // cid is updated by io.on('ready')
|
|
13
|
+
const [counts, setCounts] = useState({ instances: 0, websockets: 0 }); // State for IO counts
|
|
14
|
+
const ioRef = useRef(null); // Manage io instance with useRef
|
|
15
|
+
const messagesEndRef = useRef(null);
|
|
16
|
+
|
|
17
|
+
const scrollToBottom = () => {
|
|
18
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
scrollToBottom();
|
|
23
|
+
}, [messages]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
console.log(IO.version)
|
|
27
|
+
ioRef.current = new IO(url); // Create io instance with URL
|
|
28
|
+
setCounts({ instances: IO.instanceCount, websockets: IO.webSocketCount }); // Set initial counts
|
|
29
|
+
|
|
30
|
+
if (!ioRef.current) {
|
|
31
|
+
console.error('IO instance is not initialized.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const handleReady = () => {
|
|
36
|
+
console.log('ready cid:', ioRef.current.cid);
|
|
37
|
+
setCid(ioRef.current.cid); // Update cid state
|
|
38
|
+
ioRef.current.subscribe(channel_tag); // Subscribe to channel
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const handleChange = (state) => {
|
|
42
|
+
setIoState(state);
|
|
43
|
+
setCounts({ instances: IO.instanceCount, websockets: IO.webSocketCount }); // Update counts on state change
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const handleChannelMessage = (msgObj, tag) => {
|
|
47
|
+
console.log('Received message in App:', msgObj, tag);
|
|
48
|
+
if( typeof msgObj === 'string' ) {
|
|
49
|
+
msgObj = { text: msgObj, cid: 'cli unknown' }; // Convert string to object if necessary
|
|
50
|
+
}
|
|
51
|
+
setMessages((prevMessages) => [...prevMessages, `${msgObj.cid} : ${msgObj.text}`]); // Update messages state]);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleError = (error) => {
|
|
55
|
+
console.error('IO Error in App:', error);
|
|
56
|
+
setIoState(`Error: ${error.message}`);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
ioRef.current.on('ready', handleReady);
|
|
60
|
+
ioRef.current.on('change', handleChange);
|
|
61
|
+
ioRef.current.on(channel_tag, handleChannelMessage); // Add channel message listener
|
|
62
|
+
ioRef.current.on('error', handleError);
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
// Cleanup function: remove event listeners when component unmounts
|
|
66
|
+
return () => {
|
|
67
|
+
// ioRef.current.off('ready', handleReady);
|
|
68
|
+
// ioRef.current.off('change', handleChange);
|
|
69
|
+
// ioRef.current.off(channel_tag, handleChannelMessage);
|
|
70
|
+
// ioRef.current.off('error', handleError);
|
|
71
|
+
|
|
72
|
+
// destroy() is called to clean up the io instance, all listeners are removed automatically
|
|
73
|
+
ioRef.current.destroy(); // Clean up the io instance
|
|
74
|
+
console.log('IO instance destroyed.');
|
|
75
|
+
ioRef.current = null; // Release the io instance reference
|
|
76
|
+
};
|
|
77
|
+
}, []); // Empty dependency array ensures this runs only once on mount
|
|
78
|
+
|
|
79
|
+
const sendMessage = () => {
|
|
80
|
+
if (input.trim()) {
|
|
81
|
+
console.log('Sending message:', input);
|
|
82
|
+
const msgObj = { text: input, cid: ioRef.current.cid };
|
|
83
|
+
console.log('Sending message object:', msgObj);
|
|
84
|
+
ioRef.current.signal(channel_tag, msgObj); // Send message to channel
|
|
85
|
+
setInput('date' + Date.now());
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const ioStateStyle = {
|
|
90
|
+
color: ioState === 'ready' ? 'green' : 'red',
|
|
91
|
+
fontWeight: 'bold',
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div className="App">
|
|
96
|
+
<h1>React Chat Example</h1>
|
|
97
|
+
<div>URL: {url}</div>
|
|
98
|
+
<div>Channel: {channel_tag}</div>
|
|
99
|
+
<div>IO State: <span style={ioStateStyle}>{ioState}</span></div>
|
|
100
|
+
<div>Client ID: {cid}</div>
|
|
101
|
+
<div>IO Instances: {counts.instances}</div>
|
|
102
|
+
<div>WebSockets Created: {counts.websockets}</div>
|
|
103
|
+
<div className="messages">
|
|
104
|
+
{messages.map((msg, index) => (
|
|
105
|
+
<div key={index}>{msg}</div>
|
|
106
|
+
))}
|
|
107
|
+
<div ref={messagesEndRef} />
|
|
108
|
+
</div>
|
|
109
|
+
<div className="input-area">
|
|
110
|
+
<input
|
|
111
|
+
type="text"
|
|
112
|
+
value={input}
|
|
113
|
+
onChange={(e) => setInput(e.target.value)}
|
|
114
|
+
onKeyUp={(e) => e.key === 'Enter' && sendMessage()}
|
|
115
|
+
disabled={ioState !== 'ready'}
|
|
116
|
+
/>
|
|
117
|
+
<button onClick={sendMessage} disabled={ioState !== 'ready'}>
|
|
118
|
+
Send
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default App;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
body {
|
|
2
|
+
margin: 0;
|
|
3
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
4
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
5
|
+
sans-serif;
|
|
6
|
+
-webkit-font-smoothing: antialiased;
|
|
7
|
+
-moz-osx-font-smoothing: grayscale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
code {
|
|
11
|
+
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
12
|
+
monospace;
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom/client'
|
|
3
|
+
import App from './App.jsx'
|
|
4
|
+
import './index.css'
|
|
5
|
+
|
|
6
|
+
const strictModeEnabled = true; // true/false 바꾸기만 하면 됨
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
10
|
+
strictModeEnabled
|
|
11
|
+
? <React.StrictMode><App /></React.StrictMode>
|
|
12
|
+
: <App />
|
|
13
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Server } from "iosignal"
|
|
2
|
+
|
|
3
|
+
const server = new Server(
|
|
4
|
+
{
|
|
5
|
+
port: 7777,
|
|
6
|
+
congPort: 8888,
|
|
7
|
+
showMetric: 2,
|
|
8
|
+
showMessage: 'message' // show signal message
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
console.log('Signaling server started. Listening on port 7777 and congPort 8888');
|
package/package.json
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iosignal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "iosignal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
|
-
"import": "./dist/iosignal.
|
|
9
|
-
"require":
|
|
8
|
+
"import": "./dist/iosignal.js",
|
|
9
|
+
"require": null
|
|
10
10
|
},
|
|
11
11
|
"./io": "./dist/io.js",
|
|
12
12
|
"./io.js": "./dist/io.js",
|
|
13
13
|
"./browser": "./dist/io.js"
|
|
14
14
|
},
|
|
15
|
+
"types": "./dist/types/src/client/browser/IOWebSocket.d.ts",
|
|
15
16
|
"browser": "./dist/io.min.js",
|
|
16
17
|
"jsdelivr": "./dist/io.min.js",
|
|
17
18
|
"unpkg": "./dist/io.min.js",
|
|
18
19
|
"scripts": {
|
|
19
|
-
"build": "rollup -c",
|
|
20
|
+
"build": "rollup -c && npm run build:types",
|
|
21
|
+
"build:types": "tsc --project tsconfig.json",
|
|
20
22
|
"start": "node ./test-browser/server.js"
|
|
21
23
|
},
|
|
22
24
|
"engines": {
|
|
23
|
-
"node": ">=
|
|
25
|
+
"node": ">=18.0.0"
|
|
24
26
|
},
|
|
25
27
|
"author": {
|
|
26
28
|
"name": "Taeo Lee",
|
|
@@ -31,14 +33,18 @@
|
|
|
31
33
|
"@rollup/plugin-commonjs": "^28.0.0",
|
|
32
34
|
"@rollup/plugin-json": "^6.1.0",
|
|
33
35
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
36
|
+
"@rollup/plugin-replace": "^6.0.1",
|
|
34
37
|
"@rollup/plugin-terser": "^0.4.4",
|
|
35
|
-
"
|
|
38
|
+
"@types/node": "^24.1.0",
|
|
39
|
+
"rollup": "^4.36.0",
|
|
40
|
+
"rollup-plugin-dts": "^6.2.1",
|
|
41
|
+
"typescript": "^5.8.3"
|
|
36
42
|
},
|
|
37
43
|
"dependencies": {
|
|
38
|
-
"boho": "^1.
|
|
44
|
+
"boho": "^1.1.1",
|
|
39
45
|
"eventemitter3": "^5.0.1",
|
|
40
|
-
"meta-buffer-pack": "^2.0
|
|
41
|
-
"ws": "^8.18.
|
|
46
|
+
"meta-buffer-pack": "^2.1.0",
|
|
47
|
+
"ws": "^8.18.3"
|
|
42
48
|
},
|
|
43
49
|
"optionalDependencies": {
|
|
44
50
|
"bufferutil": "^4.0.9",
|
|
@@ -52,9 +58,15 @@
|
|
|
52
58
|
"keywords": [
|
|
53
59
|
"websocket",
|
|
54
60
|
"pubsub",
|
|
55
|
-
"
|
|
61
|
+
"messaging",
|
|
62
|
+
"real-time",
|
|
63
|
+
"signaling",
|
|
64
|
+
"signal",
|
|
65
|
+
"client-server",
|
|
66
|
+
"nodejs",
|
|
67
|
+
"browser",
|
|
56
68
|
"iot",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
69
|
+
"arduino",
|
|
70
|
+
"auth"
|
|
59
71
|
]
|
|
60
72
|
}
|
package/rollup.config.js
CHANGED
|
@@ -2,10 +2,11 @@ import resolve from '@rollup/plugin-node-resolve'
|
|
|
2
2
|
import commonjs from '@rollup/plugin-commonjs'
|
|
3
3
|
import json from '@rollup/plugin-json'
|
|
4
4
|
import terser from '@rollup/plugin-terser'
|
|
5
|
+
import dts from 'rollup-plugin-dts'
|
|
5
6
|
|
|
6
7
|
export default [
|
|
7
|
-
|
|
8
|
-
input: './src/client/IOWebSocket.js',
|
|
8
|
+
{
|
|
9
|
+
input: './src/client/browser/IOWebSocket.js',
|
|
9
10
|
output: [
|
|
10
11
|
{
|
|
11
12
|
file: './dist/io.js',
|
|
@@ -23,16 +24,21 @@ export default [
|
|
|
23
24
|
resolve({
|
|
24
25
|
preferBuiltins: false
|
|
25
26
|
}),
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
commonjs(),
|
|
28
|
+
json(),
|
|
29
|
+
terser()
|
|
29
30
|
]
|
|
30
31
|
},
|
|
31
32
|
{
|
|
33
|
+
input: './src/client/browser/IOWebSocket.js',
|
|
34
|
+
output: { file: './dist/io.d.ts', format: 'es' },
|
|
35
|
+
plugins: [dts()]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
,{
|
|
32
39
|
input: './index.js',
|
|
33
40
|
output: [
|
|
34
|
-
{ file: './dist/iosignal.
|
|
35
|
-
{ file: './dist/iosignal.mjs', format: 'es' }
|
|
41
|
+
{ file: './dist/iosignal.js', format: 'es' }
|
|
36
42
|
],
|
|
37
43
|
plugins: [
|
|
38
44
|
resolve({
|
|
@@ -40,7 +46,6 @@ export default [
|
|
|
40
46
|
}),
|
|
41
47
|
json(),
|
|
42
48
|
commonjs()
|
|
43
|
-
,terser()
|
|
44
49
|
]
|
|
45
50
|
}
|
|
46
51
|
|
package/src/auth/Auth_File.js
CHANGED
|
@@ -11,7 +11,7 @@ export class Auth_File extends BohoAuth {
|
|
|
11
11
|
this.path = path.resolve(_path)
|
|
12
12
|
console.log('auth from file path:', this.path)
|
|
13
13
|
let ext = pathObj.ext;
|
|
14
|
-
if (ext.toLowerCase() == '.js'
|
|
14
|
+
if (ext.toLowerCase() == '.js') {
|
|
15
15
|
// Read auth data from file
|
|
16
16
|
console.log("#JS path:", this.path);
|
|
17
17
|
this.loadAuthInfoFile_JS(this.path)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IOCore } from "./IOCore.js"
|
|
2
|
+
import { STATES } from "../common/constants.js";
|
|
2
3
|
import { pack, CongRx } from './CongPacket.js'
|
|
3
4
|
import net from 'net'
|
|
4
5
|
|
|
@@ -9,20 +10,30 @@ export class IOCongSocket extends IOCore {
|
|
|
9
10
|
if (url) this.open();
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Closes TCP socket and cleans resources.
|
|
15
|
+
*/
|
|
12
16
|
close() {
|
|
13
|
-
this.socket
|
|
14
|
-
|
|
17
|
+
if (this.socket) {
|
|
18
|
+
if (this.congRx) {
|
|
19
|
+
this.socket.unpipe(this.congRx);
|
|
20
|
+
this.congRx.destroy(); // Use destroy() for complete stream cleanup
|
|
21
|
+
this.congRx = null;
|
|
22
|
+
}
|
|
23
|
+
this.socket.removeAllListeners();
|
|
24
|
+
if (!this.socket.destroyed) {
|
|
25
|
+
this.socket.destroy(); // destroy() is sufficient for forceful closing
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
super.close();
|
|
15
29
|
}
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
this.close()
|
|
19
|
-
clearInterval(this.connectionCheckerIntervalID);
|
|
20
|
-
this.connectionCheckerIntervalID = null
|
|
21
|
-
}
|
|
31
|
+
|
|
22
32
|
|
|
23
33
|
keepAlive() {
|
|
24
|
-
|
|
25
|
-
if
|
|
34
|
+
if (!this.autoReconnect) return;
|
|
35
|
+
// Reconnect only if the socket is fully destroyed and the state is closed.
|
|
36
|
+
if ((!this.socket || this.socket.destroyed) && this.state === STATES.CLOSED) {
|
|
26
37
|
this.open();
|
|
27
38
|
}
|
|
28
39
|
}
|