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

websql-configurable

v4.0.0

Published

WebSQL Database API, implemented for Node using better-sqlite3

Readme

websql-configurable

A fork of websql which allows for additional configurability (with types incorporated from @types/websql).

The WebSQL Database API, implemented for Node using better-sqlite3. In the browser, it falls back to window.openDatabase.

Install

npm install websql-configurable

Usage

import openDatabase from 'websql-configurable';

Create a SQLite3 database called mydb.db:

const db = openDatabase('mydb.db', '1.0', 'description', 1);

Create an in-memory database:

const db = openDatabase(':memory:', '1.0', 'description', 1);

API

openDatabase(name, version, description, size [, callback])

The name is the name of the database. It's passed verbatim to better-sqlite3.

The version is the database version (currently ignored - see below).

The description and size attributes are ignored, but they are required for compatibility with the WebSQL API.

The callback just returns the same database object returned synchronously (migrations currently aren't supported - see below).

For more information how to use the WebSQL API, see the spec or various tutorials.

For more information on better-sqlite3, see its documentation.

In the browser

You can also use this module in the browser (via Browserify/Webpack/etc.), in which case it will just use window.openDatabase, meaning you are subject to browser WebSQL support.

readTransaction() vs transaction()

Both readTransaction() (read-only) and transaction() (read-write) are supported. readTransaction() has some small performance optimizations, so it's worthwhile to use if you're not writing any data in a transaction.

Synchronous execution

better-sqlite3 runs every statement synchronously on the main thread. Each batch of queries is executed in a tight loop and the exec() callback is then deferred by a single macrotask (setImmediate), so callers still observe the same asynchronous, one-callback-per-batch behavior as before. Two SQLiteDatabase instances pointing at the same file are coordinated by an in-process per-file reader/writer lock (any number of concurrent readTransaction()s, exclusive access for transaction()).

Goals

The WebSQL Database API is a deprecated standard, but in many cases it's useful to reuse legacy code designed for browsers that support WebSQL. Also, it allows you to quickly test WebSQL-based code in Node, which can be convenient.

The goal of this API is to exactly match the existing WebSQL API, as implemented in browsers. If there's any difference between browsers (e.g. rows[0] is supported in Chrome, whereas only rows.item(0) is supported in Safari), then the lowest-common denominator version is exported by this library.

This library has a robust test suite, and has been known to pass the PouchDB test suite as well.

Non-Goals

This library is not designed to:

  • Invent new APIs, e.g. deleting databases, supporting BLOBs, encryption, etc.
  • Support WebSQL in Firefox, IE, or other non-WebSQL browsers

In other words, the goal is not to carry the torch of WebSQL, but rather to bridge the gap from existing WebSQL-based code to Node.js.

Custom SQLite3 bindings

This library is designed to allow swappable SQLite3 implementations, beyond just node-sqlite3. Examples:

To create your own custom implementation, use this API:

import customOpenDatabase from 'websql-configurable/custom/index.js';

// The second argument is an optional options object
const openDatabase = customOpenDatabase(SQLiteDatabase, {
  sqlite: {
    busyTimeout: 1000, // The default in ms
    trace: cb, // Called with each statement's SQL text
    profile: cb, // Called with each statement's SQL text and its duration
    memoryQuota: 5000000 // Cap the database size (in bytes) via `max_page_count`
  },
  websql: {
    openDelay: cb, // Defaults to `immediate`
    transactionDelay: cb, // Defaults to `immediate`
    executeDelay: cb // Defaults to `immediate`
  }
});

This SQLiteDatabase implementation needs to be a constructor-style function with a constructor signature like so:

// takes two arguments: the database name and an optional options object
const db = new SQLiteDatabase('dbname', {busyTimeout: 1000, trace: cb, profile: cb, memoryQuota: 5000000});

The built-in implementation also exposes node-sqlite3's configure(option, value) (for 'busyTimeout', 'trace', 'profile', and 'memoryQuota') and a callback-style close(cb) for drop-in compatibility.

Then it implements a single function, exec(), like so:

/**
 *
 * @param {Array<{sql: string, args: Array}>} queries
 * @param {boolean} readOnly
 * @param {(err: Error, results?: Array) => void} callback
 */
function exec (queries, readOnly, callback) {
  // queries: an array of SQL statements and queries, with a key "sql" and "args"
  // readOnly: whether or not these queries are in "read only" mode
  // callback: callback to be called with results (first arg is error, second arg is results)
}

Here is the full specification:

SQLiteDatabase(name (String))

Construct a new SQLiteDatbase object, with the given string name.

exec(queries (Array), readOnly (boolean), callback (function))

Execute the list of SQLQuerys. If we are in readOnly mode, then any non-SELECT queries need to throw an error without executing. This function calls the Node-style callback with an error as the first argument or the Array<SQLResult> as the second argument.

SQLQuery

A SQL query and bindings to execute. This can be a plain JavaScript object or a custom class, as long as it has the following members:

sql (String)

The SQL query to execute.

args (Array)

The arguments to bind the query.

E.g.:

const args = {
  sql: 'INSERT INTO foo values (?, ?)',
  args: ['bar', 'baz']
};

SQLResult

A result returned by a SQL query. This can be a plain JavaScript object or a custom class, as long as it has the following members:

error

A JavaScript Error object, or undefined if the SQLQuery did not throw an error. If error is truthy, then it's assumed insertId, rowsAffected, and rows are falsy (they will be ignored anyway).

insertId (number)

An insertion ID representing the new row number, or undefined if nothing was inserted.

rowsAffected (number)

The number of rows affected by the query, or 0 if none.

rows (Array<object>)

The rows returned by a SELECT query, or empty if none.

Each object is a mapping of keys (columns) to values (value fetched).

E.g.:

const rows = {
  insertId: undefined,
  rowsAffected: 0,
  rows: [
    {foo: 'bar'},
    {foo: 'baz'}
  ]
};

Or:

const errorResult = {
  error: new Error('whoopsie')
};

For an example implementation (and the one used by this module) see lib/sqlite/SQLiteDatabase.js.

TODOs

The versioning and migration APIs (i.e. changeVersion()) are not supported. Pull requests welcome!

Limitations

  1. A database name is handed straight to SQLite, whose valid values are filenames, ":memory:" for an anonymous in-memory database, and an empty string for an anonymous disk-based database. This does not honor the WebSQL spec's indication that "All strings including the empty string are valid database names" (and that they are case-sensitive), so consumers will need to do their own mapping for strings in order to 1) avoid problems with invalid filenames or filenames on case insensitive file systems, and to 2) avoid user databases being given special treatment if the empty string or the string ":memory:" is used; another special purpose form of string supported by SQLite that may call for escaping are file::memory:... URLs.

  2. Each executeSql() call must contain a single statement. Unlike node-sqlite3's exec(), better-sqlite3 does preserve literal NUL bytes in string values and does not choke on SQL comments.

  3. better-sqlite3 builds SQLite with strict quoting (SQLITE_DQS=0): a double-quoted token is always an identifier and a single-quoted token is always a string literal. SQL written against node-sqlite3's more lenient build - for example INSERT INTO t VALUES ("some text") or a single-quoted table name - must be rewritten as standard SQL ('some text' for the string, "t" for the identifier).

Testing

First:

npm install

Main test suite:

npm test

Linter:

npm run lint

Test in debug mode (e.g. with the node-inspector):

npm run test-debug

Run the test suite against actual WebSQL in a browser:

npm run test-local

Run the actual-WebSQL test against PhantomJS:

npm run test-phantom