filesystemv 0.1.0
filesystemv: ^0.1.0 copied to clipboard
Virtualized filesystem writes using IOOverrides.
filesystemv #
filesystemv provides a write-overlay virtual filesystem for Dart. Reads come
from the host filesystem, while writes are redirected into a virtual store.
This is useful for tests and dry-run workflows where code should behave as if it writes to disk, without mutating the real machine state.
Sponsored by OnePub #
Help support FileSystemV by supporting OnePub, the private dart repository. OnePub allows you to privately share dart packages between your own projects or with colleagues.
API #
Future<String> withVFileSystem(
FutureOr<void> Function(String virtualRoot) action, {
List<String>? paths,
String? store,
})
virtualRoot: path to the overlay store directory.paths: optional list of paths to virtualize. If omitted, all paths are virtualized.store: optional storage directory for overlay data. If omitted, a temp directory is created and cleaned up automatically.- Return value: the overlay store path.
Installation #
dependencies:
filesystemv: ^0.1.0
Examples #
1. Virtualize all writes #
import 'dart:io';
import 'package:filesystemv/filesystemv.dart';
Future<void> main() async {
await withVFileSystem((virtualRoot) {
final hosts = File('/etc/hosts');
hosts.writeAsStringSync('127.0.0.1 local-only\n');
print('Overlay root: $virtualRoot');
print('Virtual hosts: ${hosts.readAsStringSync()}');
});
}
2. Virtualize only selected paths #
import 'dart:io';
import 'package:filesystemv/filesystemv.dart';
Future<void> main() async {
final cachePath = '${Platform.environment['HOME']}/.pub-cache';
await withVFileSystem((_) {
File('$cachePath/notes.txt').writeAsStringSync('virtual cache write\n');
File('/tmp/real-file.txt').writeAsStringSync('real write\n');
}, paths: [cachePath]);
}
3. Keep the overlay store for post-run inspection #
import 'dart:io';
import 'package:filesystemv/filesystemv.dart';
Future<void> main() async {
final store = Directory.systemTemp.createTempSync('filesystemv-store-');
final root = await withVFileSystem((_) {
File('/tmp/report.txt').writeAsStringSync('overlay report');
}, store: store.path);
print('Overlay store retained at: $root');
}
4. Spawn a process using the virtual store as CWD #
import 'dart:io';
import 'package:filesystemv/filesystemv.dart';
Future<void> main() async {
await withVFileSystem((virtualRoot) async {
'process.txt'.write('hello world');
// bash will only see files in the virtualRoot that have been modified
await Process.run(
'bash',
['-lc', 'pwd && echo "hello again" >> process.txt && cat process.txt'],
workingDirectory: virtualRoot,
);
});
}
5. Use with path and dcli #
import 'dart:io';
import 'package:dcli/dcli.dart';
import 'package:filesystemv/filesystemv.dart';
import 'package:path/path.dart' as p;
Future<void> main() async {
final root = Directory.systemTemp.createTempSync('fsv-demo-');
final pathToFile = p.join(root.path, 'demo.txt');
await withVFileSystem((_) {
pathToFile.write('line-1', newline: '');
pathToFile.append('line-2', newline: '');
print(File(pathToFile).readAsStringSync()); // line-1line-2
});
print(File(pathToFile).existsSync()); // false
}
6. Link operations #
import 'dart:io';
import 'package:filesystemv/filesystemv.dart';
Future<void> main() async {
final target = File('/tmp/target.txt')..writeAsStringSync('host');
final link = Link('/tmp/target-link')..createSync(target.path);
await withVFileSystem((_) {
Link(link.path).updateSync('/tmp/another-target.txt');
}, paths: [link.path]);
// host link remains unchanged
print(Link(link.path).targetSync() == target.path);
}
Additional Use Cases #
- Protecting host state in integration tests that call third-party libraries.
- Running migration tools in dry-run mode while preserving real data.
- Safe experimentation with config rewrites in CI smoke tests.
- Testing symlink and path handling logic without mutating real links.
- Capturing generated artifacts from tools in a temporary isolated workspace.
Example App #
A comprehensive runnable example is available at:
example/filesystemv_example.dart
Run it with:
dart run example/filesystemv_example.dart
Benchmark Harness #
Run the local micro-benchmark harness:
dart run tool/benchmark_filesystemv.dart
Each run writes a JSON result to tool/benchmark_results/ and prints a
comparison against the previous 3 runs.
Optional flags:
dart run tool/benchmark_filesystemv.dart --entries=50000 --reads=100000 --writes=25000
Current Holes and Limitations #
- Dart
File/Directory/Linkobjects created before enteringwithVFileSystemare not retroactively virtualized. Create new handles inside the zone. - Spawned native processes bypass Dart
IOOverridesunless they are directed to the overlay path explicitly (for example by usingvirtualRootas CWD and relative paths). - Directory materialization currently copies recursively (directories not files ) when needed, which may be expensive for large trees .
- Path mapping for unusual platform-specific forms (for example complex Windows UNC variations) is not fully hardened yet.