Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.0.0 #94

Merged
merged 15 commits into from
Jan 11, 2021
9 changes: 0 additions & 9 deletions .babelrc

This file was deleted.

4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules
.rpt2_cache
.ts-tmp
dist/test
dist/size-tests
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v14.15.4
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
4 changes: 4 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
205 changes: 163 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,58 @@
# IDB-Keyval

[![npm](https://img.shields.io/npm/v/idb-keyval.svg)](https://www.npmjs.com/package/idb-keyval)
[![size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/idb-keyval/dist/idb-keyval-iife.min.js?compression=gzip)](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/idb-keyval/dist/idb-keyval-iife.min.js)

This is a super-simple-small promise-based keyval store implemented with IndexedDB, largely based on [async-storage by Mozilla](https://github.com/mozilla-b2g/gaia/blob/master/shared/js/async_storage.js).
This is a super-simple promise-based keyval store implemented with IndexedDB, originally based on [async-storage by Mozilla](https://github.com/mozilla-b2g/gaia/blob/master/shared/js/async_storage.js).

[localForage](https://github.com/localForage/localForage) offers similar functionality, but supports older browsers with broken/absent IDB implementations. Because of that, it's 7.4k, whereas idb-keyval is < 600 bytes. Also, it's tree-shaking friendly, so you'll probably end up using fewer than 500 bytes. Pick whichever works best for you!
It's small and tree-shakeable. If you only use get/set, the library is ~250 bytes (brotli'd), if you use all methods it's ~450 bytes.

This is only a keyval store. If you need to do more complex things like iteration & indexing, check out [IDB on NPM](https://www.npmjs.com/package/idb) (a little heavier at 1.7k). The first example in its README is how to recreate this library.
[localForage](https://github.com/localForage/localForage) offers similar functionality, but supports older browsers with broken/absent IDB implementations. Because of that, it's orders of magnitude bigger (~7k).

This is only a keyval store. If you need to do more complex things like iteration & indexing, check out [IDB on NPM](https://www.npmjs.com/package/idb) (a little heavier at 1k). The first example in its README is how to create a keyval store.

## Installing

### Recommended: Via npm + webpack/rollup/parcel/etc

```sh
npm install idb-keyval
```

Now you can require/import `idb-keyval`:

```js
import { get, set } from 'idb-keyval';
```

If you're targeting older versions of IE, you may have more luck with:

```js
// Import a Promise polyfill
import 'es6-promise/auto';
import { get, set } from 'idb-keyval/dist/esm-compat';
```

### All bundles

- `dist/cjs/index.js` CommonJS module.
- `dist/cjs-compat/index.js` CommonJS module, transpiled for older browsers.
- `dist/esm/index.js` EcmaScript module.
- `dist/esm-compat/index.js` EcmaScript module, transpiled for older browsers.
- `dist/iife/index.js` Minified plain JS, which creates an `idbKeyval` global containing all methods.
- `dist/iife-compat/index.js` As above, but transpiled for older browsers.

These built versions are also available on jsDelivr, e.g.:

```html
<script src="https://cdn.jsdelivr.net/npm/idb-keyval@4/dist/iife/index.js"></script>
<!-- Or in modern browsers: -->
<script type="module">
import {
get,
set,
} from 'https://cdn.jsdelivr.net/npm/idb-keyval@4/dist/esm/index.js';
</script>
```

## Usage

Expand All @@ -17,10 +62,9 @@ This is only a keyval store. If you need to do more complex things like iteratio
import { set } from 'idb-keyval';

set('hello', 'world');
set('foo', 'bar');
```

Since this is IDB-backed, you can store anything structured-clonable (numbers, arrays, objects, dates, blobs etc).
Since this is IDB-backed, you can store anything structured-clonable (numbers, arrays, objects, dates, blobs etc), although old Edge doesn't support `null`. Keys can be numbers, strings, `Date`s, (IDB also allows arrays of those values, but IE doesn't support it).

All methods return promises:

Expand All @@ -29,7 +73,7 @@ import { set } from 'idb-keyval';

set('hello', 'world')
.then(() => console.log('It worked!'))
.catch(err => console.log('It failed!', err));
.catch((err) => console.log('It failed!', err));
```

### get:
Expand All @@ -38,22 +82,85 @@ set('hello', 'world')
import { get } from 'idb-keyval';

// logs: "world"
get('hello').then(val => console.log(val));
get('hello').then((val) => console.log(val));
```

If there is no 'hello' key, then `val` will be `undefined`.

### keys:
### setMany:

Set many keyval pairs at once. This is faster than calling `set` multiple times.

```js
import { keys } from 'idb-keyval';
import { set, setMany } from 'idb-keyval';

// Instead of:
Promise.all([set(123, 456), set('hello', 'world')])
.then(() => console.log('It worked!'))
.catch((err) => console.log('It failed!', err));

// It's faster to do:
setMany([
[123, 456],
['hello', 'world'],
])
.then(() => console.log('It worked!'))
.catch((err) => console.log('It failed!', err));
```

This operation is also atomic – if one of the pairs can't be added, none will be added.

### getMany:

Get many keys at once. This is faster than calling `get` multiple times. Resolves with an array of values.

```js
import { get, getMany } from 'idb-keyval';

// Instead of:
Promise.all([get(123), get('hello')]).then(([firstVal, secondVal]) =>
console.log(firstVal, secondVal),
);

// It's faster to do:
getMany([123, 'hello']).then(([firstVal, secondVal]) =>
console.log(firstVal, secondVal),
);
```

### update:

// logs: ["hello", "foo"]
keys().then(keys => console.log(keys));
Transforming a value (eg incrementing a number) using `get` and `set` is risky, as both `get` and `set` are async and non-atomic:

```js
// Don't do this:
import { get, set } from 'idb-keyval';

get('counter').then((val) =>
set('counter', (val || 0) + 1);
);

get('counter').then((val) =>
set('counter', (val || 0) + 1);
);
```

With the above, both `get` operations will complete first, each returning `undefined`, then each set operation will be setting `1`. You could fix the above by queuing the second `get` on the first `set`, but that isn't always each across multiple pieces of code. Instead:

```js
// Instead:
import { update } from 'idb-keyval';

update('counter', (val) => (val || 0) + 1);
update('counter', (val) => (val || 0) + 1);
```

This will queue the updates automatically, so the first `update` set the `counter` to `1`, and the second `update` sets it to `2`.

### del:

Delete a particular key from the store.

```js
import { del } from 'idb-keyval';

Expand All @@ -62,65 +169,79 @@ del('hello');

### clear:

Clear all values in the store.

```js
import { clear } from 'idb-keyval';

clear();
```

### Custom stores:
### entries:

By default, the methods above use an IndexedDB database named `keyval-store` and an object store named `keyval`. You can create your own store, and pass it as an additional parameter to any of the above methods:
Get all entries in the store. Each entry is an array of `[key, value]`.

```js
import { Store, set } from 'idb-keyval';
import { entries } from 'idb-keyval';

const customStore = new Store('custom-db-name', 'custom-store-name');
set('foo', 'bar', customStore);
// logs: [[123, 456], ['hello', 'world']]
entries().then((entries) => console.log(entries));
```

That's it!
### keys:

## Installing
Get all keys in the store.

### Via npm + webpack/rollup
```js
import { keys } from 'idb-keyval';

```sh
npm install idb-keyval
// logs: [123, 'hello']
keys().then((keys) => console.log(keys));
```

Now you can require/import `idb-keyval`:
### values:

Get all values in the store.

```js
import { get, set } from 'idb-keyval';
import { values } from 'idb-keyval';

// logs: [456, 'world']
values().then((values) => console.log(values));
```

If you're targeting older versions of IE, you may have more luck with:
### Custom stores:

By default, the methods above use an IndexedDB database named `keyval-store` and an object store named `keyval`. If you want to use something different, see [custom stores](./custom-stores.md).

## Updating

# Updating from 3.x

The changes between 3.x and 4.x related to custom stores.

Old way:

```js
const idb = require('idb-keyval/dist/idb-keyval-cjs-compat.min.js');
```
// This no longer works in 4.x
import { Store, set } from 'idb-keyval';

### Via `<script>`
const customStore = new Store('custom-db-name', 'custom-store-name');
set('foo', 'bar', customStore);
```

* `dist/idb-keyval.mjs` is a valid JS module.
* `dist/idb-keyval-iife.js` can be used in browsers that don't support modules. `idbKeyval` is created as a global.
* `dist/idb-keyval-iife.min.js` As above, but minified.
* `dist/idb-keyval-iife-compat.min.js` As above, but works in older browsers such as IE 10.
* `dist/idb-keyval-amd.js` is an AMD module.
* `dist/idb-keyval-amd.min.js` As above, but minified.
New way:

These built versions are also available on jsDelivr, e.g.:
```js
import { createStore, set } from 'idb-keyval';

```html
<script src="https://cdn.jsdelivr.net/npm/idb-keyval@3/dist/idb-keyval-iife.min.js"></script>
<!-- Or in modern browsers: -->
<script type="module">
import { get, set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@3/dist/idb-keyval.mjs';
</script>
const customStore = createStore('custom-db-name', 'custom-store-name');
set('foo', 'bar', customStore);
```

## Updating from 2.x
For more details, see [custom stores](./custom-stores.md).

# Updating from 2.x

2.x exported an object with methods:

Expand Down
54 changes: 54 additions & 0 deletions custom-stores.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Custom stores

Although this library allows you to use custom stores, it adds complexity and gotchas. If you need this kind of control, I recommend [IDB on NPM](https://www.npmjs.com/package/idb) instead. It's a little heavier at ~1k, but it gives you the control you need to manage things like transactions and schema migrations.

Still here? Ok, here's the deal:

## Defining a custom database & store name

The default database name is `keyval-store`, and the default store name is `keyval`. Yes, that's right, the database name contains 'store' and the store name doesn't. I don't know what I was thinking at the time, but I'm stuck with it now for compatibility.

Every method in `idb-keyval` takes an additional parameter, `customStore`, which allows you to use custom names:

```js
import { set, createStore } from 'idb-keyval';

const customStore = createStore('custom-db-name', 'custom-store-name');

set('hello', 'world', customStore);
```

But `createStore` won't let you create multiple stores within the same database. Nor will it let you create a store within an existing database.

```js
// This won't work:
const customStore = createStore('custom-db-name', 'custom-store-name');
const customStore2 = createStore('custom-db-name', 'custom-store-2');

// But this is ok, because the database name is different:
const customStore3 = createStore('db3', 'keyval');
const customStore4 = createStore('db4', 'keyval');
```

This restriction is due to how IndexedDB performs schema migrations. If you need this kind of functionality, see [IDB on NPM](https://www.npmjs.com/package/idb), which covers all the callbacks etc you need to manage multiple database connections and updates.

## Managing the custom store yourself

Ok, at this point it really is much better to use something like [IDB on NPM](https://www.npmjs.com/package/idb). But anyway:

A custom store in this library is just a function that takes `"readonly"` or `"readwrite"`, and returns a promise for an IDB store. Here's the implementation for `createStore`:

```js
import { promisifyRequest } from 'idb-keyval';

function createStore(dbName, storeName) {
const request = indexedDB.open(dbName);
request.onupgradeneeded = () => request.result.createObjectStore(storeName);
const dbp = promisifyRequest(request);

return (txMode) =>
dbp.then((db) => db.transaction(storeName, txMode).objectStore(storeName));
}
```

You could create your own that does something more complicated if you want! But hey, did I mention [IDB on NPM](https://www.npmjs.com/package/idb)?
Loading