fwdcast 1.0.0
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/README.md +67 -0
- package/dist/html-generator.d.ts +22 -0
- package/dist/html-generator.d.ts.map +1 -0
- package/dist/html-generator.js +623 -0
- package/dist/html-generator.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +229 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +109 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +138 -0
- package/dist/protocol.js.map +1 -0
- package/dist/scanner.d.ts +46 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +138 -0
- package/dist/scanner.js.map +1 -0
- package/dist/tunnel-client.d.ts +143 -0
- package/dist/tunnel-client.d.ts.map +1 -0
- package/dist/tunnel-client.js +428 -0
- package/dist/tunnel-client.js.map +1 -0
- package/dist/validator.d.ts +53 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +65 -0
- package/dist/validator.js.map +1 -0
- package/package.json +64 -0
- package/scripts/postinstall.js +23 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tunnel Client Module
|
|
4
|
+
*
|
|
5
|
+
* Handles WebSocket connection to the relay server, message handling,
|
|
6
|
+
* and file streaming for the fwdcast CLI.
|
|
7
|
+
*
|
|
8
|
+
* Requirements: 1.5, 1.6, 3.1, 3.2, 3.6, 5.1, 5.3, 5.4, 5.5
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
44
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
45
|
+
};
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.TunnelClient = void 0;
|
|
48
|
+
exports.createTunnelClient = createTunnelClient;
|
|
49
|
+
const ws_1 = __importDefault(require("ws"));
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const mime_types_1 = require("mime-types");
|
|
53
|
+
const archiver_1 = __importDefault(require("archiver"));
|
|
54
|
+
const protocol_1 = require("./protocol");
|
|
55
|
+
const scanner_1 = require("./scanner");
|
|
56
|
+
const html_generator_1 = require("./html-generator");
|
|
57
|
+
/**
|
|
58
|
+
* Chunk size for file streaming (64KB)
|
|
59
|
+
*/
|
|
60
|
+
const CHUNK_SIZE = 64 * 1024;
|
|
61
|
+
/**
|
|
62
|
+
* TunnelClient manages the WebSocket connection to the relay server
|
|
63
|
+
* and handles incoming requests by serving files or directory listings.
|
|
64
|
+
*/
|
|
65
|
+
class TunnelClient {
|
|
66
|
+
ws = null;
|
|
67
|
+
config;
|
|
68
|
+
connected = false;
|
|
69
|
+
sessionId = null;
|
|
70
|
+
registrationPromise = null;
|
|
71
|
+
constructor(config) {
|
|
72
|
+
this.config = config;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Connect to the relay server and register the session.
|
|
76
|
+
* Returns the public URL for the shared directory.
|
|
77
|
+
*
|
|
78
|
+
* Requirements: 1.5, 1.6, 5.1
|
|
79
|
+
*/
|
|
80
|
+
async connect() {
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
this.registrationPromise = { resolve, reject };
|
|
83
|
+
try {
|
|
84
|
+
this.ws = new ws_1.default(this.config.relayUrl);
|
|
85
|
+
this.ws.on('open', () => {
|
|
86
|
+
this.connected = true;
|
|
87
|
+
this.sendRegisterMessage();
|
|
88
|
+
});
|
|
89
|
+
this.ws.on('message', (data) => {
|
|
90
|
+
this.handleMessage(data);
|
|
91
|
+
});
|
|
92
|
+
this.ws.on('close', () => {
|
|
93
|
+
this.connected = false;
|
|
94
|
+
// Only call onDisconnect if we were successfully registered
|
|
95
|
+
// (not during initial connection attempts)
|
|
96
|
+
if (!this.registrationPromise && this.config.onDisconnect) {
|
|
97
|
+
this.config.onDisconnect();
|
|
98
|
+
}
|
|
99
|
+
// If still trying to register, reject the promise
|
|
100
|
+
if (this.registrationPromise) {
|
|
101
|
+
this.registrationPromise.reject(new Error('Connection closed during registration'));
|
|
102
|
+
this.registrationPromise = null;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
this.ws.on('error', (error) => {
|
|
106
|
+
if (this.registrationPromise) {
|
|
107
|
+
this.registrationPromise.reject(error);
|
|
108
|
+
this.registrationPromise = null;
|
|
109
|
+
}
|
|
110
|
+
if (this.config.onError) {
|
|
111
|
+
this.config.onError(error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
reject(error);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Disconnect from the relay server
|
|
122
|
+
*/
|
|
123
|
+
disconnect() {
|
|
124
|
+
if (this.ws) {
|
|
125
|
+
this.ws.close();
|
|
126
|
+
this.ws = null;
|
|
127
|
+
this.connected = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if the client is connected
|
|
132
|
+
*/
|
|
133
|
+
isConnected() {
|
|
134
|
+
return this.connected && this.ws !== null && this.ws.readyState === ws_1.default.OPEN;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Send the register message to the relay server
|
|
138
|
+
* Requirements: 5.1
|
|
139
|
+
*/
|
|
140
|
+
sendRegisterMessage() {
|
|
141
|
+
const message = (0, protocol_1.createRegisterMessage)(this.config.basePath, this.config.expiresAt);
|
|
142
|
+
this.send(message);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Handle incoming WebSocket messages
|
|
146
|
+
*/
|
|
147
|
+
handleMessage(data) {
|
|
148
|
+
const messageStr = data.toString();
|
|
149
|
+
const message = (0, protocol_1.deserializeMessage)(messageStr);
|
|
150
|
+
if (!message) {
|
|
151
|
+
console.error('Received invalid message:', messageStr);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if ((0, protocol_1.isRegisteredMessage)(message)) {
|
|
155
|
+
this.handleRegistered(message);
|
|
156
|
+
}
|
|
157
|
+
else if ((0, protocol_1.isRequestMessage)(message)) {
|
|
158
|
+
this.handleRequest(message);
|
|
159
|
+
}
|
|
160
|
+
else if ((0, protocol_1.isExpiredMessage)(message)) {
|
|
161
|
+
this.handleExpired();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Handle registration response from relay
|
|
166
|
+
* Requirements: 1.6
|
|
167
|
+
*/
|
|
168
|
+
handleRegistered(message) {
|
|
169
|
+
// Store session ID for use in directory listings
|
|
170
|
+
this.sessionId = message.sessionId;
|
|
171
|
+
const result = {
|
|
172
|
+
sessionId: message.sessionId,
|
|
173
|
+
url: message.url,
|
|
174
|
+
};
|
|
175
|
+
if (this.registrationPromise) {
|
|
176
|
+
this.registrationPromise.resolve(result);
|
|
177
|
+
this.registrationPromise = null;
|
|
178
|
+
}
|
|
179
|
+
if (this.config.onUrl) {
|
|
180
|
+
this.config.onUrl(message.url);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Handle incoming request from relay
|
|
185
|
+
* Routes to file server or directory listing
|
|
186
|
+
*
|
|
187
|
+
* Requirements: 3.1, 3.2, 5.3, 5.4, 5.5
|
|
188
|
+
*/
|
|
189
|
+
async handleRequest(message) {
|
|
190
|
+
const { id, method, path: requestPath } = message;
|
|
191
|
+
// Normalize the request path
|
|
192
|
+
const normalizedPath = this.normalizePath(requestPath);
|
|
193
|
+
// Check for special ZIP download request
|
|
194
|
+
if (normalizedPath === '__download__.zip' || normalizedPath.endsWith('/__download__.zip')) {
|
|
195
|
+
const dirPath = normalizedPath.replace('/__download__.zip', '').replace('__download__.zip', '');
|
|
196
|
+
await this.serveZipDownload(id, dirPath);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const absolutePath = path.join(this.config.basePath, normalizedPath);
|
|
200
|
+
// Security check: ensure the path is within the base directory
|
|
201
|
+
const resolvedBase = path.resolve(this.config.basePath);
|
|
202
|
+
const resolvedPath = path.resolve(absolutePath);
|
|
203
|
+
if (!resolvedPath.startsWith(resolvedBase)) {
|
|
204
|
+
this.sendErrorResponse(id, 403, 'Forbidden');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
// Check if path exists
|
|
209
|
+
const stat = await fs.promises.stat(resolvedPath);
|
|
210
|
+
if (stat.isDirectory()) {
|
|
211
|
+
// Serve directory listing
|
|
212
|
+
await this.serveDirectoryListing(id, normalizedPath);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
// Serve file
|
|
216
|
+
await this.serveFile(id, resolvedPath, method);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
if (error.code === 'ENOENT') {
|
|
221
|
+
this.sendErrorResponse(id, 404, 'Not Found');
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.error('Error handling request:', error);
|
|
225
|
+
this.sendErrorResponse(id, 500, 'Internal Server Error');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Handle session expired message
|
|
231
|
+
*/
|
|
232
|
+
handleExpired() {
|
|
233
|
+
if (this.config.onExpired) {
|
|
234
|
+
this.config.onExpired();
|
|
235
|
+
}
|
|
236
|
+
this.disconnect();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Serve a ZIP download of a directory
|
|
240
|
+
*/
|
|
241
|
+
async serveZipDownload(requestId, dirPath) {
|
|
242
|
+
try {
|
|
243
|
+
const absoluteDirPath = dirPath
|
|
244
|
+
? path.join(this.config.basePath, dirPath)
|
|
245
|
+
: this.config.basePath;
|
|
246
|
+
// Get directory name for the ZIP filename
|
|
247
|
+
const dirName = dirPath ? path.basename(dirPath) : 'files';
|
|
248
|
+
// Send response headers (chunked transfer since we don't know size)
|
|
249
|
+
this.sendResponse(requestId, 200, {
|
|
250
|
+
'Content-Type': 'application/zip',
|
|
251
|
+
'Content-Disposition': `attachment; filename="${dirName}.zip"`,
|
|
252
|
+
'Transfer-Encoding': 'chunked',
|
|
253
|
+
});
|
|
254
|
+
// Create archive
|
|
255
|
+
const archive = (0, archiver_1.default)('zip', { zlib: { level: 5 } });
|
|
256
|
+
// Collect chunks and send them
|
|
257
|
+
archive.on('data', (chunk) => {
|
|
258
|
+
this.sendData(requestId, chunk);
|
|
259
|
+
});
|
|
260
|
+
archive.on('end', () => {
|
|
261
|
+
this.sendEnd(requestId);
|
|
262
|
+
});
|
|
263
|
+
archive.on('error', (err) => {
|
|
264
|
+
console.error('Archive error:', err);
|
|
265
|
+
this.sendEnd(requestId);
|
|
266
|
+
});
|
|
267
|
+
// Add directory contents to archive
|
|
268
|
+
archive.directory(absoluteDirPath, false);
|
|
269
|
+
// Finalize the archive
|
|
270
|
+
await archive.finalize();
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
console.error('Error creating ZIP:', error);
|
|
274
|
+
this.sendErrorResponse(requestId, 500, 'Failed to create ZIP');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Serve a directory listing as HTML
|
|
279
|
+
* Requirements: 3.5
|
|
280
|
+
*
|
|
281
|
+
* Dynamically scans the directory on each request to reflect
|
|
282
|
+
* any files added/removed since the session started.
|
|
283
|
+
*/
|
|
284
|
+
async serveDirectoryListing(requestId, dirPath) {
|
|
285
|
+
try {
|
|
286
|
+
// Dynamically scan the directory for current contents
|
|
287
|
+
const absoluteDirPath = path.join(this.config.basePath, dirPath);
|
|
288
|
+
const children = await (0, scanner_1.scanDirectoryShallow)(absoluteDirPath, this.config.basePath);
|
|
289
|
+
// Generate HTML with session ID for correct links
|
|
290
|
+
const html = (0, html_generator_1.generateDirectoryHtml)(children, dirPath, this.sessionId || undefined);
|
|
291
|
+
const htmlBuffer = Buffer.from(html, 'utf-8');
|
|
292
|
+
// Send response headers
|
|
293
|
+
this.sendResponse(requestId, 200, {
|
|
294
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
295
|
+
'Content-Length': htmlBuffer.length.toString(),
|
|
296
|
+
});
|
|
297
|
+
// Send data
|
|
298
|
+
this.sendData(requestId, htmlBuffer);
|
|
299
|
+
// Send end
|
|
300
|
+
this.sendEnd(requestId);
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
console.error('Error serving directory listing:', error);
|
|
304
|
+
this.sendErrorResponse(requestId, 500, 'Internal Server Error');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Stream a file to the relay
|
|
309
|
+
* Requirements: 3.6
|
|
310
|
+
*/
|
|
311
|
+
async serveFile(requestId, filePath, method) {
|
|
312
|
+
const stat = await fs.promises.stat(filePath);
|
|
313
|
+
const contentType = this.getContentType(filePath);
|
|
314
|
+
// Send response headers
|
|
315
|
+
this.sendResponse(requestId, 200, {
|
|
316
|
+
'Content-Type': contentType,
|
|
317
|
+
'Content-Length': stat.size.toString(),
|
|
318
|
+
});
|
|
319
|
+
// For HEAD requests, don't send body
|
|
320
|
+
if (method === 'HEAD') {
|
|
321
|
+
this.sendEnd(requestId);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
// Stream file contents
|
|
325
|
+
await this.streamFile(requestId, filePath);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Stream file contents in chunks
|
|
329
|
+
* Requirements: 3.6, 5.4, 5.5
|
|
330
|
+
*/
|
|
331
|
+
streamFile(requestId, filePath) {
|
|
332
|
+
return new Promise((resolve, reject) => {
|
|
333
|
+
const readStream = fs.createReadStream(filePath, {
|
|
334
|
+
highWaterMark: CHUNK_SIZE,
|
|
335
|
+
});
|
|
336
|
+
readStream.on('data', (chunk) => {
|
|
337
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
338
|
+
this.sendData(requestId, buffer);
|
|
339
|
+
});
|
|
340
|
+
readStream.on('end', () => {
|
|
341
|
+
this.sendEnd(requestId);
|
|
342
|
+
resolve();
|
|
343
|
+
});
|
|
344
|
+
readStream.on('error', (error) => {
|
|
345
|
+
console.error('Error streaming file:', error);
|
|
346
|
+
this.sendEnd(requestId);
|
|
347
|
+
reject(error);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Send an error response
|
|
353
|
+
*/
|
|
354
|
+
sendErrorResponse(requestId, status, message) {
|
|
355
|
+
const html = `<!DOCTYPE html>
|
|
356
|
+
<html>
|
|
357
|
+
<head><title>${status} ${message}</title></head>
|
|
358
|
+
<body><h1>${status} ${message}</h1></body>
|
|
359
|
+
</html>`;
|
|
360
|
+
const htmlBuffer = Buffer.from(html, 'utf-8');
|
|
361
|
+
this.sendResponse(requestId, status, {
|
|
362
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
363
|
+
'Content-Length': htmlBuffer.length.toString(),
|
|
364
|
+
});
|
|
365
|
+
this.sendData(requestId, htmlBuffer);
|
|
366
|
+
this.sendEnd(requestId);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Send a response message
|
|
370
|
+
* Requirements: 5.3
|
|
371
|
+
*/
|
|
372
|
+
sendResponse(id, status, headers) {
|
|
373
|
+
const message = (0, protocol_1.createResponseMessage)(id, status, headers);
|
|
374
|
+
this.send(message);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Send a data message with base64 encoded chunk
|
|
378
|
+
* Requirements: 5.4
|
|
379
|
+
*/
|
|
380
|
+
sendData(id, chunk) {
|
|
381
|
+
const message = (0, protocol_1.createDataMessage)(id, chunk.toString('base64'));
|
|
382
|
+
this.send(message);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Send an end message
|
|
386
|
+
* Requirements: 5.5
|
|
387
|
+
*/
|
|
388
|
+
sendEnd(id) {
|
|
389
|
+
const message = (0, protocol_1.createEndMessage)(id);
|
|
390
|
+
this.send(message);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Send a message through the WebSocket
|
|
394
|
+
*/
|
|
395
|
+
send(message) {
|
|
396
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
397
|
+
this.ws.send((0, protocol_1.serializeMessage)(message));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Normalize a request path
|
|
402
|
+
*/
|
|
403
|
+
normalizePath(requestPath) {
|
|
404
|
+
// Remove leading slash and decode URI components
|
|
405
|
+
let normalized = decodeURIComponent(requestPath).replace(/^\/+/, '');
|
|
406
|
+
// Remove trailing slash
|
|
407
|
+
normalized = normalized.replace(/\/+$/, '');
|
|
408
|
+
return normalized;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Get the content type for a file based on its extension
|
|
412
|
+
*/
|
|
413
|
+
getContentType(filePath) {
|
|
414
|
+
const mimeType = (0, mime_types_1.lookup)(filePath);
|
|
415
|
+
return mimeType || 'application/octet-stream';
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
exports.TunnelClient = TunnelClient;
|
|
419
|
+
/**
|
|
420
|
+
* Create and connect a tunnel client
|
|
421
|
+
* Convenience function for simple usage
|
|
422
|
+
*/
|
|
423
|
+
async function createTunnelClient(config) {
|
|
424
|
+
const client = new TunnelClient(config);
|
|
425
|
+
const result = await client.connect();
|
|
426
|
+
return { client, result };
|
|
427
|
+
}
|
|
428
|
+
//# sourceMappingURL=tunnel-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-client.js","sourceRoot":"","sources":["../src/tunnel-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DirectoryEntry, ScanResult } from './scanner';
|
|
2
|
+
/**
|
|
3
|
+
* Default size limits for validation
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_MAX_TOTAL_SIZE: number;
|
|
6
|
+
export declare const DEFAULT_MAX_FILE_SIZE: number;
|
|
7
|
+
/**
|
|
8
|
+
* Validation error types
|
|
9
|
+
*/
|
|
10
|
+
export type ValidationErrorType = 'total_size_exceeded' | 'file_size_exceeded';
|
|
11
|
+
/**
|
|
12
|
+
* Details about a validation error
|
|
13
|
+
*/
|
|
14
|
+
export interface ValidationError {
|
|
15
|
+
type: ValidationErrorType;
|
|
16
|
+
message: string;
|
|
17
|
+
/** The file that caused the error (for file_size_exceeded) */
|
|
18
|
+
file?: DirectoryEntry;
|
|
19
|
+
/** The actual size that exceeded the limit */
|
|
20
|
+
actualSize: number;
|
|
21
|
+
/** The limit that was exceeded */
|
|
22
|
+
limit: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Result of validating a scan result
|
|
26
|
+
*/
|
|
27
|
+
export interface ValidationResult {
|
|
28
|
+
valid: boolean;
|
|
29
|
+
errors: ValidationError[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Options for validation
|
|
33
|
+
*/
|
|
34
|
+
export interface ValidationOptions {
|
|
35
|
+
maxTotalSize?: number;
|
|
36
|
+
maxFileSize?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validates a scan result against size limits.
|
|
40
|
+
*
|
|
41
|
+
* @param scanResult - The scan result to validate
|
|
42
|
+
* @param options - Optional size limits (defaults to 100MB total, 50MB per file)
|
|
43
|
+
* @returns ValidationResult indicating if the scan is valid and any errors
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateScanResult(scanResult: ScanResult, options?: ValidationOptions): ValidationResult;
|
|
46
|
+
/**
|
|
47
|
+
* Formats a size in bytes to a human-readable string.
|
|
48
|
+
*
|
|
49
|
+
* @param bytes - Size in bytes
|
|
50
|
+
* @returns Human-readable size string (e.g., "1.5 MB")
|
|
51
|
+
*/
|
|
52
|
+
export declare function formatSize(bytes: number): string;
|
|
53
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvD;;GAEG;AACH,eAAO,MAAM,sBAAsB,QAAoB,CAAC;AACxD,eAAO,MAAM,qBAAqB,QAAmB,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,GAAG,oBAAoB,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAgClB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_MAX_FILE_SIZE = exports.DEFAULT_MAX_TOTAL_SIZE = void 0;
|
|
4
|
+
exports.validateScanResult = validateScanResult;
|
|
5
|
+
exports.formatSize = formatSize;
|
|
6
|
+
/**
|
|
7
|
+
* Default size limits for validation
|
|
8
|
+
*/
|
|
9
|
+
exports.DEFAULT_MAX_TOTAL_SIZE = 100 * 1024 * 1024; // 100 MB
|
|
10
|
+
exports.DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024; // 50 MB
|
|
11
|
+
/**
|
|
12
|
+
* Validates a scan result against size limits.
|
|
13
|
+
*
|
|
14
|
+
* @param scanResult - The scan result to validate
|
|
15
|
+
* @param options - Optional size limits (defaults to 100MB total, 50MB per file)
|
|
16
|
+
* @returns ValidationResult indicating if the scan is valid and any errors
|
|
17
|
+
*/
|
|
18
|
+
function validateScanResult(scanResult, options = {}) {
|
|
19
|
+
const maxTotalSize = options.maxTotalSize ?? exports.DEFAULT_MAX_TOTAL_SIZE;
|
|
20
|
+
const maxFileSize = options.maxFileSize ?? exports.DEFAULT_MAX_FILE_SIZE;
|
|
21
|
+
const errors = [];
|
|
22
|
+
// Check each file against the individual file size limit
|
|
23
|
+
for (const entry of scanResult.entries) {
|
|
24
|
+
if (!entry.isDirectory && entry.size > maxFileSize) {
|
|
25
|
+
errors.push({
|
|
26
|
+
type: 'file_size_exceeded',
|
|
27
|
+
message: `File "${entry.relativePath}" exceeds the ${formatSize(maxFileSize)} limit (${formatSize(entry.size)})`,
|
|
28
|
+
file: entry,
|
|
29
|
+
actualSize: entry.size,
|
|
30
|
+
limit: maxFileSize,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Check total size against the total size limit
|
|
35
|
+
if (scanResult.totalSize > maxTotalSize) {
|
|
36
|
+
errors.push({
|
|
37
|
+
type: 'total_size_exceeded',
|
|
38
|
+
message: `Total directory size ${formatSize(scanResult.totalSize)} exceeds the ${formatSize(maxTotalSize)} limit`,
|
|
39
|
+
actualSize: scanResult.totalSize,
|
|
40
|
+
limit: maxTotalSize,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
valid: errors.length === 0,
|
|
45
|
+
errors,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Formats a size in bytes to a human-readable string.
|
|
50
|
+
*
|
|
51
|
+
* @param bytes - Size in bytes
|
|
52
|
+
* @returns Human-readable size string (e.g., "1.5 MB")
|
|
53
|
+
*/
|
|
54
|
+
function formatSize(bytes) {
|
|
55
|
+
if (bytes < 1024) {
|
|
56
|
+
return `${bytes} B`;
|
|
57
|
+
}
|
|
58
|
+
else if (bytes < 1024 * 1024) {
|
|
59
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":";;;AAkDA,gDAmCC;AAQD,gCAQC;AAnGD;;GAEG;AACU,QAAA,sBAAsB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,SAAS;AACrD,QAAA,qBAAqB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAG,QAAQ;AAqCjE;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAChC,UAAsB,EACtB,UAA6B,EAAE;IAE/B,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,8BAAsB,CAAC;IACpE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,6BAAqB,CAAC;IACjE,MAAM,MAAM,GAAsB,EAAE,CAAC;IAErC,yDAAyD;IACzD,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvC,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,GAAG,WAAW,EAAE,CAAC;YACnD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,SAAS,KAAK,CAAC,YAAY,iBAAiB,UAAU,CAAC,WAAW,CAAC,WAAW,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;gBAChH,IAAI,EAAE,KAAK;gBACX,UAAU,EAAE,KAAK,CAAC,IAAI;gBACtB,KAAK,EAAE,WAAW;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,IAAI,UAAU,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE,wBAAwB,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,gBAAgB,UAAU,CAAC,YAAY,CAAC,QAAQ;YACjH,UAAU,EAAE,UAAU,CAAC,SAAS;YAChC,KAAK,EAAE,YAAY;SACpB,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,GAAG,IAAI,EAAE,CAAC;QACjB,OAAO,GAAG,KAAK,IAAI,CAAC;IACtB,CAAC;SAAM,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QAC/B,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IACpD,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fwdcast",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Temporary file sharing - stream local files as a public website without uploading",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"fwdcast": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**/*",
|
|
12
|
+
"scripts/**/*"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"prebuild": "rm -rf dist",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"postinstall": "node scripts/postinstall.js || true",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"dev": "ts-node src/index.ts",
|
|
21
|
+
"test": "vitest --run",
|
|
22
|
+
"test:watch": "vitest"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"file-sharing",
|
|
26
|
+
"tunnel",
|
|
27
|
+
"temporary",
|
|
28
|
+
"streaming",
|
|
29
|
+
"share",
|
|
30
|
+
"local",
|
|
31
|
+
"websocket",
|
|
32
|
+
"cli",
|
|
33
|
+
"npx"
|
|
34
|
+
],
|
|
35
|
+
"author": "vamsiy",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/vamsiy78/fwdcast.git"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/vamsiy78/fwdcast#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/vamsiy78/fwdcast/issues"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"archiver": "^7.0.1",
|
|
50
|
+
"commander": "^12.1.0",
|
|
51
|
+
"mime-types": "^3.0.2",
|
|
52
|
+
"ws": "^8.18.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/archiver": "^7.0.0",
|
|
56
|
+
"@types/mime-types": "^3.0.1",
|
|
57
|
+
"@types/node": "^22.10.0",
|
|
58
|
+
"@types/ws": "^8.5.13",
|
|
59
|
+
"fast-check": "^3.23.0",
|
|
60
|
+
"ts-node": "^10.9.2",
|
|
61
|
+
"typescript": "^5.7.0",
|
|
62
|
+
"vitest": "^2.1.0"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Only show message for global installs
|
|
4
|
+
if (!process.env.npm_config_global) {
|
|
5
|
+
process.exit(0);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
console.log(`
|
|
9
|
+
┌─────────────────────────────────────────────────┐
|
|
10
|
+
│ │
|
|
11
|
+
│ 📡 fwdcast installed successfully! │
|
|
12
|
+
│ │
|
|
13
|
+
│ Quick start: │
|
|
14
|
+
│ fwdcast Share current folder │
|
|
15
|
+
│ fwdcast ~/Downloads Share specific folder │
|
|
16
|
+
│ │
|
|
17
|
+
│ Help: │
|
|
18
|
+
│ fwdcast --help │
|
|
19
|
+
│ │
|
|
20
|
+
│ GitHub: github.com/vamsiy78/fwdcast │
|
|
21
|
+
│ │
|
|
22
|
+
└─────────────────────────────────────────────────┘
|
|
23
|
+
`);
|