Skip to content

Commit

Permalink
feat: reinitialize FS via 'init' and provide custom store names (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
billiegoose authored Apr 7, 2020
1 parent d1c314c commit eb21a02
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 42 deletions.
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

0 comments on commit eb21a02

Please sign in to comment.