@trap_stevo/filetide 0.0.45 → 0.0.47

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.
@@ -25,11 +25,20 @@ class FileMessagerClient {
25
25
  constructor(options = {}, transportOptions = {
26
26
  parallelChunks: 3,
27
27
  maxRetries: 3
28
+ }, messagerSettings = {
29
+ maxReconnectionAttempts: 15,
30
+ reconnectionAttempts: 15,
31
+ maxReconnectionDelay: 10000
28
32
  }) {
33
+ const {
34
+ maxReconnectionAttempts = 15,
35
+ reconnectionAttempts = 15,
36
+ maxReconnectionDelay = 10000
37
+ } = messagerSettings;
29
38
  this.clientOptions = FileMessagerConfigManager.getClientOptions(options);
30
39
  this.transporter = new FileTransferManager(transportOptions);
31
40
  this.activeTransferTypes = new Map();
32
- this.clientTide = new HUDIoTide();
41
+ this.clientTide = new HUDIoTide(maxReconnectionAttempts, reconnectionAttempts, maxReconnectionDelay);
33
42
  this.clients = new Map();
34
43
  }
35
44
  createClientInstance(clientID, clientOptions, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
@@ -142,7 +151,7 @@ class FileMessagerClient {
142
151
  if (fileInfo.largeFile) {
143
152
  return await this.sendDirectoryLargeFile(clientID, recipientId, fileInfo.fileName, fileInfo.filePath, fileDestinationPath, fileInfo.fileSize, index, tideSize, minTideSize, maxTideSize);
144
153
  }
145
- return await this.sendDirectoryFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath, fileInfo.size, adjustedDestinationPath, index);
154
+ return await this.sendDirectoryFile(clientID, recipientId, fileInfo.fileName, fileInfo.filePath, fileInfo.size, fileDestinationPath, index, tideSize, minTideSize, maxTideSize);
146
155
  });
147
156
  Promise.all(sendFilePromises).then(() => console.log("\nAll files in directory transferred successfully!")).catch(error => console.error("Did not transfer directory: ", error));
148
157
  } catch (error) {
@@ -154,12 +163,13 @@ class FileMessagerClient {
154
163
  if (index === 0) {
155
164
  initiatingTransfer = true;
156
165
  }
157
- return await this.sendLargeFile(clientID, recipientId, fileName, filePath, destinationPath, fileSize, tideSize, minTideSize, maxTideSize, initiatingTransfer, true);
166
+ return await this.sendFile(clientID, recipientId, fileName, filePath, destinationPath, fileSize, tideSize, minTideSize, maxTideSize, initiatingTransfer, true, true, true);
158
167
  }
159
- sendDirectoryFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize, directoryPath = null, index = 0) {
160
- if (!file) {
168
+ async sendDirectoryFile(clientID, recipientId, fileName, filePath = process.cwd(), fileSize, directoryPath = null, index = 0, tideSize = 200, minTideSize = 10, maxTideSize = 2000) {
169
+ if (!filePath) {
161
170
  return;
162
171
  }
172
+ let initiatingTransfer = false;
163
173
  if (index === 0) {
164
174
  this.clientTide.emitEvent(clientID, "transfer-start", {
165
175
  fileName,
@@ -168,53 +178,13 @@ class FileMessagerClient {
168
178
  path: directoryPath || filePath,
169
179
  fileSize
170
180
  });
181
+ initiatingTransfer = true;
171
182
  }
172
- this.transporter.sendFile(file, {
173
- onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
174
- return new Promise((resolve, reject) => {
175
- this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
176
- senderID: clientID,
177
- recipientId,
178
- fileName,
179
- fileChunk: chunkData,
180
- completedChunks,
181
- totalChunks,
182
- chunkIndex,
183
- chunkSize,
184
- filePath,
185
- fileSize
186
- });
187
- resolve();
188
- });
189
- },
190
- onComplete: () => {
191
- return new Promise((resolve, reject) => {
192
- FileTransferRecoveryManager.clearTransferState(clientID, recipientId, `${filePath}-${fileName}`);
193
- this.clientTide.emitEvent(clientID, "transfer-complete", {
194
- senderID: clientID,
195
- recipientId,
196
- fileName,
197
- filePath
198
- });
199
- resolve();
200
- });
201
- },
202
- originDetails: {
203
- recipientID: recipientId,
204
- senderID: clientID
205
- },
206
- recipientID: recipientId,
207
- fileDetails: {
208
- destinationPath: filePath,
209
- name: fileName,
210
- path: filePath,
211
- size: fileSize
212
- }
213
- }, this.clientTide).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
183
+ return await this.sendFile(clientID, recipientId, fileName, filePath, directoryPath, fileSize, tideSize, minTideSize, maxTideSize, initiatingTransfer, true, true, false);
214
184
  }
215
- async sendLargeFile(clientID, recipientId, fileName, filePath = process.cwd(), destination = process.cwd(), fileSize, tideSize = 500, minTideSize = 10, maxTideSize = 2000, initiatingTransfer = true, inDirectory = false) {
185
+ async sendFile(clientID, recipientId, fileName, filePath = process.cwd(), destination = process.cwd(), fileSize, tideSize = 500, minTideSize = 10, maxTideSize = 2000, initiatingTransfer = true, inDirectory = false, largeFile = false) {
216
186
  const destinationPath = FileUtilityManager.normalizePath(path.join(destination, fileName));
217
- FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Requesting to transfer large file ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
187
+ FileNetUtilityManager.outputGradient(largeFile ? `[FileTide ~ File Messager] ~ Requesting to transfer large file ~ ${destinationPath} to ${recipientId}!` : `[FileTide ~ File Messager] ~ Requesting to transfer ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
218
188
  const incomingType = this.activeTransferTypes.get(filePath);
219
189
  if (!inDirectory && initiatingTransfer) {
220
190
  const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, destinationPath, fileSize, "send");
@@ -248,6 +218,7 @@ class FileMessagerClient {
248
218
  const maxBatchSize = maxTideSize;
249
219
  const minBatchSize = minTideSize;
250
220
  let batchSize = tideSize > maxBatchSize || tideSize > minBatchSize ? tideSize : tideSize <= minBatchSize ? minBatchSize : 10;
221
+ let transferInitiated = false;
251
222
  const chunkBatch = [];
252
223
  await this.transporter.sendLargeFile(fileHandle, {
253
224
  recipientID: recipientId,
@@ -270,6 +241,13 @@ class FileMessagerClient {
270
241
  filePath: destination,
271
242
  fileSize
272
243
  });
244
+ if (!transferInitiated) {
245
+ this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
246
+ chunkBatch
247
+ });
248
+ transferInitiated = true;
249
+ chunkBatch.length = 0;
250
+ }
273
251
  if (chunkBatch.length >= batchSize) {
274
252
  const now = Date.now();
275
253
  const rtt = now - lastSendTime;
@@ -312,76 +290,13 @@ class FileMessagerClient {
312
290
  });
313
291
  }
314
292
  }, this.clientTide);
315
- FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Large file ~ ${fileName} transfer complete!`, significantMessageColors);
293
+ FileNetUtilityManager.outputGradient(largeFile ? `[FileTide ~ File Messager] ~ Large file ~ ${fileName} transfer complete!` : `[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors);
316
294
  } catch (error) {
317
- FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Error transferring large file: ${error.message}`, errorMessageColors);
295
+ FileNetUtilityManager.outputGradient(largeFile ? `[FileTide ~ File Messager] ~ Error transferring large file: ${error.message}` : `[FileTide ~ File Messager] ~ Error transferring file ~ ${error.message}`, errorMessageColors);
318
296
  } finally {
319
297
  await fileHandle.close();
320
298
  }
321
299
  }
322
- async sendFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize) {
323
- if (!file) {
324
- return;
325
- }
326
- const destinationPath = FileUtilityManager.normalizePath(path.join(filePath, fileName));
327
- FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Requesting to transfer ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
328
- const incomingType = this.activeTransferTypes.get(filePath);
329
- const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, destinationPath, fileSize, "send");
330
- if (incomingType) {
331
- this.activeTransferTypes.delete(filePath);
332
- }
333
- if (!transferAllowed) {
334
- return;
335
- }
336
- this.clientTide.emitEvent(clientID, "transfer-start", {
337
- fileName,
338
- senderID: clientID,
339
- recipientID: recipientId,
340
- path: filePath,
341
- fileSize
342
- });
343
- await this.transporter.sendFile(file, {
344
- onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
345
- return new Promise((resolve, reject) => {
346
- this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
347
- senderID: clientID,
348
- recipientId,
349
- fileName,
350
- fileChunk: chunkData,
351
- completedChunks,
352
- totalChunks,
353
- chunkIndex,
354
- chunkSize,
355
- filePath,
356
- fileSize
357
- });
358
- resolve();
359
- });
360
- },
361
- onComplete: () => {
362
- return new Promise((resolve, reject) => {
363
- FileTransferRecoveryManager.clearTransferState(clientID, recipientId, `${filePath}-${fileName}`);
364
- this.clientTide.emitEvent(clientID, "transfer-complete", {
365
- senderID: clientID,
366
- recipientId,
367
- fileName,
368
- filePath
369
- });
370
- resolve();
371
- });
372
- },
373
- originDetails: {
374
- recipientID: recipientId,
375
- senderID: clientID
376
- },
377
- fileDetails: {
378
- destinationPath,
379
- name: fileName,
380
- path: filePath,
381
- size: fileSize
382
- }
383
- }, this.clientTide).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer ~ ${error}`, errorMessageColors));
384
- }
385
300
  }
386
301
  ;
387
302
  module.exports = FileMessagerClient;
@@ -4,6 +4,9 @@ var _FileTide;
4
4
  function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
5
5
  function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
6
6
  function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
7
+ const {
8
+ HUDUtilityManager
9
+ } = require("@trap_stevo/legendarybuilderpronodejs-utilities");
7
10
  const chalk = require("chalk");
8
11
  const path = require("path");
9
12
  const fs = require("fs");
@@ -68,6 +71,8 @@ class FileTide {
68
71
  * Initialize and start a new client for each user.
69
72
  * @param {Object} clientOptions - Configuration for client (e.g., serverUrl)
70
73
  * @param {Object} connectionOptions - Configuration for the client connection
74
+ * @param {Object} transportOptions - Configuration for the file transporter
75
+ * @param {Object} messagerSettings - Configuration for the messager
71
76
  * @param {String} roomName - The name of the room to join
72
77
  * @param {String} userID - The ID of the client user
73
78
  * @param {Function} onLaunch - Callback for when a file messager launches
@@ -85,11 +90,18 @@ class FileTide {
85
90
  * @param {Boolean} [useAuthentication=true] - Whether to enable connection authentication
86
91
  * @param {Boolean} [queryAuthToken=false] - Whether to enable connection authentication tokens
87
92
  */
88
- launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
93
+ launchMessager(clientOptions = {}, connectionOptions = {}, transportOptions = {
94
+ parallelChunks: 3,
95
+ maxRetries: 3
96
+ }, messagerSettings = {
97
+ maxReconnectionAttempts: 15,
98
+ reconnectionAttempts: 15,
99
+ maxReconnectionDelay: 10000
100
+ }, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
89
101
  if (this.clients.has(userID)) {
90
102
  this.stopMessager(userID);
91
103
  }
92
- const newClient = new FileMessagerClient(clientOptions);
104
+ const newClient = new FileMessagerClient(clientOptions, transportOptions, messagerSettings);
93
105
  const enterRoom = newClient.joinRoom(userID, roomName, connectionOptions, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken);
94
106
  this.transferBarrier = enableTransferBarrier;
95
107
  this.clientShorePolicies.set(userID, [FileUtilityManager.getTidePath(">CurrentUser"), FileUtilityManager.getTidePath(">Downloads"), FileUtilityManager.getTidePath(">Documents")]);
@@ -168,46 +180,47 @@ class FileTide {
168
180
  * @param {Buffer} fileData - The path of the data to send
169
181
  * @param {String} destinationPath - The destination path on the device
170
182
  * @param {Array} filterDirectoryContent - List of paths to filter
171
- * @param {Number} tideSize - The tide size in sending files (500)
183
+ * @param {Number} tideSize - The tide size in sending smaller to medium files (200)
184
+ * @param {Number} tidalSize - The tide size in sending large files (500)
172
185
  * @param {Number} minTideSize - The minimum tide size in sending files (10)
173
186
  * @param {Number} maxTideSize - The maximum tide size in sending files (2000)
174
187
  */
175
- async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = [], tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
188
+ async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = [], tideSize = 200, tidalSize = 500, minTideSize = 10, maxTideSize = 2000) {
176
189
  const clientID = this.currentUserID;
177
190
  const clientMessager = this.clients.get(clientID);
178
191
  if (!clientMessager) {
179
192
  FileTide.outputGradient(`[FileTide] Client ~ ${clientID} not found.`, ["#F94144", "#F3722C"]);
180
- return;
193
+ return false;
181
194
  }
182
195
  const recipientOnline = await clientMessager.clientTide.emitEventWithResponse(clientID, "check-client-online", "retrieved-client-online", {
183
196
  clientID: recipientID
184
197
  }, 5000);
185
198
  if (!recipientOnline || !recipientOnline.online) {
186
199
  FileTide.outputGradient(`[FileTide] ${recipientID} currently not tiding.`, ["#F94144", "#F3722C"]);
187
- return;
200
+ return false;
188
201
  }
189
202
  const fileDetails = await this.getPathData(filePath, filterDirectoryContent);
190
203
  if (!fileDetails) {
191
204
  FileTide.outputGradient(`[${clientID}] File at path ${filePath} not found.`, ["#F94144", "#F3722C"]);
192
- return;
205
+ return false;
193
206
  }
194
207
  if (fileDetails.type === "file") {
195
208
  if (fileDetails.largeFile) {
196
209
  FileTide.outputGradient(`[${clientID}] Sending large file ~ ${fileDetails.fileName} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
197
- await clientMessager.sendLargeFile(clientID, recipientID, fileDetails.fileName, fileDetails.filePath, destinationPath, fileDetails.fileSize, tideSize, minTideSize, maxTideSize);
198
- return;
210
+ await clientMessager.sendFile(clientID, recipientID, fileDetails.fileName, fileDetails.filePath, destinationPath, fileDetails.fileSize, tidalSize, minTideSize, maxTideSize, true, false, true);
211
+ return true;
199
212
  }
200
213
  FileTide.outputGradient(`[${clientID}] Sending file ~ ${fileDetails.content.fileName} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
201
- clientMessager.sendFile(clientID, recipientID, fileDetails.content.fileName, fileDetails.content.fileData, destinationPath, fileDetails.content.size);
202
- return;
214
+ await clientMessager.sendFile(clientID, recipientID, fileDetails.content.fileName, fileDetails.content.filePath, destinationPath, fileDetails.content.size, tideSize, minTideSize, maxTideSize);
215
+ return true;
203
216
  }
204
217
  if (fileDetails.type === "directory") {
205
218
  FileTide.outputGradient(`[${clientID}] Sending ${fileDetails.content.length} files in directory ~ ${path.basename(filePath)} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
206
- clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath, tideSize, minTideSize, maxTideSize);
207
- return;
219
+ await clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath, tideSize, minTideSize, maxTideSize);
220
+ return true;
208
221
  }
209
222
  FileTide.outputGradient(`[FileTide] ~ No active client ~ ${recipientID} found.`, errorMessageColors);
210
- return;
223
+ return false;
211
224
  }
212
225
 
213
226
  /**
@@ -522,6 +535,7 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
522
535
  const capacitor = new ChunkCapacitor(directoryContents, 100);
523
536
  const contents = capacitor.getAllChunks();
524
537
  for (let files of contents) {
538
+ totalFilesSent += files.length;
525
539
  client.clientTide.emitEvent(userID, "send-file-list-to-client", {
526
540
  senderID: data.recipientID,
527
541
  recipientID: data.senderID,
@@ -529,7 +543,6 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
529
543
  listedFiles: files
530
544
  });
531
545
  }
532
- totalFilesSent += totalItems;
533
546
  });
534
547
  setTimeout(() => {
535
548
  _FileTide.outputGradient(`[${userID}] Sent file list to ${data.senderID}!`, significantMessageColors);
@@ -544,13 +557,13 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
544
557
  _FileTide.outputGradient(`[${userID} | FileTide Error] ${error}`, errorMessageColors);
545
558
  });
546
559
  client.clientTide.onEvent(userID, "client-file-list-received", async data => {
547
- _FileTide.outputGradient(`[${userID}] Successfully received all ${data.totalItemsSent} contents in ${data.senderID}'s file list!`, significantMessageColors);
560
+ _FileTide.outputGradient(`[${userID}] Successfully received all ${HUDUtilityManager.convertNumberToMoneyFormat(data.totalItemsSent, false)} contents in ${data.senderID}'s file list!`, significantMessageColors);
548
561
  if (onFileListReceived) {
549
562
  onFileListReceived(data);
550
563
  }
551
564
  });
552
565
  client.clientTide.onEvent(userID, "client-file-list", async data => {
553
- _FileTide.outputGradient(`[${userID}] Received ${data.totalItems} directory contents from ${data.senderID}'s file list!`, significantMessageColors);
566
+ _FileTide.outputGradient(`[${userID}] Received ${HUDUtilityManager.convertNumberToMoneyFormat(data.totalItems, false)} directory contents from ${data.senderID}'s file list!`, significantMessageColors);
554
567
  if (onListFiles) {
555
568
  onListFiles(data);
556
569
  }
@@ -24,10 +24,11 @@ class FileTransferManager {
24
24
  onSendChunk,
25
25
  onComplete,
26
26
  onProgress,
27
- onStart
27
+ onStart,
28
+ onTiding
28
29
  }, clientTide) {
29
30
  const transferId = _assertClassBrand(_FileTransferManager_brand, this, _generateTransferId).call(this, fileDetails);
30
- return await _assertClassBrand(_FileTransferManager_brand, this, _startTransfer).call(this, file, transferId, fileDetails, originDetails, onSendChunk, onComplete, onProgress, onStart, clientTide);
31
+ return await _assertClassBrand(_FileTransferManager_brand, this, _startTransfer).call(this, file, transferId, fileDetails, originDetails, onSendChunk, onComplete, onProgress, onStart, onTiding, clientTide);
31
32
  }
32
33
  async sendLargeFile(fileHandle, {
33
34
  fileDetails,
@@ -107,31 +108,36 @@ class FileTransferManager {
107
108
  const keepAliveInterval = setInterval(() => {
108
109
  if (onTiding) onTiding();
109
110
  }, 5000);
110
- try {
111
- let adaptiveBatchSize = 100;
112
- while (currentBatchStart < totalChunks) {
113
- if (transferState.activeChunks < this.parallelChunks) {
114
- adaptiveBatchSize = Math.min(adaptiveBatchSize + 10, 100);
115
- } else {
116
- adaptiveBatchSize = Math.max(adaptiveBatchSize - 10, 10);
111
+ return new Promise(async (resolve, reject) => {
112
+ try {
113
+ let adaptiveBatchSize = 100;
114
+ while (currentBatchStart < totalChunks) {
115
+ if (transferState.activeChunks < this.parallelChunks) {
116
+ adaptiveBatchSize = Math.min(adaptiveBatchSize + 10, 100);
117
+ } else {
118
+ adaptiveBatchSize = Math.max(adaptiveBatchSize - 10, 10);
119
+ }
120
+ await sendChunkBatch(currentBatchStart, adaptiveBatchSize);
121
+ currentBatchStart += adaptiveBatchSize;
122
+ await new Promise(resolve => setTimeout(resolve, 30));
123
+ }
124
+ } finally {
125
+ clearInterval(keepAliveInterval);
126
+ fileHandle.on("close", resolve);
127
+ fileHandle.on("error", error => {
128
+ this.activeTransfers.delete(transferId);
129
+ reject(error);
130
+ });
131
+ try {
132
+ await fileHandle.close();
133
+ } catch (error) {
134
+ console.error(`[FileTide ~ Transporter] ~ Error closing file handle ~ ${error.message}`);
117
135
  }
118
- await sendChunkBatch(currentBatchStart, adaptiveBatchSize);
119
- currentBatchStart += adaptiveBatchSize;
120
- await new Promise(resolve => setTimeout(resolve, 30));
121
136
  }
122
- } finally {
123
- clearInterval(keepAliveInterval);
124
- }
125
- return new Promise((resolve, reject) => {
126
- fileHandle.on("close", resolve);
127
- fileHandle.on("error", error => {
128
- this.activeTransfers.delete(transferId);
129
- reject(error);
130
- });
131
137
  });
132
138
  }
133
139
  }
134
- async function _startTransfer(file, transferId, fileDetails, originDetails, onSendChunk, onComplete, onProgress, onStart, clientTide) {
140
+ async function _startTransfer(file, transferId, fileDetails, originDetails, onSendChunk, onComplete, onProgress, onStart, onTiding, clientTide) {
135
141
  const recoveredTransferState = (await clientTide.emitEventWithResponse(originDetails.senderID, "get-client-transfer-state", "sent-current-transfer-state", {
136
142
  recipientID: originDetails.recipientID,
137
143
  senderID: originDetails.senderID,
@@ -142,6 +148,8 @@ async function _startTransfer(file, transferId, fileDetails, originDetails, onSe
142
148
  lastChunkSent: 0
143
149
  };
144
150
  const totalChunks = Math.ceil(file.length / this.chunkSize);
151
+ let nextExpectedChunk = recoveredTransferState.lastChunkSent;
152
+ const chunkBuffer = new Map();
145
153
  this.activeTransfers.set(transferId, {
146
154
  totalChunks,
147
155
  transferredSize: recoveredTransferState.transferredSize,
@@ -151,64 +159,187 @@ async function _startTransfer(file, transferId, fileDetails, originDetails, onSe
151
159
  currentChunk: recoveredTransferState.lastChunkSent,
152
160
  size: fileDetails.size
153
161
  });
162
+ const transferState = this.activeTransfers.get(transferId);
154
163
  const updateProgress = () => {
155
- const transferState = this.activeTransfers.get(transferId);
156
164
  if (onProgress) {
157
165
  onProgress(transferState.completedChunks / transferState.totalChunks * 100, this.chunkSize);
158
166
  }
159
167
  };
160
168
  const exponentialBackoff = retryCount => Math.min(1000 * 2 ** retryCount, 10000);
161
- return new Promise((resolve, reject) => {
162
- const sendChunk = async chunkIndex => {
163
- const transferState = this.activeTransfers.get(transferId);
164
- if (chunkIndex >= transferState.totalChunks) {
165
- return;
166
- }
167
- const start = chunkIndex * this.chunkSize;
168
- const end = Math.min(start + this.chunkSize, file.length);
169
- const chunk = file.slice(start, end);
170
- if (onStart && chunkIndex === 0) {
171
- onStart({
172
- name: fileDetails.name,
173
- path: fileDetails.path,
174
- size: fileDetails.size,
175
- totalChunks: transferState.totalChunks
176
- });
177
- }
178
- transferState.activeChunks++;
179
- try {
180
- await onSendChunk(transferId, chunkIndex, this.chunkSize, chunk, transferState.totalChunks, transferState.completedChunks + 1);
181
- transferState.transferredSize += this.chunkSize;
169
+ const sendChunk = async chunkIndex => {
170
+ const start = chunkIndex * this.chunkSize;
171
+ const end = Math.min(start + this.chunkSize, file.length);
172
+ const chunk = file.slice(start, end);
173
+ transferState.activeChunks++;
174
+ try {
175
+ await onSendChunk(transferId, chunkIndex, this.chunkSize, chunk, totalChunks, transferState.completedChunks + 1);
176
+ chunkBuffer.set(chunkIndex, chunk);
177
+ while (chunkBuffer.has(nextExpectedChunk)) {
178
+ chunkBuffer.delete(nextExpectedChunk);
179
+ nextExpectedChunk++;
182
180
  transferState.completedChunks++;
183
- transferState.activeChunks--;
184
181
  updateProgress();
185
- if (transferState.activeChunks < this.parallelChunks && transferState.currentChunk < transferState.totalChunks) {
186
- sendChunk(transferState.currentChunk++);
187
- }
188
- if (transferState.completedChunks === transferState.totalChunks) {
189
- await onComplete(transferId);
190
- this.activeTransfers.delete(transferId);
191
- resolve();
192
- }
193
- } catch (error) {
194
- console.error(`[FileTide ~ File Messager] ~ Did not send chunk ${chunkIndex} ~ ${error.message}`);
195
- transferState.activeChunks--;
196
- const retryCount = (transferState.failedChunks.get(chunkIndex) || 0) + 1;
197
- if (retryCount > this.maxRetries) {
198
- this.activeTransfers.delete(transferId);
199
- reject(new Error(`Did not send chunk ${chunkIndex} after ${this.maxRetries} retries.`));
200
- } else {
201
- transferState.failedChunks.set(chunkIndex, retryCount);
202
- setTimeout(() => sendChunk(chunkIndex), exponentialBackoff(retryCount));
203
- }
204
182
  }
205
- };
206
- const transferState = this.activeTransfers.get(transferId);
207
- for (let i = 0; i < Math.min(this.parallelChunks, transferState.totalChunks); i++) {
208
- sendChunk(transferState.currentChunk++);
183
+ if (transferState.completedChunks === transferState.totalChunks) {
184
+ clearInterval(keepAliveInterval);
185
+ await onComplete(transferId);
186
+ this.activeTransfers.delete(transferId);
187
+ }
188
+ } catch (error) {
189
+ console.error(`[FileTide ~ Transporter] ~ Did not send chunk ${chunkIndex} ~ ${error.message}`);
190
+ const retryCount = (transferState.failedChunks.get(chunkIndex) || 0) + 1;
191
+ if (retryCount > this.maxRetries) {
192
+ clearInterval(keepAliveInterval);
193
+ this.activeTransfers.delete(transferId);
194
+ throw new Error(`[FileTide ~ Transporter] ~ Did not send chunk ${chunkIndex} after ${this.maxRetries} retries.`);
195
+ }
196
+ transferState.failedChunks.set(chunkIndex, retryCount);
197
+ setTimeout(() => sendChunk(chunkIndex), exponentialBackoff(retryCount));
198
+ } finally {
199
+ transferState.activeChunks--;
209
200
  }
210
- });
201
+ };
202
+ const sendChunkBatch = async (startChunkIndex, batchSize = 100, miniBatchSize = 10) => {
203
+ for (let i = 0; i < batchSize && startChunkIndex + i < totalChunks; i += miniBatchSize) {
204
+ const miniBatchPromises = [];
205
+ for (let j = 0; j < miniBatchSize && startChunkIndex + i + j < totalChunks; j++) {
206
+ const chunkIndex = startChunkIndex + i + j;
207
+ miniBatchPromises.push(sendChunk(chunkIndex));
208
+ }
209
+ await Promise.all(miniBatchPromises);
210
+ await new Promise(resolve => setTimeout(resolve, 5));
211
+ }
212
+ };
213
+ const keepAliveInterval = setInterval(() => {
214
+ if (onTiding) onTiding();
215
+ }, 5000);
216
+ let currentBatchStart = transferState.currentChunk;
217
+ let adaptiveBatchSize = 100;
218
+ try {
219
+ while (currentBatchStart < totalChunks) {
220
+ adaptiveBatchSize = transferState.activeChunks < this.parallelChunks ? Math.min(adaptiveBatchSize + 10, 100) : Math.max(adaptiveBatchSize - 10, 10);
221
+ await sendChunkBatch(currentBatchStart, adaptiveBatchSize);
222
+ currentBatchStart += adaptiveBatchSize;
223
+ await new Promise(resolve => setTimeout(resolve, 30));
224
+ }
225
+ } finally {
226
+ clearInterval(keepAliveInterval);
227
+ }
211
228
  }
229
+ /*async #startTransfer(file, transferId, fileDetails, originDetails, onSendChunk, onComplete, onProgress, onStart, onTiding, clientTide)
230
+ {
231
+ const recoveredTransferState = await clientTide.emitEventWithResponse(originDetails.senderID, "get-client-transfer-state", "sent-current-transfer-state", { recipientID : originDetails.recipientID, senderID : originDetails.senderID, filePath : fileDetails.destinationPath, fileName : fileDetails.name }, 30000) || { transferredSize : 0, lastChunkSent : 0 };
232
+
233
+ const totalChunks = Math.ceil(file.length / this.chunkSize);
234
+
235
+ this.activeTransfers.set(transferId, {
236
+ totalChunks,
237
+ transferredSize : recoveredTransferState.transferredSize,
238
+ completedChunks : recoveredTransferState.lastChunkSent,
239
+ activeChunks : 0,
240
+ failedChunks : new Map(),
241
+ currentChunk : recoveredTransferState.lastChunkSent,
242
+ size : fileDetails.size
243
+ });
244
+ const updateProgress = () => {
245
+ const transferState = this.activeTransfers.get(transferId);
246
+
247
+ if (onProgress)
248
+ {
249
+ onProgress((transferState.completedChunks / transferState.totalChunks) * 100, this.chunkSize);
250
+ }
251
+ };
252
+
253
+ const keepAliveInterval = setInterval(() => {
254
+ if (onTiding) onTiding();
255
+ }, 5000);
256
+
257
+ const exponentialBackoff = (retryCount) => Math.min(1000 * (2 ** retryCount), 10000);
258
+
259
+ return new Promise((resolve, reject) => {
260
+ const sendChunk = async (chunkIndex) => {
261
+ const transferState = this.activeTransfers.get(transferId);
262
+
263
+ if (chunkIndex >= transferState.totalChunks) { return; }
264
+ const start = chunkIndex * this.chunkSize;
265
+
266
+ const end = Math.min(start + this.chunkSize, file.length);
267
+
268
+ const chunk = file.slice(start, end);
269
+
270
+ if (onStart && chunkIndex === 0)
271
+ {
272
+ onStart({
273
+ name : fileDetails.name,
274
+ path : fileDetails.path,
275
+ size : fileDetails.size,
276
+ totalChunks : transferState.totalChunks
277
+ });
278
+ }
279
+
280
+ transferState.activeChunks++;
281
+
282
+ try
283
+ {
284
+ await onSendChunk(transferId, chunkIndex, this.chunkSize, chunk, transferState.totalChunks, transferState.completedChunks + 1);
285
+
286
+ transferState.transferredSize += this.chunkSize;
287
+
288
+ transferState.completedChunks++;
289
+
290
+ transferState.activeChunks--;
291
+
292
+ updateProgress();
293
+
294
+ if (transferState.activeChunks < this.parallelChunks && transferState.currentChunk < transferState.totalChunks)
295
+ {
296
+ sendChunk(transferState.currentChunk++);
297
+ }
298
+
299
+ if (transferState.completedChunks === transferState.totalChunks)
300
+ {
301
+ await onComplete(transferId);
302
+
303
+ this.activeTransfers.delete(transferId);
304
+
305
+ clearInterval(keepAliveInterval);
306
+
307
+ resolve();
308
+ }
309
+ }
310
+ catch (error)
311
+ {
312
+ console.error(`[FileTide ~ File Messager] ~ Did not send chunk ${chunkIndex} ~ ${error.message}`);
313
+
314
+ transferState.activeChunks--;
315
+
316
+ const retryCount = (transferState.failedChunks.get(chunkIndex) || 0) + 1;
317
+
318
+ if (retryCount > this.maxRetries)
319
+ {
320
+ this.activeTransfers.delete(transferId);
321
+
322
+ clearInterval(keepAliveInterval);
323
+
324
+ reject(new Error(`Did not send chunk ${chunkIndex} after ${this.maxRetries} retries.`));
325
+ }
326
+ else
327
+ {
328
+ transferState.failedChunks.set(chunkIndex, retryCount);
329
+
330
+ setTimeout(() => sendChunk(chunkIndex), exponentialBackoff(retryCount));
331
+ }
332
+ }
333
+ };
334
+
335
+ const transferState = this.activeTransfers.get(transferId);
336
+
337
+ for (let i = 0; i < Math.min(this.parallelChunks, transferState.totalChunks); i++)
338
+ {
339
+ sendChunk(transferState.currentChunk++);
340
+ }
341
+ });
342
+ }*/
212
343
  function _generateTransferId(file) {
213
344
  return `${file.name}-${Date.now()}`;
214
345
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trap_stevo/filetide",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
4
4
  "description": "Revolutionizing real-time file transfer with seamless, instant communication across any device. Deliver files instantly, regardless of platform, and experience unparalleled speed and control in managing transfers. Elevate your file-sharing capabilities with a tool designed for precision, efficiency, and effortless connectivity.",
5
5
  "main": "dist/cjs/FileTide.js",
6
6
  "scripts": {
@@ -31,7 +31,7 @@
31
31
  "dependencies": {
32
32
  "@trap_stevo/iotide": "^0.0.37",
33
33
  "@trap_stevo/iotide-client": "^0.0.23",
34
- "@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.42",
34
+ "@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.43",
35
35
  "chalk": "^4.1.2",
36
36
  "readline": "^1.3.0"
37
37
  },