i18nexus-cli 3.8.1 → 3.8.2
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/commands/listen.js +126 -45
- package/package.json +1 -1
package/commands/listen.js
CHANGED
|
@@ -6,6 +6,7 @@ const baseUrl = require('../baseUrl');
|
|
|
6
6
|
const { URL } = require('url');
|
|
7
7
|
|
|
8
8
|
const listen = async ({ apiKey, path, compact }) => {
|
|
9
|
+
// Initial sync
|
|
9
10
|
await pull(
|
|
10
11
|
{
|
|
11
12
|
apiKey,
|
|
@@ -15,69 +16,149 @@ const listen = async ({ apiKey, path, compact }) => {
|
|
|
15
16
|
confirmed: false,
|
|
16
17
|
compact
|
|
17
18
|
},
|
|
18
|
-
{
|
|
19
|
-
logging: false,
|
|
20
|
-
successLog: 'Latest strings downloaded'
|
|
21
|
-
}
|
|
19
|
+
{ logging: false, successLog: 'Latest strings downloaded' }
|
|
22
20
|
);
|
|
23
21
|
|
|
24
22
|
const wsUrl = new URL(`${baseUrl.replace(/^http/, 'ws')}/cable`);
|
|
25
23
|
wsUrl.searchParams.set('api_key', apiKey);
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
let ws;
|
|
26
|
+
let pingInterval; // timer sending pings
|
|
27
|
+
let watchdogInterval; // timer detecting sleep/drift
|
|
28
|
+
let lastPong = Date.now();
|
|
29
|
+
let reconnectAttempts = 0;
|
|
30
|
+
const PING_EVERY_MS = 30_000; // send ping every 30s
|
|
31
|
+
const STALE_AFTER_MS = 90_000; // if >90s since last pong => stale
|
|
32
|
+
const WATCHDOG_EVERY_MS = 10_000; // check for sleep every 10s
|
|
33
|
+
|
|
34
|
+
const backoff = () => {
|
|
35
|
+
const base = Math.min(60_000, 1000 * Math.pow(2, reconnectAttempts)); // 1s -> 60s
|
|
36
|
+
const jitter = Math.floor(Math.random() * 1000);
|
|
37
|
+
return base + jitter;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const clearTimers = () => {
|
|
41
|
+
if (pingInterval) clearInterval(pingInterval);
|
|
42
|
+
if (watchdogInterval) clearInterval(watchdogInterval);
|
|
43
|
+
pingInterval = undefined;
|
|
44
|
+
watchdogInterval = undefined;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const startHeartbeat = () => {
|
|
48
|
+
// respond to server pings automatically (ws does this), we send pings to keep NAT/CF alive
|
|
49
|
+
ws.on('pong', () => {
|
|
50
|
+
lastPong = Date.now();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
pingInterval = setInterval(() => {
|
|
54
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
55
|
+
try {
|
|
56
|
+
ws.ping();
|
|
57
|
+
} catch (_) {}
|
|
58
|
+
}
|
|
59
|
+
// consider stale?
|
|
60
|
+
if (Date.now() - lastPong > STALE_AFTER_MS) {
|
|
61
|
+
try {
|
|
62
|
+
ws.terminate();
|
|
63
|
+
} catch (_) {}
|
|
64
|
+
}
|
|
65
|
+
}, PING_EVERY_MS);
|
|
32
66
|
|
|
33
|
-
|
|
34
|
-
|
|
67
|
+
// detect sleep/wake (large timer drift)
|
|
68
|
+
let lastTick = Date.now();
|
|
69
|
+
watchdogInterval = setInterval(() => {
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
if (now - lastTick > PING_EVERY_MS * 3) {
|
|
72
|
+
// slept for a while
|
|
73
|
+
try {
|
|
74
|
+
ws.terminate();
|
|
75
|
+
} catch (_) {}
|
|
76
|
+
}
|
|
77
|
+
lastTick = now;
|
|
78
|
+
}, WATCHDOG_EVERY_MS);
|
|
79
|
+
};
|
|
35
80
|
|
|
81
|
+
const subscribe = () => {
|
|
36
82
|
ws.send(
|
|
37
83
|
JSON.stringify({
|
|
38
84
|
command: 'subscribe',
|
|
39
85
|
identifier: JSON.stringify({ channel: 'CliListenChannel' })
|
|
40
86
|
})
|
|
41
87
|
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{
|
|
60
|
-
logging: false,
|
|
61
|
-
successLog: ' ✔ Translations updated'
|
|
62
|
-
}
|
|
63
|
-
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const connect = () => {
|
|
91
|
+
ws = new WebSocket(wsUrl.toString(), {
|
|
92
|
+
headers: { Origin: 'http://cli.i18nexus.com' }
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
ws.on('open', () => {
|
|
96
|
+
reconnectAttempts = 0;
|
|
97
|
+
lastPong = Date.now();
|
|
98
|
+
console.log(colors.green('Listening for i18nexus string updates...'));
|
|
99
|
+
subscribe();
|
|
100
|
+
startHeartbeat();
|
|
101
|
+
|
|
102
|
+
// Optional: TCP keepalive at the socket layer
|
|
103
|
+
if (ws._socket && ws._socket.setKeepAlive) {
|
|
104
|
+
ws._socket.setKeepAlive(true, 60_000);
|
|
64
105
|
}
|
|
65
|
-
}
|
|
66
|
-
console.error(colors.red('i18nexus Sync Failed'));
|
|
67
|
-
}
|
|
68
|
-
});
|
|
106
|
+
});
|
|
69
107
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
108
|
+
ws.on('message', async message => {
|
|
109
|
+
try {
|
|
110
|
+
const data = JSON.parse(message);
|
|
111
|
+
const payload = data.message;
|
|
112
|
+
if (payload?.event === 'strings.changed') {
|
|
113
|
+
await pull(
|
|
114
|
+
{
|
|
115
|
+
apiKey,
|
|
116
|
+
version: 'latest',
|
|
117
|
+
path,
|
|
118
|
+
clean: false,
|
|
119
|
+
confirmed: false,
|
|
120
|
+
compact
|
|
121
|
+
},
|
|
122
|
+
{ logging: false, successLog: ' ✔ Translations updated' }
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error(colors.red('i18nexus Sync Failed'));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const scheduleReconnect = why => {
|
|
131
|
+
clearTimers();
|
|
132
|
+
if (ws) {
|
|
133
|
+
ws.removeAllListeners();
|
|
134
|
+
}
|
|
135
|
+
reconnectAttempts += 1;
|
|
136
|
+
const delay = backoff();
|
|
137
|
+
console.log(
|
|
138
|
+
colors.yellow(
|
|
139
|
+
`Attempting i18nexus reconnect in ${Math.round(delay / 1000)}s...`
|
|
140
|
+
)
|
|
141
|
+
);
|
|
142
|
+
setTimeout(connect, delay);
|
|
143
|
+
};
|
|
73
144
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
145
|
+
ws.on('close', () => scheduleReconnect('close'));
|
|
146
|
+
ws.on('error', () => scheduleReconnect('error'));
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
connect();
|
|
78
150
|
|
|
79
151
|
const cleanUp = () => {
|
|
80
|
-
|
|
152
|
+
clearTimers();
|
|
153
|
+
if (
|
|
154
|
+
ws &&
|
|
155
|
+
(ws.readyState === WebSocket.OPEN ||
|
|
156
|
+
ws.readyState === WebSocket.CONNECTING)
|
|
157
|
+
) {
|
|
158
|
+
try {
|
|
159
|
+
ws.close();
|
|
160
|
+
} catch (_) {}
|
|
161
|
+
}
|
|
81
162
|
process.exit(0);
|
|
82
163
|
};
|
|
83
164
|
|