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/dist/index.js ADDED
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * fwdcast CLI - Temporary file sharing tool
5
+ * Streams local files as a public website without uploading them
6
+ *
7
+ * Requirements: 1.1, 1.5, 1.6, 7.1, 7.2, 7.4
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ const commander_1 = require("commander");
44
+ const path = __importStar(require("path"));
45
+ const scanner_1 = require("./scanner");
46
+ const validator_1 = require("./validator");
47
+ const tunnel_client_1 = require("./tunnel-client");
48
+ /**
49
+ * Default relay server URL
50
+ */
51
+ const DEFAULT_RELAY_URL = 'wss://fwdcast.publicvm.com/ws';
52
+ /**
53
+ * Session duration in milliseconds (30 minutes)
54
+ */
55
+ const SESSION_DURATION_MS = 30 * 60 * 1000;
56
+ /**
57
+ * Maximum number of connection retry attempts
58
+ * Requirement: 7.4
59
+ */
60
+ const MAX_RETRY_ATTEMPTS = 10;
61
+ /**
62
+ * Delay between retry attempts in milliseconds
63
+ */
64
+ const RETRY_DELAY_MS = 500;
65
+ /**
66
+ * Sleep for a specified number of milliseconds
67
+ */
68
+ function sleep(ms) {
69
+ return new Promise(resolve => setTimeout(resolve, ms));
70
+ }
71
+ /**
72
+ * Main CLI entry point
73
+ */
74
+ async function main() {
75
+ const program = new commander_1.Command();
76
+ program
77
+ .name('fwdcast')
78
+ .description('Temporary file sharing - stream local files as a public website without uploading')
79
+ .version('1.0.0')
80
+ .argument('[path]', 'Directory to share (default: current directory)', '.')
81
+ .option('-r, --relay <url>', 'Custom relay server URL', DEFAULT_RELAY_URL)
82
+ .addHelpText('after', `
83
+ Examples:
84
+ $ fwdcast Share current directory
85
+ $ fwdcast . Share current directory
86
+ $ fwdcast ~/Downloads Share Downloads folder
87
+ $ fwdcast ./project Share a specific folder
88
+
89
+ Limits:
90
+ • Max total size: 500 MB
91
+ • Max file size: 100 MB
92
+ • Session duration: 30 minutes
93
+ • Concurrent viewers: 3
94
+
95
+ More info: https://github.com/vamsiy78/fwdcast
96
+ `)
97
+ .action(async (dirPath, options) => {
98
+ await runShare(dirPath, options.relay);
99
+ });
100
+ await program.parseAsync(process.argv);
101
+ }
102
+ /**
103
+ * Run the file sharing process
104
+ *
105
+ * Requirements: 1.1, 1.5, 1.6, 7.1, 7.2
106
+ */
107
+ async function runShare(dirPath, relayUrl) {
108
+ const absolutePath = path.resolve(dirPath);
109
+ console.log(`\nScanning directory: ${absolutePath}\n`);
110
+ // Step 1: Scan the directory (Requirement 1.1)
111
+ let entries;
112
+ try {
113
+ entries = await (0, scanner_1.scanDirectory)(absolutePath);
114
+ }
115
+ catch (error) {
116
+ const err = error;
117
+ if (err.code === 'ENOENT') {
118
+ console.error(`Error: Directory not found: ${absolutePath}`);
119
+ }
120
+ else if (err.code === 'EACCES') {
121
+ console.error(`Error: Permission denied: ${absolutePath}`);
122
+ }
123
+ else {
124
+ console.error(`Error scanning directory: ${err.message}`);
125
+ }
126
+ process.exit(1);
127
+ }
128
+ // Step 2: Calculate scan result (Requirement 1.2)
129
+ const scanResult = (0, scanner_1.calculateScanResult)(entries);
130
+ console.log(` Files: ${scanResult.fileCount}`);
131
+ console.log(` Directories: ${scanResult.directoryCount}`);
132
+ console.log(` Total size: ${(0, validator_1.formatSize)(scanResult.totalSize)}\n`);
133
+ // Step 3: Validate size limits (Requirements 1.3, 1.4)
134
+ const validation = (0, validator_1.validateScanResult)(scanResult);
135
+ if (!validation.valid) {
136
+ console.error('Error: Cannot share directory\n');
137
+ for (const error of validation.errors) {
138
+ if (error.type === 'total_size_exceeded') {
139
+ console.error(` - Total size (${(0, validator_1.formatSize)(error.actualSize)}) exceeds the ${(0, validator_1.formatSize)(error.limit)} limit`);
140
+ }
141
+ else if (error.type === 'file_size_exceeded') {
142
+ console.error(` - File "${error.file?.relativePath}" (${(0, validator_1.formatSize)(error.actualSize)}) exceeds the ${(0, validator_1.formatSize)(error.limit)} per-file limit`);
143
+ }
144
+ else {
145
+ console.error(` - ${error.message}`);
146
+ }
147
+ }
148
+ console.error('\nTip: Remove large files or split your share into smaller directories.\n');
149
+ process.exit(1);
150
+ }
151
+ // Step 4: Connect to relay server with retry logic (Requirements 1.5, 7.4)
152
+ console.log(`Connecting to relay server...`);
153
+ const expiresAt = Date.now() + SESSION_DURATION_MS;
154
+ const config = {
155
+ relayUrl,
156
+ basePath: absolutePath,
157
+ entries,
158
+ expiresAt,
159
+ onUrl: (url) => {
160
+ console.log(`\nShare active. URL:\n`);
161
+ console.log(` ${url}\n`);
162
+ console.log(`Session expires in 30 minutes.`);
163
+ console.log(`Press Ctrl+C to stop sharing.\n`);
164
+ },
165
+ onExpired: () => {
166
+ // Requirement 7.2
167
+ console.log('\nSession expired after 30 minutes.');
168
+ console.log('Files are no longer accessible.\n');
169
+ process.exit(0);
170
+ },
171
+ onDisconnect: () => {
172
+ // Requirement 7.1
173
+ console.log('\nDisconnected from relay server.');
174
+ console.log('Files are no longer accessible.\n');
175
+ process.exit(0);
176
+ },
177
+ onError: (error) => {
178
+ // Requirement 7.4
179
+ console.error(`\nConnection error: ${error.message}\n`);
180
+ },
181
+ };
182
+ let client = null;
183
+ let lastError = null;
184
+ // Retry connection up to MAX_RETRY_ATTEMPTS times (Requirement 7.4)
185
+ for (let attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
186
+ try {
187
+ client = new tunnel_client_1.TunnelClient(config);
188
+ await client.connect();
189
+ lastError = null;
190
+ break; // Connection successful
191
+ }
192
+ catch (error) {
193
+ lastError = error;
194
+ if (attempt < MAX_RETRY_ATTEMPTS) {
195
+ console.log(` Attempt ${attempt}/${MAX_RETRY_ATTEMPTS} failed: ${lastError.message}`);
196
+ console.log(` Retrying...\n`);
197
+ await sleep(RETRY_DELAY_MS);
198
+ }
199
+ }
200
+ }
201
+ if (lastError) {
202
+ // Requirement 7.4 - All retries failed
203
+ console.error(`\nFailed to connect after ${MAX_RETRY_ATTEMPTS} attempts.`);
204
+ console.error(`Last error: ${lastError.message}`);
205
+ console.error(`\nPlease check:`);
206
+ console.error(` - Your internet connection`);
207
+ console.error(` - The relay server (${relayUrl}) is accessible`);
208
+ console.error(` - No firewall is blocking WebSocket connections\n`);
209
+ process.exit(1);
210
+ }
211
+ // Handle graceful shutdown on Ctrl+C
212
+ const shutdown = () => {
213
+ console.log('\n\nStopping file share...');
214
+ if (client) {
215
+ client.disconnect();
216
+ }
217
+ process.exit(0);
218
+ };
219
+ process.on('SIGINT', shutdown);
220
+ process.on('SIGTERM', shutdown);
221
+ // Keep the process running
222
+ await new Promise(() => { });
223
+ }
224
+ // Run the CLI
225
+ main().catch((error) => {
226
+ console.error('Unexpected error:', error);
227
+ process.exit(1);
228
+ });
229
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Tunnel Protocol Types and Validation
3
+ *
4
+ * Defines the WebSocket-based protocol for communication between
5
+ * the CLI and Relay Server.
6
+ *
7
+ * Requirements: 5.1, 5.2, 5.3, 5.4, 5.5
8
+ */
9
+ /**
10
+ * CLI → Relay: Registration message
11
+ * Sent when CLI connects to register a new session
12
+ * Requirements: 5.1
13
+ */
14
+ export interface RegisterMessage {
15
+ type: 'register';
16
+ path: string;
17
+ expiresAt: number;
18
+ }
19
+ /**
20
+ * Relay → CLI: Registration response
21
+ * Sent after successful session creation
22
+ * Requirements: 5.1
23
+ */
24
+ export interface RegisteredMessage {
25
+ type: 'registered';
26
+ sessionId: string;
27
+ url: string;
28
+ }
29
+ /**
30
+ * Relay → CLI: Forward HTTP request
31
+ * Sent when a viewer requests a resource
32
+ * Requirements: 5.2
33
+ */
34
+ export interface RequestMessage {
35
+ type: 'request';
36
+ id: string;
37
+ method: string;
38
+ path: string;
39
+ }
40
+ /**
41
+ * CLI → Relay: Response headers
42
+ * Sent to start the response for a request
43
+ * Requirements: 5.3
44
+ */
45
+ export interface ResponseMessage {
46
+ type: 'response';
47
+ id: string;
48
+ status: number;
49
+ headers: Record<string, string>;
50
+ }
51
+ /**
52
+ * CLI → Relay: Response body chunk
53
+ * Sent for each chunk of response data
54
+ * Requirements: 5.4
55
+ */
56
+ export interface DataMessage {
57
+ type: 'data';
58
+ id: string;
59
+ chunk: string;
60
+ }
61
+ /**
62
+ * CLI → Relay: Response complete
63
+ * Sent when response streaming is finished
64
+ * Requirements: 5.5
65
+ */
66
+ export interface EndMessage {
67
+ type: 'end';
68
+ id: string;
69
+ }
70
+ /**
71
+ * Relay → CLI: Session expired
72
+ * Sent when the session has expired
73
+ */
74
+ export interface ExpiredMessage {
75
+ type: 'expired';
76
+ }
77
+ /**
78
+ * Union type of all protocol messages
79
+ */
80
+ export type ProtocolMessage = RegisterMessage | RegisteredMessage | RequestMessage | ResponseMessage | DataMessage | EndMessage | ExpiredMessage;
81
+ /**
82
+ * Message type literals for type guards
83
+ */
84
+ export type MessageType = ProtocolMessage['type'];
85
+ export declare function isRegisterMessage(msg: unknown): msg is RegisterMessage;
86
+ export declare function isRegisteredMessage(msg: unknown): msg is RegisteredMessage;
87
+ export declare function isRequestMessage(msg: unknown): msg is RequestMessage;
88
+ export declare function isResponseMessage(msg: unknown): msg is ResponseMessage;
89
+ export declare function isDataMessage(msg: unknown): msg is DataMessage;
90
+ export declare function isEndMessage(msg: unknown): msg is EndMessage;
91
+ export declare function isExpiredMessage(msg: unknown): msg is ExpiredMessage;
92
+ export declare function isProtocolMessage(msg: unknown): msg is ProtocolMessage;
93
+ /**
94
+ * Serialize a protocol message to JSON string
95
+ */
96
+ export declare function serializeMessage(msg: ProtocolMessage): string;
97
+ /**
98
+ * Deserialize a JSON string to a protocol message
99
+ * Returns null if the message is invalid
100
+ */
101
+ export declare function deserializeMessage(data: string): ProtocolMessage | null;
102
+ export declare function createRegisterMessage(path: string, expiresAt: number): RegisterMessage;
103
+ export declare function createRegisteredMessage(sessionId: string, url: string): RegisteredMessage;
104
+ export declare function createRequestMessage(id: string, method: string, path: string): RequestMessage;
105
+ export declare function createResponseMessage(id: string, status: number, headers: Record<string, string>): ResponseMessage;
106
+ export declare function createDataMessage(id: string, chunk: string): DataMessage;
107
+ export declare function createEndMessage(id: string): EndMessage;
108
+ export declare function createExpiredMessage(): ExpiredMessage;
109
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,KAAK,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,eAAe,GACf,WAAW,GACX,UAAU,GACV,cAAc,CAAC;AAEnB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;AAMlD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,eAAe,CAQtE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,iBAAiB,CAQ1E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CASpE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,eAAe,CAUtE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,WAAW,CAQ9D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU,CAO5D;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CAMpE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,eAAe,CAUtE;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAE7D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAUvE;AAMD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAEtF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAEzF;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAE7F;AAED,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,eAAe,CAEjB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAExE;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,CAEvD;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD"}
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * Tunnel Protocol Types and Validation
4
+ *
5
+ * Defines the WebSocket-based protocol for communication between
6
+ * the CLI and Relay Server.
7
+ *
8
+ * Requirements: 5.1, 5.2, 5.3, 5.4, 5.5
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.isRegisterMessage = isRegisterMessage;
12
+ exports.isRegisteredMessage = isRegisteredMessage;
13
+ exports.isRequestMessage = isRequestMessage;
14
+ exports.isResponseMessage = isResponseMessage;
15
+ exports.isDataMessage = isDataMessage;
16
+ exports.isEndMessage = isEndMessage;
17
+ exports.isExpiredMessage = isExpiredMessage;
18
+ exports.isProtocolMessage = isProtocolMessage;
19
+ exports.serializeMessage = serializeMessage;
20
+ exports.deserializeMessage = deserializeMessage;
21
+ exports.createRegisterMessage = createRegisterMessage;
22
+ exports.createRegisteredMessage = createRegisteredMessage;
23
+ exports.createRequestMessage = createRequestMessage;
24
+ exports.createResponseMessage = createResponseMessage;
25
+ exports.createDataMessage = createDataMessage;
26
+ exports.createEndMessage = createEndMessage;
27
+ exports.createExpiredMessage = createExpiredMessage;
28
+ // ============================================================================
29
+ // Type Guards
30
+ // ============================================================================
31
+ function isRegisterMessage(msg) {
32
+ return (typeof msg === 'object' &&
33
+ msg !== null &&
34
+ msg.type === 'register' &&
35
+ typeof msg.path === 'string' &&
36
+ typeof msg.expiresAt === 'number');
37
+ }
38
+ function isRegisteredMessage(msg) {
39
+ return (typeof msg === 'object' &&
40
+ msg !== null &&
41
+ msg.type === 'registered' &&
42
+ typeof msg.sessionId === 'string' &&
43
+ typeof msg.url === 'string');
44
+ }
45
+ function isRequestMessage(msg) {
46
+ return (typeof msg === 'object' &&
47
+ msg !== null &&
48
+ msg.type === 'request' &&
49
+ typeof msg.id === 'string' &&
50
+ typeof msg.method === 'string' &&
51
+ typeof msg.path === 'string');
52
+ }
53
+ function isResponseMessage(msg) {
54
+ return (typeof msg === 'object' &&
55
+ msg !== null &&
56
+ msg.type === 'response' &&
57
+ typeof msg.id === 'string' &&
58
+ typeof msg.status === 'number' &&
59
+ typeof msg.headers === 'object' &&
60
+ msg.headers !== null);
61
+ }
62
+ function isDataMessage(msg) {
63
+ return (typeof msg === 'object' &&
64
+ msg !== null &&
65
+ msg.type === 'data' &&
66
+ typeof msg.id === 'string' &&
67
+ typeof msg.chunk === 'string');
68
+ }
69
+ function isEndMessage(msg) {
70
+ return (typeof msg === 'object' &&
71
+ msg !== null &&
72
+ msg.type === 'end' &&
73
+ typeof msg.id === 'string');
74
+ }
75
+ function isExpiredMessage(msg) {
76
+ return (typeof msg === 'object' &&
77
+ msg !== null &&
78
+ msg.type === 'expired');
79
+ }
80
+ function isProtocolMessage(msg) {
81
+ return (isRegisterMessage(msg) ||
82
+ isRegisteredMessage(msg) ||
83
+ isRequestMessage(msg) ||
84
+ isResponseMessage(msg) ||
85
+ isDataMessage(msg) ||
86
+ isEndMessage(msg) ||
87
+ isExpiredMessage(msg));
88
+ }
89
+ // ============================================================================
90
+ // Serialization / Deserialization
91
+ // ============================================================================
92
+ /**
93
+ * Serialize a protocol message to JSON string
94
+ */
95
+ function serializeMessage(msg) {
96
+ return JSON.stringify(msg);
97
+ }
98
+ /**
99
+ * Deserialize a JSON string to a protocol message
100
+ * Returns null if the message is invalid
101
+ */
102
+ function deserializeMessage(data) {
103
+ try {
104
+ const parsed = JSON.parse(data);
105
+ if (isProtocolMessage(parsed)) {
106
+ return parsed;
107
+ }
108
+ return null;
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ // ============================================================================
115
+ // Message Factories
116
+ // ============================================================================
117
+ function createRegisterMessage(path, expiresAt) {
118
+ return { type: 'register', path, expiresAt };
119
+ }
120
+ function createRegisteredMessage(sessionId, url) {
121
+ return { type: 'registered', sessionId, url };
122
+ }
123
+ function createRequestMessage(id, method, path) {
124
+ return { type: 'request', id, method, path };
125
+ }
126
+ function createResponseMessage(id, status, headers) {
127
+ return { type: 'response', id, status, headers };
128
+ }
129
+ function createDataMessage(id, chunk) {
130
+ return { type: 'data', id, chunk };
131
+ }
132
+ function createEndMessage(id) {
133
+ return { type: 'end', id };
134
+ }
135
+ function createExpiredMessage() {
136
+ return { type: 'expired' };
137
+ }
138
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Represents a single entry (file or directory) in a scanned directory
3
+ */
4
+ export interface DirectoryEntry {
5
+ name: string;
6
+ relativePath: string;
7
+ absolutePath: string;
8
+ isDirectory: boolean;
9
+ size: number;
10
+ modifiedAt: Date;
11
+ }
12
+ /**
13
+ * Result of scanning a directory
14
+ */
15
+ export interface ScanResult {
16
+ entries: DirectoryEntry[];
17
+ totalSize: number;
18
+ fileCount: number;
19
+ directoryCount: number;
20
+ }
21
+ /**
22
+ * Recursively scans a directory and returns all entries.
23
+ * Symlinks are skipped.
24
+ *
25
+ * @param dirPath - The directory path to scan
26
+ * @param basePath - The base path for calculating relative paths (defaults to dirPath)
27
+ * @returns Array of DirectoryEntry objects
28
+ */
29
+ export declare function scanDirectory(dirPath: string, basePath?: string): Promise<DirectoryEntry[]>;
30
+ /**
31
+ * Calculates the total size and counts from directory entries.
32
+ *
33
+ * @param entries - Array of DirectoryEntry objects
34
+ * @returns ScanResult with entries, totalSize, fileCount, and directoryCount
35
+ */
36
+ export declare function calculateScanResult(entries: DirectoryEntry[]): ScanResult;
37
+ /**
38
+ * Scans only the immediate children of a directory (non-recursive).
39
+ * Used for dynamic directory listings.
40
+ *
41
+ * @param dirPath - The directory path to scan
42
+ * @param basePath - The base path for calculating relative paths
43
+ * @returns Array of DirectoryEntry objects for immediate children only
44
+ */
45
+ export declare function scanDirectoryShallow(dirPath: string, basePath: string): Promise<DirectoryEntry[]>;
46
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,IAAI,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,EAAE,CAAC,CAqC3B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,UAAU,CAoBzE;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,EAAE,CAAC,CA+B3B"}