@vira-ui/cli 1.1.1 → 1.2.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 +454 -965
- package/dist/go/appYaml.js +30 -30
- package/dist/go/backendEnvExample.js +17 -17
- package/dist/go/backendReadme.js +14 -14
- package/dist/go/channelHelpers.js +25 -25
- package/dist/go/configGo.js +258 -258
- package/dist/go/dbGo.js +43 -43
- package/dist/go/dbYaml.js +7 -7
- package/dist/go/dockerCompose.js +48 -48
- package/dist/go/dockerComposeProd.js +78 -78
- package/dist/go/dockerfile.js +15 -15
- package/dist/go/eventHandlerTemplate.js +22 -22
- package/dist/go/eventsAPI.js +411 -411
- package/dist/go/goMod.js +16 -16
- package/dist/go/kafkaGo.js +67 -67
- package/dist/go/kafkaYaml.js +6 -6
- package/dist/go/kanbanHandlers.js +216 -216
- package/dist/go/mainGo.js +558 -558
- package/dist/go/readme.js +27 -27
- package/dist/go/redisGo.js +31 -31
- package/dist/go/redisYaml.js +4 -4
- package/dist/go/registryGo.js +38 -38
- package/dist/go/sqlcYaml.js +13 -13
- package/dist/go/stateStore.js +115 -115
- package/dist/go/typesGo.js +11 -11
- package/dist/index.js +636 -38
- package/dist/react/envExample.js +3 -3
- package/dist/react/envLocal.js +1 -1
- package/dist/react/indexCss.js +17 -17
- package/dist/react/indexHtml.js +12 -12
- package/dist/react/kanbanAppTsx.js +29 -29
- package/dist/react/kanbanBoard.js +58 -58
- package/dist/react/kanbanCard.js +60 -60
- package/dist/react/kanbanColumn.js +62 -62
- package/dist/react/kanbanModels.js +32 -32
- package/dist/react/mainTsx.js +12 -12
- package/dist/react/viteConfig.js +27 -27
- package/package.json +47 -45
- package/dist/go/useViraState.js +0 -160
- package/dist/go/useViraStream.js +0 -167
package/dist/react/viteConfig.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.viteConfig = void 0;
|
|
4
|
-
exports.viteConfig = `import { defineConfig, loadEnv } from 'vite';
|
|
5
|
-
import react from '@vitejs/plugin-react';
|
|
6
|
-
|
|
7
|
-
export default defineConfig(({ mode }) => {
|
|
8
|
-
const env = loadEnv(mode, process.cwd(), '');
|
|
9
|
-
const API_URL = env.VITE_API_URL || 'http://localhost:8080';
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
plugins: [react()],
|
|
13
|
-
server: {
|
|
14
|
-
proxy: {
|
|
15
|
-
'/api': {
|
|
16
|
-
target: API_URL,
|
|
17
|
-
changeOrigin: true,
|
|
18
|
-
},
|
|
19
|
-
'/ws': {
|
|
20
|
-
target: API_URL,
|
|
21
|
-
ws: true,
|
|
22
|
-
changeOrigin: true,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
optimizeDeps: {
|
|
27
|
-
exclude: ['lucide-react'],
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
});
|
|
4
|
+
exports.viteConfig = `import { defineConfig, loadEnv } from 'vite';
|
|
5
|
+
import react from '@vitejs/plugin-react';
|
|
6
|
+
|
|
7
|
+
export default defineConfig(({ mode }) => {
|
|
8
|
+
const env = loadEnv(mode, process.cwd(), '');
|
|
9
|
+
const API_URL = env.VITE_API_URL || 'http://localhost:8080';
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
plugins: [react()],
|
|
13
|
+
server: {
|
|
14
|
+
proxy: {
|
|
15
|
+
'/api': {
|
|
16
|
+
target: API_URL,
|
|
17
|
+
changeOrigin: true,
|
|
18
|
+
},
|
|
19
|
+
'/ws': {
|
|
20
|
+
target: API_URL,
|
|
21
|
+
ws: true,
|
|
22
|
+
changeOrigin: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
optimizeDeps: {
|
|
27
|
+
exclude: ['lucide-react'],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
31
|
`;
|
package/package.json
CHANGED
|
@@ -1,45 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@vira-ui/cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "CLI tool for ViraJS project generation",
|
|
5
|
-
"author": "Vira Team",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/skrolikov/vira-cli.git"
|
|
10
|
-
},
|
|
11
|
-
"keywords": [
|
|
12
|
-
"vira",
|
|
13
|
-
"cli",
|
|
14
|
-
"generator",
|
|
15
|
-
"scaffold",
|
|
16
|
-
"framework",
|
|
17
|
-
"react",
|
|
18
|
-
"ui"
|
|
19
|
-
],
|
|
20
|
-
"bin": {
|
|
21
|
-
"vira": "dist/index.js"
|
|
22
|
-
},
|
|
23
|
-
"main": "dist/index.js",
|
|
24
|
-
"files": [
|
|
25
|
-
"dist"
|
|
26
|
-
],
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"inquirer": "^8.2.
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@vira-ui/cli",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "CLI tool for ViraJS project generation",
|
|
5
|
+
"author": "Vira Team",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/skrolikov/vira-cli.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"vira",
|
|
13
|
+
"cli",
|
|
14
|
+
"generator",
|
|
15
|
+
"scaffold",
|
|
16
|
+
"framework",
|
|
17
|
+
"react",
|
|
18
|
+
"ui"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"vira": "dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"main": "dist/index.js",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"commander": "^11.1.0",
|
|
29
|
+
"fs-extra": "^11.2.0",
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"inquirer": "^8.2.6",
|
|
32
|
+
"glob": "^10.3.10"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/fs-extra": "^11.0.4",
|
|
36
|
+
"@types/inquirer": "^8.2.10",
|
|
37
|
+
"@types/node": "^20.10.0",
|
|
38
|
+
"typescript": "^5.3.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"dev": "tsc --watch"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/dist/go/useViraState.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useViraState = void 0;
|
|
4
|
-
exports.useViraState = `import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
|
|
5
|
-
|
|
6
|
-
type Message = { type: string; channel?: string; data?: any; patch?: any; ts?: number; session?: string; interval?: number; versionNo?: number };
|
|
7
|
-
const WS_PATH = '/ws';
|
|
8
|
-
|
|
9
|
-
export function useViraState<T = any, C extends string = string>(channel: C, initial: T | null = null) {
|
|
10
|
-
const [data, setData] = useState<T | null>(initial);
|
|
11
|
-
const wsRef = useRef<WebSocket | null>(null);
|
|
12
|
-
const sessionRef = useRef<string | null>(null);
|
|
13
|
-
const bufferRef = useRef<Array<{ send: () => void; ver: number }>>([]);
|
|
14
|
-
const versionRef = useRef<number>(0);
|
|
15
|
-
const lastSentVersionRef = useRef<number>(0);
|
|
16
|
-
const url = useMemo(() => {
|
|
17
|
-
const api = import.meta.env.VITE_API_URL || 'http://localhost:8080';
|
|
18
|
-
return api.replace(/^http/, 'ws') + WS_PATH;
|
|
19
|
-
}, []);
|
|
20
|
-
|
|
21
|
-
const sendEvent = useCallback(
|
|
22
|
-
(name: string, payload: any) => {
|
|
23
|
-
const ver = versionRef.current;
|
|
24
|
-
const send = () =>
|
|
25
|
-
wsRef.current?.send(
|
|
26
|
-
JSON.stringify({ type: 'event', name, channel, data: payload, ts: Date.now(), versionNo: ver })
|
|
27
|
-
);
|
|
28
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
29
|
-
send();
|
|
30
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
31
|
-
} else {
|
|
32
|
-
bufferRef.current.push({ send, ver });
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
[channel]
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const sendUpdate = useCallback(
|
|
39
|
-
(payload: any) => {
|
|
40
|
-
const ver = versionRef.current;
|
|
41
|
-
const send = () =>
|
|
42
|
-
wsRef.current?.send(
|
|
43
|
-
JSON.stringify({ type: 'update', channel, data: payload, ts: Date.now(), versionNo: ver })
|
|
44
|
-
);
|
|
45
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
46
|
-
send();
|
|
47
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
48
|
-
} else {
|
|
49
|
-
bufferRef.current.push({ send, ver });
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
[channel]
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const sendDiff = useCallback(
|
|
56
|
-
(patch: any) => {
|
|
57
|
-
const ver = versionRef.current;
|
|
58
|
-
const send = () =>
|
|
59
|
-
wsRef.current?.send(
|
|
60
|
-
JSON.stringify({ type: 'diff', channel, patch, ts: Date.now(), versionNo: ver })
|
|
61
|
-
);
|
|
62
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
63
|
-
send();
|
|
64
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
65
|
-
} else {
|
|
66
|
-
bufferRef.current.push({ send, ver });
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
[channel]
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
if (!channel) return;
|
|
74
|
-
let closed = false;
|
|
75
|
-
let timeout = 500;
|
|
76
|
-
|
|
77
|
-
const connect = () => {
|
|
78
|
-
if (closed) return;
|
|
79
|
-
const ws = new WebSocket(url);
|
|
80
|
-
wsRef.current = ws;
|
|
81
|
-
|
|
82
|
-
ws.onopen = () => {
|
|
83
|
-
ws.send(
|
|
84
|
-
JSON.stringify({
|
|
85
|
-
type: 'handshake',
|
|
86
|
-
client: 'vira-react',
|
|
87
|
-
version: '0.1',
|
|
88
|
-
authToken: import.meta.env.VITE_AUTH_TOKEN || '',
|
|
89
|
-
ts: Date.now(),
|
|
90
|
-
session: sessionRef.current || undefined,
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
ws.send(JSON.stringify({ type: 'sub', channels: [channel] }));
|
|
94
|
-
// flush buffered messages
|
|
95
|
-
bufferRef.current.splice(0).forEach(({ send, ver }) => {
|
|
96
|
-
if (ver <= lastSentVersionRef.current) return;
|
|
97
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
98
|
-
send();
|
|
99
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
ws.onmessage = (evt) => {
|
|
105
|
-
try {
|
|
106
|
-
const msg = JSON.parse(evt.data as string) as Message;
|
|
107
|
-
if (msg.type === 'ack' && msg.session) {
|
|
108
|
-
sessionRef.current = msg.session;
|
|
109
|
-
}
|
|
110
|
-
if ((msg.type === 'update' || msg.type === 'event') && msg.channel === channel) {
|
|
111
|
-
if (msg.versionNo && msg.versionNo < versionRef.current) return;
|
|
112
|
-
if (msg.versionNo) versionRef.current = msg.versionNo;
|
|
113
|
-
setData(msg.data as T);
|
|
114
|
-
}
|
|
115
|
-
if (msg.type === 'diff' && msg.channel === channel && msg.patch && typeof msg.patch === 'object') {
|
|
116
|
-
if (msg.versionNo && msg.versionNo < versionRef.current) return;
|
|
117
|
-
if (msg.versionNo) versionRef.current = msg.versionNo;
|
|
118
|
-
setData((prev) => {
|
|
119
|
-
if (prev && typeof prev === 'object') {
|
|
120
|
-
return { ...(prev as any), ...(msg.patch as any) };
|
|
121
|
-
}
|
|
122
|
-
return (msg.patch as T) || prev;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
if (msg.type === 'ping') {
|
|
126
|
-
ws.send(JSON.stringify({ type: 'pong', ts: Date.now() }));
|
|
127
|
-
}
|
|
128
|
-
} catch {
|
|
129
|
-
// ignore malformed
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
ws.onclose = () => {
|
|
134
|
-
wsRef.current = null;
|
|
135
|
-
if (closed) return;
|
|
136
|
-
setTimeout(connect, timeout);
|
|
137
|
-
timeout = Math.min(timeout * 2, 5000);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
ws.onerror = () => {
|
|
141
|
-
ws.close();
|
|
142
|
-
};
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
connect();
|
|
146
|
-
|
|
147
|
-
return () => {
|
|
148
|
-
closed = true;
|
|
149
|
-
wsRef.current?.close();
|
|
150
|
-
};
|
|
151
|
-
}, [channel, url]);
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
data,
|
|
155
|
-
sendEvent,
|
|
156
|
-
sendUpdate,
|
|
157
|
-
sendDiff,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
`;
|
package/dist/go/useViraStream.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useViraStream = void 0;
|
|
4
|
-
exports.useViraStream = `import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
|
|
5
|
-
|
|
6
|
-
type Message = { type: string; channel?: string; data?: any; session?: string; interval?: number; versionNo?: number };
|
|
7
|
-
const WS_PATH = '/ws';
|
|
8
|
-
|
|
9
|
-
export function useViraStream<T = any, C extends string = string>(channel: C) {
|
|
10
|
-
const [data, setData] = useState<T | null>(null);
|
|
11
|
-
const wsRef = useRef<WebSocket | null>(null);
|
|
12
|
-
const sessionRef = useRef<string | null>(null);
|
|
13
|
-
const bufferRef = useRef<Array<{ msg: any; ver: number }>>([]);
|
|
14
|
-
const versionRef = useRef<number>(0);
|
|
15
|
-
const lastSentVersionRef = useRef<number>(0);
|
|
16
|
-
const url = useMemo(() => {
|
|
17
|
-
const api = import.meta.env.VITE_API_URL || 'http://localhost:8080';
|
|
18
|
-
return api.replace(/^http/, 'ws') + WS_PATH;
|
|
19
|
-
}, []);
|
|
20
|
-
|
|
21
|
-
const sendEvent = useCallback(
|
|
22
|
-
(name: string, payload: any, msgId?: string) => {
|
|
23
|
-
const ver = versionRef.current;
|
|
24
|
-
const msg: any = { type: 'event', name, channel, data: payload, ts: Date.now(), versionNo: ver };
|
|
25
|
-
if (msgId) msg.msgId = msgId;
|
|
26
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
27
|
-
wsRef.current.send(JSON.stringify(msg));
|
|
28
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
29
|
-
} else {
|
|
30
|
-
bufferRef.current.push({ msg, ver });
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
[channel]
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
const sendUpdate = useCallback(
|
|
37
|
-
(payload: any, msgId?: string) => {
|
|
38
|
-
const ver = versionRef.current;
|
|
39
|
-
const msg: any = { type: 'update', channel, data: payload, ts: Date.now(), versionNo: ver };
|
|
40
|
-
if (msgId) msg.msgId = msgId;
|
|
41
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
42
|
-
wsRef.current.send(JSON.stringify(msg));
|
|
43
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
44
|
-
} else {
|
|
45
|
-
bufferRef.current.push({ msg, ver });
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
[channel]
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const sendDiff = useCallback(
|
|
52
|
-
(patch: any, msgId?: string) => {
|
|
53
|
-
const ver = versionRef.current;
|
|
54
|
-
const msg: any = { type: 'diff', channel, patch, ts: Date.now(), versionNo: ver };
|
|
55
|
-
if (msgId) msg.msgId = msgId;
|
|
56
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
57
|
-
wsRef.current.send(JSON.stringify(msg));
|
|
58
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
59
|
-
} else {
|
|
60
|
-
bufferRef.current.push({ msg, ver });
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
[channel]
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
if (!channel) return;
|
|
68
|
-
let closed = false;
|
|
69
|
-
let timeout = 500;
|
|
70
|
-
let pingIntervalId: any;
|
|
71
|
-
|
|
72
|
-
const connect = () => {
|
|
73
|
-
if (closed) return;
|
|
74
|
-
const ws = new WebSocket(url);
|
|
75
|
-
wsRef.current = ws;
|
|
76
|
-
|
|
77
|
-
ws.onopen = () => {
|
|
78
|
-
console.log(\`WS connected to \${url}\`);
|
|
79
|
-
ws.send(
|
|
80
|
-
JSON.stringify({
|
|
81
|
-
type: 'handshake',
|
|
82
|
-
client: 'vira-react',
|
|
83
|
-
version: '0.1',
|
|
84
|
-
authToken: import.meta.env.VITE_AUTH_TOKEN || '',
|
|
85
|
-
session: sessionRef.current || undefined,
|
|
86
|
-
ts: Date.now(),
|
|
87
|
-
})
|
|
88
|
-
);
|
|
89
|
-
ws.send(JSON.stringify({ type: 'sub', channels: [channel] }));
|
|
90
|
-
// Resend buffered messages
|
|
91
|
-
const buffered = bufferRef.current.splice(0);
|
|
92
|
-
buffered.forEach(({ msg, ver }) => {
|
|
93
|
-
if (ver <= lastSentVersionRef.current) return;
|
|
94
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
95
|
-
ws.send(JSON.stringify(msg));
|
|
96
|
-
lastSentVersionRef.current = Math.max(lastSentVersionRef.current, ver);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
timeout = 500; // Reset timeout on successful connection
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
ws.onmessage = (evt) => {
|
|
103
|
-
try {
|
|
104
|
-
const msg = JSON.parse(evt.data as string) as Message;
|
|
105
|
-
if (msg.type === 'ack' && msg.session) {
|
|
106
|
-
sessionRef.current = msg.session;
|
|
107
|
-
if (msg.interval) {
|
|
108
|
-
clearInterval(pingIntervalId);
|
|
109
|
-
pingIntervalId = setInterval(() => {
|
|
110
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
111
|
-
ws.send(JSON.stringify({ type: 'ping', ts: Date.now() }));
|
|
112
|
-
}
|
|
113
|
-
}, msg.interval);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if ((msg.type === 'update' || msg.type === 'event') && msg.channel === channel) {
|
|
117
|
-
if (msg.versionNo && msg.versionNo < versionRef.current) return;
|
|
118
|
-
if (msg.versionNo) versionRef.current = msg.versionNo;
|
|
119
|
-
setData(msg.data as T);
|
|
120
|
-
}
|
|
121
|
-
if (msg.type === 'diff' && msg.channel === channel && msg.patch && typeof msg.patch === 'object') {
|
|
122
|
-
setData((prev) => {
|
|
123
|
-
if (prev && typeof prev === 'object') {
|
|
124
|
-
return { ...(prev as any), ...(msg.patch as any) };
|
|
125
|
-
}
|
|
126
|
-
return (msg.patch as T) || prev;
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
if (msg.type === 'ping') {
|
|
130
|
-
ws.send(JSON.stringify({ type: 'pong', ts: Date.now() }));
|
|
131
|
-
}
|
|
132
|
-
} catch {
|
|
133
|
-
// ignore malformed
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
ws.onclose = () => {
|
|
138
|
-
console.log('WS disconnected. Reconnecting...');
|
|
139
|
-
wsRef.current = null;
|
|
140
|
-
clearInterval(pingIntervalId);
|
|
141
|
-
if (closed) return;
|
|
142
|
-
setTimeout(connect, timeout);
|
|
143
|
-
timeout = Math.min(timeout * 2, 30000); // Max 30 seconds
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
ws.onerror = () => {
|
|
147
|
-
ws.close();
|
|
148
|
-
};
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
connect();
|
|
152
|
-
|
|
153
|
-
return () => {
|
|
154
|
-
closed = true;
|
|
155
|
-
clearInterval(pingIntervalId);
|
|
156
|
-
wsRef.current?.close();
|
|
157
|
-
};
|
|
158
|
-
}, [channel, url]);
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
data,
|
|
162
|
-
sendEvent,
|
|
163
|
-
sendUpdate,
|
|
164
|
-
sendDiff,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
`;
|