stayge-ws-client-sdk 0.1.0
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 +60 -0
- package/dist/index.d.mts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +18 -0
- package/dist/index.mjs +0 -0
- package/dist/react/index.d.mts +5 -0
- package/dist/react/index.d.ts +5 -0
- package/dist/react/index.js +107 -0
- package/dist/react/index.mjs +80 -0
- package/dist/react-native/index.d.mts +5 -0
- package/dist/react-native/index.d.ts +5 -0
- package/dist/react-native/index.js +108 -0
- package/dist/react-native/index.mjs +81 -0
- package/package.json +33 -0
- package/src/core/WebSocketClient.ts +20 -0
- package/src/core/WebSocketClientImpl.ts +92 -0
- package/src/index.ts +1 -0
- package/src/react/index.ts +10 -0
- package/src/react-native/index.ts +13 -0
- package/tsconfig.json +18 -0
- package/tsup.config.ts +10 -0
- package/vite.config.ts +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
## 사용예시
|
|
2
|
+
|
|
3
|
+
### React
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
import { createWebSocketClient } from 'stayge-ws-client-sdk/react';
|
|
7
|
+
|
|
8
|
+
const ws = createReactClient('wss://myserver.com');
|
|
9
|
+
ws.connect();
|
|
10
|
+
ws.send('Hello');
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### React Native
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
import { createReactNativeClient } from 'stayge-ws-client-sdk/react-native';
|
|
17
|
+
|
|
18
|
+
const ws = createReactNativeClient('wss://myserver.com');
|
|
19
|
+
ws.connect();
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 프로젝트 폴더 구조
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
stayge-ws-client-sdk/
|
|
27
|
+
├── package.json
|
|
28
|
+
├── tsconfig.json
|
|
29
|
+
├── tsup.config.ts
|
|
30
|
+
├── vite.config.ts
|
|
31
|
+
├── src/
|
|
32
|
+
│ ├── index.ts # 공통 진입점
|
|
33
|
+
│ ├── core/
|
|
34
|
+
│ ├── react/
|
|
35
|
+
│ └── react-native/
|
|
36
|
+
└── dist/ # 빌드 산출물 (tsup)
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 빌드 & 배포
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
npm run build
|
|
44
|
+
npm publish --access public
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 정리
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
공통 core는 최대한 플랫폼 비의존적으로 설계
|
|
52
|
+
|
|
53
|
+
플랫폼 별 진입점 (react, react-native) 분리
|
|
54
|
+
|
|
55
|
+
tsup으로 다중 진입점 + ESM/CJS + 타입 지원
|
|
56
|
+
|
|
57
|
+
vite는 샘플 및 테스트 개발 용도로 활용
|
|
58
|
+
|
|
59
|
+
배포 후 NPM에서 React와 React Native에서 각각 import 가능
|
|
60
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type MessageFromServer = {
|
|
2
|
+
topic: string;
|
|
3
|
+
payload: string;
|
|
4
|
+
};
|
|
5
|
+
type MessageToServer = {
|
|
6
|
+
clientId: string;
|
|
7
|
+
type: "auth" | "subscribe" | "unsubscribe";
|
|
8
|
+
token?: string;
|
|
9
|
+
topic?: string;
|
|
10
|
+
};
|
|
11
|
+
interface WebSocketClient {
|
|
12
|
+
connect(): void;
|
|
13
|
+
disconnect(): void;
|
|
14
|
+
subscribe(topic: string): void;
|
|
15
|
+
unsubscribe(topic: string): void;
|
|
16
|
+
onConnect(callback: () => void): void;
|
|
17
|
+
onMessage(callback: (message: MessageFromServer) => void): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { MessageFromServer, MessageToServer, WebSocketClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type MessageFromServer = {
|
|
2
|
+
topic: string;
|
|
3
|
+
payload: string;
|
|
4
|
+
};
|
|
5
|
+
type MessageToServer = {
|
|
6
|
+
clientId: string;
|
|
7
|
+
type: "auth" | "subscribe" | "unsubscribe";
|
|
8
|
+
token?: string;
|
|
9
|
+
topic?: string;
|
|
10
|
+
};
|
|
11
|
+
interface WebSocketClient {
|
|
12
|
+
connect(): void;
|
|
13
|
+
disconnect(): void;
|
|
14
|
+
subscribe(topic: string): void;
|
|
15
|
+
unsubscribe(topic: string): void;
|
|
16
|
+
onConnect(callback: () => void): void;
|
|
17
|
+
onMessage(callback: (message: MessageFromServer) => void): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { MessageFromServer, MessageToServer, WebSocketClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/index.ts
|
|
17
|
+
var src_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(src_exports);
|
package/dist/index.mjs
ADDED
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/react/index.ts
|
|
21
|
+
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
createWebSocketClient: () => createWebSocketClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(react_exports);
|
|
26
|
+
|
|
27
|
+
// src/core/WebSocketClientImpl.ts
|
|
28
|
+
var WebSocketClientImpl = class {
|
|
29
|
+
constructor(url, clientId, accessToken) {
|
|
30
|
+
this.url = url;
|
|
31
|
+
this.clientId = clientId;
|
|
32
|
+
this.accessToken = accessToken;
|
|
33
|
+
this.ws = null;
|
|
34
|
+
this.onConnectCallback = null;
|
|
35
|
+
this.onMessageCallback = null;
|
|
36
|
+
this.reconnectAttempts = 0;
|
|
37
|
+
}
|
|
38
|
+
connect() {
|
|
39
|
+
this.ws = new WebSocket(this.url);
|
|
40
|
+
this.ws.onopen = () => {
|
|
41
|
+
this.reconnectAttempts = 0;
|
|
42
|
+
if (this.onConnectCallback) {
|
|
43
|
+
this.onConnectCallback();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
this.ws.onerror = (error) => {
|
|
47
|
+
console.error("WebSocket error:", error);
|
|
48
|
+
this.reconnect();
|
|
49
|
+
};
|
|
50
|
+
this.ws.onmessage = (event) => {
|
|
51
|
+
if (this.onMessageCallback) {
|
|
52
|
+
try {
|
|
53
|
+
const message = JSON.parse(event.data);
|
|
54
|
+
this.onMessageCallback(message);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Error parsing message:", error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
this.ws.onclose = () => {
|
|
61
|
+
console.error("WebSocket closed");
|
|
62
|
+
this.reconnect();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
disconnect() {
|
|
66
|
+
this.ws?.close();
|
|
67
|
+
this.ws = null;
|
|
68
|
+
}
|
|
69
|
+
subscribe(topic) {
|
|
70
|
+
const message = {
|
|
71
|
+
clientId: this.clientId,
|
|
72
|
+
type: "subscribe",
|
|
73
|
+
topic
|
|
74
|
+
};
|
|
75
|
+
this.ws?.send(JSON.stringify(message));
|
|
76
|
+
}
|
|
77
|
+
unsubscribe(topic) {
|
|
78
|
+
const message = {
|
|
79
|
+
clientId: this.clientId,
|
|
80
|
+
type: "unsubscribe",
|
|
81
|
+
topic
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
onConnect(callback) {
|
|
85
|
+
this.onConnectCallback = callback;
|
|
86
|
+
}
|
|
87
|
+
onMessage(callback) {
|
|
88
|
+
this.onMessageCallback = callback;
|
|
89
|
+
}
|
|
90
|
+
reconnect() {
|
|
91
|
+
console.log("Reconnecting...");
|
|
92
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 1e4);
|
|
93
|
+
this.reconnectAttempts++;
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
this.connect();
|
|
96
|
+
}, delay);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// src/react/index.ts
|
|
101
|
+
function createWebSocketClient(url, clientId, accessToken) {
|
|
102
|
+
return new WebSocketClientImpl(url, clientId, accessToken);
|
|
103
|
+
}
|
|
104
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
105
|
+
0 && (module.exports = {
|
|
106
|
+
createWebSocketClient
|
|
107
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/core/WebSocketClientImpl.ts
|
|
2
|
+
var WebSocketClientImpl = class {
|
|
3
|
+
constructor(url, clientId, accessToken) {
|
|
4
|
+
this.url = url;
|
|
5
|
+
this.clientId = clientId;
|
|
6
|
+
this.accessToken = accessToken;
|
|
7
|
+
this.ws = null;
|
|
8
|
+
this.onConnectCallback = null;
|
|
9
|
+
this.onMessageCallback = null;
|
|
10
|
+
this.reconnectAttempts = 0;
|
|
11
|
+
}
|
|
12
|
+
connect() {
|
|
13
|
+
this.ws = new WebSocket(this.url);
|
|
14
|
+
this.ws.onopen = () => {
|
|
15
|
+
this.reconnectAttempts = 0;
|
|
16
|
+
if (this.onConnectCallback) {
|
|
17
|
+
this.onConnectCallback();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
this.ws.onerror = (error) => {
|
|
21
|
+
console.error("WebSocket error:", error);
|
|
22
|
+
this.reconnect();
|
|
23
|
+
};
|
|
24
|
+
this.ws.onmessage = (event) => {
|
|
25
|
+
if (this.onMessageCallback) {
|
|
26
|
+
try {
|
|
27
|
+
const message = JSON.parse(event.data);
|
|
28
|
+
this.onMessageCallback(message);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("Error parsing message:", error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
this.ws.onclose = () => {
|
|
35
|
+
console.error("WebSocket closed");
|
|
36
|
+
this.reconnect();
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
disconnect() {
|
|
40
|
+
this.ws?.close();
|
|
41
|
+
this.ws = null;
|
|
42
|
+
}
|
|
43
|
+
subscribe(topic) {
|
|
44
|
+
const message = {
|
|
45
|
+
clientId: this.clientId,
|
|
46
|
+
type: "subscribe",
|
|
47
|
+
topic
|
|
48
|
+
};
|
|
49
|
+
this.ws?.send(JSON.stringify(message));
|
|
50
|
+
}
|
|
51
|
+
unsubscribe(topic) {
|
|
52
|
+
const message = {
|
|
53
|
+
clientId: this.clientId,
|
|
54
|
+
type: "unsubscribe",
|
|
55
|
+
topic
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
onConnect(callback) {
|
|
59
|
+
this.onConnectCallback = callback;
|
|
60
|
+
}
|
|
61
|
+
onMessage(callback) {
|
|
62
|
+
this.onMessageCallback = callback;
|
|
63
|
+
}
|
|
64
|
+
reconnect() {
|
|
65
|
+
console.log("Reconnecting...");
|
|
66
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 1e4);
|
|
67
|
+
this.reconnectAttempts++;
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
this.connect();
|
|
70
|
+
}, delay);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/react/index.ts
|
|
75
|
+
function createWebSocketClient(url, clientId, accessToken) {
|
|
76
|
+
return new WebSocketClientImpl(url, clientId, accessToken);
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
createWebSocketClient
|
|
80
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/react-native/index.ts
|
|
21
|
+
var react_native_exports = {};
|
|
22
|
+
__export(react_native_exports, {
|
|
23
|
+
createWebSocketClient: () => createWebSocketClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(react_native_exports);
|
|
26
|
+
|
|
27
|
+
// src/core/WebSocketClientImpl.ts
|
|
28
|
+
var WebSocketClientImpl = class {
|
|
29
|
+
constructor(url, clientId, accessToken) {
|
|
30
|
+
this.url = url;
|
|
31
|
+
this.clientId = clientId;
|
|
32
|
+
this.accessToken = accessToken;
|
|
33
|
+
this.ws = null;
|
|
34
|
+
this.onConnectCallback = null;
|
|
35
|
+
this.onMessageCallback = null;
|
|
36
|
+
this.reconnectAttempts = 0;
|
|
37
|
+
}
|
|
38
|
+
connect() {
|
|
39
|
+
this.ws = new WebSocket(this.url);
|
|
40
|
+
this.ws.onopen = () => {
|
|
41
|
+
this.reconnectAttempts = 0;
|
|
42
|
+
if (this.onConnectCallback) {
|
|
43
|
+
this.onConnectCallback();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
this.ws.onerror = (error) => {
|
|
47
|
+
console.error("WebSocket error:", error);
|
|
48
|
+
this.reconnect();
|
|
49
|
+
};
|
|
50
|
+
this.ws.onmessage = (event) => {
|
|
51
|
+
if (this.onMessageCallback) {
|
|
52
|
+
try {
|
|
53
|
+
const message = JSON.parse(event.data);
|
|
54
|
+
this.onMessageCallback(message);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Error parsing message:", error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
this.ws.onclose = () => {
|
|
61
|
+
console.error("WebSocket closed");
|
|
62
|
+
this.reconnect();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
disconnect() {
|
|
66
|
+
this.ws?.close();
|
|
67
|
+
this.ws = null;
|
|
68
|
+
}
|
|
69
|
+
subscribe(topic) {
|
|
70
|
+
const message = {
|
|
71
|
+
clientId: this.clientId,
|
|
72
|
+
type: "subscribe",
|
|
73
|
+
topic
|
|
74
|
+
};
|
|
75
|
+
this.ws?.send(JSON.stringify(message));
|
|
76
|
+
}
|
|
77
|
+
unsubscribe(topic) {
|
|
78
|
+
const message = {
|
|
79
|
+
clientId: this.clientId,
|
|
80
|
+
type: "unsubscribe",
|
|
81
|
+
topic
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
onConnect(callback) {
|
|
85
|
+
this.onConnectCallback = callback;
|
|
86
|
+
}
|
|
87
|
+
onMessage(callback) {
|
|
88
|
+
this.onMessageCallback = callback;
|
|
89
|
+
}
|
|
90
|
+
reconnect() {
|
|
91
|
+
console.log("Reconnecting...");
|
|
92
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 1e4);
|
|
93
|
+
this.reconnectAttempts++;
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
this.connect();
|
|
96
|
+
}, delay);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// src/react-native/index.ts
|
|
101
|
+
function createWebSocketClient(url, clientId, accessToken) {
|
|
102
|
+
const RNWebSocket = global.WebSocket;
|
|
103
|
+
return new WebSocketClientImpl(url, clientId, accessToken);
|
|
104
|
+
}
|
|
105
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
106
|
+
0 && (module.exports = {
|
|
107
|
+
createWebSocketClient
|
|
108
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/core/WebSocketClientImpl.ts
|
|
2
|
+
var WebSocketClientImpl = class {
|
|
3
|
+
constructor(url, clientId, accessToken) {
|
|
4
|
+
this.url = url;
|
|
5
|
+
this.clientId = clientId;
|
|
6
|
+
this.accessToken = accessToken;
|
|
7
|
+
this.ws = null;
|
|
8
|
+
this.onConnectCallback = null;
|
|
9
|
+
this.onMessageCallback = null;
|
|
10
|
+
this.reconnectAttempts = 0;
|
|
11
|
+
}
|
|
12
|
+
connect() {
|
|
13
|
+
this.ws = new WebSocket(this.url);
|
|
14
|
+
this.ws.onopen = () => {
|
|
15
|
+
this.reconnectAttempts = 0;
|
|
16
|
+
if (this.onConnectCallback) {
|
|
17
|
+
this.onConnectCallback();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
this.ws.onerror = (error) => {
|
|
21
|
+
console.error("WebSocket error:", error);
|
|
22
|
+
this.reconnect();
|
|
23
|
+
};
|
|
24
|
+
this.ws.onmessage = (event) => {
|
|
25
|
+
if (this.onMessageCallback) {
|
|
26
|
+
try {
|
|
27
|
+
const message = JSON.parse(event.data);
|
|
28
|
+
this.onMessageCallback(message);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("Error parsing message:", error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
this.ws.onclose = () => {
|
|
35
|
+
console.error("WebSocket closed");
|
|
36
|
+
this.reconnect();
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
disconnect() {
|
|
40
|
+
this.ws?.close();
|
|
41
|
+
this.ws = null;
|
|
42
|
+
}
|
|
43
|
+
subscribe(topic) {
|
|
44
|
+
const message = {
|
|
45
|
+
clientId: this.clientId,
|
|
46
|
+
type: "subscribe",
|
|
47
|
+
topic
|
|
48
|
+
};
|
|
49
|
+
this.ws?.send(JSON.stringify(message));
|
|
50
|
+
}
|
|
51
|
+
unsubscribe(topic) {
|
|
52
|
+
const message = {
|
|
53
|
+
clientId: this.clientId,
|
|
54
|
+
type: "unsubscribe",
|
|
55
|
+
topic
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
onConnect(callback) {
|
|
59
|
+
this.onConnectCallback = callback;
|
|
60
|
+
}
|
|
61
|
+
onMessage(callback) {
|
|
62
|
+
this.onMessageCallback = callback;
|
|
63
|
+
}
|
|
64
|
+
reconnect() {
|
|
65
|
+
console.log("Reconnecting...");
|
|
66
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 1e4);
|
|
67
|
+
this.reconnectAttempts++;
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
this.connect();
|
|
70
|
+
}, delay);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/react-native/index.ts
|
|
75
|
+
function createWebSocketClient(url, clientId, accessToken) {
|
|
76
|
+
const RNWebSocket = global.WebSocket;
|
|
77
|
+
return new WebSocketClientImpl(url, clientId, accessToken);
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
createWebSocketClient
|
|
81
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stayge-ws-client-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Stayge WebSocket Client SDK",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.cjs"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"import": "./dist/react/index.mjs",
|
|
15
|
+
"require": "./dist/react/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./react-native": {
|
|
18
|
+
"import": "./dist/react-native/index.mjs",
|
|
19
|
+
"require": "./dist/react-native/index.cjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "vite",
|
|
25
|
+
"clean": "rm -rf dist"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"vite": "^5.0.0",
|
|
30
|
+
"tsup": "^7.2.0",
|
|
31
|
+
"typescript": "^5.3.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type MessageFromServer = {
|
|
2
|
+
topic: string;
|
|
3
|
+
payload: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type MessageToServer = {
|
|
7
|
+
clientId: string;
|
|
8
|
+
type: "auth" | "subscribe" | "unsubscribe";
|
|
9
|
+
token?: string; // access token: required for "auth"
|
|
10
|
+
topic?: string; // required for "subscribe" | "unsubscribe"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export interface WebSocketClient {
|
|
14
|
+
connect(): void;
|
|
15
|
+
disconnect(): void;
|
|
16
|
+
subscribe(topic: string): void;
|
|
17
|
+
unsubscribe(topic: string): void;
|
|
18
|
+
onConnect(callback: () => void): void;
|
|
19
|
+
onMessage(callback: (message: MessageFromServer) => void): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WebSocketClient,
|
|
3
|
+
MessageToServer,
|
|
4
|
+
MessageFromServer,
|
|
5
|
+
} from "./WebSocketClient";
|
|
6
|
+
|
|
7
|
+
export class WebSocketClientImpl implements WebSocketClient {
|
|
8
|
+
private ws: WebSocket | null = null;
|
|
9
|
+
private onConnectCallback: (() => void) | null = null;
|
|
10
|
+
private onMessageCallback: ((message: MessageFromServer) => void) | null =
|
|
11
|
+
null;
|
|
12
|
+
private reconnectAttempts = 0;
|
|
13
|
+
constructor(
|
|
14
|
+
private url: string,
|
|
15
|
+
private clientId: string,
|
|
16
|
+
private accessToken: string
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
connect() {
|
|
20
|
+
this.ws = new WebSocket(this.url);
|
|
21
|
+
|
|
22
|
+
this.ws.onopen = () => {
|
|
23
|
+
this.reconnectAttempts = 0;
|
|
24
|
+
if (this.onConnectCallback) {
|
|
25
|
+
this.onConnectCallback();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
this.ws.onerror = (error) => {
|
|
30
|
+
console.error("WebSocket error:", error);
|
|
31
|
+
this.reconnect();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
this.ws.onmessage = (event) => {
|
|
35
|
+
if (this.onMessageCallback) {
|
|
36
|
+
try {
|
|
37
|
+
const message = JSON.parse(event.data) as MessageFromServer;
|
|
38
|
+
this.onMessageCallback(message);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("Error parsing message:", error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
this.ws.onclose = () => {
|
|
46
|
+
console.error("WebSocket closed");
|
|
47
|
+
this.reconnect();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
disconnect() {
|
|
52
|
+
this.ws?.close();
|
|
53
|
+
this.ws = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
subscribe(topic: string) {
|
|
57
|
+
const message: MessageToServer = {
|
|
58
|
+
clientId: this.clientId,
|
|
59
|
+
type: "subscribe",
|
|
60
|
+
topic,
|
|
61
|
+
};
|
|
62
|
+
this.ws?.send(JSON.stringify(message));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
unsubscribe(topic: string) {
|
|
66
|
+
const message: MessageToServer = {
|
|
67
|
+
clientId: this.clientId,
|
|
68
|
+
type: "unsubscribe",
|
|
69
|
+
topic,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
onConnect(callback: () => void) {
|
|
74
|
+
this.onConnectCallback = callback;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
onMessage(callback: (message: MessageFromServer) => void) {
|
|
78
|
+
this.onMessageCallback = callback;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private reconnect() {
|
|
82
|
+
console.log("Reconnecting...");
|
|
83
|
+
|
|
84
|
+
// exponential backoff
|
|
85
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 10000);
|
|
86
|
+
this.reconnectAttempts++;
|
|
87
|
+
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
this.connect();
|
|
90
|
+
}, delay);
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./core/WebSocketClient";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { WebSocketClient } from "../core/WebSocketClient";
|
|
2
|
+
import { WebSocketClientImpl } from "../core/WebSocketClientImpl";
|
|
3
|
+
|
|
4
|
+
export function createWebSocketClient(
|
|
5
|
+
url: string,
|
|
6
|
+
clientId: string,
|
|
7
|
+
accessToken: string
|
|
8
|
+
): WebSocketClient {
|
|
9
|
+
return new WebSocketClientImpl(url, clientId, accessToken);
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare const global: typeof globalThis;
|
|
2
|
+
|
|
3
|
+
import { WebSocketClient } from "../core/WebSocketClient";
|
|
4
|
+
import { WebSocketClientImpl } from "../core/WebSocketClientImpl";
|
|
5
|
+
|
|
6
|
+
export function createWebSocketClient(
|
|
7
|
+
url: string,
|
|
8
|
+
clientId: string,
|
|
9
|
+
accessToken: string
|
|
10
|
+
): WebSocketClient {
|
|
11
|
+
const RNWebSocket = global.WebSocket; // react-native 제공
|
|
12
|
+
return new WebSocketClientImpl(url, clientId, accessToken);
|
|
13
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2020", "DOM"],
|
|
6
|
+
"moduleResolution": "Node",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"outDir": "dist"
|
|
15
|
+
},
|
|
16
|
+
"include": ["src"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|
package/tsup.config.ts
ADDED
package/vite.config.ts
ADDED