@zenfs/core
Advanced tools
Comparing version
@@ -38,5 +38,2 @@ import { FileSystem, FileSystemMetadata } from '../filesystem.js'; | ||
createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<File>; | ||
/** | ||
* @internal | ||
*/ | ||
openFile(path: string, flag: FileFlag, cred: Cred): Promise<File>; | ||
@@ -43,0 +40,0 @@ unlink(path: string, cred: Cred): Promise<void>; |
@@ -83,2 +83,3 @@ import { Cred } from '../cred.js'; | ||
declare const AsyncStoreFileSystem_base: (abstract new (...args: any[]) => { | ||
readonly metadata: FileSystemMetadata; | ||
renameSync(oldPath: string, newPath: string, cred: Cred): void; | ||
@@ -94,16 +95,2 @@ statSync(path: string, cred: Cred): Stats; | ||
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void; | ||
readonly metadata: FileSystemMetadata; | ||
ready(): Promise<any>; | ||
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
stat(path: string, cred: Cred): Promise<Stats>; | ||
openFile(path: string, flag: FileFlag, cred: Cred): Promise<File>; | ||
createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<File>; | ||
unlink(path: string, cred: Cred): Promise<void>; | ||
rmdir(path: string, cred: Cred): Promise<void>; | ||
mkdir(path: string, mode: number, cred: Cred): Promise<void>; | ||
readdir(path: string, cred: Cred): Promise<string[]>; | ||
exists(path: string, cred: Cred): Promise<boolean>; | ||
existsSync(path: string, cred: Cred): boolean; | ||
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>; | ||
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>; | ||
}) & typeof FileSystem; | ||
@@ -110,0 +97,0 @@ /** |
@@ -76,2 +76,3 @@ import { dirname, basename, join, resolve } from '../emulation/path.js'; | ||
} | ||
// @ts-expect-error 2611 | ||
get metadata() { | ||
@@ -78,0 +79,0 @@ return { |
@@ -157,2 +157,3 @@ import type { Cred } from './cred.js'; | ||
declare const FileIndexFS_base: (abstract new (...args: any[]) => { | ||
readonly metadata: import("./filesystem.js").FileSystemMetadata; | ||
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
@@ -172,12 +173,2 @@ renameSync(oldPath: string, newPath: string, cred: Cred): void; | ||
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void; | ||
readonly metadata: import("./filesystem.js").FileSystemMetadata; | ||
ready(): Promise<any>; | ||
stat(path: string, cred: Cred): Promise<Stats>; | ||
statSync(path: string, cred: Cred): Stats; | ||
openFile(path: string, flag: FileFlag, cred: Cred): Promise<import("./file.js").File>; | ||
openFileSync(path: string, flag: FileFlag, cred: Cred): import("./file.js").File; | ||
readdir(path: string, cred: Cred): Promise<string[]>; | ||
readdirSync(path: string, cred: Cred): string[]; | ||
exists(path: string, cred: Cred): Promise<boolean>; | ||
existsSync(path: string, cred: Cred): boolean; | ||
}) & typeof FileSystem; | ||
@@ -229,2 +220,3 @@ export declare abstract class FileIndexFS<TIndex> extends FileIndexFS_base { | ||
declare const AsyncFileIndexFS_base: (abstract new (...args: any[]) => { | ||
readonly metadata: import("./filesystem.js").FileSystemMetadata; | ||
renameSync(oldPath: string, newPath: string, cred: Cred): void; | ||
@@ -240,16 +232,2 @@ statSync(path: string, cred: Cred): Stats; | ||
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void; | ||
readonly metadata: import("./filesystem.js").FileSystemMetadata; | ||
ready(): Promise<any>; | ||
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
stat(path: string, cred: Cred): Promise<Stats>; | ||
openFile(path: string, flag: FileFlag, cred: Cred): Promise<import("./file.js").File>; | ||
createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<import("./file.js").File>; | ||
unlink(path: string, cred: Cred): Promise<void>; | ||
rmdir(path: string, cred: Cred): Promise<void>; | ||
mkdir(path: string, mode: number, cred: Cred): Promise<void>; | ||
readdir(path: string, cred: Cred): Promise<string[]>; | ||
exists(path: string, cred: Cred): Promise<boolean>; | ||
existsSync(path: string, cred: Cred): boolean; | ||
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>; | ||
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>; | ||
}) & (abstract new (index: ListingTree) => FileIndexFS<unknown>); | ||
@@ -256,0 +234,0 @@ export declare abstract class AsyncFileIndexFS<TIndex> extends AsyncFileIndexFS_base { |
@@ -157,11 +157,7 @@ import { ApiError } from './ApiError.js'; | ||
/** | ||
* Implements the asynchronous API in terms of the synchronous API. | ||
* | ||
* @example ```ts | ||
* class SyncFS extends Sync(FileSystem) {} | ||
* ``` | ||
* @internal | ||
*/ | ||
export declare function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => { | ||
readonly metadata: FileSystemMetadata; | ||
ready(): Promise<any>; | ||
declare abstract class SyncFileSystem extends FileSystem { | ||
get metadata(): FileSystemMetadata; | ||
ready(): Promise<this>; | ||
exists(path: string, cred: Cred): Promise<boolean>; | ||
@@ -178,57 +174,15 @@ rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>; | ||
/** | ||
* Synchronous rename. | ||
*/ | ||
} | ||
/** | ||
* Implements the asynchronous API in terms of the synchronous API. | ||
*/ | ||
export declare function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFileSystem) & T; | ||
/** | ||
* @internal | ||
*/ | ||
declare abstract class AsyncFileSystem { | ||
get metadata(): FileSystemMetadata; | ||
renameSync(oldPath: string, newPath: string, cred: Cred): void; | ||
/** | ||
* Synchronous `stat`. | ||
*/ | ||
statSync(path: string, cred: Cred): Stats; | ||
/** | ||
* Opens the file at path p with the given flag. The file must exist. | ||
* @param p The path to open. | ||
* @param flag The flag to use when opening the file. | ||
* @return A File object corresponding to the opened file. | ||
*/ | ||
openFileSync(path: string, flag: FileFlag, cred: Cred): File; | ||
/** | ||
* Create the file at path p with the given mode. Then, open it with the given | ||
* flag. | ||
*/ | ||
createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): File; | ||
/** | ||
* Synchronous `unlink`. | ||
*/ | ||
unlinkSync(path: string, cred: Cred): void; | ||
/** | ||
* Synchronous `rmdir`. | ||
*/ | ||
rmdirSync(path: string, cred: Cred): void; | ||
/** | ||
* Synchronous `mkdir`. | ||
* @param mode Mode to make the directory using. Can be ignored if | ||
* the filesystem doesn't support permissions. | ||
*/ | ||
mkdirSync(path: string, mode: number, cred: Cred): void; | ||
/** | ||
* Synchronous `readdir`. Reads the contents of a directory. | ||
*/ | ||
readdirSync(path: string, cred: Cred): string[]; | ||
/** | ||
* Test whether or not the given path exists by checking with the file system. | ||
*/ | ||
existsSync(path: string, cred: Cred): boolean; | ||
/** | ||
* Synchronous `link`. | ||
*/ | ||
linkSync(srcpath: string, dstpath: string, cred: Cred): void; | ||
/** | ||
* Synchronize the data and stats for path synchronously | ||
*/ | ||
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void; | ||
}) & T; | ||
export declare function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => { | ||
renameSync(oldPath: string, newPath: string, cred: Cred): void; | ||
statSync(path: string, cred: Cred): Stats; | ||
createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): File; | ||
openFileSync(path: string, flag: FileFlag, cred: Cred): File; | ||
@@ -241,64 +195,10 @@ unlinkSync(path: string, cred: Cred): void; | ||
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void; | ||
readonly metadata: FileSystemMetadata; | ||
ready(): Promise<any>; | ||
/** | ||
* Asynchronous rename. No arguments other than a possible exception | ||
* are given to the completion callback. | ||
*/ | ||
} | ||
export declare function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFileSystem) & T; | ||
/** | ||
* @internal | ||
*/ | ||
declare abstract class ReadonlyFileSystem { | ||
get metadata(): FileSystemMetadata; | ||
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
/** | ||
* Asynchronous `stat`. | ||
*/ | ||
stat(path: string, cred: Cred): Promise<Stats>; | ||
/** | ||
* Opens the file at path p with the given flag. The file must exist. | ||
* @param p The path to open. | ||
* @param flag The flag to use when opening the file. | ||
*/ | ||
openFile(path: string, flag: FileFlag, cred: Cred): Promise<File>; | ||
/** | ||
* Create the file at path p with the given mode. Then, open it with the given | ||
* flag. | ||
*/ | ||
createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<File>; | ||
/** | ||
* Asynchronous `unlink`. | ||
*/ | ||
unlink(path: string, cred: Cred): Promise<void>; | ||
/** | ||
* Asynchronous `rmdir`. | ||
*/ | ||
rmdir(path: string, cred: Cred): Promise<void>; | ||
/** | ||
* Asynchronous `mkdir`. | ||
* @param mode Mode to make the directory using. Can be ignored if | ||
* the filesystem doesn't support permissions. | ||
*/ | ||
mkdir(path: string, mode: number, cred: Cred): Promise<void>; | ||
/** | ||
* Asynchronous `readdir`. Reads the contents of a directory. | ||
* | ||
* The callback gets two arguments `(err, files)` where `files` is an array of | ||
* the names of the files in the directory excluding `'.'` and `'..'`. | ||
*/ | ||
readdir(path: string, cred: Cred): Promise<string[]>; | ||
/** | ||
* Test whether or not the given path exists by checking with the file system. | ||
*/ | ||
exists(path: string, cred: Cred): Promise<boolean>; | ||
/** | ||
* Test whether or not the given path exists by checking with the file system. | ||
*/ | ||
existsSync(path: string, cred: Cred): boolean; | ||
/** | ||
* Asynchronous `link`. | ||
*/ | ||
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>; | ||
/** | ||
* Synchronize the data and stats for path asynchronously | ||
*/ | ||
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>; | ||
}) & T; | ||
export declare function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => { | ||
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
renameSync(oldPath: string, newPath: string, cred: Cred): void; | ||
@@ -317,44 +217,4 @@ createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<File>; | ||
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void; | ||
readonly metadata: FileSystemMetadata; | ||
ready(): Promise<any>; | ||
/** | ||
* Asynchronous `stat`. | ||
*/ | ||
stat(path: string, cred: Cred): Promise<Stats>; | ||
/** | ||
* Synchronous `stat`. | ||
*/ | ||
statSync(path: string, cred: Cred): Stats; | ||
/** | ||
* Opens the file at path p with the given flag. The file must exist. | ||
* @param p The path to open. | ||
* @param flag The flag to use when opening the file. | ||
*/ | ||
openFile(path: string, flag: FileFlag, cred: Cred): Promise<File>; | ||
/** | ||
* Opens the file at path p with the given flag. The file must exist. | ||
* @param p The path to open. | ||
* @param flag The flag to use when opening the file. | ||
* @return A File object corresponding to the opened file. | ||
*/ | ||
openFileSync(path: string, flag: FileFlag, cred: Cred): File; | ||
/** | ||
* Asynchronous `readdir`. Reads the contents of a directory. | ||
* | ||
* The callback gets two arguments `(err, files)` where `files` is an array of | ||
* the names of the files in the directory excluding `'.'` and `'..'`. | ||
*/ | ||
readdir(path: string, cred: Cred): Promise<string[]>; | ||
/** | ||
* Synchronous `readdir`. Reads the contents of a directory. | ||
*/ | ||
readdirSync(path: string, cred: Cred): string[]; | ||
/** | ||
* Test whether or not the given path exists by checking with the file system. | ||
*/ | ||
exists(path: string, cred: Cred): Promise<boolean>; | ||
/** | ||
* Test whether or not the given path exists by checking with the file system. | ||
*/ | ||
existsSync(path: string, cred: Cred): boolean; | ||
}) & T; | ||
} | ||
export declare function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFileSystem) & T; | ||
export {}; |
@@ -54,6 +54,2 @@ import { ApiError, ErrorCode } from './ApiError.js'; | ||
* Implements the asynchronous API in terms of the synchronous API. | ||
* | ||
* @example ```ts | ||
* class SyncFS extends Sync(FileSystem) {} | ||
* ``` | ||
*/ | ||
@@ -64,3 +60,3 @@ export function Sync(FS) { | ||
*/ | ||
class SyncFileSystem extends FS { | ||
class _SyncFileSystem extends FS { | ||
get metadata() { | ||
@@ -106,6 +102,9 @@ return { ...super.metadata, synchronous: true }; | ||
} | ||
return SyncFileSystem; | ||
return _SyncFileSystem; | ||
} | ||
export function Async(FS) { | ||
class AsyncFileSystem extends FS { | ||
class _AsyncFileSystem extends FS { | ||
get metadata() { | ||
return { ...super.metadata, synchronous: false }; | ||
} | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
@@ -144,6 +143,9 @@ renameSync(oldPath, newPath, cred) { | ||
/* eslint-enable @typescript-eslint/no-unused-vars */ | ||
return AsyncFileSystem; | ||
return _AsyncFileSystem; | ||
} | ||
export function Readonly(FS) { | ||
class ReadonlyFileSystem extends FS { | ||
class _ReadonlyFileSystem extends FS { | ||
get metadata() { | ||
return { ...super.metadata, readonly: true }; | ||
} | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
@@ -193,3 +195,3 @@ async rename(oldPath, newPath, cred) { | ||
} | ||
return ReadonlyFileSystem; | ||
return _ReadonlyFileSystem; | ||
} |
{ | ||
"name": "@zenfs/core", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "A filesystem in your browser", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
118
readme.md
@@ -7,3 +7,3 @@ # ZenFS | ||
ZenFS is highly extensible, and includes many builtin filesystem backends: | ||
ZenFS is highly extensible, and includes a few built-in backends: | ||
@@ -13,6 +13,8 @@ - `InMemory`: Stores files in-memory. It is a temporary file store that clears when the user navigates away. | ||
- `AsyncMirror`: Use an asynchronous backend synchronously. Invaluable for Emscripten; let your Emscripten applications write to larger file stores with no additional effort! | ||
- `AsyncMirror` loads the entire contents of the async file system into a synchronous backend during construction. It performs operations synchronous file system and then queues them to be mirrored onto the asynchronous backend. | ||
More backends can be defined by separate libraries, so long as they extend they implement `ZenFS.FileSystem`. Multiple backends can be active at once at different locations in the directory hierarchy. | ||
> [!NOTE] | ||
> When constructed, `AsyncMirror` loads the entire contents of the async file system into a synchronous backend. It performs operations on the synchronous file system and then queues them to be mirrored onto the asynchronous backend. | ||
More backends can be defined by separate libraries, as long as they implement `FileSystem`. | ||
ZenFS supports a number of other backends (many are provided as seperate packages under `@zenfs`). | ||
@@ -30,3 +32,4 @@ | ||
> 🛈 The examples are written in ESM. If you are using CJS, you can `require` the package. If running in a browser you can add a script tag to your HTML pointing to the `browser.min.js` and use ZenFS via the global `ZenFS` object. | ||
> [!NOTE] | ||
> The examples are written in ESM. If you are using CJS, you can `require` the package. If running in a browser you can add a script tag to your HTML pointing to the `browser.min.js` and use ZenFS via the global `ZenFS` object. | ||
@@ -42,24 +45,12 @@ ```js | ||
#### Using different backends | ||
#### Using different and/or different backends | ||
A `InMemory` backend is created by default. If you would like to use a different one, you must configure ZenFS. It is recommended to do so using the `configure` function. Here is an example using the `Storage` backend from `@zenfs/dom`: | ||
A single `InMemory` backend is created by default, mounted on `/`. | ||
```js | ||
import { configure, fs } from '@zenfs/core'; | ||
import { StorageStore } from '@zenfs/dom'; | ||
You can configure ZenFS to use a different backend and mount multiple backends. It is strongly recommended to do so using the `configure` function. | ||
await configure({ backend: StorageStore }); | ||
You can use multiple backends by passing an object to `configure` which maps paths to file systems. | ||
if (!fs.existsSync('/test.txt')) { | ||
fs.writeFileSync('/test.txt', 'This will persist across reloads!'); | ||
} | ||
The following example mounts a zip file to `/zip`, in-memory storage to `/tmp`, and IndexedDB to `/home`. Note that `/` has the default in-memory backend. | ||
const contents = fs.readFileSync('/test.txt', 'utf-8'); | ||
console.log(contents); | ||
``` | ||
#### Using multiple backends | ||
You can use multiple backends by passing an object to `configure` which maps paths to file systems. The following example mounts a zip file to `/zip`, in-memory storage to `/tmp`, and IndexedDB storage to `/home` (note that `/` has the default in-memory backend): | ||
```js | ||
@@ -73,6 +64,3 @@ import { configure } from '@zenfs/core'; | ||
await configure({ | ||
'/mnt/zip': { | ||
backend: Zip, | ||
zipData: zipData, | ||
}, | ||
'/mnt/zip': { backend: Zip, zipData }, | ||
'/tmp': 'InMemory', | ||
@@ -83,4 +71,23 @@ '/home': IndexedDB, | ||
#### FS Promises API | ||
> [!TIP] | ||
> When configuring a mount point, you can pass in 1. A string that maps to a built-in backend 2. A `Backend` object, if the backend has no required options 3. An object that has a `backend` property which is a `Backend` or a string that maps to a built-in backend and the options accepted by the backend | ||
Here is an example that mounts the `Storage` backend from `@zenfs/dom` on `/`: | ||
```js | ||
import { configure, fs } from '@zenfs/core'; | ||
import { Storage } from '@zenfs/dom'; | ||
await configure({ backend: Storage }); | ||
if (!fs.existsSync('/test.txt')) { | ||
fs.writeFileSync('/test.txt', 'This will persist across reloads!'); | ||
} | ||
const contents = fs.readFileSync('/test.txt', 'utf-8'); | ||
console.log(contents); | ||
``` | ||
#### FS Promises | ||
The FS promises API is exposed as `promises`. | ||
@@ -100,7 +107,11 @@ | ||
ZenFS does _not_ provide a seperate public import for importing promises in its built form. If you are using ESM, you can import promises functions from `dist/emulation/promises`, though this may change at any time and is not recommended. | ||
> [!NOTE] | ||
> You can import the promises API using `promises`, or using `fs.promises` on the exported `fs`. | ||
> [!IMPORTANT] | ||
> ZenFS does _not_ provide a seperate public import for importing promises like `fs/promises`. If you are using ESM, you can import promises functions like `fs/promises` from the `dist/emulation/promises.ts` file, though this may change at any time and is **not recommended**. | ||
#### Using asynchronous backends synchronously | ||
You may have noticed that attempting to use a synchronous function on an asynchronous backend (e.g. IndexedDB) results in a "not supplied" error (`ENOTSUP`). If you wish to use an asynchronous backend synchronously you need to wrap it in an `AsyncMirror`: | ||
You may have noticed that attempting to use a synchronous function on an asynchronous backend (e.g. `IndexedDB`) results in a "not supplied" error (`ENOTSUP`). If you would like to use an asynchronous backend synchronously you need to wrap it in an `AsyncMirror`: | ||
@@ -119,45 +130,13 @@ ```js | ||
fs.writeFileSync('/persistant.txt', 'My persistant data'); // This fails if you configure with only IndexedDB | ||
fs.writeFileSync('/persistant.txt', 'My persistant data'); | ||
``` | ||
### Advanced usage | ||
#### Mounting and unmounting, creating backends | ||
#### Creating backends | ||
If you would like to create backends without configure (e.g. to do something dynamic at runtime), you may do so by importing the backend and calling `createBackend` with it. | ||
If you would like to create backends without configure, you may do so by importing the backend and calling `createBackend` with it. You can import the backend directly or with `backends`: | ||
You can then mount and unmount the backend instance by using `mount` and `umount`. | ||
```js | ||
import { configure, backends, InMemory } from '@zenfs/core'; | ||
console.log(backends.InMemory === InMemory); // they are the same | ||
const internalInMemoryFS = await createBackend(InMemory); | ||
``` | ||
> ⚠ Instances of backends follow the **_internal_** ZenFS API. You should never use a backend's methods unless you are extending a backend. | ||
```js | ||
import { configure, InMemory } from '@zenfs/core'; | ||
const internalInMemoryFS = new InMemory(); | ||
await internalInMemoryFS.ready(); | ||
``` | ||
#### Mounting | ||
If you would like to mount and unmount backends, you can do so using the `mount` and `umount` functions: | ||
```js | ||
import { fs, InMemory } from '@zenfs/core'; | ||
const internalInMemoryFS = await createBackend(InMemory); // create an FS instance | ||
fs.mount('/tmp', internalInMemoryFS); // mount | ||
fs.umount('/tmp'); // unmount /tmp | ||
``` | ||
This could be used in the "multiple backends" example like so: | ||
```js | ||
import { configure, createBackend } from '@zenfs/core'; | ||
import { IndexedDB } from '@zenfs/dom'; | ||
@@ -168,3 +147,3 @@ import { Zip } from '@zenfs/zip'; | ||
'/tmp': 'InMemory', | ||
'/home': 'IndexedDB', | ||
'/home': IndexedDB, | ||
}; | ||
@@ -183,2 +162,5 @@ | ||
> [!WARNING] | ||
> Instances of backends follow the **internal** ZenFS API. You should never use a backend's methods unless you are extending a backend. | ||
## Using with bundlers | ||
@@ -198,5 +180,1 @@ | ||
Run unit tests with `npm test`. | ||
### License | ||
ZenFS is licensed under the MIT License. |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1453266
-2.22%59
-9.23%11218
-8.66%170
-11.46%