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

@bquery/bquery

v1.3.0

Published

The jQuery for the Modern Web Platform - Zero build, TypeScript-first library with reactivity, components, and motion

Readme

Repo Stars Issues License npm Bundle Size unpkg CodeFactor JsDelivr

The jQuery for the modern Web Platform.

bQuery.js is a slim, TypeScript-first library that combines jQuery's direct DOM workflow with modern features like reactivity, Web Components, and motion utilities — without a mandatory build step.

Highlights

  • Zero‑build capable: runs directly in the browser; build tools are optional.
  • Security‑focused: DOM writes are sanitized by default; Trusted Types supported.
  • Modular: the core stays small; extra modules are opt‑in.
  • TypeScript‑first: clear types and strong IDE support.
  • Tree-shakeable: import only what you need.

Installation

Via npm/bun/pnpm

# npm
npm install @bquery/bquery

# bun
bun add @bquery/bquery

# pnpm
pnpm add @bquery/bquery

Via CDN (Zero-build)

ES Modules (recommended)

<script type="module">
  import { $, signal } from 'https://unpkg.com/@bquery/bquery@1/dist/full.es.mjs';

  const count = signal(0);
  $('#counter').text(`Count: ${count.value}`);
</script>

UMD (global variable)

<script src="https://unpkg.com/@bquery/bquery@1/dist/full.umd.js"></script>
<script>
  const { $, signal } = bQuery;
  const count = signal(0);
</script>

IIFE (self-executing)

<script src="https://unpkg.com/@bquery/bquery@1/dist/full.iife.js"></script>
<script>
  const { $, $$ } = bQuery;
  $$('.items').addClass('loaded');
</script>

Import Strategies

// Full bundle (all modules)
import { $, signal, component } from '@bquery/bquery';

// Core only
import { $, $$ } from '@bquery/bquery/core';

// Core utilities (named exports, tree-shakeable)
import { debounce, merge, uid, once, utils } from '@bquery/bquery/core';

// À la carte (individual modules)
import { signal, computed, effect, linkedSignal, persistedSignal } from '@bquery/bquery/reactive';
import { component, defineComponent, html } from '@bquery/bquery/component';
import { transition, spring, animate, timeline } from '@bquery/bquery/motion';
import { sanitize } from '@bquery/bquery/security';
import { storage, cache } from '@bquery/bquery/platform';
import { createRouter, navigate } from '@bquery/bquery/router';
import { createStore, defineStore } from '@bquery/bquery/store';
import { mount, createTemplate } from '@bquery/bquery/view';

Modules at a glance

| Module | Description | Size (gzip) | | ------------- | -------------------------------------------------- | ----------- | | Core | Selectors, DOM manipulation, events, utilities | ~11.3 KB | | Reactive | signal, computed, effect, batch | ~0.3 KB | | Component | Lightweight Web Components with props | ~1.9 KB | | Motion | View transitions, FLIP, timelines, scroll, springs | ~4.0 KB | | Security | HTML sanitizing, Trusted Types, CSP | ~0.7 KB | | Platform | Storage, cache, notifications, buckets | ~2.2 KB | | Router | SPA routing, navigation guards, history API | ~2.2 KB | | Store | Signal-based state management, persistence | ~0.3 KB | | View | Declarative DOM bindings, directives | ~4.3 KB |

Quick examples

Core – DOM & events

import { $, $$ } from '@bquery/bquery/core';

// jQuery-style selectors
$('#save').on('click', (event) => {
  console.log('Saved', event.type);
});

// Event delegation for dynamic content
$('#list').delegate('click', '.item', (event, target) => {
  console.log('Item clicked', target.textContent);
});

// Method chaining
$('#box').addClass('active').css({ opacity: '0.8' }).attr('data-state', 'ready');

// DOM manipulation
$('#content').wrap('div');
$('#content').unwrap(); // Remove parent wrapper

// Attribute helpers
$('#dialog').toggleAttr('open');

// Smooth scrolling
$('#section').scrollTo({ behavior: 'smooth' });

// Form serialization
const formData = $('form').serialize(); // Returns object
const queryString = $('form').serializeString(); // Returns URL-encoded string

// Collections
$$('.items').addClass('highlight');
$$('.items').append('<li class="item">New</li>');

Reactive – signals

import {
  signal,
  computed,
  effect,
  batch,
  watch,
  readonly,
  linkedSignal,
} from '@bquery/bquery/reactive';

const count = signal(0);
const doubled = computed(() => count.value * 2);

effect(() => {
  console.log('Count changed', count.value);
});

// Watch with cleanup support
const stop = watch(count, (newVal, oldVal) => {
  console.log(`Changed from ${oldVal} to ${newVal}`);
});

// Read-only signal wrapper
const readOnlyCount = readonly(count);

// Batch updates for performance
batch(() => {
  count.value++;
  count.value++;
});

// Writable computed (linked signal)
const first = signal('Ada');
const last = signal('Lovelace');
const fullName = linkedSignal(
  () => `${first.value} ${last.value}`,
  (next) => {
    const [nextFirst, nextLast] = next.split(' ');
    first.value = nextFirst ?? '';
    last.value = nextLast ?? '';
  }
);

fullName.value = 'Grace Hopper';

Components – Web Components

import { component, defineComponent, html } from '@bquery/bquery/component';

component('user-card', {
  props: {
    username: { type: String, required: true },
    age: { type: Number, validator: (v) => v >= 0 && v <= 150 },
  },
  // Extended lifecycle hooks
  beforeMount() {
    console.log('About to mount');
  },
  connected() {
    console.log('Mounted');
  },
  beforeUpdate(props) {
    // Return false to prevent update
    return props.username !== '';
  },
  onError(error) {
    console.error('Component error:', error);
  },
  render({ props }) {
    return html`<div>Hello ${props.username}</div>`;
  },
});

// Optional: create the class without auto-registration
const UserCard = defineComponent('user-card', {
  props: { username: { type: String, required: true } },
  render: ({ props }) => html`<div>Hello ${props.username}</div>`,
});
customElements.define('user-card', UserCard);

Motion – animations

import { animate, keyframePresets, spring, transition } from '@bquery/bquery/motion';

// View transitions (with fallback)
await transition(() => {
  $('#content').text('Updated');
});

// Web Animations helper
await animate(card, {
  keyframes: keyframePresets.pop(),
  options: { duration: 240, easing: 'ease-out' },
});

// Spring physics
const x = spring(0, { stiffness: 120, damping: 14 });
x.onChange((value) => {
  element.style.transform = `translateX(${value}px)`;
});
await x.to(100);

Security – sanitizing

Internally modularized (sanitize core, Trusted Types, CSP helpers) — the public API remains unchanged. For legacy deep imports, @bquery/bquery/security/sanitize also re-exports generateNonce() and isTrustedTypesSupported().

import { sanitize, escapeHtml } from '@bquery/bquery/security';

// Sanitize HTML (removes dangerous elements like script, iframe, svg)
const safeHtml = sanitize(userInput);

// DOM clobbering protection (reserved IDs are blocked)
const safe = sanitize('<form id="cookie">...</form>'); // id stripped

// Unicode bypass protection in URLs
const urlSafe = sanitize('<a href="java\u200Bscript:alert(1)">click</a>');

// Automatic link security (adds rel="noopener noreferrer" to external/target="_blank" links)
const secureLink = sanitize('<a href="https://external.com" target="_blank">Link</a>');

// Escape for text display
const escaped = escapeHtml('<script>alert(1)</script>');

Platform – storage & APIs

import { storage, notifications } from '@bquery/bquery/platform';

// Unified storage API
const local = storage.local();
await local.set('theme', 'dark');
const theme = await local.get<string>('theme');

// Notifications
const permission = await notifications.requestPermission();
if (permission === 'granted') {
  notifications.send('Build complete', {
    body: 'Your docs are ready.',
  });
}

Router – SPA navigation

Internally, the router has been split into focused submodules (matching, navigation, state, links, utilities) with no public API changes.

import { createRouter, navigate, currentRoute } from '@bquery/bquery/router';

// Create router with routes
const router = createRouter({
  routes: [
    { path: '/', name: 'home', component: HomePage },
    { path: '/user/:id', name: 'user', component: UserPage },
    { path: '*', component: NotFound },
  ],
});

// Navigation guards
router.beforeEach(async (to, from) => {
  if (to.path === '/admin' && !isAuthenticated()) {
    await navigate('/login'); // Redirect
    return false; // Cancel original navigation
  }
});

// Navigate programmatically
await navigate('/user/42');
await navigate('/search?q=bquery'); // Query params in path
await navigate('/login', { replace: true }); // Replace history entry

// Reactive current route
effect(() => {
  console.log('Current path:', currentRoute.value.path);
});

Store – state management

import { createStore, createPersistedStore } from '@bquery/bquery/store';

// Create a store (returns the store instance directly)
const counterStore = createStore({
  id: 'counter',
  state: () => ({ count: 0, name: 'Counter' }),
  getters: {
    doubled: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++;
    },
    async fetchCount() {
      this.count = await api.getCount();
    },
  },
});

// Use the store
counterStore.increment();
console.log(counterStore.doubled); // Reactive getter

// Persisted store (localStorage)
const settingsStore = createPersistedStore({
  id: 'settings',
  state: () => ({ theme: 'dark', language: 'en' }),
});

// Factory-style store definition (Pinia-style)
import { defineStore, mapGetters, watchStore } from '@bquery/bquery/store';

const useCounter = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    doubled: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++;
    },
  },
});

const counter = useCounter();
const getters = mapGetters(counter, ['doubled']);

watchStore(
  counter,
  (state) => state.count,
  (value) => {
    console.log('Count changed:', value, getters.doubled);
  }
);

View – declarative bindings

Internally modularized into focused submodules; the public API remains unchanged.

import { mount, createTemplate } from '@bquery/bquery/view';
import { signal } from '@bquery/bquery/reactive';

// Mount reactive bindings to DOM
const count = signal(0);
const items = signal(['Apple', 'Banana', 'Cherry']);

const app = mount('#app', {
  count,
  items,
  increment: () => count.value++,
});

// In HTML:
// <p bq-text="count"></p>
// <button bq-on:click="increment">+1</button>
// <ul><li bq-for="item in items" bq-text="item"></li></ul>
// <input bq-model="count" type="number" />
// <div bq-if="count > 5">Count is high!</div>
// <div bq-class="{ active: count > 0 }"></div>

Browser Support

| Browser | Version | Support | | ------- | ------- | ------- | | Chrome | 90+ | ✅ Full | | Firefox | 90+ | ✅ Full | | Safari | 15+ | ✅ Full | | Edge | 90+ | ✅ Full |

No IE support by design.

Documentation

Local Development

# Install dependencies
bun install

# Start VitePress docs
bun run dev

# Run Vite playground
bun run playground

# Run tests
bun test

# Build library
bun run build

# Generate API documentation
bun run docs:api

Project Structure

bQuery.js
├── src/
│   ├── core/       # Selectors, DOM ops, events, utils
│   ├── reactive/   # Signals, computed, effects
│   ├── component/  # Web Components helper
│   ├── motion/     # View transitions, FLIP, springs
│   ├── security/   # Sanitizer, CSP, Trusted Types
│   ├── platform/   # Storage, cache, notifications
│   ├── router/     # SPA routing, navigation guards
│   ├── store/      # State management, persistence
│   └── view/       # Declarative DOM bindings
├── docs/           # VitePress documentation
├── playground/     # Vite demo app
├── tests/          # bun:test suites
└── dist/           # Built files (ESM, UMD, IIFE)

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT – See LICENSE.md for details.