@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/src/index.d.ts ADDED
@@ -0,0 +1,288 @@
1
+ /**
2
+ * fs:browser - Browser-compatible filesystem API
3
+ * TypeScript Declaration File
4
+ */
5
+
6
+ declare module 'fs:browser' {
7
+ /**
8
+ * Configuration options for fs-browser
9
+ */
10
+ export interface FsConfig {
11
+ apiBase?: string;
12
+ }
13
+
14
+ /**
15
+ * File encoding options
16
+ */
17
+ export type BufferEncoding =
18
+ | 'ascii'
19
+ | 'utf8'
20
+ | 'utf-8'
21
+ | 'utf16le'
22
+ | 'ucs2'
23
+ | 'ucs-2'
24
+ | 'base64'
25
+ | 'base64url'
26
+ | 'latin1'
27
+ | 'binary'
28
+ | 'hex';
29
+
30
+ /**
31
+ * Options for file operations
32
+ */
33
+ export interface FileOptions {
34
+ encoding?: BufferEncoding | null;
35
+ flag?: string;
36
+ mode?: number;
37
+ }
38
+
39
+ /**
40
+ * Options for directory operations
41
+ */
42
+ export interface MkdirOptions {
43
+ recursive?: boolean;
44
+ mode?: number;
45
+ }
46
+
47
+ /**
48
+ * Options for readdir
49
+ */
50
+ export interface ReaddirOptions {
51
+ encoding?: BufferEncoding | null;
52
+ withFileTypes?: boolean;
53
+ }
54
+
55
+ /**
56
+ * Options for rm/rmdir operations
57
+ */
58
+ export interface RmOptions {
59
+ recursive?: boolean;
60
+ force?: boolean;
61
+ maxRetries?: number;
62
+ retryDelay?: number;
63
+ }
64
+
65
+ /**
66
+ * Directory entry with type information
67
+ */
68
+ export interface Dirent {
69
+ name: string;
70
+ isFile(): boolean;
71
+ isDirectory(): boolean;
72
+ isSymbolicLink(): boolean;
73
+ }
74
+
75
+ /**
76
+ * File statistics
77
+ */
78
+ export interface Stats {
79
+ isFile(): boolean;
80
+ isDirectory(): boolean;
81
+ isSymbolicLink(): boolean;
82
+ mode: number;
83
+ size: number;
84
+ atimeMs: number;
85
+ mtimeMs: number;
86
+ ctimeMs: number;
87
+ birthtimeMs: number;
88
+ atime: Date;
89
+ mtime: Date;
90
+ ctime: Date;
91
+ birthtime: Date;
92
+ }
93
+
94
+ /**
95
+ * Read file contents
96
+ * @param path - File path
97
+ * @param options - Encoding string or options object
98
+ * @returns File contents as string or Buffer
99
+ */
100
+ export function readFile(
101
+ path: string,
102
+ options?: BufferEncoding | FileOptions
103
+ ): Promise<string>;
104
+
105
+ /**
106
+ * Write data to file
107
+ * @param path - File path
108
+ * @param data - Data to write (string, Buffer, Blob, ArrayBuffer, TypedArray, or object)
109
+ * @param options - Encoding string or options object
110
+ */
111
+ export function writeFile(
112
+ path: string,
113
+ data: string | Buffer | Blob | ArrayBuffer | ArrayBufferView | object,
114
+ options?: BufferEncoding | FileOptions
115
+ ): Promise<void>;
116
+
117
+ /**
118
+ * Append data to file
119
+ * @param path - File path
120
+ * @param data - Data to append (string, Buffer, Blob, ArrayBuffer, TypedArray, or object)
121
+ * @param options - Encoding string or options object
122
+ */
123
+ export function appendFile(
124
+ path: string,
125
+ data: string | Buffer | Blob | ArrayBuffer | ArrayBufferView | object,
126
+ options?: BufferEncoding | FileOptions
127
+ ): Promise<void>;
128
+
129
+ /**
130
+ * Copy file
131
+ * @param src - Source path
132
+ * @param dest - Destination path
133
+ * @param flags - Copy flags (default: 0)
134
+ */
135
+ export function copyFile(
136
+ src: string,
137
+ dest: string,
138
+ flags?: number
139
+ ): Promise<void>;
140
+
141
+ /**
142
+ * Read directory contents
143
+ * @param path - Directory path
144
+ * @param options - Options object
145
+ * @returns Array of filenames or Dirent objects
146
+ */
147
+ export function readdir(
148
+ path: string,
149
+ options?: { withFileTypes?: false } & ReaddirOptions
150
+ ): Promise<string[]>;
151
+ export function readdir(
152
+ path: string,
153
+ options: { withFileTypes: true } & ReaddirOptions
154
+ ): Promise<Dirent[]>;
155
+ export function readdir(
156
+ path: string,
157
+ options?: ReaddirOptions
158
+ ): Promise<string[] | Dirent[]>;
159
+
160
+ /**
161
+ * Create directory
162
+ * @param path - Directory path
163
+ * @param options - Options object
164
+ */
165
+ export function mkdir(
166
+ path: string,
167
+ options?: MkdirOptions
168
+ ): Promise<void>;
169
+
170
+ /**
171
+ * Remove directory
172
+ * @param path - Directory path
173
+ * @param options - Options object
174
+ */
175
+ export function rmdir(
176
+ path: string,
177
+ options?: RmOptions
178
+ ): Promise<void>;
179
+
180
+ /**
181
+ * Remove file or directory
182
+ * @param path - Path to remove
183
+ * @param options - Options object
184
+ */
185
+ export function rm(
186
+ path: string,
187
+ options?: RmOptions
188
+ ): Promise<void>;
189
+
190
+ /**
191
+ * Rename or move file/directory
192
+ * @param oldPath - Old path
193
+ * @param newPath - New path
194
+ */
195
+ export function rename(
196
+ oldPath: string,
197
+ newPath: string
198
+ ): Promise<void>;
199
+
200
+ /**
201
+ * Delete file
202
+ * @param path - File path
203
+ */
204
+ export function unlink(path: string): Promise<void>;
205
+
206
+ /**
207
+ * Get file/directory stats
208
+ * @param path - Path
209
+ * @returns File statistics
210
+ */
211
+ export function stat(path: string): Promise<Stats>;
212
+
213
+ /**
214
+ * Get file/directory stats (doesn't follow symlinks)
215
+ * @param path - Path
216
+ * @returns File statistics
217
+ */
218
+ export function lstat(path: string): Promise<Stats>;
219
+
220
+ /**
221
+ * Get canonical path
222
+ * @param path - Path
223
+ * @returns Resolved path
224
+ */
225
+ export function realpath(path: string): Promise<string>;
226
+
227
+ /**
228
+ * Read symbolic link
229
+ * @param path - Link path
230
+ * @returns Link target path
231
+ */
232
+ export function readlink(path: string): Promise<string>;
233
+
234
+ /**
235
+ * Check if file exists (convenience method)
236
+ * @param path - Path
237
+ * @returns true if exists, false otherwise
238
+ */
239
+ export function exists(path: string): Promise<boolean>;
240
+
241
+ /**
242
+ * Configure the fs-browser module
243
+ * @param options - Configuration options
244
+ * @returns Configured fs module with all methods
245
+ */
246
+ export function configure(options: FsConfig): {
247
+ readFile: typeof readFile;
248
+ writeFile: typeof writeFile;
249
+ appendFile: typeof appendFile;
250
+ copyFile: typeof copyFile;
251
+ readdir: typeof readdir;
252
+ mkdir: typeof mkdir;
253
+ rmdir: typeof rmdir;
254
+ rm: typeof rm;
255
+ rename: typeof rename;
256
+ unlink: typeof unlink;
257
+ stat: typeof stat;
258
+ lstat: typeof lstat;
259
+ realpath: typeof realpath;
260
+ readlink: typeof readlink;
261
+ exists: typeof exists;
262
+ configure: typeof configure;
263
+ };
264
+
265
+ /**
266
+ * Default export with all methods
267
+ */
268
+ const fs: {
269
+ readFile: typeof readFile;
270
+ writeFile: typeof writeFile;
271
+ appendFile: typeof appendFile;
272
+ copyFile: typeof copyFile;
273
+ readdir: typeof readdir;
274
+ mkdir: typeof mkdir;
275
+ rmdir: typeof rmdir;
276
+ rm: typeof rm;
277
+ rename: typeof rename;
278
+ unlink: typeof unlink;
279
+ stat: typeof stat;
280
+ lstat: typeof lstat;
281
+ realpath: typeof realpath;
282
+ readlink: typeof readlink;
283
+ exists: typeof exists;
284
+ configure: typeof configure;
285
+ };
286
+
287
+ export default fs;
288
+ }
package/src/index.js ADDED
@@ -0,0 +1,402 @@
1
+ /**
2
+ * fs:browser - Browser-compatible filesystem API
3
+ * Uses fetch to communicate with backend fs API
4
+ */
5
+
6
+ let API_BASE = '/api/fs';
7
+
8
+ /**
9
+ * Configure the fs-browser module
10
+ * @param {Object} options - Configuration options
11
+ * @param {string} options.apiBase - Base URL for the fs API
12
+ * @returns {Object} - Configured fs module
13
+ */
14
+ export function configure(options = {}) {
15
+ if (options.apiBase !== undefined) {
16
+ API_BASE = options.apiBase;
17
+ // Ensure no trailing slash
18
+ if (API_BASE.endsWith('/')) {
19
+ API_BASE = API_BASE.slice(0, -1);
20
+ }
21
+ }
22
+
23
+ return {
24
+ readFile,
25
+ writeFile,
26
+ appendFile,
27
+ copyFile,
28
+ readdir,
29
+ mkdir,
30
+ rmdir,
31
+ rm,
32
+ rename,
33
+ unlink,
34
+ stat,
35
+ lstat,
36
+ realpath,
37
+ readlink,
38
+ exists,
39
+ configure
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Read file contents
45
+ * @param {string} path - File path
46
+ * @param {string|Object} options - Encoding string or options object
47
+ * @returns {Promise<string|Buffer>}
48
+ */
49
+ export async function readFile(path, options = 'utf8') {
50
+ const encoding = typeof options === 'string' ? options : options?.encoding || 'utf8';
51
+ const response = await fetch(`${API_BASE}/readFile?path=${encodeURIComponent(path)}&encoding=${encoding}`);
52
+
53
+ if (!response.ok) {
54
+ const error = await response.json();
55
+ throw new Error(error.error?.message || 'Failed to read file');
56
+ }
57
+
58
+ const { data } = await response.json();
59
+ return data;
60
+ }
61
+
62
+ /**
63
+ * Write data to file
64
+ * @param {string} path - File path
65
+ * @param {string|Buffer|Blob} data - Data to write
66
+ * @param {string|Object} options - Encoding string or options object
67
+ * @returns {Promise<void>}
68
+ */
69
+ export async function writeFile(path, data, options = 'utf8') {
70
+ const encoding = typeof options === 'string' ? options : options?.encoding || 'utf8';
71
+
72
+ let body;
73
+ let contentType;
74
+
75
+ // Handle Blob
76
+ if (data instanceof Blob) {
77
+ body = data;
78
+ contentType = data.type || 'application/octet-stream';
79
+ }
80
+ // Handle ArrayBuffer
81
+ else if (data instanceof ArrayBuffer) {
82
+ body = new Blob([data], { type: 'application/octet-stream' });
83
+ contentType = 'application/octet-stream';
84
+ }
85
+ // Handle Uint8Array or other TypedArray
86
+ else if (ArrayBuffer.isView(data)) {
87
+ body = new Blob([data], { type: 'application/octet-stream' });
88
+ contentType = 'application/octet-stream';
89
+ }
90
+ // Handle string
91
+ else if (typeof data === 'string') {
92
+ body = new Blob([data], { type: 'text/plain' });
93
+ contentType = 'text/plain';
94
+ }
95
+ // Handle object (convert to JSON)
96
+ else if (typeof data === 'object') {
97
+ body = JSON.stringify(data);
98
+ contentType = 'application/json';
99
+ }
100
+ else {
101
+ throw new Error('Unsupported data type');
102
+ }
103
+
104
+ const response = await fetch(`${API_BASE}/writeFile?path=${encodeURIComponent(path)}`, {
105
+ method: 'POST',
106
+ headers: { 'Content-Type': contentType },
107
+ body
108
+ });
109
+
110
+ if (!response.ok) {
111
+ const error = await response.json();
112
+ throw new Error(error.error?.message || 'Failed to write file');
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Append data to file
118
+ * @param {string} path - File path
119
+ * @param {string|Buffer|Blob} data - Data to append
120
+ * @param {string|Object} options - Encoding string or options object
121
+ * @returns {Promise<void>}
122
+ */
123
+ export async function appendFile(path, data, options = 'utf8') {
124
+ const encoding = typeof options === 'string' ? options : options?.encoding || 'utf8';
125
+
126
+ let body;
127
+ let contentType;
128
+
129
+ if (data instanceof Blob) {
130
+ body = data;
131
+ contentType = data.type || 'application/octet-stream';
132
+ }
133
+ else if (data instanceof ArrayBuffer) {
134
+ body = new Blob([data], { type: 'application/octet-stream' });
135
+ contentType = 'application/octet-stream';
136
+ }
137
+ else if (ArrayBuffer.isView(data)) {
138
+ body = new Blob([data], { type: 'application/octet-stream' });
139
+ contentType = 'application/octet-stream';
140
+ }
141
+ else if (typeof data === 'string') {
142
+ body = new Blob([data], { type: 'text/plain' });
143
+ contentType = 'text/plain';
144
+ }
145
+ else if (typeof data === 'object') {
146
+ body = JSON.stringify(data);
147
+ contentType = 'application/json';
148
+ }
149
+ else {
150
+ throw new Error('Unsupported data type');
151
+ }
152
+
153
+ const response = await fetch(`${API_BASE}/appendFile?path=${encodeURIComponent(path)}`, {
154
+ method: 'POST',
155
+ headers: { 'Content-Type': contentType },
156
+ body
157
+ });
158
+
159
+ if (!response.ok) {
160
+ const error = await response.json();
161
+ throw new Error(error.error?.message || 'Failed to append file');
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Copy file
167
+ * @param {string} src - Source path
168
+ * @param {string} dest - Destination path
169
+ * @param {number} flags - Copy flags
170
+ * @returns {Promise<void>}
171
+ */
172
+ export async function copyFile(src, dest, flags = 0) {
173
+ const response = await fetch(`${API_BASE}/copyFile`, {
174
+ method: 'POST',
175
+ headers: { 'Content-Type': 'application/json' },
176
+ body: JSON.stringify({ src, dest, flags })
177
+ });
178
+
179
+ if (!response.ok) {
180
+ const error = await response.json();
181
+ throw new Error(error.error?.message || 'Failed to copy file');
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Read directory contents
187
+ * @param {string} path - Directory path
188
+ * @param {Object} options - Options
189
+ * @returns {Promise<string[]|Dirent[]>}
190
+ */
191
+ export async function readdir(path, options = {}) {
192
+ const withFileTypes = options.withFileTypes || false;
193
+ const response = await fetch(`${API_BASE}/readdir?path=${encodeURIComponent(path)}&withFileTypes=${withFileTypes}`);
194
+
195
+ if (!response.ok) {
196
+ const error = await response.json();
197
+ throw new Error(error.error?.message || 'Failed to read directory');
198
+ }
199
+
200
+ const { files } = await response.json();
201
+ return files;
202
+ }
203
+
204
+ /**
205
+ * Create directory
206
+ * @param {string} path - Directory path
207
+ * @param {Object} options - Options
208
+ * @returns {Promise<void>}
209
+ */
210
+ export async function mkdir(path, options = {}) {
211
+ const recursive = options.recursive !== undefined ? options.recursive : true;
212
+ const response = await fetch(`${API_BASE}/mkdir`, {
213
+ method: 'POST',
214
+ headers: { 'Content-Type': 'application/json' },
215
+ body: JSON.stringify({ path, recursive })
216
+ });
217
+
218
+ if (!response.ok) {
219
+ const error = await response.json();
220
+ throw new Error(error.error?.message || 'Failed to create directory');
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Remove directory
226
+ * @param {string} path - Directory path
227
+ * @param {Object} options - Options
228
+ * @returns {Promise<void>}
229
+ */
230
+ export async function rmdir(path, options = {}) {
231
+ const recursive = options.recursive || false;
232
+ const response = await fetch(`${API_BASE}/rmdir`, {
233
+ method: 'DELETE',
234
+ headers: { 'Content-Type': 'application/json' },
235
+ body: JSON.stringify({ path, recursive })
236
+ });
237
+
238
+ if (!response.ok) {
239
+ const error = await response.json();
240
+ throw new Error(error.error?.message || 'Failed to remove directory');
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Remove file or directory
246
+ * @param {string} path - Path to remove
247
+ * @param {Object} options - Options
248
+ * @returns {Promise<void>}
249
+ */
250
+ export async function rm(path, options = {}) {
251
+ const recursive = options.recursive || false;
252
+ const force = options.force || false;
253
+ const response = await fetch(`${API_BASE}/rm`, {
254
+ method: 'DELETE',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: JSON.stringify({ path, recursive, force })
257
+ });
258
+
259
+ if (!response.ok) {
260
+ const error = await response.json();
261
+ throw new Error(error.error?.message || 'Failed to remove');
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Rename or move file/directory
267
+ * @param {string} oldPath - Old path
268
+ * @param {string} newPath - New path
269
+ * @returns {Promise<void>}
270
+ */
271
+ export async function rename(oldPath, newPath) {
272
+ const response = await fetch(`${API_BASE}/rename`, {
273
+ method: 'PUT',
274
+ headers: { 'Content-Type': 'application/json' },
275
+ body: JSON.stringify({ oldPath, newPath })
276
+ });
277
+
278
+ if (!response.ok) {
279
+ const error = await response.json();
280
+ throw new Error(error.error?.message || 'Failed to rename');
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Delete file
286
+ * @param {string} path - File path
287
+ * @returns {Promise<void>}
288
+ */
289
+ export async function unlink(path) {
290
+ const response = await fetch(`${API_BASE}/unlink`, {
291
+ method: 'DELETE',
292
+ headers: { 'Content-Type': 'application/json' },
293
+ body: JSON.stringify({ path })
294
+ });
295
+
296
+ if (!response.ok) {
297
+ const error = await response.json();
298
+ throw new Error(error.error?.message || 'Failed to delete file');
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Get file/directory stats
304
+ * @param {string} path - Path
305
+ * @returns {Promise<Stats>}
306
+ */
307
+ export async function stat(path) {
308
+ const response = await fetch(`${API_BASE}/stat?path=${encodeURIComponent(path)}`);
309
+
310
+ if (!response.ok) {
311
+ const error = await response.json();
312
+ throw new Error(error.error?.message || 'Failed to get stats');
313
+ }
314
+
315
+ const { stats } = await response.json();
316
+ return stats;
317
+ }
318
+
319
+ /**
320
+ * Get file/directory stats (doesn't follow symlinks)
321
+ * @param {string} path - Path
322
+ * @returns {Promise<Stats>}
323
+ */
324
+ export async function lstat(path) {
325
+ const response = await fetch(`${API_BASE}/lstat?path=${encodeURIComponent(path)}`);
326
+
327
+ if (!response.ok) {
328
+ const error = await response.json();
329
+ throw new Error(error.error?.message || 'Failed to get stats');
330
+ }
331
+
332
+ const { stats } = await response.json();
333
+ return stats;
334
+ }
335
+
336
+ /**
337
+ * Get canonical path
338
+ * @param {string} path - Path
339
+ * @returns {Promise<string>}
340
+ */
341
+ export async function realpath(path) {
342
+ const response = await fetch(`${API_BASE}/realpath?path=${encodeURIComponent(path)}`);
343
+
344
+ if (!response.ok) {
345
+ const error = await response.json();
346
+ throw new Error(error.error?.message || 'Failed to get real path');
347
+ }
348
+
349
+ const { realPath } = await response.json();
350
+ return realPath;
351
+ }
352
+
353
+ /**
354
+ * Read symbolic link
355
+ * @param {string} path - Link path
356
+ * @returns {Promise<string>}
357
+ */
358
+ export async function readlink(path) {
359
+ const response = await fetch(`${API_BASE}/readlink?path=${encodeURIComponent(path)}`);
360
+
361
+ if (!response.ok) {
362
+ const error = await response.json();
363
+ throw new Error(error.error?.message || 'Failed to read link');
364
+ }
365
+
366
+ const { target } = await response.json();
367
+ return target;
368
+ }
369
+
370
+ /**
371
+ * Check if file exists (convenience method)
372
+ * @param {string} path - Path
373
+ * @returns {Promise<boolean>}
374
+ */
375
+ export async function exists(path) {
376
+ try {
377
+ await stat(path);
378
+ return true;
379
+ } catch {
380
+ return false;
381
+ }
382
+ }
383
+
384
+ // Default export with all methods
385
+ export default {
386
+ readFile,
387
+ writeFile,
388
+ appendFile,
389
+ copyFile,
390
+ readdir,
391
+ mkdir,
392
+ rmdir,
393
+ rm,
394
+ rename,
395
+ unlink,
396
+ stat,
397
+ lstat,
398
+ realpath,
399
+ readlink,
400
+ exists,
401
+ configure
402
+ };