feat: implement synchronous file system APIs with worker support & 100% test coverage
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:js_interop';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:web_file_system/src/backend/idb_inode_service.dart';
|
||||
import 'package:web_file_system/src/backend/opfs_block_store.dart';
|
||||
import 'package:web_file_system/src/backend/sync_rpc_helper.dart';
|
||||
import 'entities/web_directory.dart';
|
||||
import 'entities/web_file.dart';
|
||||
import 'entities/web_link.dart';
|
||||
@@ -60,9 +63,43 @@ class WebFileSystem extends FileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
static void registerWorkerProxy(dynamic worker, WebFileSystem fs) {
|
||||
SyncRpcHelper.registerWorkerProxy(worker as JSAny, fs as JSAny);
|
||||
}
|
||||
|
||||
Uint8List makeSyncCall(int cmd, Uint8List request) {
|
||||
if (!SyncRpcHelper.isWorker) {
|
||||
throw UnsupportedError('Synchronous operations are only supported inside Web Workers.');
|
||||
}
|
||||
if (!SyncRpcHelper.isSharedArrayBufferSupported) {
|
||||
throw StateError('SharedArrayBuffer is not supported. Ensure your site is cross-origin isolated with COOP/COEP headers.');
|
||||
}
|
||||
return SyncRpcHelper.sendSyncRequest(cmd, request);
|
||||
}
|
||||
|
||||
@override
|
||||
FileSystemEntityType typeSync(String path, {bool followLinks = true}) {
|
||||
throw UnsupportedError('Sync type not supported');
|
||||
if (!SyncRpcHelper.isWorker) {
|
||||
throw UnsupportedError('Synchronous operations are only supported inside Web Workers.');
|
||||
}
|
||||
if (!SyncRpcHelper.isSharedArrayBufferSupported) {
|
||||
throw StateError('SharedArrayBuffer is not supported. Ensure your site is cross-origin isolated with COOP/COEP headers.');
|
||||
}
|
||||
try {
|
||||
final pathBytes = utf8.encode(path);
|
||||
final payload = Uint8List(pathBytes.length + 1);
|
||||
payload[0] = followLinks ? 1 : 0;
|
||||
payload.setRange(1, payload.length, pathBytes);
|
||||
|
||||
final respBytes = makeSyncCall(2, payload);
|
||||
final typeStr = utf8.decode(respBytes);
|
||||
if (typeStr == 'file') return FileSystemEntityType.file;
|
||||
if (typeStr == 'directory') return FileSystemEntityType.directory;
|
||||
if (typeStr == 'link') return FileSystemEntityType.link;
|
||||
return FileSystemEntityType.notFound;
|
||||
} catch (_) {
|
||||
return FileSystemEntityType.notFound;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal Resolution Logic
|
||||
@@ -176,7 +213,34 @@ class WebFileSystem extends FileSystem {
|
||||
|
||||
@override
|
||||
FileStat statSync(String path) {
|
||||
throw UnsupportedError('Sync stat not supported');
|
||||
if (!SyncRpcHelper.isWorker) {
|
||||
throw UnsupportedError('Synchronous operations are only supported inside Web Workers.');
|
||||
}
|
||||
if (!SyncRpcHelper.isSharedArrayBufferSupported) {
|
||||
throw StateError('SharedArrayBuffer is not supported. Ensure your site is cross-origin isolated with COOP/COEP headers.');
|
||||
}
|
||||
try {
|
||||
final respBytes = makeSyncCall(9, utf8.encode(path));
|
||||
final statMap = json.decode(utf8.decode(respBytes)) as Map<String, dynamic>;
|
||||
final typeStr = statMap['type'] as String;
|
||||
final size = statMap['size'] as int;
|
||||
final modified = statMap['modified'] as int;
|
||||
|
||||
FileSystemEntityType type;
|
||||
if (typeStr == 'file') {
|
||||
type = FileSystemEntityType.file;
|
||||
} else if (typeStr == 'directory') {
|
||||
type = FileSystemEntityType.directory;
|
||||
} else if (typeStr == 'link') {
|
||||
type = FileSystemEntityType.link;
|
||||
} else {
|
||||
type = FileSystemEntityType.notFound;
|
||||
}
|
||||
|
||||
return FileStatImpl(modified, size, type);
|
||||
} catch (_) {
|
||||
return FileStatImpl(0, 0, FileSystemEntityType.notFound);
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemEntityType _getType(int nodeType) {
|
||||
@@ -188,15 +252,15 @@ class WebFileSystem extends FileSystem {
|
||||
|
||||
@override
|
||||
bool isFileSync(String path) =>
|
||||
throw UnsupportedError('Sync isFile not supported');
|
||||
typeSync(path) == FileSystemEntityType.file;
|
||||
|
||||
@override
|
||||
bool isDirectorySync(String path) =>
|
||||
throw UnsupportedError('Sync isDirectory not supported');
|
||||
typeSync(path) == FileSystemEntityType.directory;
|
||||
|
||||
@override
|
||||
bool isLinkSync(String path) =>
|
||||
throw UnsupportedError('Sync isLink not supported');
|
||||
typeSync(path, followLinks: false) == FileSystemEntityType.link;
|
||||
|
||||
@override
|
||||
Future<bool> isFile(String path) async =>
|
||||
@@ -226,8 +290,15 @@ class WebFileSystem extends FileSystem {
|
||||
}
|
||||
|
||||
@override
|
||||
bool identicalSync(String path1, String path2) =>
|
||||
throw UnsupportedError('Sync not supported');
|
||||
bool identicalSync(String path1, String path2) {
|
||||
try {
|
||||
final r1 = resolveSymbolicLinksSync(path1);
|
||||
final r2 = resolveSymbolicLinksSync(path2);
|
||||
return r1 == r2;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> resolveSymbolicLinks(String pathStr) async {
|
||||
final inode = await resolvepath(pathStr, followLinks: true);
|
||||
@@ -240,6 +311,11 @@ class WebFileSystem extends FileSystem {
|
||||
if (segments.isEmpty) return '/';
|
||||
return '/' + segments.reversed.join('/');
|
||||
}
|
||||
|
||||
String resolveSymbolicLinksSync(String pathStr) {
|
||||
final respBytes = makeSyncCall(11, utf8.encode(pathStr));
|
||||
return utf8.decode(respBytes);
|
||||
}
|
||||
}
|
||||
|
||||
class FileStatImpl implements FileStat {
|
||||
|
||||
Reference in New Issue
Block a user