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.
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.
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.
dependencies:
filesystemv: ^0.1.0import '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()}');
});
}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]);
}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');
}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,
);
});
}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
}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);
}- 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.
A comprehensive runnable example is available at:
example/filesystemv_example.dart
Run it with:
dart run example/filesystemv_example.dartRun the local micro-benchmark harness:
dart run tool/benchmark_filesystemv.dartEach 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- 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.