BrowserFS icon indicating copy to clipboard operation
BrowserFS copied to clipboard

filesystem watch

Open amir-arad opened this issue 8 years ago • 7 comments

how about a watch API?

amir-arad avatar Nov 15 '16 18:11 amir-arad

I'd accept PRs for this.

If you only care about local modifications to the filesystem (meaning: you don't care about watching Dropbox files for changes, or another browser tab interfacing with your IndexedDB store), we could integrate this into the fs API emulation layer:

  • Sync API: If relevant operation successfully completes on a watched file, setImmediate a watch event.
  • Async API: If watch is enabled for the file, wrap the callback to check if it succeeded. If so, setImmediate a watch event.

For non-local changes, we'd have to figure out a change to the FileSystem interface.

Subtleties:

  • For file descriptor operations, we should only emit change events when a change is written to the file store. Most BrowserFS file systems wait until a file is closed to sync the changes. We can augment the File interface so that it's possible to watch a particular file descriptor, and then change BaseFile to emit a watch event on sync.

jvilk avatar Nov 15 '16 19:11 jvilk

I too am interested in this. Currently I'm monkey-patching fs.writeFile and fs.writeFileSync to fire events when files are saved.

billiegoose avatar Jul 23 '17 20:07 billiegoose

Am now monkey-patching fs.mkdir and fs.mkdirSync, will inevitably need to monkey-patch rmdir, rmdirSync, unlink, and unlinkSync. However I've run into a conundrum:

The Node.js fs.watch API sucks.

It is literally quite useless. It only has one event, change1, and one value filename. The event does not tell you 1) whether filename is a directory or a file 2) whether it was added, deleted, or modified.

This pretty much necessitates doing a second call with fs.stat or even a third with fs.readdir to determine what happened, which seems ~~like a waste.~~ error prone and inefficient.

To overcome the failings of the Node API, naturally most projects use a wrapper library. The most popular seems to be chokidar (2149 dependents) but there is a long tail of others: watch (510 dependents), gaze (363 dependents) watchr (124 dependents), sane (75 dependents), filewatcher (28 dependents)... and they each use their own naming scheme for file system "events".

Right now I'm leaning toward implementing the chokidar event names (which are 'add', 'change', 'unlink', 'addDir', and 'unlinkDir') or using a novel-but-impartial event naming scheme based on the function names ('write', 'rename', 'unlink', 'mkdir', 'rmdir'). The former has the advantage of not inventing another wheel, and would be familiar to (some) Node users. The latter has the advantage that I could just as easily add ('read', 'readdir', 'stat', 'open', 'close', 'symlink', 'chown', 'chmod', etc) events which could be valuable, and I wouldn't have to think twice about what to name events.

Any thoughts on what would be the most useful to others? Eventually I figure I could release the result of my monkey-patching as a BrowserFSWatcher package, but it would be cleaner if there were some hooks in BrowserFS for implementing watchers on top of it. fs.watch seems to be a terrible hook though.

1 technically it also has a "rename" event but it is weirdly inconsistant

billiegoose avatar Aug 15 '17 03:08 billiegoose

I am highly interested in this

pyramation avatar Mar 16 '20 18:03 pyramation

@wmhilton do you have any snippets you can share of your monkey patching that you did? I think even as a monkey patch solution, could provide value to the community :)

pyramation avatar Mar 16 '20 19:03 pyramation

I've also been revisiting this question occasionally over the past year, in hope that progress had been made.

In my case I only have the need for watching local changes.

Phault avatar Mar 17 '20 11:03 Phault

Here's a monkey patch I'm using:

const EventEmitter = require("events");
const fsEventEmitter = new EventEmitter();

Object.keys(fs).filter(k => typeof fs[k] === "function").forEach(k => {
  const orig = fs[k];
  fs[k] = (...args) => {
    fsEventEmitter.emit(k, ...args);
    return orig(...args);
  }
})

It'll emit events for every function call, so you can listen for those and handle them accordingly.

tjjfvi avatar Feb 08 '21 19:02 tjjfvi

Please use https://github.com/browser-fs/core/issues/6

james-pre avatar Oct 25 '23 00:10 james-pre