Files
smartfs/readme.md

446 lines
9.9 KiB
Markdown
Raw Normal View History

2025-11-21 18:36:31 +00:00
# @push.rocks/smartfs
Modern, pluggable filesystem module with fluent API, Web Streams support, and multiple storage backends.
## Features
- **🎯 Fluent API** - Action-last chainable interface for elegant code
- **🔌 Pluggable Providers** - Support for multiple storage backends (Node.js fs, memory, S3, etc.)
- **🌊 Web Streams** - Modern streaming with Web Streams API
- **💾 Transactions** - Atomic multi-file operations with automatic rollback
- **👀 File Watching** - Event-based file system monitoring
- **⚡ Async-Only** - Modern async/await patterns throughout
- **📦 Zero Dependencies** - Core functionality with minimal dependencies
- **🎨 TypeScript** - Full type safety and IntelliSense support
## Installation
```bash
pnpm install @push.rocks/smartfs
```
## Quick Start
```typescript
import { SmartFs, SmartFsProviderNode } from '@push.rocks/smartfs';
// Create a SmartFS instance with Node.js provider
const fs = new SmartFs(new SmartFsProviderNode());
// Write and read files with fluent API
await fs.file('/path/to/file.txt')
.encoding('utf8')
.write('Hello, World!');
const content = await fs.file('/path/to/file.txt')
.encoding('utf8')
.read();
console.log(content); // "Hello, World!"
```
## API Overview
### File Operations
The fluent API uses **action-last pattern** - configure first, then execute:
```typescript
// Read file
const content = await fs.file('/path/to/file.txt')
.encoding('utf8')
.read();
// Write file
await fs.file('/path/to/file.txt')
.encoding('utf8')
.mode(0o644)
.write('content');
// Atomic write (write to temp, then rename)
await fs.file('/path/to/file.txt')
.atomic()
.write('content');
// Append to file
await fs.file('/path/to/file.txt')
.encoding('utf8')
.append('more content');
// Copy file
await fs.file('/source.txt')
.preserveTimestamps()
.copy('/destination.txt');
// Move file
await fs.file('/old.txt')
.move('/new.txt');
// Delete file
await fs.file('/path/to/file.txt')
.delete();
// Check existence
const exists = await fs.file('/path/to/file.txt').exists();
// Get stats
const stats = await fs.file('/path/to/file.txt').stat();
```
### Directory Operations
```typescript
// Create directory
await fs.directory('/path/to/dir').create();
// Create nested directories
await fs.directory('/path/to/nested/dir')
.recursive()
.create();
// List directory
const entries = await fs.directory('/path/to/dir').list();
// List recursively with filter
const tsFiles = await fs.directory('/path/to/dir')
.recursive()
.filter('*.ts')
.includeStats()
.list();
// Filter with RegExp
const files = await fs.directory('/path/to/dir')
.filter(/\.txt$/)
.list();
// Filter with function
const largeFiles = await fs.directory('/path/to/dir')
.includeStats()
.filter(entry => entry.stats && entry.stats.size > 1024)
.list();
// Delete directory
await fs.directory('/path/to/dir')
.recursive()
.delete();
// Check existence
const exists = await fs.directory('/path/to/dir').exists();
```
### Streaming Operations
SmartFS uses **Web Streams API** for efficient handling of large files:
```typescript
// Read stream
const readStream = await fs.file('/large-file.bin')
.chunkSize(64 * 1024)
.readStream();
const reader = readStream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Process chunk (Uint8Array)
console.log('Chunk size:', value.length);
}
// Write stream
const writeStream = await fs.file('/output.bin').writeStream();
const writer = writeStream.getWriter();
await writer.write(new Uint8Array([1, 2, 3]));
await writer.write(new Uint8Array([4, 5, 6]));
await writer.close();
// Pipe streams
const input = await fs.file('/input.txt').readStream();
const output = await fs.file('/output.txt').writeStream();
await input.pipeTo(output);
```
### Transactions
Execute multiple file operations atomically with automatic rollback on failure:
```typescript
// Simple transaction
await fs.transaction()
.file('/file1.txt').write('content 1')
.file('/file2.txt').write('content 2')
.file('/file3.txt').delete()
.commit();
// Transaction with error handling
const tx = fs.transaction()
.file('/important.txt').write('critical data')
.file('/backup.txt').copy('/backup-old.txt')
.file('/temp.txt').delete();
try {
await tx.commit();
console.log('Transaction completed successfully');
} catch (error) {
console.error('Transaction failed and was rolled back:', error);
// All operations are automatically reverted
}
```
### File Watching
Monitor filesystem changes with event-based watching:
```typescript
// Watch a single file
const watcher = await fs.watch('/path/to/file.txt')
.onChange(event => {
console.log('File changed:', event.path);
})
.start();
// Watch directory recursively
const dirWatcher = await fs.watch('/path/to/dir')
.recursive()
.filter('*.ts')
.debounce(100)
.onChange(event => console.log('Changed:', event.path))
.onAdd(event => console.log('Added:', event.path))
.onDelete(event => console.log('Deleted:', event.path))
.start();
// Stop watching
await dirWatcher.stop();
// Watch with custom filter
const customWatcher = await fs.watch('/path/to/dir')
.recursive()
.filter(path => path.endsWith('.ts') && !path.includes('test'))
.onAll(event => {
console.log(`${event.type}: ${event.path}`);
})
.start();
```
## Providers
SmartFS supports multiple storage backends through providers:
### Node.js Provider
Uses Node.js `fs/promises` API for local filesystem operations:
```typescript
import { SmartFs, SmartFsProviderNode } from '@push.rocks/smartfs';
const fs = new SmartFs(new SmartFsProviderNode());
```
**Capabilities:**
- ✅ File watching
- ✅ Atomic writes
- ✅ Transactions
- ✅ Streaming
- ✅ Symbolic links
- ✅ File permissions
### Memory Provider
In-memory virtual filesystem, perfect for testing:
```typescript
import { SmartFs, SmartFsProviderMemory } from '@push.rocks/smartfs';
const fs = new SmartFs(new SmartFsProviderMemory());
// All operations work in memory
await fs.file('/virtual/file.txt').write('data');
const content = await fs.file('/virtual/file.txt').read();
// Clear all data
fs.provider.clear();
```
**Capabilities:**
- ✅ File watching
- ✅ Atomic writes
- ✅ Transactions
- ✅ Streaming
- ❌ Symbolic links
- ✅ File permissions
### Custom Providers
Create your own provider by implementing `ISmartFsProvider`:
```typescript
import type { ISmartFsProvider } from '@push.rocks/smartfs';
class MyCustomProvider implements ISmartFsProvider {
public readonly name = 'custom';
public readonly capabilities = {
supportsWatch: true,
supportsAtomic: true,
supportsTransactions: true,
supportsStreaming: true,
supportsSymlinks: false,
supportsPermissions: true,
};
// Implement all required methods...
async readFile(path: string, options?) { /* ... */ }
async writeFile(path: string, content, options?) { /* ... */ }
// ... etc
}
const fs = new SmartFs(new MyCustomProvider());
```
## Advanced Usage
### Encoding Options
```typescript
// UTF-8 (default for text)
await fs.file('/file.txt').encoding('utf8').write('text');
// Binary
const buffer = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
await fs.file('/file.bin').write(buffer);
// Base64
await fs.file('/file.txt').encoding('base64').write('SGVsbG8=');
// Hex
await fs.file('/file.txt').encoding('hex').write('48656c6c6f');
```
### File Permissions
```typescript
// Set file mode
await fs.file('/script.sh')
.mode(0o755)
.write('#!/bin/bash\necho "Hello"');
// Set directory mode
await fs.directory('/private')
.mode(0o700)
.create();
```
### Complex Filtering
```typescript
// Multiple conditions
const files = await fs.directory('/src')
.recursive()
.includeStats()
.filter(entry => {
if (!entry.stats) return false;
return entry.isFile &&
entry.name.endsWith('.ts') &&
entry.stats.size > 1024 &&
entry.stats.mtime > new Date('2024-01-01');
})
.list();
```
### Transaction Operations
```typescript
// Complex transaction
const tx = fs.transaction();
// Write multiple files
tx.file('/data/file1.json').write(JSON.stringify(data1));
tx.file('/data/file2.json').write(JSON.stringify(data2));
// Copy backups
tx.file('/data/file1.json').copy('/backup/file1.json');
tx.file('/data/file2.json').copy('/backup/file2.json');
// Delete old files
tx.file('/data/old1.json').delete();
tx.file('/data/old2.json').delete();
// Execute atomically
await tx.commit();
```
## Type Definitions
SmartFS is fully typed with TypeScript:
```typescript
import type {
IFileStats,
IDirectoryEntry,
IWatchEvent,
ITransactionOperation,
TEncoding,
TFileMode,
} from '@push.rocks/smartfs';
```
## Testing
```bash
# Run all tests
pnpm test
# Run specific test
pnpm tstest test/test.memory.provider.ts --verbose
# Run with log output
pnpm tstest test/test.node.provider.ts --logfile .nogit/testlogs/test.log
```
## Error Handling
SmartFS throws descriptive errors:
```typescript
try {
await fs.file('/nonexistent.txt').read();
} catch (error) {
console.error(error.message);
// "ENOENT: no such file or directory, open '/nonexistent.txt'"
}
// Transactions automatically rollback on error
try {
await fs.transaction()
.file('/file1.txt').write('data')
.file('/file2.txt').write('data')
.commit();
} catch (error) {
// All operations are reverted
console.error('Transaction failed:', error);
}
```
## Performance Tips
1. **Use streaming** for large files (> 1MB)
2. **Batch operations** with transactions
3. **Use memory provider** for testing
4. **Enable atomic writes** for critical data
5. **Debounce watchers** to reduce event spam
## Contributing
Contributions welcome! Please ensure:
- All tests pass
- Code follows existing style
- TypeScript types are complete
- Documentation is updated
## License
MIT © [Lossless GmbH](https://lossless.gmbh)
---
For more information, visit [code.foss.global](https://code.foss.global/push.rocks/smartfs)