@ooneex/fs 0.0.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ooneex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,601 @@
1
+ # @ooneex/fs
2
+
3
+ A comprehensive file system utilities library for TypeScript applications with async support and streaming capabilities. This package provides intuitive `File` and `Directory` classes for reading, writing, copying, and manipulating files and directories with proper error handling.
4
+
5
+ ![Bun](https://img.shields.io/badge/Bun-Compatible-orange?style=flat-square&logo=bun)
6
+ ![Deno](https://img.shields.io/badge/Deno-Compatible-blue?style=flat-square&logo=deno)
7
+ ![Node.js](https://img.shields.io/badge/Node.js-Compatible-green?style=flat-square&logo=node.js)
8
+ ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)
9
+ ![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
10
+
11
+ ## Features
12
+
13
+ ✅ **File Operations** - Read, write, append, copy, and delete files with ease
14
+
15
+ ✅ **Directory Management** - Create, list, copy, move, and watch directories
16
+
17
+ ✅ **Streaming Support** - Stream file contents as text, bytes, or JSON objects
18
+
19
+ ✅ **Async/Await** - Full async support for all I/O operations
20
+
21
+ ✅ **Type-Safe** - Full TypeScript support with proper type definitions
22
+
23
+ ✅ **Error Handling** - Custom exceptions with detailed error information
24
+
25
+ ✅ **URL Downloads** - Download files from URLs directly to disk
26
+
27
+ ✅ **Glob Patterns** - Filter files and directories using glob patterns
28
+
29
+ ✅ **File Watching** - Watch directories for changes with recursive support
30
+
31
+ ## Installation
32
+
33
+ ### Bun
34
+ ```bash
35
+ bun add @ooneex/fs
36
+ ```
37
+
38
+ ### pnpm
39
+ ```bash
40
+ pnpm add @ooneex/fs
41
+ ```
42
+
43
+ ### Yarn
44
+ ```bash
45
+ yarn add @ooneex/fs
46
+ ```
47
+
48
+ ### npm
49
+ ```bash
50
+ npm install @ooneex/fs
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ### File Operations
56
+
57
+ ```typescript
58
+ import { File } from '@ooneex/fs';
59
+
60
+ // Create a file instance
61
+ const file = new File('/path/to/document.txt');
62
+
63
+ // Get file information
64
+ console.log(file.getName()); // "document.txt"
65
+ console.log(file.getPath()); // "/path/to/document.txt"
66
+ console.log(file.getExtension()); // "txt"
67
+ console.log(file.getDirectory()); // "/path/to"
68
+
69
+ // Check if file exists
70
+ if (await file.exists()) {
71
+ console.log('File exists!');
72
+ }
73
+
74
+ // Read file contents
75
+ const text = await file.text();
76
+ const json = await file.json<{ name: string }>();
77
+ const bytes = await file.bytes();
78
+ const buffer = await file.arrayBuffer();
79
+
80
+ // Get file metadata
81
+ const size = file.getSize(); // Size in bytes
82
+ const type = file.getType(); // MIME type
83
+ ```
84
+
85
+ ### Writing Files
86
+
87
+ ```typescript
88
+ import { File } from '@ooneex/fs';
89
+
90
+ const file = new File('/path/to/output.txt');
91
+
92
+ // Write string content
93
+ await file.write('Hello, World!');
94
+
95
+ // Write JSON data
96
+ await file.write(JSON.stringify({ name: 'John', age: 30 }));
97
+
98
+ // Write binary data
99
+ await file.write(new Uint8Array([72, 101, 108, 108, 111]));
100
+
101
+ // Append to file
102
+ await file.append('\nAdditional content');
103
+ ```
104
+
105
+ ### Streaming File Contents
106
+
107
+ ```typescript
108
+ import { File } from '@ooneex/fs';
109
+
110
+ const file = new File('/path/to/large-file.txt');
111
+
112
+ // Stream as text chunks
113
+ for await (const chunk of file.streamAsText()) {
114
+ console.log(chunk);
115
+ }
116
+
117
+ // Stream as raw bytes
118
+ const byteStream = file.stream();
119
+
120
+ // Stream JSON objects (for JSONL or JSON array files)
121
+ for await (const obj of file.streamAsJson<{ id: number }>()) {
122
+ console.log(obj.id);
123
+ }
124
+ ```
125
+
126
+ ### File Writer
127
+
128
+ ```typescript
129
+ import { File } from '@ooneex/fs';
130
+
131
+ const file = new File('/path/to/output.txt');
132
+
133
+ // Get a file writer for efficient writing
134
+ const writer = file.writer({ highWaterMark: 1024 * 1024 });
135
+
136
+ writer.write('Line 1\n');
137
+ writer.write('Line 2\n');
138
+ writer.write('Line 3\n');
139
+
140
+ await writer.end();
141
+ ```
142
+
143
+ ### Copy and Delete Files
144
+
145
+ ```typescript
146
+ import { File } from '@ooneex/fs';
147
+
148
+ const source = new File('/path/to/source.txt');
149
+
150
+ // Copy to new location
151
+ await source.copy('/path/to/destination.txt');
152
+
153
+ // Delete the file
154
+ await source.delete();
155
+ ```
156
+
157
+ ### Download Files from URL
158
+
159
+ ```typescript
160
+ import { File } from '@ooneex/fs';
161
+
162
+ const file = new File('/path/to/downloaded-image.jpg');
163
+
164
+ // Download from URL
165
+ await file.download('https://example.com/image.jpg');
166
+ ```
167
+
168
+ ### Directory Operations
169
+
170
+ ```typescript
171
+ import { Directory } from '@ooneex/fs';
172
+
173
+ // Create a directory instance
174
+ const dir = new Directory('/path/to/folder');
175
+
176
+ // Get directory information
177
+ console.log(dir.getName()); // "folder"
178
+ console.log(dir.getPath()); // "/path/to/folder"
179
+ console.log(dir.getParent()); // "/path/to"
180
+
181
+ // Check if directory exists
182
+ if (await dir.exists()) {
183
+ console.log('Directory exists!');
184
+ }
185
+
186
+ // Create directory (with parents if needed)
187
+ await dir.mkdir({ recursive: true, mode: 0o755 });
188
+
189
+ // Check if empty
190
+ const isEmpty = await dir.isEmpty();
191
+ ```
192
+
193
+ ### Listing Directory Contents
194
+
195
+ ```typescript
196
+ import { Directory } from '@ooneex/fs';
197
+
198
+ const dir = new Directory('/path/to/folder');
199
+
200
+ // List all entries (files and directories)
201
+ const entries = await dir.ls();
202
+ console.log(entries); // ['file1.txt', 'file2.txt', 'subfolder']
203
+
204
+ // List with file types
205
+ const entriesWithTypes = await dir.lsWithTypes();
206
+ for (const entry of entriesWithTypes) {
207
+ console.log(entry.name, entry.isFile() ? 'file' : 'directory');
208
+ }
209
+
210
+ // List recursively
211
+ const allEntries = await dir.ls({ recursive: true });
212
+ ```
213
+
214
+ ### Getting Files and Directories
215
+
216
+ ```typescript
217
+ import { Directory } from '@ooneex/fs';
218
+
219
+ const dir = new Directory('/path/to/project');
220
+
221
+ // Get all files
222
+ const files = await dir.getFiles();
223
+ console.log(files); // ['src/index.ts', 'src/utils.ts', 'README.md']
224
+
225
+ // Get files with glob pattern
226
+ const tsFiles = await dir.getFiles({ pattern: '**/*.ts' });
227
+
228
+ // Get files recursively
229
+ const allFiles = await dir.getFiles({ recursive: true });
230
+
231
+ // Get all subdirectories
232
+ const directories = await dir.getDirectories();
233
+
234
+ // Get directories with pattern
235
+ const srcDirs = await dir.getDirectories({ pattern: 'src/*' });
236
+ ```
237
+
238
+ ### Copy and Move Directories
239
+
240
+ ```typescript
241
+ import { Directory } from '@ooneex/fs';
242
+
243
+ const source = new Directory('/path/to/source');
244
+
245
+ // Copy directory recursively
246
+ await source.cp('/path/to/destination', {
247
+ recursive: true,
248
+ force: true
249
+ });
250
+
251
+ // Move/rename directory
252
+ await source.mv('/path/to/new-location');
253
+ ```
254
+
255
+ ### Delete Directory
256
+
257
+ ```typescript
258
+ import { Directory } from '@ooneex/fs';
259
+
260
+ const dir = new Directory('/path/to/folder');
261
+
262
+ // Delete directory (must be empty by default)
263
+ await dir.rm();
264
+
265
+ // Delete recursively with force
266
+ await dir.rm({ recursive: true, force: true });
267
+ ```
268
+
269
+ ### Watch Directory for Changes
270
+
271
+ ```typescript
272
+ import { Directory } from '@ooneex/fs';
273
+
274
+ const dir = new Directory('/path/to/watch');
275
+
276
+ // Watch for file changes
277
+ const watcher = dir.watch({ recursive: true });
278
+
279
+ for await (const event of watcher) {
280
+ console.log(`${event.type}: ${event.filename}`);
281
+ }
282
+ ```
283
+
284
+ ### Navigate Directories
285
+
286
+ ```typescript
287
+ import { Directory } from '@ooneex/fs';
288
+
289
+ const root = new Directory('/path/to/project');
290
+
291
+ // Navigate to subdirectory
292
+ const src = root.cd('src');
293
+ const lib = src.cd('lib');
294
+
295
+ console.log(lib.getPath()); // "/path/to/project/src/lib"
296
+ ```
297
+
298
+ ### Get Directory Size
299
+
300
+ ```typescript
301
+ import { Directory } from '@ooneex/fs';
302
+
303
+ const dir = new Directory('/path/to/folder');
304
+
305
+ // Get total size of directory (recursive)
306
+ const size = await dir.getSize();
307
+ console.log(`Directory size: ${size} bytes`);
308
+ ```
309
+
310
+ ### Get Directory Stats
311
+
312
+ ```typescript
313
+ import { Directory } from '@ooneex/fs';
314
+
315
+ const dir = new Directory('/path/to/folder');
316
+
317
+ const stats = await dir.stat();
318
+ console.log('Created:', stats.birthtime);
319
+ console.log('Modified:', stats.mtime);
320
+ console.log('Is Directory:', stats.isDirectory());
321
+ ```
322
+
323
+ ## API Reference
324
+
325
+ ### Classes
326
+
327
+ #### `File`
328
+
329
+ Class for file operations.
330
+
331
+ **Constructor:**
332
+ ```typescript
333
+ new File(path: string | URL, options?: FileOptionsType)
334
+ ```
335
+
336
+ **Parameters:**
337
+ - `path` - File path as string or URL
338
+ - `options.type` - Custom MIME type (optional)
339
+
340
+ **Methods:**
341
+
342
+ | Method | Returns | Description |
343
+ |--------|---------|-------------|
344
+ | `getPath()` | `string` | Get the file path |
345
+ | `getName()` | `string` | Get the file name with extension |
346
+ | `getExtension()` | `string` | Get the file extension (without dot) |
347
+ | `getDirectory()` | `string` | Get the parent directory path |
348
+ | `getSize()` | `number` | Get file size in bytes |
349
+ | `getType()` | `string` | Get MIME type |
350
+ | `exists()` | `Promise<boolean>` | Check if file exists |
351
+ | `text()` | `Promise<string>` | Read file as text |
352
+ | `json<T>()` | `Promise<T>` | Read and parse file as JSON |
353
+ | `arrayBuffer()` | `Promise<ArrayBuffer>` | Read file as ArrayBuffer |
354
+ | `bytes()` | `Promise<Uint8Array>` | Read file as Uint8Array |
355
+ | `stream()` | `ReadableStream` | Get readable byte stream |
356
+ | `streamAsText()` | `AsyncGenerator<string>` | Stream file as text chunks |
357
+ | `streamAsJson<T>()` | `AsyncGenerator<T>` | Stream file as JSON objects |
358
+ | `write(data)` | `Promise<void>` | Write data to file |
359
+ | `append(data)` | `Promise<void>` | Append data to file |
360
+ | `copy(destination)` | `Promise<void>` | Copy file to destination |
361
+ | `delete()` | `Promise<void>` | Delete the file |
362
+ | `download(url)` | `Promise<void>` | Download from URL to file |
363
+ | `writer(options?)` | `FileSink` | Get a file writer |
364
+
365
+ ---
366
+
367
+ #### `Directory`
368
+
369
+ Class for directory operations.
370
+
371
+ **Constructor:**
372
+ ```typescript
373
+ new Directory(path: string | URL)
374
+ ```
375
+
376
+ **Methods:**
377
+
378
+ | Method | Returns | Description |
379
+ |--------|---------|-------------|
380
+ | `getPath()` | `string` | Get directory path |
381
+ | `getName()` | `string` | Get directory name |
382
+ | `getParent()` | `string` | Get parent directory path |
383
+ | `exists()` | `Promise<boolean>` | Check if directory exists |
384
+ | `mkdir(options?)` | `Promise<void>` | Create the directory |
385
+ | `rm(options?)` | `Promise<void>` | Remove the directory |
386
+ | `ls(options?)` | `Promise<string[]>` | List directory contents |
387
+ | `lsWithTypes(options?)` | `Promise<Dirent[]>` | List with file type info |
388
+ | `cp(destination, options?)` | `Promise<void>` | Copy directory |
389
+ | `mv(destination)` | `Promise<void>` | Move/rename directory |
390
+ | `stat()` | `Promise<Stats>` | Get directory stats |
391
+ | `watch(options?)` | `FSWatcher` | Watch for changes |
392
+ | `isEmpty()` | `Promise<boolean>` | Check if directory is empty |
393
+ | `getSize()` | `Promise<number>` | Get total size recursively |
394
+ | `getFiles(options?)` | `Promise<string[]>` | Get all files |
395
+ | `getDirectories(options?)` | `Promise<string[]>` | Get all subdirectories |
396
+ | `cd(subpath)` | `Directory` | Navigate to subdirectory |
397
+
398
+ ### Types
399
+
400
+ #### `FileOptionsType`
401
+
402
+ ```typescript
403
+ type FileOptionsType = {
404
+ type?: string; // MIME type
405
+ };
406
+ ```
407
+
408
+ #### `FileWriteDataType`
409
+
410
+ ```typescript
411
+ type FileWriteDataType =
412
+ | string
413
+ | Blob
414
+ | ArrayBufferLike
415
+ | ArrayBufferView
416
+ | BunFile
417
+ | Response
418
+ | ReadableStream;
419
+ ```
420
+
421
+ #### `FileWriterOptionsType`
422
+
423
+ ```typescript
424
+ type FileWriterOptionsType = {
425
+ highWaterMark?: number;
426
+ };
427
+ ```
428
+
429
+ #### `DirectoryCreateOptionsType`
430
+
431
+ ```typescript
432
+ type DirectoryCreateOptionsType = {
433
+ recursive?: boolean;
434
+ mode?: number;
435
+ };
436
+ ```
437
+
438
+ #### `DirectoryDeleteOptionsType`
439
+
440
+ ```typescript
441
+ type DirectoryDeleteOptionsType = {
442
+ recursive?: boolean;
443
+ force?: boolean;
444
+ };
445
+ ```
446
+
447
+ #### `DirectoryGetFilesOptionsType`
448
+
449
+ ```typescript
450
+ type DirectoryGetFilesOptionsType = {
451
+ recursive?: boolean;
452
+ pattern?: string; // Glob pattern
453
+ };
454
+ ```
455
+
456
+ ### Exceptions
457
+
458
+ #### `FileException`
459
+
460
+ Thrown when file operations fail.
461
+
462
+ ```typescript
463
+ import { File, FileException } from '@ooneex/fs';
464
+
465
+ try {
466
+ const file = new File('/nonexistent/file.txt');
467
+ await file.text();
468
+ } catch (error) {
469
+ if (error instanceof FileException) {
470
+ console.error('File error:', error.message);
471
+ console.error('Path:', error.data.path);
472
+ }
473
+ }
474
+ ```
475
+
476
+ #### `DirectoryException`
477
+
478
+ Thrown when directory operations fail.
479
+
480
+ ```typescript
481
+ import { Directory, DirectoryException } from '@ooneex/fs';
482
+
483
+ try {
484
+ const dir = new Directory('/nonexistent/folder');
485
+ await dir.ls();
486
+ } catch (error) {
487
+ if (error instanceof DirectoryException) {
488
+ console.error('Directory error:', error.message);
489
+ console.error('Path:', error.data.path);
490
+ }
491
+ }
492
+ ```
493
+
494
+ ## Advanced Usage
495
+
496
+ ### Processing Large Files
497
+
498
+ ```typescript
499
+ import { File } from '@ooneex/fs';
500
+
501
+ const file = new File('/path/to/large-file.jsonl');
502
+
503
+ // Process large JSON Lines file without loading into memory
504
+ let lineCount = 0;
505
+ for await (const record of file.streamAsJson<{ id: number; name: string }>()) {
506
+ lineCount++;
507
+ // Process each record individually
508
+ await processRecord(record);
509
+ }
510
+
511
+ console.log(`Processed ${lineCount} records`);
512
+ ```
513
+
514
+ ### Recursive File Processing
515
+
516
+ ```typescript
517
+ import { Directory, File } from '@ooneex/fs';
518
+
519
+ const projectDir = new Directory('/path/to/project');
520
+
521
+ // Find all TypeScript files and count lines
522
+ const tsFiles = await projectDir.getFiles({
523
+ recursive: true,
524
+ pattern: '**/*.ts'
525
+ });
526
+
527
+ let totalLines = 0;
528
+ for (const filePath of tsFiles) {
529
+ const file = new File(projectDir.getPath() + '/' + filePath);
530
+ const content = await file.text();
531
+ totalLines += content.split('\n').length;
532
+ }
533
+
534
+ console.log(`Total lines of TypeScript: ${totalLines}`);
535
+ ```
536
+
537
+ ### File Backup System
538
+
539
+ ```typescript
540
+ import { File, Directory } from '@ooneex/fs';
541
+
542
+ async function backupFiles(sourcePath: string, backupPath: string): Promise<void> {
543
+ const sourceDir = new Directory(sourcePath);
544
+ const backupDir = new Directory(backupPath);
545
+
546
+ // Create backup directory
547
+ await backupDir.mkdir({ recursive: true });
548
+
549
+ // Copy all files
550
+ const files = await sourceDir.getFiles({ recursive: true });
551
+
552
+ for (const filePath of files) {
553
+ const source = new File(`${sourcePath}/${filePath}`);
554
+ await source.copy(`${backupPath}/${filePath}`);
555
+ }
556
+ }
557
+ ```
558
+
559
+ ### Watch and Auto-Reload
560
+
561
+ ```typescript
562
+ import { Directory } from '@ooneex/fs';
563
+
564
+ const configDir = new Directory('./config');
565
+
566
+ // Watch for configuration changes
567
+ const watcher = configDir.watch({ recursive: true });
568
+
569
+ for await (const event of watcher) {
570
+ if (event.filename?.endsWith('.json')) {
571
+ console.log(`Config changed: ${event.filename}`);
572
+ await reloadConfiguration();
573
+ }
574
+ }
575
+ ```
576
+
577
+ ## License
578
+
579
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
580
+
581
+ ## Contributing
582
+
583
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
584
+
585
+ ### Development Setup
586
+
587
+ 1. Clone the repository
588
+ 2. Install dependencies: `bun install`
589
+ 3. Run tests: `bun run test`
590
+ 4. Build the project: `bun run build`
591
+
592
+ ### Guidelines
593
+
594
+ - Write tests for new features
595
+ - Follow the existing code style
596
+ - Update documentation for API changes
597
+ - Ensure all tests pass before submitting PR
598
+
599
+ ---
600
+
601
+ Made with ❤️ by the Ooneex team