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

@deebeetech/sqleasy-engine

v2.0.0

Published

A thin, opt-in executor for @deebeetech/sqleasy: runs its prepared SQL and transactions on Postgres, MySQL, SQL Server, and SQLite — loading only the driver you import.

Readme

A thin, opt-in SQL executor — born alongside @deebeetech/sqleasy, but not limited to it.

Hand it any { sql, params } (SQLEasy builders, hand-written SQL, another codegen tool). It runs it: one small run / transaction / explain / close surface per dialect, and — crucially — you load only the driver you import.

npm install @deebeetech/sqleasy-engine @libsql/client   # only the driver(s) you use

Each dialect lives at its own subpath, so importing one pulls in only its driver:

| Import | Driver (optional peer) | Factory | | ------------------------------------------ | --------------------------- | ---------------------------------------------- | | @deebeetech/sqleasy-engine/sqlite | @libsql/client | createSqliteExecutor | | @deebeetech/sqleasy-engine/postgres | pg | createPostgresExecutor (+ …FromPool) | | @deebeetech/sqleasy-engine/mysql | mysql2 | createMysqlExecutor (+ …FromPool) | | @deebeetech/sqleasy-engine/mssql | mssql | createMssqlExecutor (owns its pool) | | @deebeetech/sqleasy-engine/introspection | (none — uses an executor) | introspectSchema / per-dialect introspect* |

import { createSqliteExecutor } from '@deebeetech/sqleasy-engine/sqlite';

const db = createSqliteExecutor({ file: './app.db' });

// One statement — any { sql, params }, including a builder's parsePrepared() output.
const { rows } = await db.run({ sql: 'SELECT * FROM "users" WHERE "id" = ?;', params: [1] });

// A whole batch as one atomic transaction. Commits on success, rolls back on any error; each
// statement runs as its own prepared statement, in order.
await db.transaction([
  { sql: 'INSERT INTO "users" ("id", "name") VALUES (?, ?);', params: [1, 'Ada'] },
  { sql: 'INSERT INTO "users" ("id", "name") VALUES (?, ?);', params: [2, 'Grace'] },
]);

await db.close();

The drivers are optional peer dependencies — install pg, mysql2, mssql, or @libsql/client yourself, only for the dialects you actually use. Nothing is loaded for a dialect you never import.

Sharing a pool (Postgres / MySQL)

createPostgresExecutorFromPool / createMysqlExecutorFromPool wrap a pool you already manage. close() on those executors is a no-op — ending a shared pool is your job. Factory-created executors (createPostgresExecutor / createMysqlExecutor) own their pool and end it on close().

SQL Server has no FromPool helper: createMssqlExecutor owns the pool so it can rebuild after a failed connect (the mssql driver caches a rejected connect promise).

The factory-created Postgres executor also attaches a pool 'error' handler, so an idle client whose socket the backend drops cannot crash the process — pg re-emits that as an unhandled 'error' otherwise. (…FromPool leaves your pool's error handling to you.)

Statement timeouts

Every factory takes an optional second argument, { statementTimeoutMs } — one ceiling for how long a single statement may run — realized with each driver's native mechanism:

const db = createPostgresExecutor(config, { statementTimeoutMs: 30_000 });

| Dialect | Mechanism | | --------------- | --------------------------------------------------------------------------------------------------------------- | | Postgres | statement_timeout merged into the pool config (server cancels; a no-op on …FromPool, a foreign pool) | | MySQL | per-query client timeout — mysql2 destroys the connection, killing the statement | | SQL Server | per-Request requestTimeout (reaches the connectionString form, which config-level requestTimeout can't) | | SQLite / libSQL | a client-side deadline that rejects the awaited promise |

The SQLite/libSQL path is best-effort: @libsql/client has no interrupt(), so a running remote (Turso) statement keeps burning server time after the reject — the promise is bounded, the server-side work is not. Omit the option for no timeout.

Schema introspection

import { createPostgresExecutor } from '@deebeetech/sqleasy-engine/postgres';
import { introspectSchema } from '@deebeetech/sqleasy-engine/introspection';

const db = createPostgresExecutor({ connectionString: process.env.DATABASE_URL });
const schema = await introspectSchema(db, 'postgres'); // tables, columns, PKs, FKs, indexes, …
await db.close();

Pass the dialect matching your executor ('postgres' | 'mysql' | 'mssql' | 'sqlite'). Optional schema scopes the namespace (defaults: public / current DB / dbo / main).

Why a transaction runs statement by statement

SQLEasy's MultiBuilder.parse() renders a batch as one string for display — but placeholder numbering restarts per statement, so that string is not a runnable parameterized call on Postgres/MySQL/SQLite. preparedStatements() returns each statement's own { sql, params }; this engine opens a real driver-level transaction and runs them in order. The same API works for any array of prepared statements, not only SQLEasy's.

Status

All four dialect executors — SQLite/libSQL, Postgres, MySQL, SQL Server — implement the full run / transaction / explain / close surface. Postgres and MySQL also expose …FromPool for sharing an app-managed pool. Schema introspection ships at @deebeetech/sqleasy-engine/introspection.

Tested: SQLite runs against real in-memory (and file) libSQL. The others verify transaction orchestration against a recording fake driver, EXPLAIN parsers against real plan shapes, and carry real-database integration blocks gated on DATABASE_URL / MYSQL_URL / MSSQL_CONNECTION_STRING for CI.

Part of the DeeBee ecosystem.

License

MIT © DeeBee Tech