Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ While the mutex is being held by another thread, any fs operations will be stuck
### `new FS(name, opts?)`
First, create or open a "filesystem". (The name is used to determine the IndexedDb store name.)

```
```js
import FS from '@isomorphic-git/lightning-fs';

const fs = new FS("testfs")
Expand All @@ -59,11 +59,32 @@ const fs = new FS("testfs")

Options object:

| Param | Type [= default] | Description |
| --------- | ------------------ | --------------------------------------------------------------------- |
| `wipe` | boolean = false | Delete the database and start with an empty filesystem |
| `url` | string = undefined | Let `readFile` requests fall back to an HTTP request to this base URL |
| `urlauto` | boolean = false | Fall back to HTTP for every read of a missing file, even if unbacked |
| Param | Type [= default] | Description |
| --------------- | ------------------ | --------------------------------------------------------------------- |
| `wipe` | boolean = false | Delete the database and start with an empty filesystem |
| `url` | string = undefined | Let `readFile` requests fall back to an HTTP request to this base URL |
| `urlauto` | boolean = false | Fall back to HTTP for every read of a missing file, even if unbacked |
| `fileDbName` | string | Customize the database name |
| `fileStoreName` | string | Customize the store name |
| `lockDbName` | string | Customize the database name for the lock mutex |
| `lockStoreName` | string | Customize the store name for the lock mutex |

#### Advanced usage

You can procrastinate initializing the FS object until later.
And, if you're really adventurous, you can _re-initialize_ it with a different name to switch between IndexedDb databases.

```js
import FS from '@isomorphic-git/lightning-fs';

const fs = new FS()

// Some time later...
fs.init(name, options)

// Some time later...
fs.init(different_name, different_options)
```

### `fs.mkdir(filepath, opts?, cb)`

Expand Down Expand Up @@ -157,9 +178,9 @@ Note that stat data is made automatically from the file `/.superblock.txt` if fo

Options object:

| Param | Type [= default] | Description |
| ---------- | ------------------ | -------------------------------- |
| `mode` | number = 0o666 | Posix mode permissions |
| Param | Type [= default] | Description |
| ------ | ---------------- | ---------------------- |
| `mode` | number = 0o666 | Posix mode permissions |

### `fs.promises`

Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"semantic-release": "semantic-release"
},
"dependencies": {
"@isomorphic-git/idb-keyval": "^3.3.1",
"@isomorphic-git/idb-keyval": "3.3.2",
"isomorphic-textencoder": "1.0.1",
"just-debounce-it": "1.1.0",
"just-once": "1.1.0"
Expand Down
7 changes: 4 additions & 3 deletions src/IdbBackend.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const idb = require("@isomorphic-git/idb-keyval");

module.exports = class IdbBackend {
constructor(name) {
this._database = name;
this._store = new idb.Store(this._database, this._database + "_files");
constructor(dbname, storename) {
this._database = dbname;
this._storename = storename;
this._store = new idb.Store(this._database, this._storename);
}
saveSuperblock(superblock) {
return idb.set("!root", superblock, this._store);
Expand Down
7 changes: 4 additions & 3 deletions src/Mutex.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ const idb = require("@isomorphic-git/idb-keyval");
const sleep = ms => new Promise(r => setTimeout(r, ms))

module.exports = class Mutex {
constructor(name) {
constructor(dbname, storename) {
this._id = Math.random()
this._database = name
this._store = new idb.Store(this._database + "_lock", this._database + "_lock")
this._database = dbname
this._storename = storename
this._store = new idb.Store(this._database, this._storename)
this._lock = null
}
async has ({ margin = 2000 } = {}) {
Expand Down
78 changes: 55 additions & 23 deletions src/PromisifiedFS.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,8 @@ function cleanParams2(oldFilepath, newFilepath) {
}

module.exports = class PromisifiedFS {
constructor(name, { wipe, url, urlauto } = {}) {
this._name = name
this._idb = new IdbBackend(name);
this._mutex = navigator.locks ? new Mutex2(name) : new Mutex(name);
this._cache = new CacheFS(name);
this._opts = { wipe, url };
this._needsWipe = !!wipe;
this.saveSuperblock = debounce(() => {
this._saveSuperblock();
}, 500);
if (url) {
this._http = new HttpBackend(url)
this._urlauto = !!urlauto
}
this._operations = new Set()

constructor(name, options) {
this.init = this.init.bind(this)
this.readFile = this._wrap(this.readFile, false)
this.writeFile = this._wrap(this.writeFile, true)
this.unlink = this._wrap(this.unlink, true)
Expand All @@ -63,17 +49,60 @@ module.exports = class PromisifiedFS {
this.symlink = this._wrap(this.symlink, true)
this.backFile = this._wrap(this.backFile, true)

this.saveSuperblock = debounce(() => {
this._saveSuperblock();
}, 500);

this._deactivationPromise = null
this._deactivationTimeout = null
this._activationPromise = null

this._operations = new Set()

if (name) {
this.init(name, options)
}
}
async init (...args) {
if (this._initPromiseResolve) await this._initPromise;
this._initPromise = this._init(...args)
return this._initPromise
}
async _init (name, {
wipe,
url,
urlauto,
fileDbName = name,
fileStoreName = name + "_files",
lockDbName = name + "_lock",
lockStoreName = name + "_lock",
} = {}) {
await this._gracefulShutdown()
this._name = name
this._idb = new IdbBackend(fileDbName, fileStoreName);
this._mutex = navigator.locks ? new Mutex2(name) : new Mutex(lockDbName, lockStoreName);
this._cache = new CacheFS(name);
this._opts = { wipe, url };
this._needsWipe = !!wipe;
if (url) {
this._http = new HttpBackend(url)
this._urlauto = !!urlauto
}
if (this._initPromiseResolve) {
this._initPromiseResolve();
this._initPromiseResolve = null;
}
// The fs is initially activated when constructed (in order to wipe/save the superblock)
// but there might not be any other fs operations needed until later. Therefore we
// need to attempt to release the mutex
this._activate().then(() => {
if (this._operations.size === 0 && !this._deactivationTimeout) {
this._deactivationTimeout = setTimeout(this._deactivate.bind(this), 100)
}
})
// This is not awaited, because that would create a cycle.
this.stat('/')
}
async _gracefulShutdown () {
if (this._operations.size > 0) {
this._isShuttingDown = true
await new Promise(resolve => this._gracefulShutdownResolve = resolve);
this._isShuttingDown = false
this._gracefulShutdownResolve = null
}
}
_wrap (fn, mutating) {
let i = 0
Expand All @@ -97,6 +126,8 @@ module.exports = class PromisifiedFS {
}
}
async _activate() {
if (!this._initPromise) console.warn(new Error(`Attempted to use LightningFS ${this._name} before it was initialized.`))
await this._initPromise
if (this._deactivationTimeout) {
clearTimeout(this._deactivationTimeout)
this._deactivationTimeout = null
Expand Down Expand Up @@ -138,6 +169,7 @@ module.exports = class PromisifiedFS {
if (this._activationPromise) await this._activationPromise
if (!this._deactivationPromise) this._deactivationPromise = this.__deactivate()
this._activationPromise = null
if (this._gracefulShutdownResolve) this._gracefulShutdownResolve()
return this._deactivationPromise
}
async __deactivate() {
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ module.exports = class FS {
this.symlink = this.symlink.bind(this)
this.backFile = this.backFile.bind(this)
}
init(name, options) {
this.promises.init(name, options)
}
readFile(filepath, opts, cb) {
const [resolve, reject] = wrapCallback(opts, cb);
this.promises.readFile(filepath, opts).then(resolve).catch(reject)
Expand Down