@trap_stevo/filetide 0.0.32 → 0.0.34
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/LICENSE.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# License Agreement for @trap_stevo/filetide
|
|
2
|
+
|
|
3
|
+
**Effective Date**: September 10, 2024
|
|
4
|
+
|
|
5
|
+
This License Agreement ("Agreement") is entered into by and between **Steven Compton**, the owner of the package ("Licensor"), and any individual or entity attempting to use the package ("Licensee"). By attempting to install, use, or modify the package, Licensee agrees to the following terms and conditions:
|
|
6
|
+
|
|
7
|
+
## 1. **Ownership**
|
|
8
|
+
**Steven Compton** is the sole owner of all rights, title, and interest in and to the @trap_stevo/filetide, including all related intellectual property rights.
|
|
9
|
+
|
|
10
|
+
## 2. **Grant of License**
|
|
11
|
+
Licensee is **NOT** granted any rights to use, modify, distribute, or create derivative works of @trap_stevo/filetide, either in part or in whole, without prior express written consent from the Licensor. Any use of the package without permission constitutes a violation of this Agreement and applicable intellectual property laws.
|
|
12
|
+
|
|
13
|
+
## 3. **Prohibited Uses**
|
|
14
|
+
Without the express written permission of the Licensor, Licensee shall not:
|
|
15
|
+
- Use the package for any purpose, including but not limited to personal, commercial, or educational purposes.
|
|
16
|
+
- Copy, modify, distribute, or sublicense the package.
|
|
17
|
+
- Reverse-engineer, decompile, or disassemble the package.
|
|
18
|
+
|
|
19
|
+
## 4. **Request for Permission**
|
|
20
|
+
Licensee may request permission to use the package by contacting the Licensor at **h.steven.compton13@gmail.com**. The Licensor reserves the right to approve or deny any such request at its sole discretion.
|
|
21
|
+
|
|
22
|
+
## 5. **Violation of Terms**
|
|
23
|
+
Any unauthorized use of @trap_stevo/filetide will result in immediate termination of any rights granted and may lead to legal action. Licensor reserves the right to seek damages and remedies to the fullest extent permitted by law.
|
|
24
|
+
|
|
25
|
+
## 6. **No Warranty**
|
|
26
|
+
@trap_stevo/filetide is provided "as is," without any warranty of any kind, express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, or non-infringement. Licensee assumes all risks associated with the use of the package, including but not limited to potential damage to systems or data.
|
|
27
|
+
|
|
28
|
+
## 7. **Indemnification**
|
|
29
|
+
Licensee agrees to indemnify and hold harmless the Licensor from any and all claims, damages, liabilities, and expenses arising out of Licensee's use of the package in violation of this Agreement.
|
|
30
|
+
|
|
31
|
+
## 8. **Governing Law**
|
|
32
|
+
This Agreement shall be governed by and construed in accordance with the laws of **Georgia**, without regard to its conflict of law principles.
|
|
33
|
+
|
|
34
|
+
## 9. **Amendments**
|
|
35
|
+
Licensor reserves the right to modify or update this Agreement at any time. Any changes to the Agreement will be effective immediately upon being posted to **[Location of Agreement, e.g., repository or website]**.
|
package/dist/cjs/FileMessager.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
const {
|
|
11
11
|
FileTransferManager
|
|
12
12
|
} = require("./HUDManagers/FileTransferManager");
|
|
13
|
+
const HUDTargetQueue = require("./HUDManagers/HUDTargetQueueManager");
|
|
13
14
|
class FileMessager {
|
|
14
15
|
constructor(options = {
|
|
15
16
|
pingTimeout: 3600000,
|
|
@@ -20,6 +21,9 @@ class FileMessager {
|
|
|
20
21
|
}) {
|
|
21
22
|
const serverOptions = FileMessagerConfigManager.getServerOptions(options);
|
|
22
23
|
this.transporter = new FileTransferManager(transportOptions);
|
|
24
|
+
this.transferQueue = new HUDTargetQueue();
|
|
25
|
+
this.currentTransferBarrierTransfers = new Map();
|
|
26
|
+
this.clientTransferBarriers = new Map();
|
|
23
27
|
this.fileNet = new IoTide(serverOptions.port, serverOptions, true, this.onConnect.bind(this), this.onDisconnect.bind(this));
|
|
24
28
|
this.onlineClients = new Map();
|
|
25
29
|
return;
|
|
@@ -35,6 +39,10 @@ class FileMessager {
|
|
|
35
39
|
return;
|
|
36
40
|
}
|
|
37
41
|
start() {
|
|
42
|
+
this.fileNet.on("get-client-transfer-barrier-response", this.handleClientTransferBarrierResponse.bind(this));
|
|
43
|
+
this.fileNet.on("notify-transfer-barrier", this.handleNotifyTransferBarrier.bind(this));
|
|
44
|
+
this.fileNet.on("send-file-list-to-client", this.handleSendFileListToClient.bind(this));
|
|
45
|
+
this.fileNet.on("list-client-files", this.handleListClientFiles.bind(this));
|
|
38
46
|
this.fileNet.on("transfer-start", this.handleFileTransferStart.bind(this));
|
|
39
47
|
this.fileNet.on("transfer-progress", this.handleFileTransferProgress.bind(this));
|
|
40
48
|
this.fileNet.on("transfer-complete", this.handleFileTransferComplete.bind(this));
|
|
@@ -63,7 +71,8 @@ class FileMessager {
|
|
|
63
71
|
senderID,
|
|
64
72
|
requesterID,
|
|
65
73
|
path: filePath,
|
|
66
|
-
destinationPath
|
|
74
|
+
destinationPath,
|
|
75
|
+
type: "requestedTransfer"
|
|
67
76
|
});
|
|
68
77
|
}
|
|
69
78
|
|
|
@@ -118,6 +127,41 @@ class FileMessager {
|
|
|
118
127
|
} = transferData;
|
|
119
128
|
this.requestFromClient(requesterID, senderID, filePath, destinationPath);
|
|
120
129
|
}
|
|
130
|
+
handleSendFileListToClient(listData) {
|
|
131
|
+
const {
|
|
132
|
+
listedFiles,
|
|
133
|
+
recipientID,
|
|
134
|
+
senderID
|
|
135
|
+
} = listData;
|
|
136
|
+
const recipientConnectionID = FileNetClientManager.getOnlineClient(recipientID).id;
|
|
137
|
+
const senderConnectionID = FileNetClientManager.getOnlineClient(senderID).id;
|
|
138
|
+
const senderClient = this.getClientFromID(senderConnectionID);
|
|
139
|
+
const client = this.getClientFromID(recipientConnectionID);
|
|
140
|
+
if (!client || !senderClient) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.fileNet.emitToTide(client.tideID, "client-file-list", {
|
|
144
|
+
senderID,
|
|
145
|
+
listedFiles
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
handleListClientFiles(clientData) {
|
|
149
|
+
const {
|
|
150
|
+
requesterID,
|
|
151
|
+
recipientID
|
|
152
|
+
} = clientData;
|
|
153
|
+
const recipientConnectionID = FileNetClientManager.getOnlineClient(recipientID).id;
|
|
154
|
+
const senderConnectionID = FileNetClientManager.getOnlineClient(requesterID).id;
|
|
155
|
+
const senderClient = this.getClientFromID(senderConnectionID);
|
|
156
|
+
const client = this.getClientFromID(recipientConnectionID);
|
|
157
|
+
if (!client || !senderClient) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
this.fileNet.emitToTide(client.tideID, "list-files", {
|
|
161
|
+
senderID: requesterID,
|
|
162
|
+
recipientID
|
|
163
|
+
});
|
|
164
|
+
}
|
|
121
165
|
handleClientToClientTransfer(transferData) {
|
|
122
166
|
const {
|
|
123
167
|
senderID,
|
|
@@ -147,9 +191,48 @@ class FileMessager {
|
|
|
147
191
|
console.log(`[FileTide ~ File Messager] ~ File transfer completed: ${fileName} from ${senderID} to ${recipientId}`);
|
|
148
192
|
}
|
|
149
193
|
}
|
|
194
|
+
handleClientTransferBarrierResponse(data) {
|
|
195
|
+
console.log(`Checking ${data.recipientID}'s transfer barrier for transfer ~ ${data.transferID}...`);
|
|
196
|
+
const recipientConnectionID = FileNetClientManager.getOnlineClient(data.recipientID).id;
|
|
197
|
+
const senderConnectionID = FileNetClientManager.getOnlineClient(data.clientID).id;
|
|
198
|
+
const senderClient = this.getClientFromID(senderConnectionID);
|
|
199
|
+
const client = this.getClientFromID(recipientConnectionID);
|
|
200
|
+
if (!client || !senderClient) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
this.fileNet.emitToTide(client.tideID, "queue-incoming-transfer", {
|
|
204
|
+
...data,
|
|
205
|
+
transferType: "send"
|
|
206
|
+
});
|
|
207
|
+
this.transferQueue.waitForData(data.transferID, () => this.currentTransferBarrierTransfers.get(data.transferID), [true, false], 100, 50000).then(accepted => {
|
|
208
|
+
console.log(`Transfer ${accepted ? "accepted!" : "denied."}`);
|
|
209
|
+
this.fileNet.emitToTide(senderClient.tideID, "transfer-barrier-response", accepted);
|
|
210
|
+
this.currentTransferBarrierTransfers.delete(data.transferID);
|
|
211
|
+
if (accepted) {
|
|
212
|
+
this.fileNet.emitToTide(client.tideID, "accepted-transfer", {
|
|
213
|
+
accepted,
|
|
214
|
+
...data
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this.fileNet.emitToTide(client.tideID, "denied-transfer", {
|
|
219
|
+
accepted,
|
|
220
|
+
...data
|
|
221
|
+
});
|
|
222
|
+
}).catch(error => {
|
|
223
|
+
console.error(error.message);
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
handleNotifyTransferBarrier(data, emitToChannel) {
|
|
228
|
+
const accepted = data.accepted;
|
|
229
|
+
const transferID = data.data.transferID;
|
|
230
|
+
this.currentTransferBarrierTransfers.set(transferID, accepted);
|
|
231
|
+
}
|
|
150
232
|
handleFileTransferStart(data, emitToChannel) {
|
|
151
233
|
console.log(`Alerting client of incoming transfer from ${data.senderID}...`);
|
|
152
|
-
const
|
|
234
|
+
const recipientConnectionID = FileNetClientManager.getOnlineClient(data.recipientID).id;
|
|
235
|
+
const client = this.getClientFromID(recipientConnectionID);
|
|
153
236
|
if (!client) {
|
|
154
237
|
return;
|
|
155
238
|
}
|
|
@@ -175,11 +258,15 @@ class FileMessager {
|
|
|
175
258
|
this.fileNet.emitToTide(connectionDetails.tideID, "current-online-clients", Object.fromEntries(FileNetClientManager.getOnlineClients()));
|
|
176
259
|
return;
|
|
177
260
|
}
|
|
178
|
-
handleClientOnline(clientID, tideID, pClientID, connectionID) {
|
|
261
|
+
handleClientOnline(clientID, tideID, pClientID, connectionID, activeTransferBarrier) {
|
|
179
262
|
if (!this.onlineClients.has(connectionID)) {
|
|
180
263
|
return;
|
|
181
264
|
}
|
|
182
265
|
FileNetClientManager.addOnlineClient(clientID, pClientID, tideID, connectionID);
|
|
266
|
+
this.clientTransferBarriers.set(tideID, {
|
|
267
|
+
clientID,
|
|
268
|
+
activeTransferBarrier
|
|
269
|
+
});
|
|
183
270
|
this.fileNet.emitToTide(tideID, "current-online-clients", Object.fromEntries(FileNetClientManager.getOnlineClients()));
|
|
184
271
|
return;
|
|
185
272
|
}
|
|
@@ -30,6 +30,7 @@ class FileMessagerClient {
|
|
|
30
30
|
this.progressBarManager = new ConsoleProgressBarManager([], {
|
|
31
31
|
completionMessageColor: chalk.blue.bold
|
|
32
32
|
});
|
|
33
|
+
this.activeTransferTypes = new Map();
|
|
33
34
|
this.clientTide = new HUDIoTide();
|
|
34
35
|
this.clients = new Map();
|
|
35
36
|
}
|
|
@@ -52,15 +53,56 @@ class FileMessagerClient {
|
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
|
-
requestTransfer(clientID, senderID, filePath, destinationPath) {
|
|
56
|
+
async requestTransfer(clientID, senderID, filePath, destinationPath, filterContent = []) {
|
|
57
|
+
const transferAllowed = await this.allowedTransfer(clientID, senderID, destinationPath, "N/A", "requestSend");
|
|
58
|
+
if (!transferAllowed) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
56
61
|
this.clientTide.emitEvent(clientID, "request-transfer-from-client", {
|
|
57
62
|
requesterID: clientID,
|
|
58
63
|
senderID,
|
|
59
64
|
destinationPath,
|
|
65
|
+
filterContent,
|
|
60
66
|
filePath
|
|
61
67
|
});
|
|
62
68
|
return;
|
|
63
69
|
}
|
|
70
|
+
listFiles(clientID, recipientID, depth = 1) {
|
|
71
|
+
this.clientTide.emitEvent(clientID, "list-client-files", {
|
|
72
|
+
requesterID: clientID,
|
|
73
|
+
recipientID,
|
|
74
|
+
depth
|
|
75
|
+
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
async allowedTransfer(clientID, recipientID, filePath, fileSize, transferType = "send") {
|
|
79
|
+
try {
|
|
80
|
+
const currentDate = Date.now();
|
|
81
|
+
const transferID = `${clientID}_${filePath}_${currentDate}`;
|
|
82
|
+
const transferAccepted = await this.clientTide.emitEventWithResponse(clientID, "get-client-transfer-barrier-response", "transfer-barrier-response", {
|
|
83
|
+
clientID,
|
|
84
|
+
recipientID,
|
|
85
|
+
transferID,
|
|
86
|
+
path: filePath,
|
|
87
|
+
date: currentDate,
|
|
88
|
+
transferType
|
|
89
|
+
}, 30000);
|
|
90
|
+
if (!transferAccepted) {
|
|
91
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ${recipientID} denied your transfer.`, errorMessageColors);
|
|
92
|
+
return false;
|
|
93
|
+
} else if (transferAccepted) {
|
|
94
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ${recipientID} accepted your transfer!`, significantMessageColors);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (error.message.startsWith("Timeout: No response")) {
|
|
99
|
+
FileNetUtilityManager.outputGradient("[FileTide ~ File Messager] Transfer approval timed out.", errorMessageColors);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] Transfer approval refused.\n${error}`, errorMessageColors);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
64
106
|
|
|
65
107
|
/**
|
|
66
108
|
* Sends all files in a directory in parallel using the sendFile method.
|
|
@@ -70,21 +112,33 @@ class FileMessagerClient {
|
|
|
70
112
|
* @param {string} baseDirectory - The base directory path that contains the files.
|
|
71
113
|
* @param {string} destinationPath - The path to which the files should be sent.
|
|
72
114
|
*/
|
|
73
|
-
sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
|
|
115
|
+
async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
|
|
74
116
|
try {
|
|
75
117
|
const baseDirectoryName = path.basename(baseDirectory);
|
|
76
118
|
const adjustedDestinationPath = FileUtilityManager.normalizePath(path.join(destinationPath, baseDirectoryName));
|
|
77
|
-
|
|
119
|
+
let totalSize = 0;
|
|
120
|
+
filesData.forEach(fileInfo => {
|
|
121
|
+
totalSize += fileInfo.size;
|
|
122
|
+
});
|
|
123
|
+
const incomingType = this.activeTransferTypes.get(destinationPath);
|
|
124
|
+
const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, adjustedDestinationPath, totalSize, "send");
|
|
125
|
+
if (incomingType) {
|
|
126
|
+
this.activeTransferTypes.delete(destinationPath);
|
|
127
|
+
}
|
|
128
|
+
if (!transferAllowed) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const sendFilePromises = filesData.map(async (fileInfo, index) => {
|
|
78
132
|
const relativeFilePath = path.dirname(path.relative(baseDirectory, fileInfo.filePath));
|
|
79
133
|
const fileDestinationPath = FileUtilityManager.normalizePath(path.join(adjustedDestinationPath, relativeFilePath));
|
|
80
|
-
return this.
|
|
134
|
+
return await this.sendDirectoryFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath, fileInfo.size, adjustedDestinationPath, index);
|
|
81
135
|
});
|
|
82
136
|
Promise.all(sendFilePromises).then(() => console.log("\nAll files in directory transferred successfully!")).catch(error => console.error("Did not transfer directory: ", error));
|
|
83
137
|
} catch (error) {
|
|
84
138
|
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused directory transfer: ${error}`, errorMessageColors);
|
|
85
139
|
}
|
|
86
140
|
}
|
|
87
|
-
|
|
141
|
+
sendDirectoryFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize, directoryPath = null, index = 0) {
|
|
88
142
|
if (!file) {
|
|
89
143
|
return;
|
|
90
144
|
}
|
|
@@ -146,6 +200,76 @@ class FileMessagerClient {
|
|
|
146
200
|
}
|
|
147
201
|
}).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
|
|
148
202
|
}
|
|
203
|
+
async sendFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize) {
|
|
204
|
+
if (!file) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const destinationPath = FileUtilityManager.normalizePath(path.join(filePath, fileName));
|
|
208
|
+
FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Requesting to transfer ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
|
|
209
|
+
const incomingType = this.activeTransferTypes.get(filePath);
|
|
210
|
+
const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, destinationPath, fileSize, "send");
|
|
211
|
+
if (incomingType) {
|
|
212
|
+
this.activeTransferTypes.delete(filePath);
|
|
213
|
+
}
|
|
214
|
+
if (!transferAllowed) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
|
|
218
|
+
this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
|
|
219
|
+
this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
|
|
220
|
+
this.clientTide.emitEvent(clientID, "transfer-start", {
|
|
221
|
+
fileName,
|
|
222
|
+
senderID: clientID,
|
|
223
|
+
recipientID: recipientId,
|
|
224
|
+
path: filePath
|
|
225
|
+
});
|
|
226
|
+
this.transporter.sendFile(file, {
|
|
227
|
+
onSendChunk: (transferId, chunkIndex, chunkData, totalChunks) => {
|
|
228
|
+
if (chunkIndex === 0) {
|
|
229
|
+
this.progressBarManager.addTask({
|
|
230
|
+
name: fileName,
|
|
231
|
+
size: fileSize
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return new Promise((resolve, reject) => {
|
|
235
|
+
this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
|
|
236
|
+
senderID: clientID,
|
|
237
|
+
recipientId,
|
|
238
|
+
fileName,
|
|
239
|
+
fileChunk: chunkData,
|
|
240
|
+
totalChunks,
|
|
241
|
+
chunkIndex,
|
|
242
|
+
filePath,
|
|
243
|
+
fileSize
|
|
244
|
+
});
|
|
245
|
+
resolve();
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
onComplete: () => {
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
this.clientTide.emitEvent(clientID, "transfer-complete", {
|
|
251
|
+
senderID: clientID,
|
|
252
|
+
recipientId,
|
|
253
|
+
fileName,
|
|
254
|
+
filePath
|
|
255
|
+
});
|
|
256
|
+
resolve();
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
onProgress: progress => {
|
|
260
|
+
this.progressBarManager.updateTaskProgress(fileName, 512 * 1024);
|
|
261
|
+
},
|
|
262
|
+
originDetails: {
|
|
263
|
+
recipientID: recipientId,
|
|
264
|
+
senderID: clientID
|
|
265
|
+
},
|
|
266
|
+
fileDetails: {
|
|
267
|
+
name: fileName,
|
|
268
|
+
path: filePath,
|
|
269
|
+
size: fileSize
|
|
270
|
+
}
|
|
271
|
+
}).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
|
|
272
|
+
}
|
|
149
273
|
}
|
|
150
274
|
;
|
|
151
275
|
module.exports = FileMessagerClient;
|
package/dist/cjs/FileTide.js
CHANGED
|
@@ -34,6 +34,7 @@ class FileTide {
|
|
|
34
34
|
completionMessageColor: chalk.blue.bold
|
|
35
35
|
});
|
|
36
36
|
this.clientShorePolicies = new Map();
|
|
37
|
+
this.transferBarrier = false;
|
|
37
38
|
this.clients = new Map();
|
|
38
39
|
this.currentUserID = "";
|
|
39
40
|
return;
|
|
@@ -65,34 +66,51 @@ class FileTide {
|
|
|
65
66
|
* @param {String} roomName - The name of the room to join
|
|
66
67
|
* @param {String} userID - The ID of the client user
|
|
67
68
|
* @param {Function} onLaunch - Callback for when a file messager launches
|
|
69
|
+
* @param {Function} onIncomingFile - Callback for incoming files in transit
|
|
70
|
+
* @param {Function} onIncomingTransfer - Callback for incoming transfers
|
|
71
|
+
* @param {Function} onTransferBarrier - Callback for when an incoming transfer status updates
|
|
72
|
+
* @param {Function} onTransferProgress - Callback for when an current transfers progress
|
|
73
|
+
* @param {Boolean} [enableTransferBarrier=true] - Whether to enable the transfer barrier
|
|
74
|
+
* @param {Object} [options = {}] - Additional options
|
|
68
75
|
*/
|
|
69
|
-
launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile) {
|
|
76
|
+
launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}) {
|
|
70
77
|
if (this.clients.has(userID)) {
|
|
71
78
|
this.stopMessager(userID);
|
|
72
79
|
}
|
|
73
80
|
const newClient = new FileMessagerClient(clientOptions);
|
|
74
81
|
newClient.joinRoom(userID, roomName, connectionOptions);
|
|
82
|
+
this.transferBarrier = enableTransferBarrier;
|
|
75
83
|
this.clientShorePolicies.set(userID, [FileUtilityManager.getTidePath(">CurrentUser"), FileUtilityManager.getTidePath(">Downloads"), FileUtilityManager.getTidePath(">Documents")]);
|
|
76
84
|
this.clients.set(userID, newClient);
|
|
77
85
|
FileTide.outputGradient(`[FileTide] ~ Messager launched successfully for client ~ ${userID}!`, significantMessageColors);
|
|
78
|
-
_assertClassBrand(_FileTide_brand, this, _setupFileEventListeners).call(this, userID, newClient, onIncomingFile);
|
|
86
|
+
_assertClassBrand(_FileTide_brand, this, _setupFileEventListeners).call(this, userID, newClient, onTransferProgress, onIncomingTransfer, onIncomingFile, onTransferBarrier, options);
|
|
79
87
|
if (onLaunch) {
|
|
80
88
|
onLaunch(newClient);
|
|
81
89
|
}
|
|
82
90
|
return;
|
|
83
91
|
}
|
|
84
|
-
getDirectoryData(directoryPath) {
|
|
85
|
-
return FileUtilityManager.getDirectoryData(directoryPath);
|
|
92
|
+
async getDirectoryData(directoryPath) {
|
|
93
|
+
return await FileUtilityManager.getDirectoryData(directoryPath);
|
|
86
94
|
}
|
|
87
95
|
getFileData(filePath) {
|
|
88
96
|
return FileUtilityManager.getFileData(filePath);
|
|
89
97
|
}
|
|
90
|
-
getPathData(inputPath) {
|
|
91
|
-
return FileUtilityManager.getPathData(inputPath);
|
|
98
|
+
async getPathData(inputPath, filterContent = []) {
|
|
99
|
+
return await FileUtilityManager.getPathData(inputPath, filterContent);
|
|
92
100
|
}
|
|
93
101
|
getTidePath(inputPath) {
|
|
94
102
|
return FileUtilityManager.getTidePath(inputPath);
|
|
95
103
|
}
|
|
104
|
+
filterFilesAndDirectories(dirPath, filterContent = [], callback) {
|
|
105
|
+
FileUtilityManager.filterFilesAndDirectories(dirPath, filterContent, (files, error) => {
|
|
106
|
+
if (callback) {
|
|
107
|
+
callback(files, error);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async listFiles(directories, depth = 1) {
|
|
112
|
+
return await FileUtilityManager.listFiles(directories, depth);
|
|
113
|
+
}
|
|
96
114
|
clearShorePolicies(clientID, shores = []) {
|
|
97
115
|
let clientShorePolicies = this.clientShorePolicies.get(clientID);
|
|
98
116
|
if (!shores || shores.length <= 0 || !clientShorePolicies || !clientID) {
|
|
@@ -122,15 +140,16 @@ class FileTide {
|
|
|
122
140
|
* @param {String} recipientID - The ID of the client device to send the data to
|
|
123
141
|
* @param {Buffer} fileData - The path of the data to send
|
|
124
142
|
* @param {String} destinationPath - The destination path on the device
|
|
143
|
+
* @param {Array} filterDirectoryContent - List of paths to filter
|
|
125
144
|
*/
|
|
126
|
-
sendToDevice(recipientID, filePath, destinationPath) {
|
|
127
|
-
const fileDetails = this.getPathData(filePath);
|
|
145
|
+
async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = []) {
|
|
128
146
|
const clientID = this.currentUserID;
|
|
129
147
|
const clientMessager = this.clients.get(clientID);
|
|
130
148
|
if (!clientMessager) {
|
|
131
149
|
FileTide.outputGradient(`[FileTide] Client ~ ${clientID} not found.`, ["#F94144", "#F3722C"]);
|
|
132
150
|
return;
|
|
133
151
|
}
|
|
152
|
+
const fileDetails = await this.getPathData(filePath, filterDirectoryContent);
|
|
134
153
|
if (!fileDetails) {
|
|
135
154
|
FileTide.outputGradient(`[${clientID}] File at path ${filePath} not found.`, ["#F94144", "#F3722C"]);
|
|
136
155
|
return;
|
|
@@ -203,7 +222,14 @@ class FileTide {
|
|
|
203
222
|
}
|
|
204
223
|
}
|
|
205
224
|
_FileTide = FileTide;
|
|
206
|
-
function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
225
|
+
function _setupFileEventListeners(userID, client, onTransferProgress, onIncomingTransfer, onIncomingFile, onTransferBarrier, options = {}) {
|
|
226
|
+
const {
|
|
227
|
+
onTransferStart,
|
|
228
|
+
onTransferComplete,
|
|
229
|
+
onTransferRequested,
|
|
230
|
+
onListFiles,
|
|
231
|
+
onCurrentOnlineClients
|
|
232
|
+
} = options;
|
|
207
233
|
const cliTable = new ConsoleTable({
|
|
208
234
|
padding: 2,
|
|
209
235
|
headerAlign: "center",
|
|
@@ -244,10 +270,33 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
244
270
|
cliTable.setTitle(`✨ Online >${Object.keys(currentClients).length}< ✨`, 1);
|
|
245
271
|
cliTable.setData(currentClients);
|
|
246
272
|
console.log(`\n[FileTide ~ FileNet]\n\n`, cliTable.render(), "\n");
|
|
273
|
+
if (onCurrentOnlineClients) {
|
|
274
|
+
onCurrentOnlineClients(currentClients);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
client.clientTide.onEvent(userID, "queue-incoming-transfer", async data => {
|
|
278
|
+
if (this.transferBarrier && onIncomingTransfer) {
|
|
279
|
+
onIncomingTransfer(data, status => {
|
|
280
|
+
client.clientTide.emitEvent(userID, "notify-transfer-barrier", {
|
|
281
|
+
accepted: status,
|
|
282
|
+
data
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
return;
|
|
286
|
+
} else if (!this.transferBarrier) {
|
|
287
|
+
client.clientTide.emitEvent(userID, "notify-transfer-barrier", {
|
|
288
|
+
accepted: true,
|
|
289
|
+
data
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
247
293
|
});
|
|
248
|
-
client.clientTide.onEvent(userID, "incoming-transfer", data => {
|
|
294
|
+
client.clientTide.onEvent(userID, "incoming-transfer", async data => {
|
|
249
295
|
_FileTide.outputGradient(`[${userID}] Preparing to receive transfer : ${FileUtilityManager.getTidePath(data.path)}`, significantMessageColors);
|
|
250
|
-
|
|
296
|
+
if (onTransferStart) {
|
|
297
|
+
onTransferStart(data);
|
|
298
|
+
}
|
|
299
|
+
this.progressBarManager.completionMessage = `Successfully received transfer from ${data.senderID}!`;
|
|
251
300
|
this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
|
|
252
301
|
this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
|
|
253
302
|
});
|
|
@@ -260,11 +309,17 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
260
309
|
fileInfo: data
|
|
261
310
|
});
|
|
262
311
|
const saveDir = FileUtilityManager.getTidePath(data.path);
|
|
263
|
-
|
|
312
|
+
const verifiedSender = data.senderID && data.senderID !== userID;
|
|
313
|
+
if (verifiedSender) {
|
|
264
314
|
this.progressBarManager.addTask({
|
|
265
315
|
name: data.fileName,
|
|
266
316
|
size: data.fileSize
|
|
267
317
|
});
|
|
318
|
+
}
|
|
319
|
+
if (onIncomingFile) {
|
|
320
|
+
onIncomingFile(verifiedSender, data, activeTransfers);
|
|
321
|
+
}
|
|
322
|
+
if (verifiedSender && !fs.existsSync(saveDir)) {
|
|
268
323
|
fs.mkdirSync(saveDir, {
|
|
269
324
|
recursive: true
|
|
270
325
|
});
|
|
@@ -274,6 +329,18 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
274
329
|
_FileTide.outputGradient(`[${userID}] Halted file recieval preparation : ${data.fileName}\n${error}`, errorMessageColors);
|
|
275
330
|
}
|
|
276
331
|
});
|
|
332
|
+
client.clientTide.onEvent(userID, "accepted-transfer", async data => {
|
|
333
|
+
_FileTide.outputGradient(`[${userID}] Accepted ${data.clientID}'s transfer!`, significantMessageColors);
|
|
334
|
+
if (onTransferBarrier) {
|
|
335
|
+
onTransferBarrier(data);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
client.clientTide.onEvent(userID, "denied-transfer", async data => {
|
|
339
|
+
_FileTide.outputGradient(`[${userID}] Denied ${data.clientID}'s transfer.`, errorMessageColors);
|
|
340
|
+
if (onTransferBarrier) {
|
|
341
|
+
onTransferBarrier(data);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
277
344
|
client.clientTide.onEvent(userID, "transfer-progress", data => {
|
|
278
345
|
if (!data || data.fileChunk === undefined || data.fileChunk === null) {
|
|
279
346
|
return;
|
|
@@ -285,8 +352,8 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
285
352
|
transferState.receivedChunks[data.chunkIndex] = Buffer.from(data.fileChunk);
|
|
286
353
|
const progress = (data.chunkIndex + 1) / transferState.totalChunks * 100;
|
|
287
354
|
this.progressBarManager.updateTaskProgress(data.fileName, 512 * 1024);
|
|
288
|
-
if (
|
|
289
|
-
|
|
355
|
+
if (onTransferProgress) {
|
|
356
|
+
onTransferProgress(data);
|
|
290
357
|
}
|
|
291
358
|
});
|
|
292
359
|
client.clientTide.onEvent(userID, "transfer-complete", data => {
|
|
@@ -301,14 +368,41 @@ function _setupFileEventListeners(userID, client, onIncomingFile) {
|
|
|
301
368
|
fs.writeFileSync(savePath, fullFile);
|
|
302
369
|
_FileTide.outputGradient(`[${userID}] File saved at : ${savePath}`, significantMessageColors);
|
|
303
370
|
activeTransfers.delete(data.fileName);
|
|
371
|
+
if (onTransferComplete) {
|
|
372
|
+
onTransferComplete(data, activeTransfers, true);
|
|
373
|
+
}
|
|
304
374
|
} catch (error) {
|
|
305
375
|
_FileTide.outputGradient(`[${userID}] Did not save file at : ${savePath}\n${error}`, errorMessageColors);
|
|
306
376
|
activeTransfers.delete(data.fileName);
|
|
377
|
+
if (onTransferComplete) {
|
|
378
|
+
onTransferComplete(data, activeTransfers, false);
|
|
379
|
+
}
|
|
307
380
|
}
|
|
308
381
|
});
|
|
309
|
-
client.clientTide.onEvent(userID, "transfer-requested", data => {
|
|
382
|
+
client.clientTide.onEvent(userID, "transfer-requested", async data => {
|
|
310
383
|
_FileTide.outputGradient(`[${userID}] Transfer requested from: ${data.requesterID}`, significantMessageColors);
|
|
311
|
-
this.
|
|
384
|
+
this.clients.get(userID).activeTransferTypes.set(data.destinationPath, data.type);
|
|
385
|
+
if (onTransferRequested) {
|
|
386
|
+
onTransferRequested(data);
|
|
387
|
+
}
|
|
388
|
+
await this.sendToDevice(data.requesterID, data.path, data.destinationPath, data.filterContent || []);
|
|
389
|
+
return;
|
|
390
|
+
});
|
|
391
|
+
client.clientTide.onEvent(userID, "list-files", async data => {
|
|
392
|
+
_FileTide.outputGradient(`[${userID}] Sending file list!`, significantMessageColors);
|
|
393
|
+
const currentClientShorePolicies = this.getShorePolicies(userID);
|
|
394
|
+
const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1);
|
|
395
|
+
client.clientTide.emitEvent(userID, "send-file-list-to-client", {
|
|
396
|
+
senderID: data.recipientID,
|
|
397
|
+
recipientID: data.senderID,
|
|
398
|
+
listedFiles
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
client.clientTide.onEvent(userID, "client-file-list", async data => {
|
|
402
|
+
_FileTide.outputGradient(`[${userID}] Listed ${data.senderID}'s files!`, significantMessageColors);
|
|
403
|
+
if (onListFiles) {
|
|
404
|
+
onListFiles(data);
|
|
405
|
+
}
|
|
312
406
|
});
|
|
313
407
|
this.currentUserID = userID;
|
|
314
408
|
return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const path = require("path");
|
|
4
|
+
const fsAsync = require("fs").promises;
|
|
4
5
|
const fs = require("fs");
|
|
5
6
|
const os = require("os");
|
|
6
7
|
const filesPath = path.join(__dirname, "../files");
|
|
@@ -27,46 +28,169 @@ class FileUtilityManager {
|
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Function to filter files and directories.
|
|
30
|
-
* @param {string} dirPath - The directory path to scan
|
|
31
|
-
* @param {Array} filterContent - Array of paths (files, directories, extensions) to avoid (e.g., ['docs', 'example.txt', '.js'])
|
|
32
|
-
* @
|
|
31
|
+
* @param {string} dirPath - The directory path to scan.
|
|
32
|
+
* @param {Array} filterContent - Array of paths (files, directories, extensions) to avoid (e.g., ['docs', 'example.txt', '.js']).
|
|
33
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of objects containing details about filtered files and directories.
|
|
33
34
|
*/
|
|
34
|
-
static filterFilesAndDirectories(dirPath, filterContent = []
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
35
|
+
static async filterFilesAndDirectories(dirPath, filterContent = []) {
|
|
36
|
+
try {
|
|
37
|
+
const currentDirPath = this.getTidePath(dirPath);
|
|
38
|
+
const items = await fs.promises.readdir(currentDirPath, {
|
|
39
|
+
withFileTypes: true
|
|
40
|
+
});
|
|
41
41
|
const results = [];
|
|
42
|
-
|
|
43
|
-
const itemPath = path.join(
|
|
42
|
+
for (const item of items) {
|
|
43
|
+
const itemPath = path.join(currentDirPath, item.name);
|
|
44
44
|
const filtered = filterContent.some(filterEntry => {
|
|
45
|
-
const resolvedFilterEntry = path.resolve(filterEntry);
|
|
45
|
+
const resolvedFilterEntry = path.resolve(currentDirPath, filterEntry);
|
|
46
46
|
const fileExtension = filterEntry.startsWith(".");
|
|
47
47
|
return itemPath === resolvedFilterEntry || item.name === filterEntry || fileExtension && path.extname(item.name) === filterEntry;
|
|
48
48
|
});
|
|
49
49
|
if (!filtered) {
|
|
50
|
-
fs.stat(itemPath
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
size: stats.size,
|
|
58
|
-
name: item.name,
|
|
59
|
-
path: itemPath
|
|
60
|
-
});
|
|
61
|
-
if (results.length === items.length) {
|
|
62
|
-
callback(null, results);
|
|
63
|
-
}
|
|
50
|
+
const stats = await fs.promises.stat(itemPath);
|
|
51
|
+
results.push({
|
|
52
|
+
type: item.isDirectory() ? "directory" : "file",
|
|
53
|
+
dateCreated: stats.birthtime,
|
|
54
|
+
size: stats.size,
|
|
55
|
+
name: item.name,
|
|
56
|
+
path: itemPath
|
|
64
57
|
});
|
|
65
|
-
} else if (results.length === items.length) {
|
|
66
|
-
callback(null, results);
|
|
67
58
|
}
|
|
68
|
-
}
|
|
59
|
+
}
|
|
60
|
+
return results;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error("Did not read directory: ", error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Function to list all paths across the system using wildcard '*'.
|
|
69
|
+
* @param {number} depth - The maximum depth limit (from the root). Defaults to 1.
|
|
70
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of file and directory metadata across the system.
|
|
71
|
+
*/
|
|
72
|
+
static async listAllPathsInOS(depth = 1) {
|
|
73
|
+
const rootPaths = ["/", "C:/"];
|
|
74
|
+
let allResults = [];
|
|
75
|
+
const rootPromises = rootPaths.map(async rootPath => {
|
|
76
|
+
if (await this.exists(rootPath)) {
|
|
77
|
+
const subResult = await this.listPaths(rootPath, depth);
|
|
78
|
+
allResults.push(...subResult.results);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
await Promise.all(rootPromises);
|
|
82
|
+
return allResults;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Function to list all paths and their metadata recursively.
|
|
87
|
+
* @param {string} dir - The directory path to scan.
|
|
88
|
+
* @param {number} maxDepth - The maximum depth limit (from the root). Defaults to 1.
|
|
89
|
+
* @param {number} currentDepth - The current depth level. Starts at 0 (for the root).
|
|
90
|
+
* @returns {Promise<object>} - A promise that resolves to an object containing a list of files/directories with metadata.
|
|
91
|
+
*/
|
|
92
|
+
static async listPaths(dir, maxDepth = 1, currentDepth = 0) {
|
|
93
|
+
let results = [];
|
|
94
|
+
if (!maxDepth || currentDepth === undefined || currentDepth === null || currentDepth > maxDepth) {
|
|
95
|
+
return {
|
|
96
|
+
totalItems: 0,
|
|
97
|
+
totalSize: 0,
|
|
98
|
+
results
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const list = await fsAsync.readdir(dir, {
|
|
102
|
+
withFileTypes: true
|
|
103
|
+
});
|
|
104
|
+
const stats = await fsAsync.stat(dir);
|
|
105
|
+
const dirMetadata = {
|
|
106
|
+
totalItems: 0,
|
|
107
|
+
totalSize: 0,
|
|
108
|
+
createdTime: stats.birthtime,
|
|
109
|
+
lastModified: stats.mtime,
|
|
110
|
+
type: "directory",
|
|
111
|
+
path: dir
|
|
112
|
+
};
|
|
113
|
+
results.push(dirMetadata);
|
|
114
|
+
const promises = list.map(async file => {
|
|
115
|
+
const filePath = this.getTidePath(path.join(dir, file.name));
|
|
116
|
+
try {
|
|
117
|
+
if (file.isDirectory()) {
|
|
118
|
+
const subDirResult = await this.listPaths(filePath, maxDepth, currentDepth + 1);
|
|
119
|
+
dirMetadata.totalItems += subDirResult.totalItems;
|
|
120
|
+
dirMetadata.totalSize += subDirResult.totalSize;
|
|
121
|
+
results.push(...subDirResult.results);
|
|
122
|
+
} else {
|
|
123
|
+
const fileStats = await fsAsync.stat(filePath);
|
|
124
|
+
const fileMetadata = {
|
|
125
|
+
createdTime: fileStats.birthtime,
|
|
126
|
+
lastModified: fileStats.mtime,
|
|
127
|
+
size: fileStats.size,
|
|
128
|
+
path: filePath,
|
|
129
|
+
type: "file"
|
|
130
|
+
};
|
|
131
|
+
dirMetadata.totalSize += fileStats.size;
|
|
132
|
+
dirMetadata.totalItems += 1;
|
|
133
|
+
results.push(fileMetadata);
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.log(`[FileTide] Path not found: ${filePath}`);
|
|
137
|
+
}
|
|
69
138
|
});
|
|
139
|
+
await Promise.all(promises);
|
|
140
|
+
return {
|
|
141
|
+
totalItems: dirMetadata.totalItems,
|
|
142
|
+
totalSize: dirMetadata.totalSize,
|
|
143
|
+
results
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Allows for listing files in a specific directory or all files in the system '*'.
|
|
149
|
+
* @param {Array} directories - Array of directories to scan or '*' for entire system.
|
|
150
|
+
* @param {number} depth - The maximum depth limit (from the root). Defaults to 1.
|
|
151
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of file and directory metadata from specified directories or the entire system.
|
|
152
|
+
*/
|
|
153
|
+
static async listFiles(directories, depth = 1) {
|
|
154
|
+
let fileList = [];
|
|
155
|
+
if (directories.includes("*")) {
|
|
156
|
+
fileList = await this.listAllPathsInOS(depth);
|
|
157
|
+
} else {
|
|
158
|
+
const dirPromises = directories.map(async dir => {
|
|
159
|
+
const currentDir = this.getTidePath(dir);
|
|
160
|
+
if (await this.exists(currentDir)) {
|
|
161
|
+
const subResult = await this.listPaths(currentDir, depth);
|
|
162
|
+
fileList.push(...subResult.results);
|
|
163
|
+
} else {
|
|
164
|
+
console.warn(`Directory does not exist: ${currentDir}`);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
await Promise.all(dirPromises);
|
|
168
|
+
}
|
|
169
|
+
return fileList;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Checks the path's type.
|
|
174
|
+
* @param {string} inputPath - The path to check.
|
|
175
|
+
* @returns {Promise<boolean>} - Returns a promise that resolves to true if a directory, false otherwise.
|
|
176
|
+
*/
|
|
177
|
+
static async directoryPath(inputPath) {
|
|
178
|
+
const stats = await fsAsync.stat(inputPath);
|
|
179
|
+
return stats.isDirectory();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Helper function to check if a path exists.
|
|
184
|
+
* @param {string} inputPath - The path to check.
|
|
185
|
+
* @returns {Promise<boolean>} - Returns a promise that resolves to true if the path exists, false otherwise.
|
|
186
|
+
*/
|
|
187
|
+
static async exists(inputPath) {
|
|
188
|
+
try {
|
|
189
|
+
await fsAsync.access(inputPath);
|
|
190
|
+
return true;
|
|
191
|
+
} catch (e) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
70
194
|
}
|
|
71
195
|
|
|
72
196
|
/**
|
|
@@ -153,31 +277,31 @@ class FileUtilityManager {
|
|
|
153
277
|
/**
|
|
154
278
|
* Gets all files' data from a given directory.
|
|
155
279
|
* @param {string} dirPath - The path to the directory.
|
|
280
|
+
* @param {Array} filterContent - The paths to filter from the directory.
|
|
156
281
|
* @returns {Array} - An array of objects containing file data, name, and path.
|
|
157
282
|
*/
|
|
158
|
-
static getDirectoryData(inputDirPath) {
|
|
283
|
+
static async getDirectoryData(inputDirPath, filterContent = []) {
|
|
159
284
|
const dirPath = this.getTidePath(inputDirPath);
|
|
160
285
|
if (!fs.existsSync(dirPath)) {
|
|
161
286
|
console.log(`Directory at path ${dirPath} not found.`);
|
|
162
287
|
return [];
|
|
163
288
|
}
|
|
164
289
|
const filesData = [];
|
|
165
|
-
function readDirectory(currentPath) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const stats = fs.statSync(filePath);
|
|
290
|
+
async function readDirectory(currentPath) {
|
|
291
|
+
const files = await FileUtilityManager.filterFilesAndDirectories(currentPath, filterContent);
|
|
292
|
+
for (const file of files) {
|
|
293
|
+
const stats = await fs.promises.stat(file.path);
|
|
170
294
|
if (stats.isFile()) {
|
|
171
|
-
const fileData = FileUtilityManager.getFileData(
|
|
295
|
+
const fileData = FileUtilityManager.getFileData(file.path);
|
|
172
296
|
if (fileData) {
|
|
173
297
|
filesData.push(fileData);
|
|
174
298
|
}
|
|
175
299
|
} else if (stats.isDirectory()) {
|
|
176
|
-
readDirectory(
|
|
300
|
+
await readDirectory(file.path);
|
|
177
301
|
}
|
|
178
|
-
}
|
|
302
|
+
}
|
|
179
303
|
}
|
|
180
|
-
readDirectory(dirPath);
|
|
304
|
+
await readDirectory(dirPath);
|
|
181
305
|
return filesData;
|
|
182
306
|
}
|
|
183
307
|
|
|
@@ -207,9 +331,10 @@ class FileUtilityManager {
|
|
|
207
331
|
/**
|
|
208
332
|
* Gets data from a file or directory based on the provided path.
|
|
209
333
|
* @param {string} inputPath - The path to a file or directory.
|
|
334
|
+
* @param {Array} filterContent - The paths to filter from the directory if getting a directory.
|
|
210
335
|
* @returns {Object|Array|null} - File data if it's a file, an array of file data if it's a directory, or null if the path doesn't exist.
|
|
211
336
|
*/
|
|
212
|
-
static getPathData(inputPath) {
|
|
337
|
+
static async getPathData(inputPath, filterContent = []) {
|
|
213
338
|
const currentInputPath = this.getTidePath(inputPath);
|
|
214
339
|
if (!fs.existsSync(currentInputPath)) {
|
|
215
340
|
console.log(`Path at ${currentInputPath} not found.`);
|
|
@@ -223,7 +348,7 @@ class FileUtilityManager {
|
|
|
223
348
|
};
|
|
224
349
|
} else if (stats.isDirectory()) {
|
|
225
350
|
return {
|
|
226
|
-
content: this.getDirectoryData(currentInputPath),
|
|
351
|
+
content: await this.getDirectoryData(currentInputPath, filterContent),
|
|
227
352
|
type: "directory"
|
|
228
353
|
};
|
|
229
354
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class HUDTargetQueue {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.queues = new Map();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adds a function to the queue for a specific ID and target value.
|
|
10
|
+
* @param {string} id - A unique identifier for the value being watched.
|
|
11
|
+
* @param {function} fn - The function to execute when the target value is reached.
|
|
12
|
+
*/
|
|
13
|
+
enqueue(id, fn) {
|
|
14
|
+
if (!this.queues.has(id)) {
|
|
15
|
+
this.queues.set(id, []);
|
|
16
|
+
}
|
|
17
|
+
this.queues.get(id).push(fn);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Waits for a value to change to the target value and executes only the functions
|
|
22
|
+
* in the queue associated with the specific ID.
|
|
23
|
+
* @param {string} id - A unique identifier for the value being watched.
|
|
24
|
+
* @param {function} getValue - A function that returns the current value.
|
|
25
|
+
* @param {any|array} targetValues - The value(s) to wait for.
|
|
26
|
+
* @param {number} interval - The interval (in milliseconds) to check the value.
|
|
27
|
+
* @param {number} [timeout] - Optional. The maximum time (in milliseconds) to wait before rejecting.
|
|
28
|
+
* @returns {Promise} - Resolves when the value matches the target value, executes queued functions.
|
|
29
|
+
*/
|
|
30
|
+
waitForData(id, getValue, targetValues, interval = 100, timeout) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let elapsedTime = 0;
|
|
33
|
+
const targets = Array.isArray(targetValues) ? targetValues : [targetValues];
|
|
34
|
+
const checkData = async () => {
|
|
35
|
+
const currentValue = await getValue();
|
|
36
|
+
if (typeof currentValue === "object" && targets.some(target => typeof target === "object")) {
|
|
37
|
+
if (targets.some(target => JSON.stringify(currentValue) === JSON.stringify(target))) {
|
|
38
|
+
this.executeQueue(id);
|
|
39
|
+
resolve(currentValue);
|
|
40
|
+
}
|
|
41
|
+
} else if (targets.includes(currentValue)) {
|
|
42
|
+
this.executeQueue(id);
|
|
43
|
+
resolve(currentValue);
|
|
44
|
+
} else if (timeout && elapsedTime >= timeout) {
|
|
45
|
+
reject(new Error(`Timeout: Value did not reach target within ${timeout}ms`));
|
|
46
|
+
} else {
|
|
47
|
+
elapsedTime += interval;
|
|
48
|
+
setTimeout(checkData, interval);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
checkData();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Executes all functions in the queue associated with the specific ID.
|
|
57
|
+
* @param {string} id - A unique identifier for the value being watched.
|
|
58
|
+
*/
|
|
59
|
+
executeQueue(id) {
|
|
60
|
+
const queue = this.queues.get(id);
|
|
61
|
+
if (queue) {
|
|
62
|
+
while (queue.length > 0) {
|
|
63
|
+
const fn = queue.shift();
|
|
64
|
+
if (typeof fn === "function") {
|
|
65
|
+
fn();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.queues.delete(id);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
module.exports = HUDTargetQueue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trap_stevo/filetide",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
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": {
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"seamless connectivity"
|
|
28
28
|
],
|
|
29
29
|
"author": "Steven Compton",
|
|
30
|
-
"license": "
|
|
30
|
+
"license": "See License in LICENSE.md",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@trap_stevo/iotide": "^0.0.37",
|
|
33
|
-
"@trap_stevo/iotide-client": "^0.0.
|
|
33
|
+
"@trap_stevo/iotide-client": "^0.0.20",
|
|
34
34
|
"@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.40",
|
|
35
35
|
"chalk": "^4.1.2",
|
|
36
36
|
"readline": "^1.3.0"
|