npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@missive/ndex

v1.0.0

Published

Ndex is an indexedDB wrapper

Readme

Ndex — An indexedDB wrapper

It will automatically handle transactions and reuse them as many times as possible within the same event loop. You can therefore Ndex.get in a loop and Ndex will still only create a single transaction for maximum performance.

Table of Content

API

Ndex methods return a Promise, which means you can chain them or call them concurrently with Promise.all.

// Result is accessible via .then
Ndex.users.get(1).then(function(data) {
  console.log(data) // => { id: 1 }
})

// All you can chain™
Ndex.open('test', migrations)
  .then(function() {
    return Promise.all([
      Ndex.users.add({ id: 1, name: 'e' })
      Ndex.users.add({ id: 2, name: 'r' })
    ])
  })
  .then(function() {
    return Ndex.users.get(1)
  })
  .then(function() {
    console.log('done')
  })

CoffeeScript sweetness

# Haters gonna hate
Ndex.open('test', migrations)
  .then -> Promise.all([
    Ndex.users.add(id: 1, name: 'e')
    Ndex.users.add(id: 2, name: 'r')
   ])
  .then -> Ndex.users.get(1)
  .then -> console.log('done')

connect

Before using Ndex, you must always open a connection with the database. You must provide a name and a migrations object. You don’t have to provide a database version, Ndex will take care of that for you. See the migrations section.

Ndex.connect('test', migrations).then(function(connection) {
  connection.users.get(1).then(function(userData) {
    console.log(userData)
  })
})

get

Connection#get returns a single item (unless an array of keys is passed) even if multiple items match the search for it doesn’t use a cursor. If you want all results (i.e. Connection.users.index('job').get('developer')) use Connection#where.

connection.users.get(1)
connection.users.get([1, 4])

getAll

connection.users.getAll()

add

Connection#add overwrites the entry with the data passed.

// Don’t provide a key for objectStores with a keyPath
connection.users.add({ id: 1, name: 'e', job: 'developer' })
connection.users.add([
  { id: 1, name: 'e', job: 'developer' },
  { id: 2, name: 'r', job: 'developer' },
])

// Provide a key for objectStores without keyPath
connection.organizations.add('missive', { name: 'missive', est: 2014 })
connection.organizations.add(
  ['missive', 'heliom'],
  [
    { name: 'missive', est: 2014 },
    { name: 'heliom',  est: 2012 },
  ]
)

update

Connection#update only updates passed data without overwriting the entry. It will also insert the entry when non-existent.

connection.users.get(1) // { id: 1, name: 'e' }

// Waiting for the update success is required for the get to be accurate
connection.users.update(1, { name: 'r' }).then(function() {
  connection.users.get(1) // { id: 1, name: 'r' }
})

increment

Connection#increment initializes attribute to zero if null and adds the value passed (default is 1). Only makes sense for number-based attributes.

// For objectStores without keyPath
connection.stats.increment('visits')    // Increments visits entry by 1
connection.stats.increment('visits', 4) // Increments visits entry by 4

// For objectStores with a keyPath
connection.stats.increment(1, { count: 1 })            // Increments id 1’s count attribute by 1
connection.stats.increment(1, { visits: { count: 4 }}) // Increments id 1’s visits.count by 4

decrement

Ndex#decrement is an alias for Ndex#increment where the value passed is changed to a negative.

connection.stats.decrement('visits')    // Increments visits entry by -1
connection.stats.decrement('visits', 4) // Increments visits entry by -4

delete

connection.users.delete(1)
connection.users.delete([1, 4])

deleteWhere

connection#deleteWhere is an alias for Ndex#where:remove, see Ndex#where.

connection.users.deleteWhere({ gteq: 3 })
connection.users.index('job').deleteWhere({ eq: 'developer' })

clear

Clear the given object store. Note that if the object store has an autoIncrement: true key, the key won’t be reseted.

connection.users.clear()

clearAll

connection.clearAll()

index

Indexes can be used with get, getAll and where.

connection.users.index('job').get('developer')

where

Connection#where uses a cursor to iterate on a given range. Use the keyPath predicates to narrow down your range and the keys predicates to filter items in your range. For maximum performance, you really want to be as precise as possible with the range.

// keyPath predicates
// These will be applied to your objectStore’s key
connection.users.where({ lt: 3 })
connection.users.where({ lteq: 3 })
connection.users.where({ gt: 3 })
connection.users.where({ gteq: 3 })
connection.users.where({ eq: 3 })

// :eq also supports arrays. It will create a range between min (1) and max (3) (both inclusive) and filter out any results that aren’t 1 or 3 (2)
// Depending on the range it creates (i.e. `eq: [1, 1000]`), `Ndex.get([1, 1000])` will definitely be much more performant
connection.users.where({ eq: [1, 3] })

// When using an index, the predicate is applied to the index’s key
connection.users.index('job').where({ eq: 'developer' })

// Any keys predicates
// These can be used on any keys, even the non-indexed ones
connection.users.where({ only: { job: ['developer'] } })
connection.users.where({ except: { job: ['developer'] } })
connection.users.where({ uniq: 'job' })

// Pagination
connection.users.where({ limit: 3 })
connection.users.where({ offset: 2 })
connection.users.where({ order: 'desc' })

// By adding the :remove key, Ndex will delete found items from indexedDB
connection.users.where({ gteq: 3, remove: true }) // Is equivalent to `connection.users.deleteWhere({ gteq: 3 })`

Migrations

With Ndex, you don’t have handle the database version. It will always increase on each reload. Fear not! Ndex is aware of its migrations and will never run the same migration twice. We haven’t experienced any performance issue with that checkup (that is done with the existing transaction when opening the database).

migrations = {
  '201412041358_CreateUsersObjectStore': {
    type: 'createObjectStore',
    args: ['users', { keyPath: 'id', autoIncrement: true }],
  },

  '201412041527_CreateOrganizationsObjectStore': [
    { type: 'createObjectStore', args: ['organizations'] },
    { type: 'createIndex', args: ['organizations', 'est', 'est'] },
  ],

  '201412041527_AddJobIndexToUsers': {
    type: 'createIndex',
    args: ['users', 'job', 'job'],
  },
}

Logging

Ndex has a logging system that will group requests by transaction. Gives you a pretty accurate idea of what Ndex does for you and where you can refactor your requests. You can implement your own handler of use the built-in console one.

connection.handleLogging(console)

connection.users.add({ id: 1, name: 'e', job: 'developer' })
connection.users.add({ id: 2, name: 'r', job: 'developer' })
connection.users.add({ id: 3, name: 'p', job: 'developer' })
connection.users.add({ id: 4, name: 't', job: 'designer'  })

setTimeout(function() {
  connection.users.get(1)
  connection.users.get(3)
}, 100)

Without the timeout, Ndex will reuse the same transaction

connection.handleLogging(console)

connection.users.add({ id: 1, name: 'e', job: 'developer' })
connection.users.add({ id: 2, name: 'r', job: 'developer' })
connection.users.add({ id: 3, name: 'p', job: 'developer' })
connection.users.add({ id: 4, name: 't', job: 'designer'  })

connection.users.get(1)
connection.users.get(3)

Dist

$ gulp dist

Specs

$ gulp
$ open http://localhost:8080/spec