Giter VIP home page Giter VIP logo

idb's Introduction

IndexedDB with usability.

This is a tiny (~1.19kB brotli'd) library that mostly mirrors the IndexedDB API, but with small improvements that make a big difference to usability.

  1. Installation
  2. Changes
  3. Browser support
  4. API
    1. openDB
    2. deleteDB
    3. unwrap
    4. wrap
    5. General enhancements
    6. IDBDatabase enhancements
    7. IDBTransaction enhancements
    8. IDBCursor enhancements
    9. Async iterators
  5. Examples
  6. TypeScript

Installation

Using npm

npm install idb

Then, assuming you're using a module-compatible system (like webpack, Rollup etc):

import { openDB, deleteDB, wrap, unwrap } from 'idb';

async function doDatabaseStuff() {
  const db = await openDB();
}

Directly in a browser

Using the modules method directly via jsdelivr:

<script type="module">
  import { openDB, deleteDB, wrap, unwrap } from 'https://cdn.jsdelivr.net/npm/idb@8/+esm';

  async function doDatabaseStuff() {
    const db = await openDB();
  }
</script>

Using external script reference

<script src="https://cdn.jsdelivr.net/npm/idb@8/build/umd.js"></script>
<script>
  async function doDatabaseStuff() {
    const db = await idb.openDB();
  }
</script>

A global, idb, will be created, containing all exports of the module version.

Changes

See details of (potentially) breaking changes.

Browser support

This library targets modern browsers, as in Chrome, Firefox, Safari, and other browsers that use those engines, such as Edge. IE is not supported.

API

openDB

This method opens a database, and returns a promise for an enhanced IDBDatabase.

const db = await openDB(name, version, {
  upgrade(db, oldVersion, newVersion, transaction, event) {
    // …
  },
  blocked(currentVersion, blockedVersion, event) {
    // …
  },
  blocking(currentVersion, blockedVersion, event) {
    // …
  },
  terminated() {
    // …
  },
});
  • name: Name of the database.
  • version (optional): Schema version, or undefined to open the current version.
  • upgrade (optional): Called if this version of the database has never been opened before. Use it to specify the schema for the database. This is similar to the upgradeneeded event in plain IndexedDB.
    • db: An enhanced IDBDatabase.
    • oldVersion: Last version of the database opened by the user.
    • newVersion: Whatever new version you provided.
    • transaction: An enhanced transaction for this upgrade. This is useful if you need to get data from other stores as part of a migration.
    • event: The event object for the associated upgradeneeded event.
  • blocked (optional): Called if there are older versions of the database open on the origin, so this version cannot open. This is similar to the blocked event in plain IndexedDB.
    • currentVersion: Version of the database that's blocking this one.
    • blockedVersion: The version of the database being blocked (whatever version you provided to openDB).
    • event: The event object for the associated blocked event.
  • blocking (optional): Called if this connection is blocking a future version of the database from opening. This is similar to the versionchange event in plain IndexedDB.
    • currentVersion: Version of the open database (whatever version you provided to openDB).
    • blockedVersion: The version of the database that's being blocked.
    • event: The event object for the associated versionchange event.
  • terminated (optional): Called if the browser abnormally terminates the connection, but not on regular closures like calling db.close(). This is similar to the close event in plain IndexedDB.

deleteDB

Deletes a database.

await deleteDB(name, {
  blocked() {
    // …
  },
});
  • name: Name of the database.
  • blocked (optional): Called if the database already exists and there are open connections that don’t close in response to a versionchange event, the request will be blocked until they all close.
    • currentVersion: Version of the database that's blocking the delete operation.
    • event: The event object for the associated 'versionchange' event.

unwrap

Takes an enhanced IndexedDB object and returns the plain unmodified one.

const unwrapped = unwrap(wrapped);

This is useful if, for some reason, you want to drop back into plain IndexedDB. Promises will also be converted back into IDBRequest objects.

wrap

Takes an IDB object and returns a version enhanced by this library.

const wrapped = wrap(unwrapped);

This is useful if some third party code gives you an IDBDatabase object and you want it to have the features of this library.

General enhancements

Once you've opened the database the API is the same as IndexedDB, except for a few changes to make things easier.

Firstly, any method that usually returns an IDBRequest object will now return a promise for the result.

const store = db.transaction(storeName).objectStore(storeName);
const value = await store.get(key);

Promises & throwing

The library turns all IDBRequest objects into promises, but it doesn't know in advance which methods may return promises.

As a result, methods such as store.put may throw instead of returning a promise.

If you're using async functions, there's no observable difference.

Transaction lifetime

TL;DR: Do not await other things between the start and end of your transaction, otherwise the transaction will close before you're done.

An IDB transaction auto-closes if it doesn't have anything left do once microtasks have been processed. As a result, this works fine:

const tx = db.transaction('keyval', 'readwrite');
const store = tx.objectStore('keyval');
const val = (await store.get('counter')) || 0;
await store.put(val + 1, 'counter');
await tx.done;

But this doesn't:

const tx = db.transaction('keyval', 'readwrite');
const store = tx.objectStore('keyval');
const val = (await store.get('counter')) || 0;
// This is where things go wrong:
const newVal = await fetch('/increment?val=' + val);
// And this throws an error:
await store.put(newVal, 'counter');
await tx.done;

In this case, the transaction closes while the browser is fetching, so store.put fails.

IDBDatabase enhancements

Shortcuts to get/set from an object store

It's common to create a transaction for a single action, so helper methods are included for this:

// Get a value from a store:
const value = await db.get(storeName, key);
// Set a value in a store:
await db.put(storeName, value, key);

The shortcuts are: get, getKey, getAll, getAllKeys, count, put, add, delete, and clear. Each method takes a storeName argument, the name of the object store, and the rest of the arguments are the same as the equivalent IDBObjectStore method.

Shortcuts to get from an index

The shortcuts are: getFromIndex, getKeyFromIndex, getAllFromIndex, getAllKeysFromIndex, and countFromIndex.

// Get a value from an index:
const value = await db.getFromIndex(storeName, indexName, key);

Each method takes storeName and indexName arguments, followed by the rest of the arguments from the equivalent IDBIndex method.

IDBTransaction enhancements

tx.store

If a transaction involves a single store, the store property will reference that store.

const tx = db.transaction('whatever');
const store = tx.store;

If a transaction involves multiple stores, tx.store is undefined, you need to use tx.objectStore(storeName) to get the stores.

tx.done

Transactions have a .done promise which resolves when the transaction completes successfully, and otherwise rejects with the transaction error.

const tx = db.transaction(storeName, 'readwrite');
await Promise.all([
  tx.store.put('bar', 'foo'),
  tx.store.put('world', 'hello'),
  tx.done,
]);

If you're writing to the database, tx.done is the signal that everything was successfully committed to the database. However, it's still beneficial to await the individual operations, as you'll see the error that caused the transaction to fail.

IDBCursor enhancements

Cursor advance methods (advance, continue, continuePrimaryKey) return a promise for the cursor, or null if there are no further values to provide.

let cursor = await db.transaction(storeName).store.openCursor();

while (cursor) {
  console.log(cursor.key, cursor.value);
  cursor = await cursor.continue();
}

Async iterators

You can iterate over stores, indexes, and cursors:

const tx = db.transaction(storeName);

for await (const cursor of tx.store) {
  // …
}

Each yielded object is an IDBCursor. You can optionally use the advance methods to skip items (within an async iterator they return void):

const tx = db.transaction(storeName);

for await (const cursor of tx.store) {
  console.log(cursor.value);
  // Skip the next item
  cursor.advance(2);
}

If you don't manually advance the cursor, cursor.continue() is called for you.

Stores and indexes also have an iterate method which has the same signature as openCursor, but returns an async iterator:

const index = db.transaction('books').store.index('author');

for await (const cursor of index.iterate('Douglas Adams')) {
  console.log(cursor.value);
}

Examples

Keyval store

This is very similar to localStorage, but async. If this is all you need, you may be interested in idb-keyval. You can always upgrade to this library later.

import { openDB } from 'idb';

const dbPromise = openDB('keyval-store', 1, {
  upgrade(db) {
    db.createObjectStore('keyval');
  },
});

export async function get(key) {
  return (await dbPromise).get('keyval', key);
}
export async function set(key, val) {
  return (await dbPromise).put('keyval', val, key);
}
export async function del(key) {
  return (await dbPromise).delete('keyval', key);
}
export async function clear() {
  return (await dbPromise).clear('keyval');
}
export async function keys() {
  return (await dbPromise).getAllKeys('keyval');
}

Article store

import { openDB } from 'idb/with-async-ittr.js';

async function demo() {
  const db = await openDB('Articles', 1, {
    upgrade(db) {
      // Create a store of objects
      const store = db.createObjectStore('articles', {
        // The 'id' property of the object will be the key.
        keyPath: 'id',
        // If it isn't explicitly set, create a value by auto incrementing.
        autoIncrement: true,
      });
      // Create an index on the 'date' property of the objects.
      store.createIndex('date', 'date');
    },
  });

  // Add an article:
  await db.add('articles', {
    title: 'Article 1',
    date: new Date('2019-01-01'),
    body: '…',
  });

  // Add multiple articles in one transaction:
  {
    const tx = db.transaction('articles', 'readwrite');
    await Promise.all([
      tx.store.add({
        title: 'Article 2',
        date: new Date('2019-01-01'),
        body: '…',
      }),
      tx.store.add({
        title: 'Article 3',
        date: new Date('2019-01-02'),
        body: '…',
      }),
      tx.done,
    ]);
  }

  // Get all the articles in date order:
  console.log(await db.getAllFromIndex('articles', 'date'));

  // Add 'And, happy new year!' to all articles on 2019-01-01:
  {
    const tx = db.transaction('articles', 'readwrite');
    const index = tx.store.index('date');

    for await (const cursor of index.iterate(new Date('2019-01-01'))) {
      const article = { ...cursor.value };
      article.body += ' And, happy new year!';
      cursor.update(article);
    }

    await tx.done;
  }
}

TypeScript

This library is fully typed, and you can improve things by providing types for your database:

import { openDB, DBSchema } from 'idb';

interface MyDB extends DBSchema {
  'favourite-number': {
    key: string;
    value: number;
  };
  products: {
    value: {
      name: string;
      price: number;
      productCode: string;
    };
    key: string;
    indexes: { 'by-price': number };
  };
}

async function demo() {
  const db = await openDB<MyDB>('my-db', 1, {
    upgrade(db) {
      db.createObjectStore('favourite-number');

      const productStore = db.createObjectStore('products', {
        keyPath: 'productCode',
      });
      productStore.createIndex('by-price', 'price');
    },
  });

  // This works
  await db.put('favourite-number', 7, 'Jen');
  // This fails at compile time, as the 'favourite-number' store expects a number.
  await db.put('favourite-number', 'Twelve', 'Jake');
}

To define types for your database, extend DBSchema with an interface where the keys are the names of your object stores.

For each value, provide an object where value is the type of values within the store, and key is the type of keys within the store.

Optionally, indexes can contain a map of index names, to the type of key within that index.

Provide this interface when calling openDB, and from then on your database will be strongly typed. This also allows your IDE to autocomplete the names of stores and indexes.

Opting out of types

If you call openDB without providing types, your database will use basic types. However, sometimes you'll need to interact with stores that aren't in your schema, perhaps during upgrades. In that case you can cast.

Let's say we were renaming the 'favourite-number' store to 'fave-nums':

import { openDB, DBSchema, IDBPDatabase } from 'idb';

interface MyDBV1 extends DBSchema {
  'favourite-number': { key: string; value: number };
}

interface MyDBV2 extends DBSchema {
  'fave-num': { key: string; value: number };
}

const db = await openDB<MyDBV2>('my-db', 2, {
  async upgrade(db, oldVersion) {
    // Cast a reference of the database to the old schema.
    const v1Db = db as unknown as IDBPDatabase<MyDBV1>;

    if (oldVersion < 1) {
      v1Db.createObjectStore('favourite-number');
    }
    if (oldVersion < 2) {
      const store = v1Db.createObjectStore('favourite-number');
      store.name = 'fave-num';
    }
  },
});

You can also cast to a typeless database by omitting the type, eg db as IDBPDatabase.

Note: Types like IDBPDatabase are used by TypeScript only. The implementation uses proxies under the hood.

Developing

npm run dev

This will also perform type testing.

To test, navigate to build/test/ in a browser. You'll need to set up a basic web server for this.

idb's People

Contributors

aleclarson avatar alexeyraspopov avatar aretecode avatar asiaracz avatar bochap avatar dependabot[bot] avatar filipproch avatar frlinw avatar grebe avatar jack-works avatar jakearchibald avatar jeffposnick avatar keithhenry avatar mariusgundersen avatar mbrookes avatar mitchellwills avatar mlrv avatar nachoab avatar notwoods avatar randname avatar rmacklin avatar samccone avatar surma avatar tom5760 avatar tropicadri avatar wellspringcs avatar xiaoxingwa avatar zhouhanseng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

idb's Issues

Support ES modules

Currently idb does not support ES modules within the browser. To illustrate this, I've created a simple demo at puradox/idb-demo.

The following error is shown in the console after opening the demo:

Uncaught SyntaxError: The requested module '.../idb/lib/idb.js' does not provide an export named 'default'

Handle case when open connection gets blocked

Sometimes in firefox the open call gets blocked and the callback sent to idb never gets called.
It might be helpful to add an event listener for request.onblocked with a promise rejection inside.
If you think this is a good idea I can make a PR.

IDBIndex is not defined

I am working on single page web app and trying to make it offline using serviceworker and IndexedDb, But I am not able to use this library for IndexedDb becasue during the gulp serve it is throwing this: 'ReferenceError: IDBIndex is not defined
at /home/user/Desktop/cincinati/my_app/node_modules/idb/lib/idb.js:87:40' tell me what to do
'

gulp serve failing on Windows 10 x64

I'm trying to use idb on Windows for a Udacity project, but I can't even get gulp serve to succeed. Can you please tell me what I can do so that I can run gulp serve?

C:\Users\avite\OneDrive\Documents\github\idb>npm --version
3.10.8

C:\Users\avite\OneDrive\Documents\github\idb>node --version
v6.9.1

C:\Users\avite\OneDrive\Documents\github\idb>git log -n 1
commit 314309cf7ed3db66468a0d12e31663904dcc59f9
Author: Jake Archibald <[email protected]>
Date:   Fri Aug 12 14:00:31 2016 +0100

    README consistency

C:\Users\avite\OneDrive\Documents\github\idb>git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

C:\Users\avite\OneDrive\Documents\github\idb>gulp serve
[21:33:57] Using gulpfile ~\OneDrive\Documents\github\idb\gulpfile.js
[21:33:57] Starting 'serve'...
[21:33:57] Starting 'clean'...
[21:33:57] Finished 'clean' after 196 ms
[21:33:57] Starting 'copy'...
[21:33:57] Starting 'js'...
[21:33:58] Starting 'copy-lib'...
[21:33:58] Finished 'copy-lib' after 238 ms
[21:33:58] Finished 'copy' after 714 ms
[21:33:58] Browserify Error { Error: Cannot find module 'babelify/node_modules/babel-core/node_modules/regenerator/runtime' from 'C:\Users\avite\OneDrive\Documents\github\idb\test'
    at C:\Users\avite\OneDrive\Documents\github\idb\node_modules\resolve\lib\async.js:46:17
    at process (C:\Users\avite\OneDrive\Documents\github\idb\node_modules\resolve\lib\async.js:173:43)
    at ondir (C:\Users\avite\OneDrive\Documents\github\idb\node_modules\resolve\lib\async.js:188:17)
    at load (C:\Users\avite\OneDrive\Documents\github\idb\node_modules\resolve\lib\async.js:69:43)
    at onex (C:\Users\avite\OneDrive\Documents\github\idb\node_modules\resolve\lib\async.js:92:31)
    at C:\Users\avite\OneDrive\Documents\github\idb\node_modules\resolve\lib\async.js:22:47
    at FSReqWrap.oncomplete (fs.js:123:15)
  stream:
   Labeled {
     _readableState:
      ReadableState {
        highWaterMark: 16,
        buffer: [],
        length: 0,
        pipes: [Object],
        pipesCount: 1,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        objectMode: true,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null,
        resumeScheduled: false },
     readable: true,
     domain: null,
     _events:
      { end: [Object],
        error: [Object],
        data: [Function: ondata],
        _mutate: [Object] },
     _eventsCount: 4,
     _maxListeners: undefined,
     _writableState:
      WritableState {
        highWaterMark: 16,
        objectMode: true,
        needDrain: false,
        ending: true,
        ended: true,
        finished: true,
        decodeStrings: true,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        buffer: [],
        pendingcb: 0,
        prefinished: true,
        errorEmitted: false },
     writable: true,
     allowHalfOpen: true,
     _options: { objectMode: true },
     _wrapOptions: { objectMode: true },
     _streams: [ [Object] ],
     length: 1,
     label: 'deps' } }

Safari IndexedDb duplicate db

I created issue on stackoverflow link with screenshort
This bug reproduce only in Safari
https://stackoverflow.com/questions/53560736/indexeddb-duplicate-only-in-safari
Code:

    Idb.initDb()
        .then(isUpdate => {
                console.log(isUpdate);
            }
        ).catch((error) => {
        console.log(error);
    });

    static initDb = async () => {
        const db = await idb.open(dbState.name, dbState.version, (upgradeDB) => {
            dbState.stores.forEach((store) => {
                if (!upgradeDB.objectStoreNames.contains(store)) {
                    upgradeDB.createObjectStore(store);
                    console.log('The store -', store, ' was added to db');
                }
            })
        });
    };

Safari Worker issue

When using idb.js in a worker context on Safari, you get

TransactionInactiveError: Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished.

However, when running the same code in the main context, or on chrome or firefox in the main or in a worker context, it works as expected.

const DB_NAME = "db-123";
const STORE_NAME = "store-name";

const dbPromise = idb.open(DB_NAME, 1, upgradeDB => {
  upgradeDB.createObjectStore(STORE_NAME);
});

const init = async () => {
  const db = await dbPromise;

  const txWrite = db.transaction(STORE_NAME, "readwrite");
  await txWrite.objectStore(STORE_NAME).put("hello", "foo");

  const txRead = db.transaction(STORE_NAME);

  console.log(type, "got transaction");

  const a = await txRead.objectStore(STORE_NAME).get("foo");

  console.log(type, "got a", a);

  const b = await txRead.objectStore(STORE_NAME).get("foo");

  console.log(type, "got b", b);
};

init().catch(e => console.error(e));

In Safari, this code will output

[Log] MAIN – "got transaction"
[Log] MAIN – "got a" – "hello"
[Log] MAIN – "got b" – "hello"

[Log] WORKER – "got transaction" 
[Log] WORKER – "got a" – "hello"
TransactionInactiveError: Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished.

Whereas in Chrome or Firefox you don't get the TransactionInactiveError.

There is a sandbox here https://codesandbox.io/s/rj4wnn5lzn

Cannot open cursor on iOS with 'nextunique' direction

Issue description

If openCursor is called with the direction nextunique an exception is thrown on iOS Safari:

ERROR Error: Uncaught (in promise): UnknownError: Unable to open cursor

Additional details

  • If I overwrite the attribute with next, the cursor is opened successfully, but that's not how it supposed to work! Any idea why this happens?
  function promisifyRequestCall(obj, method, args) {
    var request;
    var p = new Promise(function(resolve, reject) {
      if (method === 'openCursor' && args[1] && args[1] === 'nextunique' ) {
        args[1] = 'next'
      }
      request = obj[method].apply(obj, args);
      promisifyRequest(request).then(resolve, reject);
    });

    p.request = request;
    return p;
  }

window.webkitStorageInfo deprecated

Hello.

I'm using this library for couple of months and everything works great.
Lately I've started getting this warning in chrome's console :

upload_3_15_2017_at_6_41_08_pm

I'm guessing that this warning came from this library because this is the only one witch handle my app's indexed-DB.

Is there any new version in progress that fix this issue?

dbPromise.keys() throws error 'Cannot read property 'openKeyCursor' of undefined' in latest chrome

this is for udacity project 2,
I need to retrieve all data from indexed db to hydrate a redux store,

I wanted to just get the keys, and then pull in the relevent ones, however, the following throws error VM892:247 Uncaught (in promise) TypeError: Cannot read property 'openKeyCursor' of undefined(…)

keys() {
    return dbPromise.then(db => {
      const tx = db.transaction('keyval');
      const keys = [];
      const store = tx.objectStore('keyval');

      // This would be store.getAllKeys(), but it isn't supported by Edge or Safari.
      // openKeyCursor isn't supported by Safari, so we fall back
      (store.iterateKeyCursor || store.iterateCursor).call(store, cursor => {
        if (!cursor) return;
        keys.push(cursor.key);
        cursor.continue();
      });

      return tx.complete.then(() => keys);
    });
  }

not a blocker for me, as this following functions does indeed work
dbPromise.then(db => {
return db.transaction('objs')
.objectStore('objs').getAll();
}).then(allObjs => console.log(allObjs));

Batch store operation

I have a use case to insert about 15000 records. Does the library support something like a batch operation instead of running a loop and creating multiple transactions?

AddAll

Hello! How can I insert an array of objects using idb? Thanks!

'Unhandled Promise Rejection' thrown by idb.open

Env: iOS Safari 11.1

Safari still forbids IndexedDB in iframe, so when idb.open() is called in an iframe, IDBFactory.open() function fails down, which causes the promise rejection, code debug as follows:

code1

request.onupgradeneeded throws request is undefined error before idb.open() can normally return a promise object.
That's why even i do write idb.open().catch() in order to catch the promise rejection, this doesn't work. The Unhandled Promise Rejectionis thrown as follows:

code2

I can do nothing to avoid this error console.

Plz solve this request is undefined error when db fails to open, and return a promise object normally.

Looking forward to your reply. Thank you.

Update readme

Dear Jake,

One nice update would be to (1) add an index at the top of the Readme on which you can click to go to the chapter down below, and (2) add a chapter on the install method. Just a simple:

"To install do npm install idb"

will do.

Another think I wanted to ask is that you are currently linking a post to an article about Safari bugs from 2014... Is this still relevant now in almost 2018?

I can't import idb

Hello, please I have a small issue, when trying to import idb locally, I get this error: '/idb.js' does not provide an export named 'default', where as I followed you Udacity course and it worked fine on node witter app.

Ability to keep transaction open while writes are happening every X seconds?

I've got a project which writes to IndexedDB in spurts of possibly ~100-1000 writes within 30-60seconds. Traditionally, if a transaction closes after every write, the performance is awful. So I was wondering if this library could be used to keep a transaction open for X amount of seconds after the last write? This way, after a burst of writes, the transaction would close within a few seconds. With home-brew testing the performance increases considerably, however there are other intricacies of the IndexedDB API that I was having some trouble with and thought it might be better to find an existing (lightweight) library which takes this use-case into account.

Using library in angular 2+

I am following the Udacity course - Offline web applications

I already made npm i idb

In my service (user.service.ts):
import {IDBStatic} from 'idb';
...
createIdbHospital () { idb.open ('hospitaldb', 1, (upgradeDB) => { let store = upgradeDB.createObjectStore ('users', {keyPath: '_id'}); store.createIndex ('by-name', 'name'); }); }

but it does not work, please, I need to be able to use the library in angular, there is nothing like adding it in my service.modulo.ts or as I should do, thanks

unable to import idb into react component

working on project 2 of udacity course

see relevant code here:
https://github.com/noahehall/udacity-trainschedule/blob/indexeddb/src/containers/start/start.js

if i attempt to
import IdbKeyval from 'serviceworkers/idb/idb';

it throws error
`
/Users/halln/git/udacity/projects/trainschedule/dist/server.js:20898
proxyRequestMethods(Index, '_index', IDBIndex, [
^

ReferenceError: IDBIndex is not defined
at /Users/halln/git/udacity/projects/trainschedule/dist/server.js:20898:40
at Object.__dirname./Users/halln/git/udacity/projects/trainschedule/node_modules/idb/lib/idb.js (/Users/halln/git/udacity/projects/trainschedule/dist/server.js:21114:2)
at s (/Users/halln/git/udacity/projects/trainschedule/dist/server.js:1:316)
at /Users/halln/git/udacity/projects/trainschedule/dist/server.js:1:367
at Object.__dirname./Users/halln/git/udacity/projects/trainschedule/src/serviceworkers/idb/idb.js.../../constants.js (/Users/halln/git/udacity/projects/trainschedule/dist/server.js:94623:12)
at s (/Users/halln/git/udacity/projects/trainschedule/dist/server.js:1:316)
at /Users/halln/git/udacity/projects/trainschedule/dist/server.js:1:367
at Object.__dirname./Users/halln/git/udacity/projects/trainschedule/src/containers/start/start.js.../../components/forms/forms.js (/Users/halln/git/udacity/projects/trainschedule/dist/server.js:93753:12)
at s (/Users/halln/git/udacity/projects/trainschedule/dist/server.js:1:316)
at /Users/halln/git/udacity/projects/trainschedule/dist/server.js:1:367
`

However, it works perfectly in service worker context

im trying to do what this guy did here:
https://github.com/nreoch25/react-redux-indexeddb/blob/master/app/components/Arts.js

and retrieve items from indexedb and inject them into redux store

Browser compatibility update for Firefox, Edge & Safari

Thank-you for this library Jake. I've been testing the transaction lifetime issues in current versions of browsers and I think the status has changed and the README can be updated.

I created a test case at https://gist.github.com/robertknight/462d16b46a438ab67e29301861f5d55f which tests the use of IDB + Promises for a use case that tries to read-modify-write a value in a single transaction.

The test passes in current versions of Safari, Edge & Chrome but still fails in Firefox. I filed a bug for Firefox at https://bugzilla.mozilla.org/show_bug.cgi?id=1383029 .

[pre-script] put method not working

I am using idb-promised by including this script before dbhelper.js

Put method is not working,
dbpromise

Uncaught (in promise) DOMException: Failed to execute 'put' on 'IDBObjectStore': The object store uses out-of-line keys and has no key generator and the key parameter was not provided.

This is where the DevTools say that the code is failing:

idb/lib/idb.js

Line 23 in b17dfbd

request = obj[method].apply(obj, args);

I think the issue is with this function in particular:

idb/lib/idb.js

Line 52 in b17dfbd

function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) {

Please look into this

Null and Undefined types in tsd with --strictNullChecks

Looks like the tsd might need to be updated for TypeScript 2. When using --strictNullChecks the type definitions need to explicitly allow null or undefined for parameter types.

Using e.g. iterateCursor(null, "prev", callback) gives a ts compiler error because the type definition doesn't explicitly allow null.

Electron js support

This may seem a little trivial, but I guess it could help some other person.

I decided to mess around with IDB in electron js. I started to get some errors in the console saying that .open wasn't a function. So I looked into the package.json file and I saw that the node specific main.js file would reject any method request because it would have to be in a browser. So all I did was change the start js file in node seeing as electron is counted as a node environment.

So I don't know if you would just want to delete the node.js specific main.js so it could be used in electronjs for anyone else without too much hassle, or maybe even a little small note in the readme saying to change the node specific one.

I hope this makes sense.

v4 feedback/suggestions

First of all, great work on new API changes in v4! I spent some time trying out the new version today, and I didn't run into any issue. I'm pretty sure we could migrate to this in Workbox once we move away from running our unit tests in a node-mock environment (where the IDB-mock library we use only supports IndexedDB v1).

Also, I really like the use of proxies to make things smaller, and having a weakmap-backed unwrap function is a lot nicer than _request, _cursor, _index, etc..

A few suggestions, which I think are definitely optional, but could provide some nice sugar and sensible defaults for folks that don't necessarily know IndexedDB best practices.

Add some shorthand methods for common requests.

Some really common pattern with idb are to do things like this:

// Get all entries in an object store matching a query
db.transaction('my-store').objectStore('my-store').getAll(query);

// Get the first entry in an object store via an index
db.transaction('my-store').objectStore('my-store').index('my-index').getAll(query, 1)

// Add an entry to an object store.
db.transaction('my-store', 'readwrite').objectStore('my-store').put({...});

There could be shorthand methods for these types of requests. E.g. something like from(), and in(), where the above would become:

// `.from(store)` is shorthand for `.transaction(store).objectStore(store)`
db.from('my-store').getAll(query);

// `.from(store, index)` is shorthand for `.transaction(store).objectStore(store).index(index)`
db.from('my-store', 'my-index').getAll(query);

// `.in(store)` is shorthand for `.transaction(store, 'readwrite').objectStore(store)`
db.in('my-store').put({...});

Add an open timeout

Due to this IndexedDB issue there are cases where the openDb() promise will never resolve. While this is a bit of an edge case, when it does happen (which I've run into several times when running tests) it's very difficult to debug.

How we solved this in our Workbox IndexedDB wrapper is to use a timeout that causes the promise to reject after 2 seconds (by default, the timeout is customizable).

I found this to be quite helpful when debugging tests, because seeing that error in the console made it much easier to spot issues.

Make the blocking callback default to calling db.close()

Most developers (AFAIK) open connections to a database and then keep the connection open for the life of the page. While in general this is a good practice (opening can be slow), it does increase the risk that a newer version of the page in another tab will get blocked when trying to upgrade the DB.

If the blocking callback could call db.close() by default (when not set), this situation would be far less likely to occur. It'll also make the above scenario far less likely as well.

The implication is now the old page will not be able to connect to the database, but this seems better than a brand new page not being able to connect.

Of course, if this behavior is not what a developer wants, they can provide their own blocking callback. I think it's fairly safe to assume that anyone who specifically doesn't want the auto-close behavior is advanced enough to know how to use the blocking callback to suit their site's needs.


On the other hand, if you wanted to keep the core exports of idb really small and as true to the IndexedDB spec as possible, the idb library could come with another, separate module file that exports a wrapper class with some of the functionality I described above.

The wrapper could also include features like lazily opening a connection to a DB in idle periods or after the first request is made. We've found this to be really nice in service worker, where you don't necessarily want to be opening IndexedDB connection eagerly every time the service worker starts up.

Anyway, again, nice work on the update! And I'd be happy to submit PRs for any of the features I suggested here.

Cannot install tools from Device Status

Issue

Expected behaviour

Install all tools from Device Status

Actual behaviour

Following tools cant be installed.

issue

following erros are happening after clicking on Install

dumpdecrypted: application crashes
all others: nothing

Environment

idb Version

recent from kali repos

Workstation Operating System

Recent Kali Rolling (2018.4)

Device iOS Version

10.2.1

Include IDBDatabase event handlers

I noticed that the db object returned by promise is currently missing IDBDatabase event handlers props: onclose, onabort, onerror, onversionchange. Instead of accessing through db._db, these could be added to the proxyProperties on the DB prototype. Unless there was some reason for leaving them out.

  proxyProperties(DB, '_db', [
    'name',
    'version',
    'objectStoreNames',
    'onclose',
    'onabort',
    'onerror',
    'onversionchange'
  ]);

Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.

I am getting this error Failed to store record in an IDBObjectStore: BlobURLs are not yet supported. in ios 11 safari. To my knowlege (caniuse), this is incorrect, and bloburls are supported in this browser.

In the following code, icimageStore.put is where the error is thrown from.

    function populateIcImageTable(xml, clearIdbData, imageUrl) {

        const dbPromise = idb.open(idbHelper.databaseName, idbHelper.databaseVersionNumber, idbHelper.setupDatabase);

        let imageBlob;
        // fetch blob
        imageBlob = xml.response;

        // workout image key from the image filename
        const icimageKey = getImageKeyFromUrl(imageUrl);

        if (icimageKey === null){
            // Silently exit as we don't care if the icimage URL is invalid and its highly unlikely that it would be.
            return true;
        }

        // populate pdr table
        dbPromise.then(function (db) {
            const tx = db.transaction('icimage', 'readwrite');
            const icimageStore = tx.objectStore('icimage');

            // clear the existing data?
            if (clearIdbData){
                icimageStore.clear();
                console.log('icimage: store clear requested');
            }

            return icimageStore;

        }).then(function(icimageStore){

            // empty the table before updating it.
            if (clearIdbData) {
                console.log('icimage: store cleared');
            }

            return icimageStore.put({
                icimageKey: icimageKey,
                imageUrl: imageUrl,
                imageBlob: imageBlob

            });

        }).then(function () {

            console.log('icimage stored');

        }).catch(function(e){

            if (Raven){
                Raven.captureException(e);
            }

            console.error(e.stack);

            console.log('Error storing icimage for : '+e);
            displayNotification('danger', 'There was a problem updating the interchange image cache, please try again.', e, 'timeout');

        });

    }

Below is the log. 61089.JPG is the last image and apparently it is stored correctly.

log_censored

This bug may be due to my code, but it seems like an incorrect error message at least. Thanks.

"keys()" from keyval store example from README breaks on Edge and IE

Thanks for the library before anything, it helped me a lot to deal with IndexedDB.
I'm using the Keyval Store example you provided, with some adaptations (because I wanted many stores in a single DB), and the method keys was breaking in Edge and IE.
After debugging, I found that the promisified store in these browsers doesn't have the openKeyCursor property, only openCursor, but both iterateCursor and iterateKeyCursor exist, because of that the keys method calls iterateKeyCursor and results in an error.
I fixed the problem by replacing (store.iterateKeyCursor || store.iterateCursor) for (store.iterateCursor || store.iterateKeyCursor), thought I didn't test yet on Safari, this OR expression still seems pointless for me. Should it be necessary when creating the "iterateCursor" methods in the prototype, to check if exists the corresponding "openCursor" in the nativeObject?
If yes, I can gladly implement that, although the indexedDB API is still obscure for me.

InvalidStateError: A mutation operation was attempted on a database that did not allow mutations.

Hi, I am struggling with an error trying to create the database
It works only on Chrome,
Firefox gives the error:
InvalidStateError: A mutation operation was attempted on a database that did not allow mutations.
MS Edge does not work at all.

My code is something like this:

STORE = "storeName";
VERSION = 1;

  dbPromise = openDb("dbName", this.VERSION, upgradeDB =>
      upgradeDB
        .createObjectStore(this.STORE, { keyPath: "name" })
        .createIndex("name", "name", { unique: true })
    );

Catching and consolidation errors

Following the example on the page for having a method that sets the keyval in a store, but here with a created index having a unique constraint.

If we were to generate an error on the the uniqueness constraint and try to catch these errors, reasoning about them is proven difficult.

  function set(key, val) {
    return dbPromise.then(db => {
      const tx = db.transaction('keyval', 'readwrite');
      const req = tx.objectStore('keyval').index('myIndex').put(val, key);
      return tx.complete;
    }).catch(console.error);
  }

Here we catch on the whole method, from the promise returned by tx.complete. The request returned, by trying to put the value in the store, will throw a unique index constraint error request.onerror, which will propagate/bubble up to the transaction causing it to abort due the failed request.
When the transaction is aborted transaction.error is set to null and since the tx.complete will reject from either of the transaction.onerror or transaction.onabort events, we just catch error null from transaction.onerror. The failed request will just throw as an uncaught error.

Since transactions can fail for reasons not tied to a particular request, e.g. IO errors, there still needs to be a listener to the transaction.onerror event, but transaction.onabort seems lost in translation.

We could catch on both the request and the transaction, since the request provides a more detailed error of what caused the error being thrown, thought not guaranteed.

Request error event not guaranteed when aborting a transaction.
This does not always result in any error events being fired. For example if a transaction is aborted due to an error while committing the transaction, or if it was the last remaining request that failed.
https://www.w3.org/TR/IndexedDB/#abort-transaction

Catch for all requests and transactions individually or consolidate them as an iterable where the first request failed will be caught.

Promise.all([req, tx.complete]).catch(console.error);

Await not working on promise

As I understand, the calls get, put return a promise when using idb. I have the following snippet in my service worker. All it does is get an object from IndexedDB, update a property and write it back. I get an error Uncaught SyntaxError: Unexpected identifier for the line containing await. Wondering what is wrong ?

async function updateNoteId(localid, realid) {
  return getDb.then(db => {
    const tx = db.transaction('notes', 'readwrite');
    let note = await tx.objectStore('notes').get(localid);
    note.id = realid;
    return tx.objectStore('notes').put(note);
  });
} 

This is more of a question, i figured posting it as an issue as people may not know of this specific library.

simple import

Trying to switch to typescript. I do

 import * as idb from 'idb';

and then:

private readonly dbPromise = idb.open('theDbName', 1, upgradeDB => { /* etc */ });

Immediately, the editor complains that:

Property 'open' does not exist on type 'typeof "long/path"'.

so I try:

private readonly dbPromise = idb.default.open('theDbName', 1, upgradeDB => { /* etc */ });

which passes from the editor, but produces a run-time error:

Cannot read property 'open' of undefined

How should this be done?

cursor.continuePrimaryKey is not a function??

I'm trying to use this function on the cursor object as per MDN doc here. As written, I'm using this method inside web worker but seems like it's not available. Can it be confirmed? All I want to achieve is to find a row in the idb with a given id and update one of it's fields.

Unable to idb.open()

Hi Jake,

Thanks for such amazing library.
It has greatly simplified and enhanced developer experience w.r.t. working with Indexed DB.

It has been awesome so far, until recently we tried running our app on iOS safari and iOS chrome.
Unfortunately site doesn't seem to open at all!!

We examined and found that issue is because of IndexedDB Promised API.

We tried connecting our iPhone and tried remote safari debugger.
It throws following error message.

screen shot 2016-09-04 at 9 00 55 pm

Later, we also found that it doesn't even open on Desktop Safari!!! Same error!!

Can you please look into the issue and see if you can fix it.

Thanks, Vinay

iterateCursor

Thanks for your excellent library, it's convenient and works well.

My problem is with your "iterateCursor" function. I am trying to use it like this:

getSomeData(store) {
  let results = [];

  return DBPROMISE
    .then((db) => {
      const tx = db.transaction([store]);
      tx.objectStore([store]).iterateCursor(cursor => {
        if (!cursor) return;
        results.push(cursor.value);
        cursor.continue();
      });
      return tx.complete.then(() => { return results; });
    });
}

The above code, which, I believe, should work under all browsers, does work in Chrome Canary v.56 (i.e. it returns data) but doesn't work (i.e. returns no data) in normal Chrome v.54 or Firefox v.47. What am I missing?

Save datas inside async function

Hello,

I'm following this tutorial :

I put this code inside an async function :

 if (!('indexedDB' in window)) {return null;}
  return dbPromise.then(db => {
    const tx = db.transaction('events', 'readwrite');
    const store = tx.objectStore('events');
    return Promise.all(events.map(event => store.put(event)))
    .catch(() => {
      tx.abort();
      throw Error('Events were not added to the store');
    });
  });

and i get Events were not added to the store error
Maybe it's related to #31 but i'm new to async/await and indexedDB sorry ;)
Any ideas please ?

thx

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.