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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@allnulled/browsie

v1.0.4

Published

IndexedDB wrapper.

Readme

browsie

IndexedDB wrapper.

Installation

Download from console:

npm i -s @allnulled/browsie

Import from code:

<script src="node_modules/@allnulled/browsie/browsie.bundled.js"></script>

The global window.Browsie should now be available.

API

await Browsie.listDatabases()
await Browsie.deleteDatabase(dbName)
await Browsie.createDatabase(dbName, storeDefinition, version = 1, versionUpgrades = [])
await Browsie.getSchema(dbName)
await Browsie.globMatch(patterns, texts)
browsie = new Browsie(dbName)
await browsie.open()
await browsie.select(store, filter)
await browsie.insert(store, item)
await browsie.update(store, id, item)
await browsie.delete(store, id)
await browsie.insertMany(store, items)
await browsie.updateMany(store, filter, item)
await browsie.deleteMany(store, filter)
await browsie.triggers.register(eventId, triggerId, callback, options)
await browsie.triggers.emit(eventId, parameters)
await browsie.triggers.unregister(triggerId)
await browsie.triggers.load(triggersScript) // For this method, the library occupies like x20 times hahahaha maybe more HAHAHAHAHAH!

On storeDefinitions:object we want an object like:

{
  articulos: ["!nombre", "categorias", "resumen", "fecha", "autor", "inspiracion", "tags"],
  productos: ["!nombre", "!modeloId", "categorias", "descripcion"]
}

Here, the ! is to declare unique indexes.

On version:integer we want the current version of the database. This must be updated when a versionUpgrades is added.

On versionUpgrades:object we want an object like this:

{
  2: function(db) {
    if (!db.objectStoreNames.contains("orders")) {
      const ordersStore = db.createObjectStore("orders", {
        keyPath: "id",
        autoIncrement: true,
      });
      ordersStore.createIndex("orderDate", "orderDate", { unique: false });
      ordersStore.createIndex("userId", "userId", { unique: false });
    }
  }
}

Here, every version should have its own function.

On eventId:string, triggers section, we want:

  • On browsie.triggers.register(eventId, ...): a selector of events that accepts * as magic character.
  • On browsie.triggers.emit(eventId, ...): the name of the event, that does NOT accept * as magic character.

On triggersScript:string we want a @allnulled/triggers-script script as string.

Example

This is the test that is ensuring the API right now. There are 3 tests in 1 function: Data_api, Schema_api and Triggers_api.

(async function main() {
  try {
    Data_api: {
      await Browsie.deleteDatabase("browsie_test_data");
      await Browsie.createDatabase("browsie_test_data", {
        articulos: ["!nombre", "categorias", "resumen", "fecha", "autor", "inspiracion", "tags"],
        productos: ["!nombre", "!modeloId", "categorias", "descripcion"]
      }, 2, {
        2: function(db) {
          if (!db.objectStoreNames.contains("orders")) {
            const ordersStore = db.createObjectStore("orders", {
              keyPath: "id",
              autoIncrement: true,
            });
            ordersStore.createIndex("orderDate", "orderDate", { unique: false });
            ordersStore.createIndex("userId", "userId", { unique: false });
          }
        }
      });
      const db = new Browsie("browsie_test_data");
      await db.open();
      const id1 = await db.insert("articulos", { nombre: "Artículo 1" });
      const id2 = await db.insert("articulos", { nombre: "Artículo 2" });
      const id3 = await db.insert("articulos", { nombre: "Artículo 3" });
      const selection1 = await db.select("articulos", i => i);
      if (selection1.length !== 3) {
        throw new Error("Test falló en aserción 1");
      }
      await db.update("articulos", id1, { nombre: "Artículo cambiado 1" });
      await db.update("articulos", id2, { nombre: "Artículo cambiado 2" });
      await db.update("articulos", id3, { nombre: "Artículo cambiado 3" });
      const selection2 = await db.select("articulos", i => i);
      if (selection2.length !== 3) {
        throw new Error("Test falló en aserción 2");
      }
      if (selection2[0].nombre !== "Artículo cambiado 1") {
        throw new Error("Test falló en aserción 3");
      }
      await db.delete("articulos", id3);
      await db.delete("articulos", id2);
      await db.delete("articulos", id1);
      const selection3 = await db.select("articulos", i => i);
      if (selection3.length !== 0) {
        throw new Error("Test falló en aserción 4");
      }
      await db.insertMany("articulos", [{
        nombre: "Papaguata1",
        categorias: "Papaguata, tocolombo, meketino, paquetumba, nonomuni",
        resumen: "wherever",
      }, {
        nombre: "Papaguata2",
        categorias: "Papaguata, tocolombo, meketino, paquetumba, nonomuni",
        resumen: "wherever",
      }, {
        nombre: "Papaguata3",
        categorias: "Papaguata, tocolombo, meketino, paquetumba, nonomuni",
        resumen: "wherever",
      }]);
      const selection4 = await db.select("articulos", i => i);
      if (selection4.length !== 3) {
        throw new Error("Test falló en aserción 5");
      }
      await db.close();
    }
    document.querySelector("#test").textContent += "\n[✔] Browsie Data API Tests passed successfully.";
    Schema_api: {
      // let schema = await Browsie.getSchema("browsie_test"); console.log(schema);
      await Browsie.deleteDatabase("browsie_test_schema");
      // console.log("Create database..");
      await Browsie.createDatabase("browsie_test_schema", {
        tabla1: ["!uuid", "columna1", "columna2", "columna3"],
        tabla2: ["!uuid", "columna1", "columna2", "columna3"],
        tabla3: ["!uuid", "columna1", "columna2", "columna3"],
        tabla4: ["!uuid", "columna1", "columna2", "columna3"],
      });
      const db = await Browsie.open("browsie_test_schema");
      await db.insert("tabla1", { uuid: "1", columna1: "a", columna2: "b", columna3: "c" });
      await db.insert("tabla2", { uuid: "2", columna1: "a", columna2: "b", columna3: "c" });
      await db.insert("tabla3", { uuid: "3", columna1: "a", columna2: "b", columna3: "c" });
      await db.insert("tabla4", { uuid: "4", columna1: "a", columna2: "b", columna3: "c" });
      schema = await Browsie.getSchema("browsie_test_schema");
      console.log(schema);
      await db.close();
    }
    // await Browsie.deleteDatabase("browsie_test_data");
    // await Browsie.deleteDatabase("browsie_test_schema");
    document.querySelector("#test").textContent += "\n[✔] Browsie Schema API Tests passed successfully.";
    Triggers_api: {
      console.log(await Browsie.globMatch([
        "crud.insert.*.users",
        "crud.*.many.users"
      ], [
        "crud.select.one.users",
        "crud.select.many.users",
        "crud.insert.one.users",
        "crud.insert.many.users",
        "crud.update.one.users",
        "crud.update.many.users",
        "crud.delete.one.users",
        "crud.delete.many.users",
      ]));
      const db = await Browsie.open("browsie_test_schema");
      let counter = 0;
      await db.triggers.register("crud.insert.one.tabla1", "temp1", function() {
        console.log("triggering temp1");
        counter -= 2;
      }, {
        priority: 20
      });
      await db.triggers.register("crud.insert.one.tabla1", "temp2", function() {
        console.log("triggering temp2");
        counter *= 10;
      }, {
        priority: 10
      });
      await db.triggers.register("crud.insert.one.tabla1", "temp3", function() {
        console.log("triggering temp3");
        counter += 5;
      }, {
        priority: 30
      });
      await db.insert("tabla1", { uuid: "5", columna1: "a", columna2: "b", columna3: "c" });
      schema = await Browsie.getSchema("browsie_test_schema");
      console.log(schema);
      console.log(db);
      setTimeout(() => {
        console.log(counter);
        if(counter !== 30) {
          console.error("Failed calculus");
        } else {
          console.log("Triggers are working fine");
        }
      }, 1000);
      await db.close();
    }
    document.querySelector("#test").textContent += "\n[✔] Browsie Triggers API Tests passed successfully.";
  } catch (error) {
    console.log(error);
  }
})();

You can run it using npm test.

The versionation

This was a stopper when developing, and I want to clarify it.

The method of versionation IndexedDB uses is a very correct one, in which we print the mutations of the database schema. This creates the ADN along time of the database schema. So it is a good pattern. Despite it can seem annoying at first sight.

This library implements, via parameters of the createDatabase method:

  • storeDefinition: a declarative way of defining the first schema shape.
  • versionUpgrades: an imperative way of defining the next database schema modifications.
  • version: current version.

So.

When you want to upgrade a version, to apply a schema mutation, you append a hardcoded function into the versionUpgrades parameter of the browsie instance constructor call.

Finally, to apply the version upgrade, increase the version parameter of the same call.

Applying these 2 changes into your code, the database can migrate by itself, and track the ADN, to keep compatibility with previous schemas that users can have in their own browsers.

So it is a good pattern. And we have close it the best way possible no, but close enough.

The triggerization

We are using @allnulled/triggers-api to cover the topic.

This allows you to use triggers pattern in multiple places, for multiple purposes or APIs.

It is up to you. On TriggersApi global, you can find.