@peers-app/peers-sdk 0.27.4 → 0.27.5
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/contracts/contract-transport.d.ts +2 -1
- package/dist/contracts/contract-transport.js +24 -2
- package/dist/contracts/tool-catalog.js +6 -2
- package/dist/device/binary-peer-connection-v2.d.ts +7 -15
- package/dist/device/binary-peer-connection-v2.js +14 -30
- package/dist/device/connection.d.ts +34 -43
- package/dist/device/connection.js +188 -186
- package/dist/device/device.d.ts +1 -8
- package/dist/device/device.js +1 -30
- package/dist/device/socket.type.d.ts +6 -12
- package/package.json +1 -1
- package/dist/device/binary-peer-connection.d.ts +0 -58
- package/dist/device/binary-peer-connection.js +0 -516
- package/dist/device/streamed-socket.d.ts +0 -23
- package/dist/device/streamed-socket.js +0 -255
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StreamedSocket = void 0;
|
|
4
|
-
exports.createSocketStats = createSocketStats;
|
|
5
|
-
const msgpack_1 = require("@msgpack/msgpack");
|
|
6
|
-
const lodash_1 = require("lodash");
|
|
7
|
-
const utils_1 = require("../utils");
|
|
8
|
-
const socket_type_1 = require("./socket.type");
|
|
9
|
-
const tx_encoding_1 = require("./tx-encoding");
|
|
10
|
-
/** Creates a new stats object for tracking socket throughput */
|
|
11
|
-
function createSocketStats() {
|
|
12
|
-
const now = Date.now();
|
|
13
|
-
return {
|
|
14
|
-
bytesSent: 0,
|
|
15
|
-
bytesReceived: 0,
|
|
16
|
-
messagesSent: 0,
|
|
17
|
-
messagesReceived: 0,
|
|
18
|
-
startTime: now,
|
|
19
|
-
// For calculating recent rates
|
|
20
|
-
lastSampleTime: now,
|
|
21
|
-
lastSampleBytesSent: 0,
|
|
22
|
-
lastSampleBytesReceived: 0,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
// Encode chunk metadata + data into a single Uint8Array
|
|
26
|
-
function encodeChunk(chunk) {
|
|
27
|
-
// Encode metadata using msgpack
|
|
28
|
-
const metadata = {
|
|
29
|
-
messageId: chunk.messageId,
|
|
30
|
-
chunkIndex: chunk.chunkIndex,
|
|
31
|
-
...(chunk.totalChunks !== undefined && { totalChunks: chunk.totalChunks }),
|
|
32
|
-
...(chunk.eventName !== undefined && { eventName: chunk.eventName }),
|
|
33
|
-
};
|
|
34
|
-
const metadataBytes = (0, msgpack_1.encode)(metadata);
|
|
35
|
-
const metadataLength = metadataBytes.length;
|
|
36
|
-
// Create combined buffer: [4 bytes length][metadata][data]
|
|
37
|
-
const combined = new Uint8Array(4 + metadataLength + chunk.data.length);
|
|
38
|
-
// Write metadata length as 32-bit unsigned integer (big-endian)
|
|
39
|
-
const view = new DataView(combined.buffer, combined.byteOffset, combined.byteLength);
|
|
40
|
-
view.setUint32(0, metadataLength, false); // big-endian
|
|
41
|
-
// Write metadata
|
|
42
|
-
combined.set(metadataBytes, 4);
|
|
43
|
-
// Write data
|
|
44
|
-
combined.set(chunk.data, 4 + metadataLength);
|
|
45
|
-
return combined;
|
|
46
|
-
}
|
|
47
|
-
// Decode binary chunk back into IMessageChunk
|
|
48
|
-
function decodeChunk(bytes) {
|
|
49
|
-
// Ensure we have a proper Uint8Array with a valid ArrayBuffer
|
|
50
|
-
// Socket.io might send data that doesn't have a proper buffer property
|
|
51
|
-
let normalizedBytes;
|
|
52
|
-
if (bytes.buffer instanceof ArrayBuffer) {
|
|
53
|
-
normalizedBytes = bytes;
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
// Create a new Uint8Array copy which will have a proper buffer
|
|
57
|
-
normalizedBytes = new Uint8Array(bytes);
|
|
58
|
-
}
|
|
59
|
-
// Read metadata length
|
|
60
|
-
const view = new DataView(normalizedBytes.buffer, normalizedBytes.byteOffset, normalizedBytes.byteLength);
|
|
61
|
-
const metadataLength = view.getUint32(0, false); // big-endian
|
|
62
|
-
// Extract metadata
|
|
63
|
-
const metadataBytes = normalizedBytes.subarray(4, 4 + metadataLength);
|
|
64
|
-
const metadata = (0, msgpack_1.decode)(metadataBytes);
|
|
65
|
-
// Extract data
|
|
66
|
-
const data = normalizedBytes.subarray(4 + metadataLength);
|
|
67
|
-
return {
|
|
68
|
-
messageId: metadata.messageId,
|
|
69
|
-
chunkIndex: metadata.chunkIndex,
|
|
70
|
-
totalChunks: metadata.totalChunks,
|
|
71
|
-
eventName: metadata.eventName,
|
|
72
|
-
data: data,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
class StreamedSocket {
|
|
76
|
-
socket;
|
|
77
|
-
maxChunkSize;
|
|
78
|
-
safeSocketChunkEventName = "__streamed-socket-chunk";
|
|
79
|
-
safeSocketResponseEventName = "__streamed-socket-response";
|
|
80
|
-
stats = createSocketStats();
|
|
81
|
-
constructor(socket, maxChunkSize = socket_type_1.DEFAULT_MAX_CHUNK_SIZE) {
|
|
82
|
-
this.socket = socket;
|
|
83
|
-
this.maxChunkSize = maxChunkSize;
|
|
84
|
-
socket.on(this.safeSocketChunkEventName, (encodedChunk) => {
|
|
85
|
-
// Track received bytes
|
|
86
|
-
this.stats.bytesReceived += encodedChunk.length;
|
|
87
|
-
const chunk = decodeChunk(encodedChunk);
|
|
88
|
-
this.handleChunk(chunk);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
get id() {
|
|
92
|
-
return this.socket.id;
|
|
93
|
-
}
|
|
94
|
-
get connected() {
|
|
95
|
-
return this.socket.connected;
|
|
96
|
-
}
|
|
97
|
-
disconnect(close) {
|
|
98
|
-
this.socket.disconnect(close);
|
|
99
|
-
}
|
|
100
|
-
callbacks = {};
|
|
101
|
-
emit(eventName, args, callback) {
|
|
102
|
-
const encoded = (0, tx_encoding_1.txEncode)(args);
|
|
103
|
-
// if (encoded.length < this.maxChunkSize) {
|
|
104
|
-
// // If the data is small enough, send it directly
|
|
105
|
-
// this.socket.emit(eventName, args, callback);
|
|
106
|
-
// return;
|
|
107
|
-
// }
|
|
108
|
-
const messageId = (0, utils_1.newid)();
|
|
109
|
-
if (callback !== lodash_1.noop) {
|
|
110
|
-
this.callbacks[messageId] = callback;
|
|
111
|
-
}
|
|
112
|
-
const chunks = [];
|
|
113
|
-
// Reserve space for metadata overhead (typically ~50-100 bytes)
|
|
114
|
-
const metadataOverhead = 128;
|
|
115
|
-
if (this.maxChunkSize < metadataOverhead * 2) {
|
|
116
|
-
throw new Error(`maxChunkSize ${this.maxChunkSize} is too small to accommodate metadata overhead, must be greater than ${metadataOverhead * 2}`);
|
|
117
|
-
}
|
|
118
|
-
const effectiveChunkSize = this.maxChunkSize - metadataOverhead;
|
|
119
|
-
for (let i = 0; i < encoded.length; i += effectiveChunkSize) {
|
|
120
|
-
const chunkData = encoded.subarray(i, i + effectiveChunkSize);
|
|
121
|
-
chunks.push({
|
|
122
|
-
messageId,
|
|
123
|
-
chunkIndex: Math.floor(i / effectiveChunkSize),
|
|
124
|
-
data: chunkData,
|
|
125
|
-
});
|
|
126
|
-
if (i === 0) {
|
|
127
|
-
// The first chunk contains metadata about the chunks
|
|
128
|
-
chunks[0].eventName = eventName;
|
|
129
|
-
chunks[0].totalChunks = Math.ceil(encoded.length / effectiveChunkSize);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (chunks.length > 1) {
|
|
133
|
-
console.debug(`Sending ${chunks.length} chunks for event ${eventName} with messageId ${messageId}`);
|
|
134
|
-
}
|
|
135
|
-
let totalBytesSent = 0;
|
|
136
|
-
for (const chunk of chunks) {
|
|
137
|
-
// Encode chunk with metadata into single Uint8Array
|
|
138
|
-
const encodedChunk = encodeChunk(chunk);
|
|
139
|
-
totalBytesSent += encodedChunk.length;
|
|
140
|
-
// TODO on error or timeout retry
|
|
141
|
-
// TODO respond to backpressure from socket
|
|
142
|
-
this.socket.emit(this.safeSocketChunkEventName, encodedChunk, lodash_1.noop);
|
|
143
|
-
}
|
|
144
|
-
// Track stats
|
|
145
|
-
this.stats.bytesSent += totalBytesSent;
|
|
146
|
-
this.stats.messagesSent++;
|
|
147
|
-
}
|
|
148
|
-
handlers = {};
|
|
149
|
-
on(eventName, handler) {
|
|
150
|
-
this.handlers[eventName] = handler;
|
|
151
|
-
// TODO I think this needs to come back to a handleResult method on this class
|
|
152
|
-
this.socket.on(eventName, handler);
|
|
153
|
-
}
|
|
154
|
-
removeAllListeners(eventName) {
|
|
155
|
-
this.socket.removeAllListeners(eventName);
|
|
156
|
-
delete this.handlers[eventName];
|
|
157
|
-
}
|
|
158
|
-
chunkBuffers = {};
|
|
159
|
-
async handleChunk(chunk) {
|
|
160
|
-
if (!this.chunkBuffers[chunk.messageId]) {
|
|
161
|
-
// TODO handle timeout for chunks and cleanup data to prevent memory leaks
|
|
162
|
-
this.chunkBuffers[chunk.messageId] = [];
|
|
163
|
-
}
|
|
164
|
-
const chunks = this.chunkBuffers[chunk.messageId];
|
|
165
|
-
const chunkIndex = chunk.chunkIndex;
|
|
166
|
-
if (chunkIndex < 0) {
|
|
167
|
-
console.warn(`Received chunk with negative index ${chunk.chunkIndex} for messageId: ${chunk.messageId}`, chunk);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
chunks[chunkIndex] = chunk;
|
|
171
|
-
const chunkZero = chunks[0];
|
|
172
|
-
if (!chunkZero && chunks.length > 100) {
|
|
173
|
-
console.error(`Received over 100 chunks without a chunkZero for messageId: ${chunk.messageId}. This could indicate a memory attack`, chunk);
|
|
174
|
-
}
|
|
175
|
-
if (chunkZero) {
|
|
176
|
-
if (chunkZero?.totalChunks <= chunkIndex) {
|
|
177
|
-
console.warn(`Received chunk with index ${chunk.chunkIndex} which is greater than totalChunks ${chunkZero.totalChunks} for messageId: ${chunk.messageId}`, chunk);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
if (chunks.length >= chunkZero.totalChunks) {
|
|
181
|
-
this.processChunks(chunks);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
async processChunks(chunks) {
|
|
186
|
-
try {
|
|
187
|
-
const chunkZero = chunks[0];
|
|
188
|
-
// Reassemble Uint8Array chunks (already decoded from binary format)
|
|
189
|
-
const totalSize = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);
|
|
190
|
-
const reassembled = new Uint8Array(totalSize);
|
|
191
|
-
let offset = 0;
|
|
192
|
-
for (const chunk of chunks) {
|
|
193
|
-
reassembled.set(chunk.data, offset);
|
|
194
|
-
offset += chunk.data.length;
|
|
195
|
-
}
|
|
196
|
-
// Track message received
|
|
197
|
-
this.stats.messagesReceived++;
|
|
198
|
-
delete this.chunkBuffers[chunkZero.messageId];
|
|
199
|
-
const eventName = chunkZero.eventName;
|
|
200
|
-
const args = (0, tx_encoding_1.txDecode)(reassembled);
|
|
201
|
-
if (eventName === this.safeSocketResponseEventName) {
|
|
202
|
-
this.handleResponse(args.messageId, args.error, args.result);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
const handler = this.handlers[eventName];
|
|
206
|
-
if (!handler) {
|
|
207
|
-
throw new Error(`No handler registered for event ${eventName}`);
|
|
208
|
-
}
|
|
209
|
-
let error;
|
|
210
|
-
let result;
|
|
211
|
-
try {
|
|
212
|
-
await new Promise((resolve) => {
|
|
213
|
-
handler(args, (...params) => {
|
|
214
|
-
[error, result] = params;
|
|
215
|
-
resolve();
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
catch (err) {
|
|
220
|
-
error = err;
|
|
221
|
-
}
|
|
222
|
-
this.emit(this.safeSocketResponseEventName, {
|
|
223
|
-
messageId: chunkZero.messageId,
|
|
224
|
-
error,
|
|
225
|
-
result,
|
|
226
|
-
}, lodash_1.noop);
|
|
227
|
-
}
|
|
228
|
-
catch (error) {
|
|
229
|
-
if (String(error).includes("No handler registered for event")) {
|
|
230
|
-
console.warn(String(error));
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
console.error(`Error handling chunked request:`, error);
|
|
234
|
-
}
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
async handleResponse(messageId, error, result) {
|
|
239
|
-
const callback = this.callbacks[messageId];
|
|
240
|
-
if (!callback) {
|
|
241
|
-
console.warn(`No callback found for messageId ${messageId}`);
|
|
242
|
-
}
|
|
243
|
-
delete this.callbacks[messageId];
|
|
244
|
-
if (error) {
|
|
245
|
-
console.error(`Error in SafeSocket response for messageId ${messageId}:`, error);
|
|
246
|
-
}
|
|
247
|
-
try {
|
|
248
|
-
await callback(error, result);
|
|
249
|
-
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
console.error(`Error in SafeSocket callback for messageId ${messageId}:`, err);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
exports.StreamedSocket = StreamedSocket;
|