choo-persist
Advanced tools
Comparing version 2.0.1 to 3.0.0
105
index.js
@@ -1,94 +0,33 @@ | ||
const explain = require('explain-error') | ||
const window = require('global/window') | ||
const Idb = require('idb-wrapper') | ||
const assert = require('assert') | ||
const xtend = require('xtend') | ||
var mutate = require('xtend/mutable') | ||
module.exports = persist | ||
// create a choo persistance plugin that stores stuff in indexedDB | ||
// (obj?, fn(obj) -> null | ||
function persist (opts, cb) { | ||
if (!cb) { | ||
cb = opts | ||
opts = {} | ||
} | ||
function persist (opts) { | ||
opts = opts || {} | ||
assert.equal(typeof opts, 'object', 'choo-persist: opts should be an object') | ||
assert.equal(typeof cb, 'function', 'choo-persist: cb should be an function') | ||
var name = opts.name || 'choo-persist' | ||
var filter = opts.filter | ||
const name = opts.name || 'app' | ||
const filter = opts.filter || noop | ||
if (!window.indexedDB) return cb({}) | ||
return function (state, bus) { | ||
var savedState = null | ||
try { | ||
savedState = JSON.parse(window.localStorage.getItem(name)) | ||
} catch (e) { | ||
savedState = {} | ||
} | ||
const db = new Idb({ | ||
dbVersion: 1, | ||
storeName: name, | ||
keyPath: 'id', | ||
autoIncrement: false, | ||
onStoreReady: onStoreReady | ||
}) | ||
mutate(state, savedState) | ||
bus.on('*', listener) | ||
function onStoreReady () { | ||
getState(db, function (err, state) { | ||
if (err) throw err | ||
cb({ | ||
onStateChange: createStateChange(db, filter), | ||
wrapInitialState: function (appState) { | ||
return xtend(appState, state) | ||
} | ||
}) | ||
bus.on('clear', function () { | ||
bus.removeListener('*', listener) | ||
window.localStorage.removeItem(name) | ||
bus.emit('log:warn', 'Wiping localStorage ' + name) | ||
}) | ||
} | ||
} | ||
// persist stuff to the local database | ||
// (obj) -> (obj, obj, obj, str, fn) -> null | ||
function createStateChange (db, filter) { | ||
return function onStateChange (state, data, prev, caller, createSend) { | ||
if (filter) state = filter(state) | ||
setState(db, state, function (err) { | ||
if (err) { | ||
const send = createSend('choo-persist') | ||
send('error', err) | ||
} | ||
}) | ||
function listener (eventName, data) { | ||
var savedState = filter ? filter(state) : state | ||
window.localStorage.setItem(name, JSON.stringify(savedState)) | ||
} | ||
} | ||
} | ||
// get the initial values from the database | ||
// (obj, fn(err?, obj)) -> null | ||
function getState (db, cb) { | ||
db.getAll(onSuccess, onError) | ||
function onSuccess (data) { | ||
var state = data[data.length - 1] || {} | ||
delete state.id | ||
delete state.location | ||
cb(null, state) | ||
} | ||
function onError (err) { | ||
const nwErr = explain(err, 'choo-persist: something went wrong accessing the database while starting up') | ||
cb(nwErr) | ||
} | ||
} | ||
// set values on the database | ||
// (obj, obj, fn(err?, obj)) -> null | ||
function setState (db, state, cb) { | ||
db.put(xtend(state), onSuccess, onError) | ||
function onSuccess () { | ||
cb() | ||
} | ||
function onError (err) { | ||
const newErr = explain(err, 'choo-persist: expected database write to be successful') | ||
cb(newErr) | ||
} | ||
} | ||
function noop (state) { | ||
return state | ||
} |
{ | ||
"name": "choo-persist", | ||
"version": "2.0.1", | ||
"version": "3.0.0", | ||
"description": "Synchronize choo state with indexedDB", | ||
@@ -8,5 +8,5 @@ "main": "index.js", | ||
"deps": "dependency-check . && dependency-check . --extra --no-dev", | ||
"test": "standard && npm run deps && NODE_ENV=test node test", | ||
"test": "standard && npm run deps", | ||
"start": "bankai start --entry=example.js -p 1337 --open", | ||
"test:cov": "standard && npm run deps && NODE_ENV=test istanbul cover test.js" | ||
"test:cov": "standard && npm run deps" | ||
}, | ||
@@ -24,5 +24,2 @@ "repository": "yoshuawuyts/choo-persist", | ||
"dependencies": { | ||
"explain-error": "^1.0.3", | ||
"global": "^4.3.0", | ||
"idb-wrapper": "^1.7.1", | ||
"xtend": "^4.0.1" | ||
@@ -29,0 +26,0 @@ }, |
# choo-persist [![stability][0]][1] | ||
[![npm version][2]][3] [![build status][4]][5] [![test coverage][6]][7] | ||
[![npm version][2]][3] [![build status][4]][5] | ||
[![downloads][8]][9] [![js-standard-style][10]][11] | ||
![choo-persist gif](./reload.gif) | ||
Synchronize [choo][choo] state with `localStorage` | ||
Synchronize [choo][choo] state with [indexedDB][mdn]. Does nothing in [browsers | ||
that don't support indexedDB][caniuse]. | ||
## Usage | ||
```js | ||
const persist = require('choo-persist') | ||
const choo = require('choo') | ||
var persist = require('choo-persist') | ||
var choo = require('choo') | ||
const app = choo() | ||
var app = choo() | ||
app.use(persist()) | ||
/* register router, views, models */ | ||
persist((persist) => { | ||
app.use(persist) | ||
const tree = app.start() | ||
document.body.appendChild(tree) | ||
}) | ||
``` | ||
## API | ||
### persist(opts?, fn(plugin)) | ||
### `instance = persist([opts])` | ||
Create a new `indexedDB` database instance, and call the callback with the | ||
@@ -46,11 +37,2 @@ plugin when done. Can take an optional first argument of options: | ||
### plugin.onStateChange | ||
The plugin should be passed into `app.use()` directly. `onStateChange` listens | ||
for every state change in the application and persists it to `indexedDB`. | ||
### plugin.wrapInitialState | ||
The plugin should be passed into `app.use()` directly. `wrapInitialState` | ||
reads the state from `indexedDB` on start and overrides the initial application | ||
state. | ||
## Installation | ||
@@ -61,28 +43,2 @@ ```sh | ||
## FAQ | ||
### Why does this plugin wrap the start() method? | ||
Choo is initialized completely synchronously. This makes it easy to reason | ||
about, implement, and profile. IndexedDB, however, cannot be accessed | ||
synchronously so in order to get the initial state from the DB we must wait for | ||
it to load first. This is why the application start must be wrapped in this | ||
plugin. | ||
### Why not load the default initial state first, and then apply indexedDB? | ||
If the default initial state is loaded from the application before `indexedDB` | ||
kicks in, there will be a flash of unstyled content. It's better to have | ||
generic server side rendering (e.g. can be cached by CDNs) to lower the time to | ||
first rendering, and then load the initial state from the local database after | ||
that to serve the first uncacheable content. This will provide a smooth | ||
experience; and with the addition of a few CSS transitions it might become | ||
impressive even. | ||
## Why would I want to load content from indexedDB? | ||
Because time to first render matters, and connectivity might not always be | ||
great, or available even. By synchronizing the application state with indexedDB | ||
the biggest win for offline has already been achieved: users will be able to | ||
pick up right where they left off; even if internet goes down. From there on | ||
out, caching outgoing HTTP requests and other fancy connectivity tricks could | ||
be applied. Consider this package as an easy way to get a big win for offline | ||
first. | ||
## Should I use this while developing. | ||
@@ -94,11 +50,7 @@ No; state is persisted between page reloads which might put your page in very | ||
## How / when should I invalidate the database cache? | ||
Not sure yet; if you've got good ideas for upgrading / invalidating the local | ||
database I'd love to hear from you; I'm sure people will run into this down the | ||
road anyway. | ||
Ah, this is where good ol' data persistance comes into play - there's loads of | ||
approaches on this, but yeah you should def find a way to migrate data between | ||
incompatible models. Perhaps some day we'll have a good chapter on this in the | ||
choo handbook. Until then: have fun I guess? | ||
## Can I use this with something other than choo? | ||
Welp, there's not much code but if it talks the same API then yeah sure you | ||
can. This is a bit specific to choo tho; but there's nothing stopping people | ||
from building their own choo soooo... | ||
## License | ||
@@ -105,0 +57,0 @@ [MIT](https://tldrlegal.com/license/mit-license) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1
5415
6
26
71
- Removedexplain-error@^1.0.3
- Removedglobal@^4.3.0
- Removedidb-wrapper@^1.7.1
- Removeddom-walk@0.1.2(transitive)
- Removedexplain-error@1.0.4(transitive)
- Removedglobal@4.4.0(transitive)
- Removedidb-wrapper@1.7.2(transitive)
- Removedmin-document@2.19.0(transitive)
- Removedprocess@0.11.10(transitive)