Skip to content
File

Blob: src/workerd/api/filesystem.h

cpp637 lines
1#pragma once
2 
3#include <workerd/api/streams/writable.h>
4#include <workerd/io/worker-fs.h>
5#include <workerd/jsg/iterator.h>
6#include <workerd/jsg/jsg.h>
7 
8namespace workerd::api {
9 
10class Blob;
11class File;
12 
13class URL; // Legacy URL class impl
14namespace url {
15class URL;
16} // namespace url
17 
18// Metadata about a file, directory, or link.
19struct Stat {
20 kj::StringPtr type; // One of either "file", "directory", or "symlink"
21 uint32_t size;
22 uint64_t lastModified;
23 uint64_t created;
24 bool writable;
25 bool device;
26 JSG_STRUCT(type, size, lastModified, created, writable, device);
27 
28 Stat(const workerd::Stat& stat);
29};
30 
31// A simple RAII handle for a file descriptor. When the instance is
32// destroyed, the file descriptor is closed if it hasn't already
33// been closed.
34class FileFdHandle final: public jsg::Object {
35 public:
36 FileFdHandle(jsg::Lock& js, const workerd::VirtualFileSystem& vfs, int fd);
37 ~FileFdHandle() noexcept;
38 
39 static jsg::Ref<FileFdHandle> constructor(jsg::Lock& js, int fd);
40 
41 void close(jsg::Lock&);
42 
43 JSG_RESOURCE_TYPE(FileFdHandle) {
44 JSG_METHOD(close);
45 }
46 
47 private:
48 kj::Maybe<kj::Own<void>> fdHandle;
49 v8::Isolate* isolate;
50};
51 
52class FileSystemModule final: public jsg::Object {
53 public:
54 using FilePath = kj::OneOf<jsg::Ref<URL>, jsg::Ref<url::URL>>;
55 
56 struct StatOptions {
57 jsg::Optional<bool> followSymlinks;
58 JSG_STRUCT(followSymlinks);
59 };
60 
61 kj::Maybe<Stat> stat(jsg::Lock& js, kj::OneOf<int, FilePath> pathOrFd, StatOptions options);
62 void setLastModified(
63 jsg::Lock& js, kj::OneOf<int, FilePath> pathOrFd, kj::Date lastModified, StatOptions options);
64 void truncate(jsg::Lock& js, kj::OneOf<int, FilePath> pathOrFd, uint32_t size);
65 
66 struct ReadLinkOptions {
67 // The readLink method is used for both realpath and readlink. The readlink
68 // API will throw an error if the path is not a symlink. The realpath API
69 // will return the resolved path either way.
70 bool failIfNotSymlink = false;
71 JSG_STRUCT(failIfNotSymlink);
72 };
73 kj::String readLink(jsg::Lock& js, FilePath path, ReadLinkOptions options);
74 
75 struct LinkOptions {
76 bool symbolic;
77 JSG_STRUCT(symbolic);
78 };
79 
80 void link(jsg::Lock& js, FilePath from, FilePath to, LinkOptions options);
81 
82 void unlink(jsg::Lock& js, FilePath path);
83 
84 struct OpenOptions {
85 // File is opened to supprt reads.
86 bool read;
87 // File is opened to support writes.
88 bool write;
89 // File is opened in append mode. Ignored if write is false.
90 bool append;
91 // If the exclusive option is set, throw if the file already exists.
92 bool exclusive;
93 // If the followSymlinks option is set, follow symbolic links.
94 bool followSymlinks = true;
95 JSG_STRUCT(read, write, append, exclusive, followSymlinks);
96 };
97 
98 int open(jsg::Lock& js, FilePath path, OpenOptions options);
99 void close(jsg::Lock& js, int fd);
100 
101 struct WriteOptions {
102 kj::Maybe<uint64_t> position;
103 JSG_STRUCT(position);
104 };
105 
106 uint32_t write(jsg::Lock& js, int fd, kj::Array<jsg::BufferSource> data, WriteOptions options);
107 uint32_t read(jsg::Lock& js, int fd, kj::Array<jsg::BufferSource> data, WriteOptions options);
108 
109 jsg::BufferSource readAll(jsg::Lock& js, kj::OneOf<int, FilePath> pathOrFd);
110 
111 struct WriteAllOptions {
112 bool exclusive;
113 bool append;
114 JSG_STRUCT(exclusive, append);
115 };
116 
117 uint32_t writeAll(jsg::Lock& js,
118 kj::OneOf<int, FilePath> pathOrFd,
119 jsg::BufferSource data,
120 WriteAllOptions options);
121 
122 struct RenameOrCopyOptions {
123 bool copy;
124 JSG_STRUCT(copy);
125 };
126 
127 void renameOrCopy(jsg::Lock& js, FilePath src, FilePath dest, RenameOrCopyOptions options);
128 
129 struct MkdirOptions {
130 bool recursive = false;
131 // When temp is true, the directory name will be augmented with an
132 // additional random string to make it unique and the directory name
133 // will be returned.
134 bool tmp = false;
135 JSG_STRUCT(recursive, tmp);
136 };
137 jsg::Optional<kj::String> mkdir(jsg::Lock& js, FilePath path, MkdirOptions options);
138 
139 struct RmOptions {
140 bool recursive = false;
141 bool force = false;
142 bool dironly = false;
143 JSG_STRUCT(recursive, force, dironly);
144 };
145 void rm(jsg::Lock& js, FilePath path, RmOptions options);
146 
147 struct DirEntHandle {
148 kj::String name;
149 kj::String parentPath;
150 int type;
151 JSG_STRUCT(name, parentPath, type);
152 };
153 struct ReadDirOptions {
154 bool recursive = false;
155 JSG_STRUCT(recursive);
156 };
157 kj::Array<DirEntHandle> readdir(jsg::Lock& js, FilePath path, ReadDirOptions options);
158 
159 FileSystemModule() = default;
160 FileSystemModule(jsg::Lock&, const jsg::Url&) {}
161 
162 jsg::Ref<FileFdHandle> getFdHandle(jsg::Lock& js, int fd) {
163 return FileFdHandle::constructor(js, fd);
164 }
165 
166 struct CpOptions {
167 bool deferenceSymlinks;
168 bool recursive;
169 bool force;
170 bool errorOnExist;
171 JSG_STRUCT(deferenceSymlinks, recursive, force, errorOnExist);
172 };
173 
174 void cp(jsg::Lock& js, FilePath src, FilePath dest, CpOptions options);
175 
176 struct OpenAsBlobOptions {
177 jsg::Optional<kj::String> type;
178 JSG_STRUCT(type);
179 };
180 jsg::Ref<Blob> openAsBlob(jsg::Lock& js, FilePath path, OpenAsBlobOptions options);
181 
182 JSG_RESOURCE_TYPE(FileSystemModule) {
183 JSG_METHOD(stat);
184 JSG_METHOD(setLastModified);
185 JSG_METHOD(truncate);
186 JSG_METHOD(readLink);
187 JSG_METHOD(link);
188 JSG_METHOD(unlink);
189 JSG_METHOD(open);
190 JSG_METHOD(close);
191 JSG_METHOD(write);
192 JSG_METHOD(read);
193 JSG_METHOD(readAll);
194 JSG_METHOD(writeAll);
195 JSG_METHOD(renameOrCopy);
196 JSG_METHOD(mkdir);
197 JSG_METHOD(rm);
198 JSG_METHOD(readdir);
199 JSG_METHOD(getFdHandle);
200 JSG_METHOD(cp);
201 JSG_METHOD(openAsBlob);
202 }
203 
204 private:
205 // Rather than relying on random generation of temp directory names we
206 // use a simple counter. Once the counter reaches the max value we will
207 // refuse to create any more temp directories. This is a bit of a hack
208 // to allow temp dirs to be created outside of an IoContext.
209 uint32_t tmpFileCounter = 0;
210};
211 
212// ======================================================================================
213// An implementation of the WHATWG Web File System API (https://fs.spec.whatwg.org/)
214// All of the classes in this part of the impl are defined by the spec.
215 
216class FileSystemWritableFileStream;
217 
218// This provides access to a file or directory in the virutual file system via a "standardized"
219// API. We use quotes around standardized because the API is still very much a work in progress
220// as a Web standard and is not fully baked yet, at least not within a single specification
221// document. There are bits and pieces of the API defined in various places, including the WHATWG
222// spec, WICG proposals, Web Platform Tests, etc. The Web Platform Tests include tests for parts of
223// the API that are not yet fully defined with the tests not actually marked as tentative, etc. We
224// implement what is defined so far using the Web Platform Tests and the specific characteristics
225// of the underlying virtual file system in workers as a guide.
226//
227// There are two kinds of handles, FileSystemFileHandle and FileSystemDirectoryHandle. Both of these
228// extend from FileSystemHandle. A FileSystemHandle holds the locator, which in our implementation
229// is a file URL pointing to a location in the worker's virtual file system. When the
230// FileSystemHandle is created, we validate that the locator points to a valid location in the
231// VFS, however the file may be modified, deleted, etc after the handle is created. This means
232// that the handle may become invalid after creation but then may become valid again. The actual
233// underlying file or directory is checked each time an operation is performed on the handle.
234//
235// Two handles are considered the same if they point to the same location and have the same type.
236class FileSystemHandle: public jsg::Object {
237 // TODO(node-fs): The spec defines FileSystemHandle objects as being
238 // serializable, meaning that they should work with structured cloning.
239 // In workers, this would suggest also that they should work with jsrpc.
240 // We don't yet implement any form of serialization for these objects.
241 // Since each worker has its own file system, it might be a bit weird
242 // as the received file on the other side would not actually be in that
243 // destination worker's file system... that is, it would be an orphaned
244 // file handle. It would still likely be useful tho. As a follow up
245 // step, we will implement serialization/deserialization of these objects.
246 public:
247 FileSystemHandle(const workerd::VirtualFileSystem& vfs, jsg::Url&& locator, jsg::USVString name);
248 
249 const jsg::USVString& getName(jsg::Lock& js) {
250 return name;
251 }
252 
253 virtual kj::StringPtr getKind(jsg::Lock& js) {
254 // Implemented by subclasses.
255 KJ_UNIMPLEMENTED("getKind() not implemented");
256 }
257 
258 jsg::Promise<bool> isSameEntry(jsg::Lock& js, jsg::Ref<FileSystemHandle> other);
259 
260 struct RemoveOptions {
261 jsg::Optional<bool> recursive;
262 JSG_STRUCT(recursive);
263 };
264 jsg::Promise<void> remove(jsg::Lock& js,
265 jsg::Optional<RemoveOptions> options,
266 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler);
267 
268 jsg::Promise<kj::StringPtr> getUniqueId(
269 jsg::Lock& js, const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler);
270 
271 JSG_RESOURCE_TYPE(FileSystemHandle) {
272 JSG_READONLY_PROTOTYPE_PROPERTY(kind, getKind);
273 JSG_READONLY_PROTOTYPE_PROPERTY(name, getName);
274 JSG_METHOD(isSameEntry);
275 JSG_METHOD(getUniqueId);
276 JSG_METHOD(remove);
277 }
278 
279 protected:
280 const jsg::Url& getLocator() const {
281 return locator;
282 }
283 const workerd::VirtualFileSystem& getVfs() const {
284 return vfs;
285 }
286 
287 bool canBeModifiedCurrently(jsg::Lock&) const;
288 
289 private:
290 const workerd::VirtualFileSystem& vfs;
291 const jsg::Url locator;
292 jsg::USVString name;
293};
294 
295struct FileSystemFileWriteParams {
296 kj::String type; // one of: write, seek, truncate
297 jsg::Optional<uint32_t> size;
298 jsg::Optional<uint32_t> position;
299 // Yes, wrapping the kj::Maybe with a jsg::Optional is intentional here. We need to
300 // be able to accept null or undefined values and handle them per the spec.
301 jsg::Optional<kj::Maybe<kj::OneOf<jsg::Ref<Blob>, jsg::BufferSource, kj::String>>> data;
302 JSG_STRUCT(type, size, position, data);
303};
304 
305using FileSystemWritableData =
306 kj::OneOf<jsg::Ref<Blob>, jsg::BufferSource, kj::String, FileSystemFileWriteParams>;
307 
308class FileSystemFileHandle final: public FileSystemHandle {
309 public:
310 FileSystemFileHandle(
311 const workerd::VirtualFileSystem& vfs, jsg::Url locator, jsg::USVString name);
312 
313 kj::StringPtr getKind(jsg::Lock& js) override {
314 return "file"_kj;
315 }
316 
317 struct FileSystemCreateWritableOptions {
318 jsg::Optional<bool> keepExistingData;
319 JSG_STRUCT(keepExistingData);
320 };
321 
322 jsg::Promise<jsg::Ref<File>> getFile(
323 jsg::Lock& js, const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler);
324 
325 jsg::Promise<jsg::Ref<FileSystemWritableFileStream>> createWritable(jsg::Lock& js,
326 jsg::Optional<FileSystemCreateWritableOptions> options,
327 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler,
328 const jsg::TypeHandler<FileSystemWritableData>& dataHandler);
329 
330 JSG_RESOURCE_TYPE(FileSystemFileHandle) {
331 JSG_INHERIT(FileSystemHandle);
332 JSG_METHOD(getFile);
333 JSG_METHOD(createWritable);
334 }
335 
336 private:
337 mutable size_t writableCount = 0;
338 friend class FileSystemWritableFileStream;
339};
340 
341class FileSystemDirectoryHandle final: public FileSystemHandle {
342 private:
343 // The actual entry type is an array of two elements, the first being the key,
344 // the second being the value.
345 using EntryType = kj::OneOf<jsg::JsRef<jsg::JsString>, jsg::Ref<FileSystemHandle>>;
346 using EntryIteratorType = kj::Array<EntryType>;
347 using KeyIteratorType = jsg::USVString;
348 using ValueIteratorType = jsg::Ref<FileSystemHandle>;
349 
350 struct IteratorState final {
351 struct Listing {
352 jsg::Ref<FileSystemDirectoryHandle> parent;
353 kj::Array<jsg::Ref<FileSystemHandle>> entries;
354 uint index = 0;
355 };
356 using Errored = jsg::JsRef<jsg::JsValue>;
357 kj::OneOf<Listing, Errored> state;
358 
359 IteratorState(
360 jsg::Ref<FileSystemDirectoryHandle> parent, kj::Array<jsg::Ref<FileSystemHandle>> entries)
361 : state(Listing{
362 .parent = kj::mv(parent),
363 .entries = kj::mv(entries),
364 }) {}
365 IteratorState(jsg::JsRef<jsg::JsValue> exception): state(kj::mv(exception)) {}
366 
367 void visitForGc(jsg::GcVisitor& visitor) {
368 KJ_SWITCH_ONEOF(state) {
369 KJ_CASE_ONEOF(listing, Listing) {
370 visitor.visit(listing.parent);
371 visitor.visitAll(listing.entries);
372 }
373 KJ_CASE_ONEOF(exception, jsg::JsRef<jsg::JsValue>) {
374 visitor.visit(exception);
375 }
376 }
377 }
378 
379 JSG_MEMORY_INFO(IteratorState) {
380 KJ_SWITCH_ONEOF(state) {
381 KJ_CASE_ONEOF(listing, Listing) {
382 tracker.trackField("parent", listing.parent);
383 for (auto& entry: listing.entries) {
384 tracker.trackField("entry", entry);
385 }
386 }
387 KJ_CASE_ONEOF(exception, jsg::JsRef<jsg::JsValue>) {
388 tracker.trackField("exception", exception);
389 }
390 }
391 }
392 };
393 
394 public:
395 FileSystemDirectoryHandle(
396 const workerd::VirtualFileSystem& vfs, jsg::Url locator, jsg::USVString name);
397 
398 kj::StringPtr getKind(jsg::Lock& js) override {
399 return "directory"_kj;
400 }
401 
402 struct FileSystemGetFileOptions {
403 bool create = false;
404 JSG_STRUCT(create);
405 };
406 
407 struct FileSystemGetDirectoryOptions {
408 bool create = false;
409 JSG_STRUCT(create);
410 };
411 
412 struct FileSystemRemoveOptions {
413 bool recursive = false;
414 JSG_STRUCT(recursive);
415 };
416 
417 jsg::Promise<jsg::Ref<FileSystemFileHandle>> getFileHandle(jsg::Lock& js,
418 jsg::USVString name,
419 jsg::Optional<FileSystemGetFileOptions> options,
420 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& exception);
421 
422 jsg::Promise<jsg::Ref<FileSystemDirectoryHandle>> getDirectoryHandle(jsg::Lock& js,
423 jsg::USVString name,
424 jsg::Optional<FileSystemGetDirectoryOptions> options,
425 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& exception);
426 
427 jsg::Promise<void> removeEntry(jsg::Lock& js,
428 jsg::USVString name,
429 jsg::Optional<FileSystemRemoveOptions> options,
430 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& exception);
431 
432 // TODO(node-fs): We are not currently implementing the resolve() method
433 // as defined in the spec.
434 jsg::Promise<kj::Array<jsg::USVString>> resolve(
435 jsg::Lock& js, jsg::Ref<FileSystemHandle> possibleDescendant);
436 
437 JSG_ASYNC_ITERATOR(EntryIterator,
438 entries,
439 EntryIteratorType,
440 IteratorState,
441 iteratorNext<EntryIteratorType>,
442 iteratorReturn<EntryIteratorType>);
443 JSG_ASYNC_ITERATOR(KeyIterator,
444 keys,
445 KeyIteratorType,
446 IteratorState,
447 iteratorNext<KeyIteratorType>,
448 iteratorReturn<KeyIteratorType>);
449 JSG_ASYNC_ITERATOR(ValueIterator,
450 values,
451 ValueIteratorType,
452 IteratorState,
453 iteratorNext<ValueIteratorType>,
454 iteratorReturn<ValueIteratorType>);
455 
456 void forEach(jsg::Lock& js,
457 jsg::Function<void(
458 jsg::USVString, jsg::Ref<FileSystemHandle>, jsg::Ref<FileSystemDirectoryHandle>)>
459 callback,
460 jsg::Optional<jsg::Value> thisArg,
461 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& exception);
462 
463 JSG_RESOURCE_TYPE(FileSystemDirectoryHandle) {
464 JSG_INHERIT(FileSystemHandle);
465 JSG_METHOD(getFileHandle);
466 JSG_METHOD(getDirectoryHandle);
467 JSG_METHOD(removeEntry);
468 JSG_METHOD(resolve);
469 JSG_METHOD(entries);
470 JSG_METHOD(keys);
471 JSG_METHOD(values);
472 JSG_METHOD(forEach);
473 JSG_ASYNC_ITERABLE(entries);
474 
475 JSG_TS_OVERRIDE({
476 entries(): AsyncIterableIterator<[string, FileSystemHandle]>;
477 [Symbol.asyncIterator]() : AsyncIterableIterator<[string, FileSystemHandle]>;
478 });
479 }
480 
481 private:
482 template <typename Type>
483 static jsg::Promise<kj::Maybe<Type>> iteratorNext(jsg::Lock& js, IteratorState& state) {
484 KJ_SWITCH_ONEOF(state.state) {
485 KJ_CASE_ONEOF(listing, IteratorState::Listing) {
486 if (listing.index >= listing.entries.size()) {
487 return js.resolvedPromise<kj::Maybe<Type>>(kj::none);
488 }
489 
490 auto& entry = listing.entries[listing.index++];
491 
492 if constexpr (kj::isSameType<Type, EntryIteratorType>()) {
493 EntryIteratorType result = kj::arr(
494 EntryType(jsg::JsRef(js, js.str(entry->getName(js)))), EntryType(entry.addRef()));
495 return js.resolvedPromise<kj::Maybe<Type>>(kj::mv(result));
496 } else if constexpr (kj::isSameType<Type, KeyIteratorType>()) {
497 return js.resolvedPromise<kj::Maybe<Type>>(jsg::USVString(kj::str(entry->getName(js))));
498 } else if constexpr (kj::isSameType<Type, ValueIteratorType>()) {
499 return js.resolvedPromise<kj::Maybe<Type>>(entry.addRef());
500 } else {
501 static_assert(false, "invalid iterator type");
502 }
503 }
504 KJ_CASE_ONEOF(exception, jsg::JsRef<jsg::JsValue>) {
505 return js.rejectedPromise<kj::Maybe<Type>>(exception.getHandle(js));
506 }
507 }
508 KJ_UNREACHABLE;
509 }
510 
511 template <typename Type>
512 static jsg::Promise<void> iteratorReturn(
513 jsg::Lock& js, IteratorState& state, jsg::Optional<Type>& value) {
514 return js.resolvedPromise();
515 }
516};
517 
518class FileSystemWritableFileStream final: public WritableStream {
519 public:
520 struct State final: public kj::Refcounted {
521 const workerd::VirtualFileSystem& vfs;
522 
523 // The FileSystemWritableFileStream is a bit transactional in nature.
524 // All writes are done to a temporary file. When the stream is closed
525 // without an error, the original file data is replaced with the data
526 // in the temporary buffer. If the stream is aborted, the temporary file
527 // is deleted and the original contents are left intact.
528 // The temporary file is not visible to the user and is not included in
529 // any directory. It is only used to hold the data until the stream
530 // is closed.
531 kj::Maybe<kj::Rc<workerd::File>> temp;
532 
533 // The file handle we are writing to
534 jsg::Ref<FileSystemFileHandle> file;
535 
536 kj::Maybe<kj::Own<void>> lock;
537 
538 // A note on file position and sizes. In the spec, file sizes and positions
539 // are defined in terms of unsigned long long, or 64-bits. We are using
540 // unsigned long (uint32_t) for these instead. This is largely because
541 // the underlying implementation is holding the file in memory and 4 GB
542 // is WAY more than our isolate heap limit. A uint32_t is more than enough
543 // for our purposes.
544 uint32_t position = 0;
545 State(jsg::Lock& js,
546 const workerd::VirtualFileSystem& vfs,
547 jsg::Ref<FileSystemFileHandle> file,
548 kj::Rc<workerd::File> temp)
549 : vfs(vfs),
550 temp(kj::mv(temp)),
551 file(kj::mv(file)),
552 lock(vfs.lock(js, this->file->getLocator())) {}
553 
554 void clear() {
555 temp = kj::none;
556 position = 0;
557 lock = kj::none;
558 }
559 };
560 
561 FileSystemWritableFileStream(
562 kj::Own<WritableStreamController> controller, kj::Rc<State> sharedState);
563 
564 static jsg::Ref<FileSystemWritableFileStream> constructor() = delete;
565 
566 using WriteParams = FileSystemFileWriteParams;
567 
568 jsg::Promise<void> write(jsg::Lock& js,
569 FileSystemWritableData data,
570 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler);
571 jsg::Promise<void> seek(jsg::Lock& js,
572 uint32_t position,
573 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler);
574 jsg::Promise<void> truncate(
575 jsg::Lock& js, uint32_t size, const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler);
576 
577 JSG_RESOURCE_TYPE(FileSystemWritableFileStream) {
578 JSG_INHERIT(WritableStream);
579 JSG_METHOD(write);
580 JSG_METHOD(seek);
581 JSG_METHOD(truncate);
582 }
583 
584 static jsg::Promise<void> writeImpl(jsg::Lock& js,
585 FileSystemWritableData data, State& state,
586 const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& deHandler);
587 
588 private:
589 kj::Rc<State> sharedState;
590 
591 void visitForGc(jsg::GcVisitor& visitor) {
592 visitor.visit(sharedState->file);
593 }
594};
595 
596class StorageManager final: public jsg::Object {
597 public:
598 jsg::Promise<jsg::Ref<FileSystemDirectoryHandle>> getDirectory(
599 jsg::Lock& js, const jsg::TypeHandler<jsg::Ref<jsg::DOMException>>& exception);
600 
601 JSG_RESOURCE_TYPE(StorageManager) {
602 JSG_METHOD(getDirectory);
603 }
604};
605 
606#define EW_WEB_FILESYSTEM_ISOLATE_TYPE \
607 workerd::api::FileSystemHandle, workerd::api::FileSystemFileHandle, \
608 workerd::api::FileSystemDirectoryHandle, workerd::api::FileSystemWritableFileStream, \
609 workerd::api::StorageManager, \
610 workerd::api::FileSystemFileHandle::FileSystemCreateWritableOptions, \
611 workerd::api::FileSystemDirectoryHandle::FileSystemGetFileOptions, \
612 workerd::api::FileSystemDirectoryHandle::FileSystemGetDirectoryOptions, \
613 workerd::api::FileSystemDirectoryHandle::FileSystemRemoveOptions, \
614 workerd::api::FileSystemWritableFileStream::WriteParams, \
615 workerd::api::FileSystemDirectoryHandle::EntryIterator, \
616 workerd::api::FileSystemDirectoryHandle::KeyIterator, \
617 workerd::api::FileSystemDirectoryHandle::ValueIterator, \
618 workerd::api::FileSystemDirectoryHandle::EntryIterator::Next, \
619 workerd::api::FileSystemDirectoryHandle::KeyIterator::Next, \
620 workerd::api::FileSystemDirectoryHandle::ValueIterator::Next, \
621 workerd::api::FileSystemHandle::RemoveOptions
622 
623#define EW_FILESYSTEM_ISOLATE_TYPES \
624 workerd::api::FileSystemModule, workerd::api::Stat, workerd::api::FileSystemModule::StatOptions, \
625 workerd::api::FileSystemModule::ReadLinkOptions, \
626 workerd::api::FileSystemModule::LinkOptions, workerd::api::FileSystemModule::OpenOptions, \
627 workerd::api::FileSystemModule::WriteOptions, \
628 workerd::api::FileSystemModule::WriteAllOptions, \
629 workerd::api::FileSystemModule::RenameOrCopyOptions, \
630 workerd::api::FileSystemModule::MkdirOptions, workerd::api::FileSystemModule::RmOptions, \
631 workerd::api::FileSystemModule::DirEntHandle, \
632 workerd::api::FileSystemModule::ReadDirOptions, workerd::api::FileFdHandle, \
633 workerd::api::FileSystemModule::CpOptions, \
634 workerd::api::FileSystemModule::OpenAsBlobOptions
635 
636} // namespace workerd::api