@jisan901/fs-browser 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/bin/withfs.js ADDED
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * withfs - CLI utility to serve built projects with fs API support
5
+ * Usage: withfs [projectDir] [options]
6
+ */
7
+
8
+ import http from 'http';
9
+ import fs from 'fs/promises';
10
+ import fsSync from 'fs';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+ import { parse as parseUrl } from 'url';
14
+ import { exec } from 'child_process';
15
+ import { createFsHandlers } from '../plugin/fs-handlers.js';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+
20
+ // Parse command line arguments
21
+ const args = process.argv.slice(2);
22
+ let projectDir = '.';
23
+ let host = 'localhost';
24
+ let port = 3000;
25
+ let baseDir = './data';
26
+ let apiPrefix = '/api/fs';
27
+ let openBrowser = false;
28
+ let justFs = false;
29
+
30
+ // Parse arguments
31
+ for (let i = 0; i < args.length; i++) {
32
+ const arg = args[i];
33
+
34
+ if (arg === '--host' || arg === '-h') {
35
+ const next = args[i + 1];
36
+ if (next && !next.startsWith('-')) {
37
+ host = next;
38
+ i++;
39
+ } else {
40
+ host = '0.0.0.0';
41
+ }
42
+ } else if (arg === '--port' || arg === '-p') {
43
+ port = parseInt(args[++i]) || 3000;
44
+ } else if (arg === '--base-dir' || arg === '-b') {
45
+ baseDir = args[++i] || './data';
46
+ } else if (arg === '--api-prefix' || arg === '-a') {
47
+ apiPrefix = args[++i] || '/api/fs';
48
+ } else if (arg === '--open' || arg === '-o') {
49
+ openBrowser = true;
50
+ } else if (arg === '--justfs') {
51
+ justFs = true;
52
+ } else if (arg === '--help') {
53
+ showHelp();
54
+ process.exit(0);
55
+ } else if (!arg.startsWith('-')) {
56
+ projectDir = arg;
57
+ }
58
+ }
59
+
60
+ // Resolve paths
61
+ const PROJECT_DIR = path.resolve(process.cwd(), projectDir);
62
+ const BASE_DIR = path.resolve(PROJECT_DIR, baseDir);
63
+
64
+ // Ensure directories exist
65
+ if (!justFs && !fsSync.existsSync(PROJECT_DIR)) {
66
+ console.error(`Error: Project directory not found: ${PROJECT_DIR}`);
67
+ process.exit(1);
68
+ }
69
+
70
+ if (!fsSync.existsSync(BASE_DIR)) {
71
+ fsSync.mkdirSync(BASE_DIR, { recursive: true });
72
+ }
73
+
74
+ // Create FS handlers
75
+ const fsHandlers = createFsHandlers(BASE_DIR);
76
+
77
+ // MIME types
78
+ const mimeTypes = {
79
+ '.html': 'text/html',
80
+ '.js': 'application/javascript',
81
+ '.css': 'text/css',
82
+ '.json': 'application/json',
83
+ '.png': 'image/png',
84
+ '.jpg': 'image/jpeg',
85
+ '.jpeg': 'image/jpeg',
86
+ '.gif': 'image/gif',
87
+ '.svg': 'image/svg+xml',
88
+ '.ico': 'image/x-icon',
89
+ '.woff': 'font/woff',
90
+ '.woff2': 'font/woff2',
91
+ '.ttf': 'font/ttf',
92
+ '.eot': 'application/vnd.ms-fontobject',
93
+ '.otf': 'font/otf',
94
+ '.txt': 'text/plain',
95
+ '.xml': 'application/xml',
96
+ '.pdf': 'application/pdf',
97
+ '.zip': 'application/zip',
98
+ '.wasm': 'application/wasm'
99
+ };
100
+
101
+
102
+
103
+ // Serve static files
104
+ async function serveStaticFile(req, res) {
105
+ const parsedUrl = parseUrl(req.url);
106
+ let pathname = parsedUrl.pathname;
107
+
108
+ // Default to index.html
109
+ if (pathname === '/') {
110
+ pathname = '/index.html';
111
+ }
112
+
113
+ const filePath = path.join(PROJECT_DIR, pathname);
114
+
115
+ // Security check
116
+ if (!filePath.startsWith(PROJECT_DIR)) {
117
+ res.writeHead(403, { 'Content-Type': 'text/plain' });
118
+ res.end('Forbidden');
119
+ return;
120
+ }
121
+
122
+ try {
123
+ const stats = await fs.stat(filePath);
124
+
125
+ if (stats.isDirectory()) {
126
+ const indexPath = path.join(filePath, 'index.html');
127
+ if (fsSync.existsSync(indexPath)) {
128
+ const content = await fs.readFile(indexPath);
129
+ res.writeHead(200, { 'Content-Type': 'text/html' });
130
+ res.end(content);
131
+ } else {
132
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
133
+ res.end('Not Found');
134
+ }
135
+ return;
136
+ }
137
+
138
+ const ext = path.extname(filePath);
139
+ const contentType = mimeTypes[ext] || 'application/octet-stream';
140
+
141
+ const content = await fs.readFile(filePath);
142
+ res.writeHead(200, { 'Content-Type': contentType });
143
+ res.end(content);
144
+ } catch (err) {
145
+ if (err.code === 'ENOENT') {
146
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
147
+ res.end('Not Found');
148
+ } else {
149
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
150
+ res.end('Internal Server Error');
151
+ }
152
+ }
153
+ }
154
+
155
+ // Create server
156
+ const server = http.createServer(async (req, res) => {
157
+ // CORS headers
158
+ res.setHeader('Access-Control-Allow-Origin', '*');
159
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
160
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
161
+
162
+ if (req.method === 'OPTIONS') {
163
+ res.writeHead(200);
164
+ res.end();
165
+ return;
166
+ }
167
+
168
+ try {
169
+ // Handle FS API routes
170
+ if (req.url.startsWith(apiPrefix)) {
171
+ const routePath = req.url.substring(apiPrefix.length) || '/';
172
+ const handlerKey = `${req.method} ${routePath.split('?')[0]}`;
173
+ const handler = fsHandlers[handlerKey];
174
+
175
+ if (handler) {
176
+ await handler(req, res);
177
+ } else {
178
+ res.writeHead(404, { 'Content-Type': 'application/json' });
179
+ res.end(JSON.stringify({ error: 'Route not found' }));
180
+ }
181
+ }
182
+ // Serve static files (only if not in justFs mode)
183
+ else if (!justFs) {
184
+ await serveStaticFile(req, res);
185
+ }
186
+ // In justFs mode, reject non-API requests
187
+ else {
188
+ res.writeHead(404, { 'Content-Type': 'application/json' });
189
+ res.end(JSON.stringify({
190
+ error: 'Not Found',
191
+ message: 'Running in --justfs mode. Only fs API endpoints are available.'
192
+ }));
193
+ }
194
+ } catch (error) {
195
+ console.error('Server error:', error);
196
+ res.writeHead(500, { 'Content-Type': 'application/json' });
197
+ res.end(JSON.stringify({
198
+ error: {
199
+ message: error.message,
200
+ status: 500
201
+ }
202
+ }));
203
+ }
204
+ });
205
+
206
+ // Start server
207
+ server.listen(port, host, () => {
208
+ console.log('\n┌─────────────────────────────────────────┐');
209
+ console.log(`│ 🚀 withfs - ${justFs ? 'FS API Server' : 'Server Running'} │`);
210
+ console.log('└─────────────────────────────────────────┘\n');
211
+ if (!justFs) {
212
+ console.log(` Project: ${PROJECT_DIR}`);
213
+ }
214
+ console.log(` Local: http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`);
215
+ if (host === '0.0.0.0') {
216
+ console.log(` Network: http://<your-ip>:${port}`);
217
+ }
218
+ console.log(` FS API: ${apiPrefix}`);
219
+ console.log(` Base Dir: ${BASE_DIR}`);
220
+ if (justFs) {
221
+ console.log(` Mode: Just FS (no static files)`);
222
+ }
223
+ console.log('\n Press Ctrl+C to stop\n');
224
+
225
+ if (openBrowser && !justFs) {
226
+ const url = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;
227
+ const start = process.platform === 'darwin' ? 'open' :
228
+ process.platform === 'win32' ? 'start' : 'xdg-open';
229
+ exec(`${start} ${url}`);
230
+ }
231
+ });
232
+
233
+ // Help text
234
+ function showHelp() {
235
+ console.log(`
236
+ withfs - Serve built projects with fs API support
237
+
238
+ USAGE:
239
+ withfs [projectDir] [options]
240
+
241
+ OPTIONS:
242
+ --host, -h [host] Host to bind to (default: localhost, --host alone = 0.0.0.0)
243
+ --port, -p <port> Port to use (default: 3000)
244
+ --base-dir, -b <dir> Base directory for fs operations (default: ./data)
245
+ --api-prefix, -a <prefix> API route prefix (default: /api/fs)
246
+ --open, -o Open browser automatically
247
+ --justfs Only run fs API (no static file serving)
248
+ --help Show this help message
249
+
250
+ EXAMPLES:
251
+ withfs # Serve current directory
252
+ withfs ./dist # Serve dist directory
253
+ withfs --host # Serve on all interfaces (0.0.0.0)
254
+ withfs --port 8080 # Use custom port
255
+ withfs --open # Open browser automatically
256
+ withfs --justfs # Only fs API, no static files
257
+ withfs --justfs --port 5001 # Dedicated fs API server
258
+
259
+ AFTER BUILD WORKFLOW:
260
+ npm run build
261
+ withfs ./dist --host --open
262
+
263
+ JUST FS MODE:
264
+ # Run only fs API without static file serving
265
+ withfs --justfs --port 5001
266
+ # Useful for microservices or separate frontend servers
267
+ `);
268
+ }
269
+
270
+ // Handle graceful shutdown
271
+ process.on('SIGINT', () => {
272
+ console.log('\n\n 👋 Shutting down server...\n');
273
+ server.close(() => {
274
+ process.exit(0);
275
+ });
276
+ });
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@jisan901/fs-browser",
3
+ "version": "1.0.0",
4
+ "description": "Browser-compatible filesystem API with Vite plugin support",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "type": "module",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "bin": {
12
+ "withfs": "bin/withfs.js"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./src/index.d.ts",
17
+ "import": "./src/index.js"
18
+ },
19
+ "./plugin": {
20
+ "import": "./plugin/vite.js",
21
+ "require": "./plugin/vite.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "bin",
26
+ "src",
27
+ "plugin",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "scripts": {
32
+ "test": "echo \"Error: no test specified\" && exit 1"
33
+ },
34
+ "keywords": [
35
+ "filesystem",
36
+ "browser",
37
+ "fs",
38
+ "vite",
39
+ "plugin",
40
+ "file-api",
41
+ "node-fs",
42
+ "browser-fs",
43
+ "cli",
44
+ "server"
45
+ ],
46
+ "author": "claude:sonnet4.5",
47
+ "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/Jisan901/fs-browser.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/Jisan901/fs-browser/issues"
54
+ },
55
+ "homepage": "https://github.com/Jisan901/fs-browser#readme",
56
+ "peerDependencies": {
57
+ "vite": "^4.0.0 || ^5.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "vite": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "engines": {
65
+ "node": ">=16.0.0"
66
+ }
67
+ }