|
| 1 | +Virtual File System Requirements |
| 2 | +================================ |
| 3 | + |
| 4 | +This document aims to list all the requirements of the Virtual File System. |
| 5 | + |
| 6 | +# Supported |
| 7 | + |
| 8 | +## Random access reads |
| 9 | + |
| 10 | +The VFS must support random access reads just like any other real file system, |
| 11 | +so that the read operations can be at least as fast as reading files from the |
| 12 | +real file system. |
| 13 | + |
| 14 | +## Symbolic links |
| 15 | + |
| 16 | +This is critical for applications that want to use packages like [dugite][] that |
| 17 | +attempt to download [Git executables][] that contain symlinks. Since |
| 18 | +Electron's [ASAR][] does not support symlinks, including [dugite][] as a |
| 19 | +dependency in an Electron app would expand every symlink into individual files, |
| 20 | +thus significantly increase the package size which is not nice. |
| 21 | + |
| 22 | +## Preserve the executable bit of the file permissions |
| 23 | + |
| 24 | +It is important to preserve the executable bit of the file permissions, so that |
| 25 | +it is possible for the single-executable to be able to execute only executable |
| 26 | +files. Other than that, all the bundled files would be readable and none will be |
| 27 | +writable. |
| 28 | + |
| 29 | +## Preserve file-hierarchy information |
| 30 | + |
| 31 | +A filesystem is incomplete without this because there's no way for the |
| 32 | +single-executable to be able to access nested file paths. |
| 33 | + |
| 34 | +## No interference with valid paths in the file system |
| 35 | + |
| 36 | +If the bundled files in the VFS correspond to certain paths that already exist |
| 37 | +in the real file system, that will break certain use-cases, so it should use |
| 38 | +such paths that cannot be used by existing files. |
| 39 | + |
| 40 | +Pkg uses [`/snapshot`](https://github.com/vercel/pkg#snapshot-filesystem) as the |
| 41 | +prefix for all the embedded files. This is confusing if `/snapshot` is an |
| 42 | +existing directory on the file system. Docker workflows routinely copy files to, |
| 43 | +and run things at, the root of the filesystem, so following that approach too |
| 44 | +would run into the same problem. |
| 45 | + |
| 46 | +Boxednode allows users to enter a [namespace](https://github.com/mongodb-js/boxednode/blob/6326e3277469e8cfe593616a0ed152600a5f9045/README.md?plain=1#L69-L72) |
| 47 | +and uses it like so: |
| 48 | +```js |
| 49 | + // Specify the entrypoint target name. If this is 'foo', then the resulting |
| 50 | + // binary will be able to load the source file as 'require("foo/foo")'. |
| 51 | + // This defaults to the basename of sourceFile, e.g. 'bar' for '/path/bar.js'. |
| 52 | + namespace?: string; |
| 53 | +``` |
| 54 | + |
| 55 | +A possible solution is to use the single executable path as the base path for |
| 56 | +the files in the VFS, i.e., if the executable has `/a/b/sea` as the path and the |
| 57 | +VFS contains a file named `file.txt`, it would be accessible by the application |
| 58 | +using `/a/b/sea/file.txt`. This approach is similar to how Electron's [ASAR][] |
| 59 | +works, i.e., if the application asar is placed in `/a/b/app.asar`, the |
| 60 | +embedded `file.txt` file would use `/a/b/app.asar/file.txt` as the path. |
| 61 | + |
| 62 | +## Globbing |
| 63 | + |
| 64 | +`fs.statSync(process.execPath).isDirectory()` will return `true` and |
| 65 | +`fs.statSync(process.execPath).isFile()` will return `false`. That way, if code |
| 66 | +within the single-executable does naive globbing using an off-the-shelf glob |
| 67 | +library, paths inside the VFS would also get picked up. |
| 68 | + |
| 69 | +## Accept file paths in the VFS as arguments |
| 70 | + |
| 71 | +If a single-executable formatter is run with an argument that is a path to a |
| 72 | +file inside the VFS, it should be able to use the `fs` APIs to read, format and |
| 73 | +print the formatted contents to `stdout`. |
| 74 | + |
| 75 | +## Cross-platform tooling |
| 76 | + |
| 77 | +The tooling required for archiving / extracting files into / from the VFS must |
| 78 | +be available on all the [platforms supported by Node.js][]. |
| 79 | + |
| 80 | +## File path contents |
| 81 | + |
| 82 | +Should not limit the size or the character contents of the file paths to stay as |
| 83 | +close as possible to what a real file system provides. |
| 84 | + |
| 85 | +## Case Sensitive |
| 86 | + |
| 87 | +From Yarn's experience with zip, forcing case sensitivity within the archives |
| 88 | +didn't break anything, improved consistency. By contrast, making the code case |
| 89 | +insensitive would have increased the complexity, worsened the runtime |
| 90 | +performance, increased the attack surface, for a use case that virtually no-one |
| 91 | +cares about. Hence, the paths in the VFS will be case sensitive. |
| 92 | + |
| 93 | +## Dynamic imports and requires |
| 94 | + |
| 95 | +`require(require.resolve('./file.js'))` should work for files that are on the |
| 96 | +real file system and the VFS. |
| 97 | + |
| 98 | +## VFS path manipulation as strings and URL objects |
| 99 | + |
| 100 | +If someone proposes that the VFS exist at a `vfs-file://` prefix, then this |
| 101 | +might become an issue. `fs` APIs accept `URL` objects, but this means code in |
| 102 | +(transitive) dependencies which assumes all native paths are strings may fail |
| 103 | +when passed `URL` objects. Perhaps a (transitive) dependency uses |
| 104 | +`require.resolve()`. |
| 105 | + |
| 106 | +Using something like `vfs-file://` might be a potential solution for placing the |
| 107 | +VFS contents somewhere that has no interference with valid paths in the file |
| 108 | +system. |
| 109 | + |
| 110 | +## Interaction with Native Addons |
| 111 | + |
| 112 | +TODO: Still under discussion in https://github.com/nodejs/single-executable/discussions/29. |
| 113 | + |
| 114 | +# Not supported |
| 115 | + |
| 116 | +## No need for supporting write operations |
| 117 | + |
| 118 | +Since the VFS is going to be embedded into the single-executable and also |
| 119 | +protected by codesigning, making changes to the contents of the VFS should |
| 120 | +invalidate the signature and crash the application if run again. Hence, no write |
| 121 | +operation needs to be supported. |
| 122 | + |
| 123 | +# Optionally support |
| 124 | + |
| 125 | +## Increase locality of related files |
| 126 | + |
| 127 | +For performance reasons. |
| 128 | + |
| 129 | +## Format implementation in multiple languages |
| 130 | + |
| 131 | +We want this format to already have implementation in *multiple* languages (not |
| 132 | +just JS, since not all tools used in the JS ecosystem are written in JS), all |
| 133 | +ideally production-grade and well-maintained. |
| 134 | + |
| 135 | +## Consensus with third-party tools on building native integrations |
| 136 | + |
| 137 | +We want this format to be consensual enough that third-party tools (VSCode, |
| 138 | +emacs, ...) won't object to build native integrations with it (for instance, |
| 139 | +Esbuild recently added zip support to integrate w/ Yarn's zip installs; it would |
| 140 | +have been a much harder sell if Yarn had used a custom-made format). |
| 141 | + |
| 142 | +## Optional data compression |
| 143 | + |
| 144 | +As an application grows, bundling all the source code, dependencies and static |
| 145 | +assets into a single file without compression would quickly reach the maximum |
| 146 | +segment / file (depending on the embedding approach) size limit imposed by the |
| 147 | +single executable file format / OS. A solution to this problem would be to |
| 148 | +minify the JS source files but that might not be enough for other kinds of |
| 149 | +files, so supporting data compression seems to be a better solution. |
| 150 | + |
| 151 | +[ASAR]: https://github.com/electron/asar |
| 152 | +[Git executables]: https://github.com/desktop/dugite-native/releases/ |
| 153 | +[dugite]: https://www.npmjs.com/package/dugite |
| 154 | +[platforms supported by Node.js]: https://github.com/nodejs/node/blob/main/BUILDING.md#supported-platforms |
0 commit comments