@jisan901/fs-browser 1.0.0 → 1.0.2
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 +21 -21
- package/package.json +1 -1
- package/plugin/fs-handlers.js +502 -393
- package/src/index.js +13 -4
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# fs-browser
|
|
1
|
+
# @jisan901/fs-browser
|
|
2
2
|
|
|
3
3
|
Browser-compatible filesystem API with Vite plugin support. Write Node.js-style fs code that works in the browser!
|
|
4
4
|
|
|
@@ -14,7 +14,7 @@ Browser-compatible filesystem API with Vite plugin support. Write Node.js-style
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm install fs-browser
|
|
17
|
+
npm install @jisan901/fs-browser
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Quick Start
|
|
@@ -26,7 +26,7 @@ npm install fs-browser
|
|
|
26
26
|
```javascript
|
|
27
27
|
// vite.config.js
|
|
28
28
|
import { defineConfig } from 'vite';
|
|
29
|
-
import fsPlugin from 'fs-browser/plugin';
|
|
29
|
+
import fsPlugin from '@jisan901/fs-browser/plugin';
|
|
30
30
|
|
|
31
31
|
export default defineConfig({
|
|
32
32
|
plugins: [
|
|
@@ -41,13 +41,13 @@ export default defineConfig({
|
|
|
41
41
|
**2. Use in Browser Code**
|
|
42
42
|
|
|
43
43
|
```javascript
|
|
44
|
-
import { readFile, writeFile, mkdir } from 'fs-browser';
|
|
44
|
+
import { readFile, writeFile, mkdir } from '@jisan901/fs-browser';
|
|
45
45
|
|
|
46
46
|
// Write a file
|
|
47
47
|
await writeFile('hello.txt', 'Hello World!');
|
|
48
48
|
|
|
49
49
|
// Read a file
|
|
50
|
-
const content = await readFile('hello.txt');
|
|
50
|
+
const content = await readFile('hello.txt', {encoding: 'utf-8'});
|
|
51
51
|
console.log(content); // "Hello World!"
|
|
52
52
|
|
|
53
53
|
// Create directory
|
|
@@ -115,7 +115,7 @@ withfs ./dist --host --open
|
|
|
115
115
|
|
|
116
116
|
## Development with Other Frameworks
|
|
117
117
|
|
|
118
|
-
While
|
|
118
|
+
While `@jisan901/fs-browser` includes a Vite plugin, you can use it with any framework or development setup by running the `withfs` server separately.
|
|
119
119
|
|
|
120
120
|
### React (Create React App, Next.js)
|
|
121
121
|
|
|
@@ -138,7 +138,7 @@ npm start
|
|
|
138
138
|
|
|
139
139
|
```javascript
|
|
140
140
|
// App.js
|
|
141
|
-
import { readFile, writeFile } from 'fs-browser';
|
|
141
|
+
import { readFile, writeFile } from '@jisan901/fs-browser';
|
|
142
142
|
|
|
143
143
|
function App() {
|
|
144
144
|
const handleSave = async () => {
|
|
@@ -153,7 +153,7 @@ function App() {
|
|
|
153
153
|
|
|
154
154
|
```javascript
|
|
155
155
|
// lib/fs.js
|
|
156
|
-
import fs from 'fs-browser';
|
|
156
|
+
import fs from '@jisan901/fs-browser';
|
|
157
157
|
|
|
158
158
|
export default fs.configure({
|
|
159
159
|
apiBase: 'http://localhost:5001/api/fs'
|
|
@@ -202,7 +202,7 @@ npm run serve
|
|
|
202
202
|
```vue
|
|
203
203
|
<!-- App.vue -->
|
|
204
204
|
<script setup>
|
|
205
|
-
import { readFile, writeFile } from 'fs-browser';
|
|
205
|
+
import { readFile, writeFile } from '@jisan901/fs-browser';
|
|
206
206
|
|
|
207
207
|
const saveFile = async () => {
|
|
208
208
|
await writeFile('vue-data.json', { framework: 'Vue' });
|
|
@@ -218,7 +218,7 @@ const saveFile = async () => {
|
|
|
218
218
|
|
|
219
219
|
```javascript
|
|
220
220
|
// composables/useFs.js
|
|
221
|
-
import fs from 'fs-browser';
|
|
221
|
+
import fs from '@jisan901/fs-browser';
|
|
222
222
|
|
|
223
223
|
export const useFs = () => {
|
|
224
224
|
return fs.configure({
|
|
@@ -274,7 +274,7 @@ ng serve
|
|
|
274
274
|
|
|
275
275
|
```typescript
|
|
276
276
|
// app.component.ts
|
|
277
|
-
import { readFile, writeFile } from 'fs-browser';
|
|
277
|
+
import { readFile, writeFile } from '@jisan901/fs-browser';
|
|
278
278
|
|
|
279
279
|
export class AppComponent {
|
|
280
280
|
async saveFile() {
|
|
@@ -315,7 +315,7 @@ npm run dev
|
|
|
315
315
|
```svelte
|
|
316
316
|
<!-- +page.svelte -->
|
|
317
317
|
<script>
|
|
318
|
-
import { readFile, writeFile } from 'fs-browser';
|
|
318
|
+
import { readFile, writeFile } from '@jisan901/fs-browser';
|
|
319
319
|
|
|
320
320
|
async function saveFile() {
|
|
321
321
|
await writeFile('svelte-data.txt', 'Hello SvelteKit!');
|
|
@@ -360,7 +360,7 @@ npm run dev
|
|
|
360
360
|
// src/pages/index.astro
|
|
361
361
|
---
|
|
362
362
|
<script>
|
|
363
|
-
import { writeFile } from 'fs-browser';
|
|
363
|
+
import { writeFile } from '@jisan901/fs-browser';
|
|
364
364
|
|
|
365
365
|
document.querySelector('#save')?.addEventListener('click', async () => {
|
|
366
366
|
await writeFile('astro-data.json', { framework: 'Astro' });
|
|
@@ -378,7 +378,7 @@ npm run dev
|
|
|
378
378
|
<!DOCTYPE html>
|
|
379
379
|
<html>
|
|
380
380
|
<head>
|
|
381
|
-
<title
|
|
381
|
+
<title>@jisan901/fs-browser Demo</title>
|
|
382
382
|
</head>
|
|
383
383
|
<body>
|
|
384
384
|
<button id="save">Save File</button>
|
|
@@ -386,7 +386,7 @@ npm run dev
|
|
|
386
386
|
<pre id="output"></pre>
|
|
387
387
|
|
|
388
388
|
<script type="module">
|
|
389
|
-
import fs from 'https://cdn.jsdelivr.net/npm/fs-browser/src/index.js';
|
|
389
|
+
import fs from 'https://cdn.jsdelivr.net/npm/@jisan901/fs-browser/src/index.js';
|
|
390
390
|
|
|
391
391
|
// Configure to point to your fs server
|
|
392
392
|
const myFs = fs.configure({
|
|
@@ -399,7 +399,7 @@ npm run dev
|
|
|
399
399
|
};
|
|
400
400
|
|
|
401
401
|
document.getElementById('read').onclick = async () => {
|
|
402
|
-
const content = await myFs.readFile('demo.txt');
|
|
402
|
+
const content = await myFs.readFile('demo.txt', {encoding: 'utf-8'});
|
|
403
403
|
document.getElementById('output').textContent = content;
|
|
404
404
|
};
|
|
405
405
|
</script>
|
|
@@ -448,7 +448,7 @@ npx parcel index.html
|
|
|
448
448
|
|
|
449
449
|
```javascript
|
|
450
450
|
// Configure in your JS
|
|
451
|
-
import { configure } from 'fs-browser';
|
|
451
|
+
import { configure } from '@jisan901/fs-browser';
|
|
452
452
|
|
|
453
453
|
configure({ apiBase: 'http://localhost:5001/api/fs' });
|
|
454
454
|
```
|
|
@@ -493,7 +493,7 @@ Set API base via environment variables:
|
|
|
493
493
|
|
|
494
494
|
```javascript
|
|
495
495
|
// config/fs.js
|
|
496
|
-
import fs from 'fs-browser';
|
|
496
|
+
import fs from '@jisan901/fs-browser';
|
|
497
497
|
|
|
498
498
|
const API_BASE = import.meta.env.VITE_FS_API_BASE ||
|
|
499
499
|
process.env.REACT_APP_FS_API_BASE ||
|
|
@@ -517,8 +517,8 @@ FROM node:18
|
|
|
517
517
|
|
|
518
518
|
WORKDIR /app
|
|
519
519
|
|
|
520
|
-
# Install fs-browser globally
|
|
521
|
-
RUN npm install -g fs-browser
|
|
520
|
+
# Install @jisan901/fs-browser globally
|
|
521
|
+
RUN npm install -g @jisan901/fs-browser
|
|
522
522
|
|
|
523
523
|
# Copy your built frontend
|
|
524
524
|
COPY ./dist ./dist
|
|
@@ -584,7 +584,7 @@ services:
|
|
|
584
584
|
Full TypeScript definitions included:
|
|
585
585
|
|
|
586
586
|
```typescript
|
|
587
|
-
import { readFile, writeFile, Stats, Dirent } from 'fs-browser';
|
|
587
|
+
import { readFile, writeFile, Stats, Dirent } from '@jisan901/fs-browser';
|
|
588
588
|
|
|
589
589
|
const content: string = await readFile('test.txt', 'utf8');
|
|
590
590
|
const stats: Stats = await stat('test.txt');
|
package/package.json
CHANGED
package/plugin/fs-handlers.js
CHANGED
|
@@ -2,418 +2,527 @@
|
|
|
2
2
|
* Shared FS API handlers for both Vite plugin and withfs CLI
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import fs from
|
|
6
|
-
import path from
|
|
5
|
+
import fs from "fs/promises";
|
|
6
|
+
import path from "path";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Helper to read raw body from request
|
|
10
10
|
*/
|
|
11
|
-
export const getRawBody =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
export const getRawBody = req => {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
req.on("data", chunk => chunks.push(chunk));
|
|
15
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
16
|
+
req.on("error", reject);
|
|
17
|
+
});
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Helper to parse query string
|
|
22
22
|
*/
|
|
23
|
-
export const parseQuery =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
export const parseQuery = url => {
|
|
24
|
+
const queryString = url.split("?")[1];
|
|
25
|
+
if (!queryString) return {};
|
|
26
|
+
return Object.fromEntries(new URLSearchParams(queryString));
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Create path resolver with base directory restriction
|
|
31
31
|
*/
|
|
32
|
-
export const createPathResolver =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
export const createPathResolver = baseDir => {
|
|
33
|
+
return filePath => {
|
|
34
|
+
// Remove leading slash to treat all paths as relative
|
|
35
|
+
const normalizedPath = filePath.startsWith("/")
|
|
36
|
+
? filePath.slice(1)
|
|
37
|
+
: filePath;
|
|
38
|
+
const resolved = path.resolve(baseDir, normalizedPath);
|
|
39
|
+
if (!resolved.startsWith(baseDir)) {
|
|
40
|
+
throw new Error("Invalid path: outside base directory");
|
|
41
|
+
}
|
|
42
|
+
return resolved;
|
|
43
|
+
};
|
|
42
44
|
};
|
|
43
45
|
|
|
46
|
+
|
|
44
47
|
/**
|
|
45
48
|
* Create FS API handlers
|
|
46
49
|
* @param {string} baseDir - Base directory for file operations
|
|
47
50
|
* @returns {Object} - Handler functions mapped by route key
|
|
48
51
|
*/
|
|
49
|
-
export const createFsHandlers =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
52
|
+
export const createFsHandlers = baseDir => {
|
|
53
|
+
const resolvePath = createPathResolver(baseDir);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
// Root endpoint
|
|
57
|
+
"GET /": async (req, res) => {
|
|
58
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
59
|
+
res.end(
|
|
60
|
+
JSON.stringify({
|
|
61
|
+
message: "fs-browser API",
|
|
62
|
+
baseDirectory: baseDir
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// List all methods
|
|
68
|
+
"GET /methods": async (req, res) => {
|
|
69
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
70
|
+
res.end(
|
|
71
|
+
JSON.stringify({
|
|
72
|
+
methods: [
|
|
73
|
+
"readFile",
|
|
74
|
+
"writeFile",
|
|
75
|
+
"appendFile",
|
|
76
|
+
"copyFile",
|
|
77
|
+
"readdir",
|
|
78
|
+
"mkdir",
|
|
79
|
+
"rmdir",
|
|
80
|
+
"rm",
|
|
81
|
+
"rename",
|
|
82
|
+
"unlink",
|
|
83
|
+
"stat",
|
|
84
|
+
"lstat",
|
|
85
|
+
"readlink",
|
|
86
|
+
"realpath"
|
|
87
|
+
]
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// Read file
|
|
93
|
+
|
|
94
|
+
"GET /readFile": async (req, res) => {
|
|
95
|
+
try {
|
|
96
|
+
const query = parseQuery(req.url);
|
|
97
|
+
const { path: filePath, encoding } = query;
|
|
98
|
+
const fullPath = resolvePath(filePath);
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
const contentType = encoding ? "text/plain" : "application/octet-stream";
|
|
102
|
+
|
|
103
|
+
// 2. Reading file WITHOUT an encoding (returns a Buffer/binary)
|
|
104
|
+
let options = {}
|
|
105
|
+
if (encoding) {options.encoding = encoding || 'utf8';}
|
|
106
|
+
const data = await fs.readFile(fullPath, options );
|
|
107
|
+
|
|
108
|
+
// 3. Write the headers and send the data
|
|
109
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
110
|
+
res.end(data);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
113
|
+
res.end("File not found or error reading file");
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Read directory
|
|
118
|
+
"GET /readdir": async (req, res) => {
|
|
119
|
+
const query = parseQuery(req.url);
|
|
120
|
+
const { path: dirPath = ".", withFileTypes = "false" } = query;
|
|
121
|
+
const fullPath = resolvePath(dirPath);
|
|
122
|
+
const files = await fs.readdir(fullPath, {
|
|
123
|
+
withFileTypes: withFileTypes === "true"
|
|
124
|
+
});
|
|
125
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
126
|
+
res.end(JSON.stringify({ files }));
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// Get file stats
|
|
130
|
+
"GET /stat": async (req, res) => {
|
|
131
|
+
const query = parseQuery(req.url);
|
|
132
|
+
const { path: filePath } = query;
|
|
133
|
+
const fullPath = resolvePath(filePath);
|
|
134
|
+
const stats = await fs.stat(fullPath);
|
|
135
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
136
|
+
res.end(
|
|
137
|
+
JSON.stringify({
|
|
138
|
+
stats: {
|
|
139
|
+
isFile: stats.isFile(),
|
|
140
|
+
isDirectory: stats.isDirectory(),
|
|
141
|
+
isSymbolicLink: stats.isSymbolicLink(),
|
|
142
|
+
size: stats.size,
|
|
143
|
+
mode: stats.mode,
|
|
144
|
+
mtime: stats.mtime,
|
|
145
|
+
atime: stats.atime,
|
|
146
|
+
ctime: stats.ctime,
|
|
147
|
+
birthtime: stats.birthtime
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
// Get file stats (no symlink follow)
|
|
154
|
+
"GET /lstat": async (req, res) => {
|
|
155
|
+
const query = parseQuery(req.url);
|
|
156
|
+
const { path: filePath } = query;
|
|
157
|
+
const fullPath = resolvePath(filePath);
|
|
158
|
+
const stats = await fs.lstat(fullPath);
|
|
159
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
160
|
+
res.end(
|
|
161
|
+
JSON.stringify({
|
|
162
|
+
stats: {
|
|
163
|
+
isFile: stats.isFile(),
|
|
164
|
+
isDirectory: stats.isDirectory(),
|
|
165
|
+
isSymbolicLink: stats.isSymbolicLink(),
|
|
166
|
+
size: stats.size,
|
|
167
|
+
mode: stats.mode,
|
|
168
|
+
mtime: stats.mtime,
|
|
169
|
+
atime: stats.atime,
|
|
170
|
+
ctime: stats.ctime,
|
|
171
|
+
birthtime: stats.birthtime
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Get real path
|
|
178
|
+
"GET /realpath": async (req, res) => {
|
|
179
|
+
const query = parseQuery(req.url);
|
|
180
|
+
const { path: filePath } = query;
|
|
181
|
+
const fullPath = resolvePath(filePath);
|
|
182
|
+
const realPath = await fs.realpath(fullPath);
|
|
183
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
184
|
+
res.end(JSON.stringify({ realPath }));
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// Read symlink
|
|
188
|
+
"GET /readlink": async (req, res) => {
|
|
189
|
+
const query = parseQuery(req.url);
|
|
190
|
+
const { path: linkPath } = query;
|
|
191
|
+
const fullPath = resolvePath(linkPath);
|
|
192
|
+
const target = await fs.readlink(fullPath);
|
|
193
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
194
|
+
res.end(JSON.stringify({ target }));
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
// Write file
|
|
198
|
+
"POST /writeFile": async (req, res) => {
|
|
199
|
+
const contentType = req.headers["content-type"] || "";
|
|
200
|
+
const query = parseQuery(req.url);
|
|
201
|
+
let filePath = query.path;
|
|
202
|
+
let writeData;
|
|
203
|
+
let type;
|
|
204
|
+
|
|
205
|
+
const rawBody = await getRawBody(req);
|
|
206
|
+
|
|
207
|
+
// Handle binary content types
|
|
208
|
+
if (
|
|
209
|
+
contentType.includes("application/octet-stream") ||
|
|
210
|
+
contentType.includes("image/") ||
|
|
211
|
+
contentType.includes("video/") ||
|
|
212
|
+
contentType.includes("audio/") ||
|
|
213
|
+
contentType.includes("application/pdf")
|
|
214
|
+
) {
|
|
215
|
+
writeData = rawBody;
|
|
216
|
+
type = "binary";
|
|
217
|
+
}
|
|
218
|
+
// Handle plain text
|
|
219
|
+
else if (contentType.includes("text/plain")) {
|
|
220
|
+
writeData = rawBody;
|
|
221
|
+
type = "text";
|
|
222
|
+
}
|
|
223
|
+
// Handle JSON
|
|
224
|
+
else if (contentType.includes("application/json")) {
|
|
225
|
+
// If path is in query string, treat rawBody as the content to write
|
|
226
|
+
if (filePath) {
|
|
227
|
+
writeData = rawBody;
|
|
228
|
+
type = "json";
|
|
229
|
+
}
|
|
230
|
+
// Otherwise, parse as structured request body
|
|
231
|
+
else {
|
|
232
|
+
if (rawBody.length === 0) {
|
|
233
|
+
res.writeHead(400, {
|
|
234
|
+
"Content-Type": "application/json"
|
|
235
|
+
});
|
|
236
|
+
res.end(
|
|
237
|
+
JSON.stringify({ error: "Empty request body" })
|
|
238
|
+
);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const body = JSON.parse(rawBody.toString());
|
|
243
|
+
filePath = body.path;
|
|
244
|
+
|
|
245
|
+
if (!filePath) {
|
|
246
|
+
res.writeHead(400, {
|
|
247
|
+
"Content-Type": "application/json"
|
|
248
|
+
});
|
|
249
|
+
res.end(JSON.stringify({ error: "Path is required" }));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const {
|
|
254
|
+
data,
|
|
255
|
+
type: dataType = "text",
|
|
256
|
+
encoding = "utf8"
|
|
257
|
+
} = body;
|
|
258
|
+
|
|
259
|
+
if (data === undefined || data === null) {
|
|
260
|
+
res.writeHead(400, {
|
|
261
|
+
"Content-Type": "application/json"
|
|
262
|
+
});
|
|
263
|
+
res.end(JSON.stringify({ error: "Data is required" }));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
type = dataType;
|
|
268
|
+
|
|
269
|
+
switch (dataType) {
|
|
270
|
+
case "text":
|
|
271
|
+
writeData = Buffer.from(String(data), encoding);
|
|
272
|
+
break;
|
|
273
|
+
case "json":
|
|
274
|
+
writeData = Buffer.from(
|
|
275
|
+
JSON.stringify(data, null, 2),
|
|
276
|
+
encoding
|
|
277
|
+
);
|
|
278
|
+
break;
|
|
279
|
+
case "buffer":
|
|
280
|
+
writeData = Array.isArray(data)
|
|
281
|
+
? Buffer.from(data)
|
|
282
|
+
: Buffer.from(data, "base64");
|
|
283
|
+
break;
|
|
284
|
+
default:
|
|
285
|
+
writeData = Buffer.from(String(data), encoding);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Fallback for unknown content types
|
|
290
|
+
else {
|
|
291
|
+
writeData = rawBody;
|
|
292
|
+
type = "unknown";
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Validate we have data to write
|
|
296
|
+
if (!writeData || writeData.length === 0) {
|
|
297
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
298
|
+
res.end(JSON.stringify({ error: "No data to write" }));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const fullPath = resolvePath(filePath);
|
|
303
|
+
await fs.writeFile(fullPath, writeData);
|
|
304
|
+
|
|
305
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
306
|
+
res.end(
|
|
307
|
+
JSON.stringify({
|
|
308
|
+
message: "File written successfully",
|
|
309
|
+
path: filePath,
|
|
310
|
+
type,
|
|
311
|
+
size: writeData.length
|
|
312
|
+
})
|
|
313
|
+
);
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// Append file
|
|
317
|
+
"POST /appendFile": async (req, res) => {
|
|
318
|
+
const contentType = req.headers["content-type"] || "";
|
|
319
|
+
const query = parseQuery(req.url);
|
|
320
|
+
let filePath = query.path;
|
|
321
|
+
let appendData;
|
|
322
|
+
let type;
|
|
323
|
+
|
|
324
|
+
const rawBody = await getRawBody(req);
|
|
325
|
+
|
|
326
|
+
// Handle binary content types
|
|
327
|
+
if (
|
|
328
|
+
contentType.includes("application/octet-stream") ||
|
|
329
|
+
contentType.includes("image/") ||
|
|
330
|
+
contentType.includes("video/") ||
|
|
331
|
+
contentType.includes("audio/") ||
|
|
332
|
+
contentType.includes("application/pdf")
|
|
333
|
+
) {
|
|
334
|
+
appendData = rawBody;
|
|
335
|
+
type = "binary";
|
|
336
|
+
}
|
|
337
|
+
// Handle plain text
|
|
338
|
+
else if (contentType.includes("text/plain")) {
|
|
339
|
+
appendData = rawBody;
|
|
340
|
+
type = "text";
|
|
341
|
+
}
|
|
342
|
+
// Handle JSON
|
|
343
|
+
else if (contentType.includes("application/json")) {
|
|
344
|
+
// If path is in query string, treat rawBody as the content to append
|
|
345
|
+
if (filePath) {
|
|
346
|
+
appendData = rawBody;
|
|
347
|
+
type = "json";
|
|
348
|
+
}
|
|
349
|
+
// Otherwise, parse as structured request body
|
|
350
|
+
else {
|
|
351
|
+
if (rawBody.length === 0) {
|
|
352
|
+
res.writeHead(400, {
|
|
353
|
+
"Content-Type": "application/json"
|
|
354
|
+
});
|
|
355
|
+
res.end(
|
|
356
|
+
JSON.stringify({ error: "Empty request body" })
|
|
357
|
+
);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const body = JSON.parse(rawBody.toString());
|
|
362
|
+
filePath = body.path;
|
|
363
|
+
|
|
364
|
+
if (!filePath) {
|
|
365
|
+
res.writeHead(400, {
|
|
366
|
+
"Content-Type": "application/json"
|
|
367
|
+
});
|
|
368
|
+
res.end(JSON.stringify({ error: "Path is required" }));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const {
|
|
373
|
+
data,
|
|
374
|
+
type: dataType = "text",
|
|
375
|
+
encoding = "utf8"
|
|
376
|
+
} = body;
|
|
377
|
+
|
|
378
|
+
if (data === undefined || data === null) {
|
|
379
|
+
res.writeHead(400, {
|
|
380
|
+
"Content-Type": "application/json"
|
|
381
|
+
});
|
|
382
|
+
res.end(JSON.stringify({ error: "Data is required" }));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
type = dataType;
|
|
387
|
+
|
|
388
|
+
switch (dataType) {
|
|
389
|
+
case "text":
|
|
390
|
+
appendData = Buffer.from(String(data), encoding);
|
|
391
|
+
break;
|
|
392
|
+
case "json":
|
|
393
|
+
appendData = Buffer.from(
|
|
394
|
+
JSON.stringify(data, null, 2),
|
|
395
|
+
encoding
|
|
396
|
+
);
|
|
397
|
+
break;
|
|
398
|
+
case "buffer":
|
|
399
|
+
appendData = Array.isArray(data)
|
|
400
|
+
? Buffer.from(data)
|
|
401
|
+
: Buffer.from(data, "base64");
|
|
402
|
+
break;
|
|
403
|
+
default:
|
|
404
|
+
appendData = Buffer.from(String(data), encoding);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Fallback
|
|
409
|
+
else {
|
|
410
|
+
appendData = rawBody;
|
|
411
|
+
type = "unknown";
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Validate we have data
|
|
415
|
+
if (!appendData || appendData.length === 0) {
|
|
416
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
417
|
+
res.end(JSON.stringify({ error: "No data to append" }));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const fullPath = resolvePath(filePath);
|
|
422
|
+
await fs.appendFile(fullPath, appendData);
|
|
423
|
+
|
|
424
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
425
|
+
res.end(
|
|
426
|
+
JSON.stringify({
|
|
427
|
+
message: "Data appended successfully",
|
|
428
|
+
path: filePath,
|
|
429
|
+
type,
|
|
430
|
+
size: appendData.length
|
|
431
|
+
})
|
|
432
|
+
);
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
// Copy file
|
|
436
|
+
"POST /copyFile": async (req, res) => {
|
|
437
|
+
const rawBody = await getRawBody(req);
|
|
438
|
+
const { src, dest, flags = 0 } = JSON.parse(rawBody.toString());
|
|
439
|
+
const srcPath = resolvePath(src);
|
|
440
|
+
const destPath = resolvePath(dest);
|
|
441
|
+
await fs.copyFile(srcPath, destPath, flags);
|
|
442
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
443
|
+
res.end(
|
|
444
|
+
JSON.stringify({
|
|
445
|
+
message: "File copied successfully",
|
|
446
|
+
src,
|
|
447
|
+
dest
|
|
448
|
+
})
|
|
449
|
+
);
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
// Create directory
|
|
453
|
+
"POST /mkdir": async (req, res) => {
|
|
454
|
+
const rawBody = await getRawBody(req);
|
|
455
|
+
const { path: dirPath, recursive = true } = JSON.parse(
|
|
456
|
+
rawBody.toString()
|
|
457
|
+
);
|
|
458
|
+
const fullPath = resolvePath(dirPath);
|
|
459
|
+
await fs.mkdir(fullPath, { recursive });
|
|
460
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
461
|
+
res.end(
|
|
462
|
+
JSON.stringify({ message: "Directory created", path: dirPath })
|
|
463
|
+
);
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
// Remove directory
|
|
467
|
+
"DELETE /rmdir": async (req, res) => {
|
|
468
|
+
const rawBody = await getRawBody(req);
|
|
469
|
+
const { path: dirPath, recursive = false } = JSON.parse(
|
|
470
|
+
rawBody.toString()
|
|
471
|
+
);
|
|
472
|
+
const fullPath = resolvePath(dirPath);
|
|
473
|
+
await fs.rmdir(fullPath, { recursive });
|
|
474
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
475
|
+
res.end(
|
|
476
|
+
JSON.stringify({ message: "Directory removed", path: dirPath })
|
|
477
|
+
);
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
// Remove file or directory
|
|
481
|
+
"DELETE /rm": async (req, res) => {
|
|
482
|
+
const rawBody = await getRawBody(req);
|
|
483
|
+
const {
|
|
484
|
+
path: targetPath,
|
|
485
|
+
recursive = false,
|
|
486
|
+
force = false
|
|
487
|
+
} = JSON.parse(rawBody.toString());
|
|
488
|
+
const fullPath = resolvePath(targetPath);
|
|
489
|
+
await fs.rm(fullPath, { recursive, force });
|
|
490
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
491
|
+
res.end(
|
|
492
|
+
JSON.stringify({
|
|
493
|
+
message: "Removed successfully",
|
|
494
|
+
path: targetPath
|
|
495
|
+
})
|
|
496
|
+
);
|
|
497
|
+
},
|
|
498
|
+
|
|
499
|
+
// Rename
|
|
500
|
+
"PUT /rename": async (req, res) => {
|
|
501
|
+
const rawBody = await getRawBody(req);
|
|
502
|
+
const { oldPath, newPath } = JSON.parse(rawBody.toString());
|
|
503
|
+
const oldFullPath = resolvePath(oldPath);
|
|
504
|
+
const newFullPath = resolvePath(newPath);
|
|
505
|
+
await fs.rename(oldFullPath, newFullPath);
|
|
506
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
507
|
+
res.end(
|
|
508
|
+
JSON.stringify({
|
|
509
|
+
message: "Renamed successfully",
|
|
510
|
+
oldPath,
|
|
511
|
+
newPath
|
|
512
|
+
})
|
|
513
|
+
);
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
// Delete file
|
|
517
|
+
"DELETE /unlink": async (req, res) => {
|
|
518
|
+
const rawBody = await getRawBody(req);
|
|
519
|
+
const { path: filePath } = JSON.parse(rawBody.toString());
|
|
520
|
+
const fullPath = resolvePath(filePath);
|
|
521
|
+
await fs.unlink(fullPath);
|
|
522
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
523
|
+
res.end(
|
|
524
|
+
JSON.stringify({ message: "File deleted", path: filePath })
|
|
525
|
+
);
|
|
232
526
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
else {
|
|
236
|
-
writeData = rawBody;
|
|
237
|
-
type = 'unknown';
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Validate we have data to write
|
|
241
|
-
if (!writeData || writeData.length === 0) {
|
|
242
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
243
|
-
res.end(JSON.stringify({ error: 'No data to write' }));
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const fullPath = resolvePath(filePath);
|
|
248
|
-
await fs.writeFile(fullPath, writeData);
|
|
249
|
-
|
|
250
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
251
|
-
res.end(JSON.stringify({
|
|
252
|
-
message: 'File written successfully',
|
|
253
|
-
path: filePath,
|
|
254
|
-
type,
|
|
255
|
-
size: writeData.length
|
|
256
|
-
}));
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
// Append file
|
|
260
|
-
'POST /appendFile': async (req, res) => {
|
|
261
|
-
const contentType = req.headers['content-type'] || '';
|
|
262
|
-
const query = parseQuery(req.url);
|
|
263
|
-
let filePath = query.path;
|
|
264
|
-
let appendData;
|
|
265
|
-
let type;
|
|
266
|
-
|
|
267
|
-
const rawBody = await getRawBody(req);
|
|
268
|
-
|
|
269
|
-
// Handle binary content types
|
|
270
|
-
if (contentType.includes('application/octet-stream') ||
|
|
271
|
-
contentType.includes('image/') ||
|
|
272
|
-
contentType.includes('video/') ||
|
|
273
|
-
contentType.includes('audio/') ||
|
|
274
|
-
contentType.includes('application/pdf')) {
|
|
275
|
-
appendData = rawBody;
|
|
276
|
-
type = 'binary';
|
|
277
|
-
}
|
|
278
|
-
// Handle plain text
|
|
279
|
-
else if (contentType.includes('text/plain')) {
|
|
280
|
-
appendData = rawBody;
|
|
281
|
-
type = 'text';
|
|
282
|
-
}
|
|
283
|
-
// Handle JSON
|
|
284
|
-
else if (contentType.includes('application/json')) {
|
|
285
|
-
// If path is in query string, treat rawBody as the content to append
|
|
286
|
-
if (filePath) {
|
|
287
|
-
appendData = rawBody;
|
|
288
|
-
type = 'json';
|
|
289
|
-
}
|
|
290
|
-
// Otherwise, parse as structured request body
|
|
291
|
-
else {
|
|
292
|
-
if (rawBody.length === 0) {
|
|
293
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
294
|
-
res.end(JSON.stringify({ error: 'Empty request body' }));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const body = JSON.parse(rawBody.toString());
|
|
299
|
-
filePath = body.path;
|
|
300
|
-
|
|
301
|
-
if (!filePath) {
|
|
302
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
303
|
-
res.end(JSON.stringify({ error: 'Path is required' }));
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const { data, type: dataType = 'text', encoding = 'utf8' } = body;
|
|
308
|
-
|
|
309
|
-
if (data === undefined || data === null) {
|
|
310
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
311
|
-
res.end(JSON.stringify({ error: 'Data is required' }));
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
type = dataType;
|
|
316
|
-
|
|
317
|
-
switch (dataType) {
|
|
318
|
-
case 'text':
|
|
319
|
-
appendData = Buffer.from(String(data), encoding);
|
|
320
|
-
break;
|
|
321
|
-
case 'json':
|
|
322
|
-
appendData = Buffer.from(JSON.stringify(data, null, 2), encoding);
|
|
323
|
-
break;
|
|
324
|
-
case 'buffer':
|
|
325
|
-
appendData = Array.isArray(data) ? Buffer.from(data) : Buffer.from(data, 'base64');
|
|
326
|
-
break;
|
|
327
|
-
default:
|
|
328
|
-
appendData = Buffer.from(String(data), encoding);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
// Fallback
|
|
333
|
-
else {
|
|
334
|
-
appendData = rawBody;
|
|
335
|
-
type = 'unknown';
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Validate we have data
|
|
339
|
-
if (!appendData || appendData.length === 0) {
|
|
340
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
341
|
-
res.end(JSON.stringify({ error: 'No data to append' }));
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const fullPath = resolvePath(filePath);
|
|
346
|
-
await fs.appendFile(fullPath, appendData);
|
|
347
|
-
|
|
348
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
349
|
-
res.end(JSON.stringify({
|
|
350
|
-
message: 'Data appended successfully',
|
|
351
|
-
path: filePath,
|
|
352
|
-
type,
|
|
353
|
-
size: appendData.length
|
|
354
|
-
}));
|
|
355
|
-
},
|
|
356
|
-
|
|
357
|
-
// Copy file
|
|
358
|
-
'POST /copyFile': async (req, res) => {
|
|
359
|
-
const rawBody = await getRawBody(req);
|
|
360
|
-
const { src, dest, flags = 0 } = JSON.parse(rawBody.toString());
|
|
361
|
-
const srcPath = resolvePath(src);
|
|
362
|
-
const destPath = resolvePath(dest);
|
|
363
|
-
await fs.copyFile(srcPath, destPath, flags);
|
|
364
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
365
|
-
res.end(JSON.stringify({ message: 'File copied successfully', src, dest }));
|
|
366
|
-
},
|
|
367
|
-
|
|
368
|
-
// Create directory
|
|
369
|
-
'POST /mkdir': async (req, res) => {
|
|
370
|
-
const rawBody = await getRawBody(req);
|
|
371
|
-
const { path: dirPath, recursive = true } = JSON.parse(rawBody.toString());
|
|
372
|
-
const fullPath = resolvePath(dirPath);
|
|
373
|
-
await fs.mkdir(fullPath, { recursive });
|
|
374
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
375
|
-
res.end(JSON.stringify({ message: 'Directory created', path: dirPath }));
|
|
376
|
-
},
|
|
377
|
-
|
|
378
|
-
// Remove directory
|
|
379
|
-
'DELETE /rmdir': async (req, res) => {
|
|
380
|
-
const rawBody = await getRawBody(req);
|
|
381
|
-
const { path: dirPath, recursive = false } = JSON.parse(rawBody.toString());
|
|
382
|
-
const fullPath = resolvePath(dirPath);
|
|
383
|
-
await fs.rmdir(fullPath, { recursive });
|
|
384
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
385
|
-
res.end(JSON.stringify({ message: 'Directory removed', path: dirPath }));
|
|
386
|
-
},
|
|
387
|
-
|
|
388
|
-
// Remove file or directory
|
|
389
|
-
'DELETE /rm': async (req, res) => {
|
|
390
|
-
const rawBody = await getRawBody(req);
|
|
391
|
-
const { path: targetPath, recursive = false, force = false } = JSON.parse(rawBody.toString());
|
|
392
|
-
const fullPath = resolvePath(targetPath);
|
|
393
|
-
await fs.rm(fullPath, { recursive, force });
|
|
394
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
395
|
-
res.end(JSON.stringify({ message: 'Removed successfully', path: targetPath }));
|
|
396
|
-
},
|
|
397
|
-
|
|
398
|
-
// Rename
|
|
399
|
-
'PUT /rename': async (req, res) => {
|
|
400
|
-
const rawBody = await getRawBody(req);
|
|
401
|
-
const { oldPath, newPath } = JSON.parse(rawBody.toString());
|
|
402
|
-
const oldFullPath = resolvePath(oldPath);
|
|
403
|
-
const newFullPath = resolvePath(newPath);
|
|
404
|
-
await fs.rename(oldFullPath, newFullPath);
|
|
405
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
406
|
-
res.end(JSON.stringify({ message: 'Renamed successfully', oldPath, newPath }));
|
|
407
|
-
},
|
|
408
|
-
|
|
409
|
-
// Delete file
|
|
410
|
-
'DELETE /unlink': async (req, res) => {
|
|
411
|
-
const rawBody = await getRawBody(req);
|
|
412
|
-
const { path: filePath } = JSON.parse(rawBody.toString());
|
|
413
|
-
const fullPath = resolvePath(filePath);
|
|
414
|
-
await fs.unlink(fullPath);
|
|
415
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
416
|
-
res.end(JSON.stringify({ message: 'File deleted', path: filePath }));
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
};
|
|
527
|
+
};
|
|
528
|
+
};
|
package/src/index.js
CHANGED
|
@@ -46,8 +46,8 @@ export function configure(options = {}) {
|
|
|
46
46
|
* @param {string|Object} options - Encoding string or options object
|
|
47
47
|
* @returns {Promise<string|Buffer>}
|
|
48
48
|
*/
|
|
49
|
-
export async function readFile(path, options = '
|
|
50
|
-
const encoding = typeof options === 'string' ? options : options?.encoding || '
|
|
49
|
+
export async function readFile(path, options = '') {
|
|
50
|
+
const encoding = typeof options === 'string' ? options : options?.encoding || '';
|
|
51
51
|
const response = await fetch(`${API_BASE}/readFile?path=${encodeURIComponent(path)}&encoding=${encoding}`);
|
|
52
52
|
|
|
53
53
|
if (!response.ok) {
|
|
@@ -55,8 +55,17 @@ export async function readFile(path, options = 'utf8') {
|
|
|
55
55
|
throw new Error(error.error?.message || 'Failed to read file');
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const
|
|
59
|
-
|
|
58
|
+
const contentType = response.headers.get("content-type");
|
|
59
|
+
|
|
60
|
+
if (contentType?.includes("text/plain")) {
|
|
61
|
+
const data = await response.text();
|
|
62
|
+
return data;
|
|
63
|
+
} else {
|
|
64
|
+
const buffer = await response.arrayBuffer();
|
|
65
|
+
return buffer;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return response;
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
/**
|