@cendarsoss/pusher-js 8.4.11 → 8.4.13
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/dist/node/pusher.js +35 -15
- package/dist/node/pusher.js.map +1 -1
- package/dist/web/pusher.mjs +35 -15
- package/dist/web/pusher.mjs.map +1 -1
- package/examples/test-trident.html +263 -0
- package/package.json +1 -1
- package/src/core/delta/manager.ts +38 -15
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Trident Delta Test</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: monospace;
|
|
10
|
+
max-width: 1200px;
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
background: #1a1a2e;
|
|
14
|
+
color: #eee;
|
|
15
|
+
}
|
|
16
|
+
.container {
|
|
17
|
+
background: #16213e;
|
|
18
|
+
padding: 20px;
|
|
19
|
+
border-radius: 8px;
|
|
20
|
+
margin-bottom: 20px;
|
|
21
|
+
}
|
|
22
|
+
h1 { color: #00ff88; margin-top: 0; }
|
|
23
|
+
.status {
|
|
24
|
+
padding: 8px 15px;
|
|
25
|
+
border-radius: 4px;
|
|
26
|
+
display: inline-block;
|
|
27
|
+
font-weight: bold;
|
|
28
|
+
}
|
|
29
|
+
.status.connected { background: #00ff88; color: #000; }
|
|
30
|
+
.status.disconnected { background: #ff4757; color: #fff; }
|
|
31
|
+
.status.connecting { background: #ffa502; color: #000; }
|
|
32
|
+
.log {
|
|
33
|
+
background: #0f0f23;
|
|
34
|
+
border: 1px solid #333;
|
|
35
|
+
border-radius: 4px;
|
|
36
|
+
padding: 15px;
|
|
37
|
+
max-height: 500px;
|
|
38
|
+
overflow-y: auto;
|
|
39
|
+
font-size: 12px;
|
|
40
|
+
line-height: 1.4;
|
|
41
|
+
}
|
|
42
|
+
.log-entry { padding: 3px 0; border-bottom: 1px solid #222; }
|
|
43
|
+
.log-time { color: #666; }
|
|
44
|
+
.log-delta { color: #ff6b6b; }
|
|
45
|
+
.log-full { color: #00ff88; }
|
|
46
|
+
.log-error { color: #ff4757; font-weight: bold; }
|
|
47
|
+
.stats {
|
|
48
|
+
display: grid;
|
|
49
|
+
grid-template-columns: repeat(4, 1fr);
|
|
50
|
+
gap: 10px;
|
|
51
|
+
margin: 15px 0;
|
|
52
|
+
}
|
|
53
|
+
.stat-box {
|
|
54
|
+
background: #0f3460;
|
|
55
|
+
padding: 10px;
|
|
56
|
+
border-radius: 4px;
|
|
57
|
+
text-align: center;
|
|
58
|
+
}
|
|
59
|
+
.stat-label { font-size: 10px; color: #888; }
|
|
60
|
+
.stat-value { font-size: 18px; color: #00ff88; font-weight: bold; }
|
|
61
|
+
button {
|
|
62
|
+
background: #00ff88;
|
|
63
|
+
color: #000;
|
|
64
|
+
border: none;
|
|
65
|
+
padding: 10px 20px;
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
font-weight: bold;
|
|
69
|
+
margin-right: 10px;
|
|
70
|
+
}
|
|
71
|
+
button:hover { background: #00cc6a; }
|
|
72
|
+
button:disabled { background: #444; color: #888; cursor: not-allowed; }
|
|
73
|
+
</style>
|
|
74
|
+
</head>
|
|
75
|
+
<body>
|
|
76
|
+
<div class="container">
|
|
77
|
+
<h1>Trident Delta Compression Test</h1>
|
|
78
|
+
<p>Channel: <code>trident_swaps_v1-501_9FvUQNw2Y7zSpxyh7BvxVKwx3iNrjwQ5X6XwEFeZpump</code></p>
|
|
79
|
+
|
|
80
|
+
<div style="margin: 15px 0;">
|
|
81
|
+
<button id="connect-btn">Connect</button>
|
|
82
|
+
<button id="disconnect-btn" disabled>Disconnect</button>
|
|
83
|
+
Status: <span id="status" class="status disconnected">Disconnected</span>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="stats">
|
|
87
|
+
<div class="stat-box">
|
|
88
|
+
<div class="stat-label">Total Messages</div>
|
|
89
|
+
<div class="stat-value" id="total-messages">0</div>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="stat-box">
|
|
92
|
+
<div class="stat-label">Delta Messages</div>
|
|
93
|
+
<div class="stat-value" id="delta-messages">0</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="stat-box">
|
|
96
|
+
<div class="stat-label">Full Messages</div>
|
|
97
|
+
<div class="stat-value" id="full-messages">0</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="stat-box">
|
|
100
|
+
<div class="stat-label">Bandwidth Saved</div>
|
|
101
|
+
<div class="stat-value" id="bandwidth-saved">0%</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="container">
|
|
107
|
+
<h2 style="color: #00ff88; margin-top: 0;">Message Log</h2>
|
|
108
|
+
<div id="log" class="log">
|
|
109
|
+
<div class="log-entry">Waiting for connection...</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<!-- Fossil Delta Library -->
|
|
114
|
+
<script src="https://cdn.jsdelivr.net/npm/fossil-delta@1.0.2/fossil-delta.min.js"></script>
|
|
115
|
+
|
|
116
|
+
<!-- Pusher JS - use local build -->
|
|
117
|
+
<script type="module">
|
|
118
|
+
import Pusher from '../dist/web/pusher.mjs';
|
|
119
|
+
|
|
120
|
+
const CHANNEL_NAME = 'trident_swaps_v1-501_9FvUQNw2Y7zSpxyh7BvxVKwx3iNrjwQ5X6XwEFeZpump';
|
|
121
|
+
const EVENT_NAME = 'e';
|
|
122
|
+
|
|
123
|
+
let pusher = null;
|
|
124
|
+
let channel = null;
|
|
125
|
+
|
|
126
|
+
const connectBtn = document.getElementById('connect-btn');
|
|
127
|
+
const disconnectBtn = document.getElementById('disconnect-btn');
|
|
128
|
+
const statusEl = document.getElementById('status');
|
|
129
|
+
const logEl = document.getElementById('log');
|
|
130
|
+
|
|
131
|
+
function log(message, type = 'info') {
|
|
132
|
+
const time = new Date().toLocaleTimeString();
|
|
133
|
+
const entry = document.createElement('div');
|
|
134
|
+
entry.className = 'log-entry';
|
|
135
|
+
|
|
136
|
+
let typeClass = '';
|
|
137
|
+
if (type === 'delta') typeClass = 'log-delta';
|
|
138
|
+
if (type === 'full') typeClass = 'log-full';
|
|
139
|
+
if (type === 'error') typeClass = 'log-error';
|
|
140
|
+
|
|
141
|
+
entry.innerHTML = `<span class="log-time">[${time}]</span> <span class="${typeClass}">${message}</span>`;
|
|
142
|
+
logEl.appendChild(entry);
|
|
143
|
+
logEl.scrollTop = logEl.scrollHeight;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function updateStats() {
|
|
147
|
+
if (!pusher) return;
|
|
148
|
+
const stats = pusher.getDeltaStats();
|
|
149
|
+
if (!stats) return;
|
|
150
|
+
|
|
151
|
+
document.getElementById('total-messages').textContent = stats.totalMessages;
|
|
152
|
+
document.getElementById('delta-messages').textContent = stats.deltaMessages;
|
|
153
|
+
document.getElementById('full-messages').textContent = stats.fullMessages;
|
|
154
|
+
document.getElementById('bandwidth-saved').textContent = stats.bandwidthSavedPercent.toFixed(1) + '%';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function updateStatus(status, text) {
|
|
158
|
+
statusEl.className = `status ${status}`;
|
|
159
|
+
statusEl.textContent = text;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function connect() {
|
|
163
|
+
log('Connecting to stream-v2.projectscylla.com...');
|
|
164
|
+
updateStatus('connecting', 'Connecting');
|
|
165
|
+
|
|
166
|
+
pusher = new Pusher('knife-library-likely', {
|
|
167
|
+
cluster: 'a',
|
|
168
|
+
wsHost: 'stream-v2.projectscylla.com',
|
|
169
|
+
wssPort: 443,
|
|
170
|
+
forceTLS: true,
|
|
171
|
+
disableStats: true,
|
|
172
|
+
enabledTransports: ['ws', 'wss'],
|
|
173
|
+
deltaCompression: {
|
|
174
|
+
enabled: true,
|
|
175
|
+
algorithms: ['fossil', 'xdelta3'],
|
|
176
|
+
debug: true,
|
|
177
|
+
onStats: (stats) => {
|
|
178
|
+
console.log('Delta Stats:', stats);
|
|
179
|
+
updateStats();
|
|
180
|
+
},
|
|
181
|
+
onError: (error) => {
|
|
182
|
+
console.error('Delta Error:', error);
|
|
183
|
+
log('DELTA ERROR: ' + JSON.stringify(error), 'error');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
pusher.connection.bind('connected', () => {
|
|
189
|
+
log('Connected! Socket ID: ' + pusher.connection.socket_id);
|
|
190
|
+
updateStatus('connected', 'Connected');
|
|
191
|
+
connectBtn.disabled = true;
|
|
192
|
+
disconnectBtn.disabled = false;
|
|
193
|
+
subscribe();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
pusher.connection.bind('disconnected', () => {
|
|
197
|
+
log('Disconnected');
|
|
198
|
+
updateStatus('disconnected', 'Disconnected');
|
|
199
|
+
connectBtn.disabled = false;
|
|
200
|
+
disconnectBtn.disabled = true;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
pusher.connection.bind('error', (err) => {
|
|
204
|
+
log('Connection error: ' + JSON.stringify(err), 'error');
|
|
205
|
+
console.error('Pusher error:', err);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
pusher.bind('pusher:delta_compression_enabled', (data) => {
|
|
209
|
+
log('Delta compression enabled: ' + JSON.stringify(data));
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function subscribe() {
|
|
214
|
+
log('Subscribing to: ' + CHANNEL_NAME);
|
|
215
|
+
|
|
216
|
+
channel = pusher.subscribe(CHANNEL_NAME);
|
|
217
|
+
|
|
218
|
+
channel.bind('pusher:subscription_succeeded', () => {
|
|
219
|
+
log('Subscribed successfully!');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
channel.bind('pusher:subscription_error', (status) => {
|
|
223
|
+
log('Subscription error: ' + JSON.stringify(status), 'error');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
channel.bind('pusher:delta_cache_sync', (data) => {
|
|
227
|
+
log('Cache sync received');
|
|
228
|
+
console.log('Cache sync data:', data);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
channel.bind(EVENT_NAME, (data) => {
|
|
232
|
+
const stats = pusher.getDeltaStats();
|
|
233
|
+
const msgNum = stats ? stats.totalMessages : '?';
|
|
234
|
+
|
|
235
|
+
// Try to extract some info from the data
|
|
236
|
+
let info = '';
|
|
237
|
+
if (data && typeof data === 'object') {
|
|
238
|
+
if (data.token) info = `token: ${data.token.substring(0, 8)}...`;
|
|
239
|
+
else if (data.mint) info = `mint: ${data.mint.substring(0, 8)}...`;
|
|
240
|
+
else info = Object.keys(data).slice(0, 3).join(', ');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
log(`[#${msgNum}] Event received - ${info}`, 'full');
|
|
244
|
+
console.log('Event data: ' + typeof data, data);
|
|
245
|
+
updateStats();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function disconnect() {
|
|
250
|
+
if (pusher) {
|
|
251
|
+
pusher.disconnect();
|
|
252
|
+
pusher = null;
|
|
253
|
+
channel = null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
connectBtn.addEventListener('click', connect);
|
|
258
|
+
disconnectBtn.addEventListener('click', disconnect);
|
|
259
|
+
|
|
260
|
+
log('Ready. Click Connect to start.');
|
|
261
|
+
</script>
|
|
262
|
+
</body>
|
|
263
|
+
</html>
|
package/package.json
CHANGED
|
@@ -198,30 +198,43 @@ export default class DeltaCompressionManager {
|
|
|
198
198
|
baseMessage = channelState.getBaseMessage(conflationKey, baseIndex);
|
|
199
199
|
if (!baseMessage) {
|
|
200
200
|
this.error(
|
|
201
|
-
`No base message for channel ${channel}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.log('Current conflation cache snapshot', {
|
|
201
|
+
`No base message (conflation path) for channel ${channel}`,
|
|
202
|
+
{
|
|
203
|
+
path: 'conflation',
|
|
205
204
|
channel,
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
deltaConflationKey: conflationKey,
|
|
206
|
+
deltaBaseIndex: baseIndex,
|
|
207
|
+
deltaSeq: sequence,
|
|
208
|
+
channelStateConflationKey: channelState.conflationKey,
|
|
209
|
+
channelStateBaseMessage: channelState.baseMessage ? 'exists' : null,
|
|
210
|
+
channelStateBaseSequence: channelState.baseSequence,
|
|
211
|
+
channelStateLastSequence: channelState.lastSequence,
|
|
212
|
+
conflationCacheKeys: Array.from(channelState.conflationCaches.keys()),
|
|
213
|
+
conflationCacheSizes: Array.from(
|
|
208
214
|
channelState.conflationCaches.entries(),
|
|
209
215
|
).map(([key, cache]) => ({ key, size: cache.length })),
|
|
210
|
-
}
|
|
211
|
-
|
|
216
|
+
},
|
|
217
|
+
);
|
|
212
218
|
this.requestResync(channel);
|
|
213
219
|
return null;
|
|
214
220
|
}
|
|
215
221
|
} else {
|
|
216
222
|
baseMessage = channelState.baseMessage;
|
|
217
223
|
if (!baseMessage) {
|
|
218
|
-
this.error(
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
this.error(
|
|
225
|
+
`No base message (legacy path) for channel ${channel}`,
|
|
226
|
+
{
|
|
227
|
+
path: 'legacy',
|
|
221
228
|
channel,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
229
|
+
deltaConflationKey: conflationKey,
|
|
230
|
+
deltaBaseIndex: baseIndex,
|
|
231
|
+
deltaSeq: sequence,
|
|
232
|
+
channelStateConflationKey: channelState.conflationKey,
|
|
233
|
+
channelStateBaseMessage: null,
|
|
234
|
+
channelStateBaseSequence: channelState.baseSequence,
|
|
235
|
+
channelStateLastSequence: channelState.lastSequence,
|
|
236
|
+
},
|
|
237
|
+
);
|
|
225
238
|
this.requestResync(channel);
|
|
226
239
|
return null;
|
|
227
240
|
}
|
|
@@ -284,10 +297,20 @@ export default class DeltaCompressionManager {
|
|
|
284
297
|
// Parse and return the reconstructed event
|
|
285
298
|
try {
|
|
286
299
|
const parsedMessage = JSON.parse(reconstructedMessage);
|
|
300
|
+
let data = parsedMessage.data || parsedMessage;
|
|
301
|
+
// Try to parse data if it's a string (Pusher protocol double-encodes data)
|
|
302
|
+
// This matches the behavior in protocol.ts decodeMessage
|
|
303
|
+
if (typeof data === 'string') {
|
|
304
|
+
try {
|
|
305
|
+
data = JSON.parse(data);
|
|
306
|
+
} catch (e) {
|
|
307
|
+
// Keep as string if not valid JSON
|
|
308
|
+
}
|
|
309
|
+
}
|
|
287
310
|
return {
|
|
288
311
|
event: event,
|
|
289
312
|
channel: channel,
|
|
290
|
-
data:
|
|
313
|
+
data: data,
|
|
291
314
|
};
|
|
292
315
|
} catch (e) {
|
|
293
316
|
// If not JSON, return as-is
|