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

@kitwork/kitjs

v1.0.0

Published

A CDN-first, no-build browser runtime for scoped reactive HTML and optional navigation continuity.

Readme

KitJS

Reactive HTML from one browser script.

KitJS adds local state, bindings, events, conditional rendering, lists, and small components to ordinary HTML through data-kit-* attributes. It is a standalone classic script: no frontend build, virtual DOM, package loader, Go runtime, or Kitwork server is required.

Stable release build: this checkout and its generated browser artifacts identify as 1.0.0. Public npm/CDN availability and npm latest resolution are recorded only after independent retrieval; see release evidence. Pin the exact version and SRI shown below for production.

@kitwork/kitjs is the standalone browser package. Kitwork JITJS— router.jitjs(...), /jit/... delivery, managed component/service graphs, and data-kit-action—is server-side Kitwork functionality and is not a KitJS package API.

Start with one CDN script

For a quick experiment, the bare jsDelivr entry follows npm latest and may be automatically minified by jsDelivr:

<script defer src="https://cdn.jsdelivr.net/npm/@kitwork/kitjs"></script>

That URL upgrades automatically and its bytes may change. Do not attach the dist/kit.js SRI to it. Production pages should pin the exact readable file and its matching SRI as shown next.

Use the Kit profile when links and forms should use normal browser navigation:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>KitJS counter</title>
  <script
    defer
    src="https://cdn.jsdelivr.net/npm/@kitwork/[email protected]/dist/kit.js"
    integrity="sha256-LXt1DK4QG43susUNwzTQp7H046G1/gONdOhBAcXVIZI="
    crossorigin="anonymous"></script>
</head>
<body>
  <section data-kit-scope="count: 0">
    <button type="button" data-kit-click="count--">−</button>
    <output data-kit-text="count">0</output>
    <button type="button" data-kit-click="count++">+</button>
  </section>
</body>
</html>

That is enough for local reactive UI. data-kit-scope creates a shallow state boundary, descendant directives use the nearest boundary, and synchronous writes are batched into one render. No component registration is needed.

Choose one profile

KitJS publishes two complete, mutually exclusive profiles:

| Profile | Exact file | Navigation | |---|---|---| | Kit | dist/kit.js | Normal browser document navigation | | Hydrate | dist/hydrate.kit.js | Optional compatibility-checked Drive/Morph, with native fallback |

Use Hydrate only when you want eligible same-origin links and GET forms to continue without replacing the live document:

<script
  defer
  src="https://cdn.jsdelivr.net/npm/@kitwork/[email protected]/dist/hydrate.kit.js"
  integrity="sha256-AbI+PkXOVgSxZDNiMjQCxehrcASWQuW+mJHrSmfS57k="
  crossorigin="anonymous"></script>

Hydrate already contains the complete Kit runtime. Never load both profiles on one page. When a Hydrate destination is incompatible, the browser performs a normal navigation before KitJS mutates the current document.

Scopes and directives

A scope contains data, not arbitrary JavaScript behavior:

<section data-kit-scope="count: 3; open: true; profile: { name: 'Ada' }">
  <button type="button" data-kit-click="open = !open">Toggle</button>
  <p data-kit-show="open">
    <strong data-kit-text="profile.name">Ada</strong>
    clicked <output data-kit-text="count">3</output> times.
  </p>
</section>

The object form is also supported:

<section data-kit-scope="{ count: 3, open: true }"></section>

Top-level state names are ASCII identifiers. Values may contain null, booleans, finite numbers, strings, arrays, and plain objects. Scope declarations do not accept calls, property reads, lambdas, or executable initialization programs.

The main authored directives are:

| Purpose | Example | |---|---| | Local state | data-kit-scope="count: 0; open: true" | | Text | data-kit-text="user?.name ?? 'Guest'" | | Visibility | data-kit-show="open" | | Safe attributes | data-kit-bind="aria-expanded: open; disabled: busy" | | Classes | data-kit-class="open ? 'block opacity-100' : 'hidden opacity-0'" | | Continuous styles | data-kit-style="width: progress + '%'; opacity: visible ? 1 : 0" | | Form state | data-kit-model="name" | | Events | data-kit-click:prevent="save()" | | Conditional element | <p data-kit-if="ready">Ready</p> | | Conditional fragment | <template data-kit-if="ready">...</template> | | Keyed list | <template data-kit-for="item, i of items" data-kit-key="item.id">...</template> | | Ownership opt-out | data-kit-ignore |

Unknown directives and invalid expressions fail closed.

if, show, and for

Use data-kit-if directly on an ordinary element for a single branch:

<section data-kit-scope="ready: false">
  <button type="button" data-kit-click="ready = !ready">Toggle</button>
  <p data-kit-if="ready">Ready.</p>
</section>

Use a template when the branch needs multiple top-level nodes, no wrapper, or content that must remain inert until materialized:

<template data-kit-if="ready">
  <h2>Ready</h2>
  <p>The complete fragment mounts together.</p>
</template>

data-kit-for and data-kit-key remain template-only. data-kit-show keeps a node mounted and changes visibility; data-kit-if disposes the branch when it becomes false. An initially true direct branch keeps the exact authored host. After it unmounts, a later true value creates fresh node and component identity.

An ordinary element with data-kit-if is normal browser DOM before KitJS prepares it, so it may paint or start resources first. Client-side conditions are not authorization, secrecy, inertness, or no-flash boundaries. Scripts are forbidden in structural branches; keep secrets and authorization decisions out of client-authored HTML.

Events and expressions

Event attributes use the native event name:

<form data-kit-submit:prevent="save()">
  <input data-kit-keydown:escape="close()">
  <button data-kit-click:once="count++">Run once</button>
  <button data-kit-click:debounce(250)="search()">Search</button>
</form>

Supported events are click, dblclick, submit, input, change, keydown, keyup, pointerdown, pointerup, focusin, and focusout. Modifiers are self, prevent, stop, once, outside, enter, escape, and debounce(ms).

Bindings are read-only. Actions may assign or apply ++/-- to an existing writable top-level field. Multiple writes commit together only when the whole synchronous action succeeds.

The expression language supports literals, arrays and objects, arithmetic, comparisons, logical and nullish operators, conditionals, safe method calls, expression lambdas, and continuous optional chains. It is deliberately not JavaScript: declarations, loops, constructors, template literals, member assignment, browser globals, and prototype escape names are unavailable.

Standalone components

Use a component only when a boundary needs trusted methods or lifecycle. CDN package components are registered directly with an unversioned name; the general host form is data-kit-component="name":

<section data-kit-component="counter">
  <button type="button" data-kit-click="increment()">Increment</button>
  <output data-kit-text="count">0</output>
</section>

<script
  defer
  src="https://cdn.jsdelivr.net/npm/@kitwork/[email protected]/dist/kit.js"
  integrity="sha256-LXt1DK4QG43susUNwzTQp7H046G1/gONdOhBAcXVIZI="
  crossorigin="anonymous"></script>
<script defer src="/assets/components.js"></script>
// /assets/components.js
kit.component("counter", {
  count: 0,
  increment() {
    this.count += 1;
  }
});

Each host receives an isolated shallow instance. A data-kit-scope on the same host may seed existing writable data fields, but it cannot add fields or replace methods. data-kit-as="$name" gives the component an action-only alias.

If a host names a component that has not been registered, KitJS reports the missing definition once and leaves the authored fallback DOM in place. It does not fetch component code and it does not reload the page. Register definitions through the ordered script shown above; a later explicit registration can still mount matching connected hosts.

The optional trusted init(context) hook receives a frozen context:

| Member | Purpose | |---|---| | host | Current component host, then null after disposal | | owned(selector) | Fresh matches owned by this boundary | | listen(target, type, fn, options) | Native listener with automatic cleanup | | cleanup(fn) | Register an idempotent disposer | | afterRender(fn) | Run once after this boundary's next render |

Simple components do not need init. Trusted component JavaScript has normal page authority and must never be built from untrusted source.

The complete public JavaScript API is:

kit.version
kit.component(name, plainObject)

globalThis.kit is frozen. There is no public compiler, renderer, manual mount/destroy control, plugin loader, navigation object, or service registry. data-kit-version is unsupported, and there is no data-kit-local marker.

Hydrate and Drive

Drive enhances compatible same-origin links and GET forms. It does not require a special router and it never executes scripts discovered in fetched HTML.

For a route group to remain compatible:

  • the Hydrate script must be a classic external defer script and a direct child of head;
  • every page must preserve its resolved URL, position, order, and complete attributes;
  • every other executable script that must persist must follow the same external direct-head topology;
  • cross-origin scripts, including jsDelivr, must carry valid SRI and must not use data-kit-drive="stable".

For a self-hosted same-origin external script, data-kit-drive="stable" may be used as an author promise that its URL, bytes, position, order, and attributes will remain unchanged:

<script defer src="/assets/hydrate.kit.js" data-kit-drive="stable"></script>

The marker is not a content check. Prefer a versioned or content-hashed URL with SRI for production. Inline scripts, body scripts, modules, import maps, speculation rules, async, nomodule, or any executable topology change make the destination incompatible. The result is supported native navigation, not a partially applied Morph.

An inline kit.component(...) registration is therefore fine with the Kit profile, but it makes Hydrate leave navigation to the browser. To preserve Drive eligibility, put component registrations in one external file whose exact tag is shared by every compatible route.

The same rule applies to any executable inline script, including a simple <script>console.log("hello world")</script>. Hydrate does not reload because of the script itself; it declines Drive, and the next navigation becomes a normal full document load where the browser executes that script.

Use data-kit-drive="false" on a link, GET form, submitter, or ancestor when a route must always navigate normally.

Hydrate validates the initial script topology before intercepting navigation. An invalid initial page leaves links and forms native and emits one diagnostic. An incompatible fetched destination falls back before mutating title, head, history, or body. Responses over 8 MiB, parsed documents over 100,000 nodes, or documents deeper than 256 also fall back natively.

Same-document fragment links remain browser-native. Compatible cross-route visits preserve requested fragments, focus, scroll, forms, Back/Forward state, and component cleanup according to the browser contract.

Ownership during Morph

  • data-kit-ignore leaves a host and its subtree inert to KitJS and opaque to Morph. It is an ownership marker, not a sanitizer.
  • data-kit-retain="key" preserves a registered standalone component host and its live state across compatible Hydrate navigation. Keys must be unique and stable; retained components cannot live inside structural branches.

Security model

  • Authored expressions never execute through eval() or Function.
  • Expressions cannot access window, document, the native event object, or the trusted kit object.
  • data-kit-bind blocks event handlers, raw style, srcdoc, HTML replacement sinks, unsafe URL schemes, and data-kit-* targets.
  • data-kit-style validates the complete property map before writing.
  • Hydrate never executes scripts from fetched HTML; incompatible pages navigate through the browser document loader.
  • Component definitions are trusted application JavaScript and retain normal browser authority.

KitJS narrows the authority of authored expressions; it is not a general HTML sanitizer and cannot make an unsafe application safe by itself.

Release artifact identity

The deterministic 1.0.0 build in this checkout produces these exact readable profile files:

| Profile | Bytes | SHA-256 | SRI | |---|---:|---|---| | Kit | 206,607 | 2d7b750cae101b8decbac50dc334d0a7b1f4e3a1b5fe038d74e84101c5d52192 | sha256-LXt1DK4QG43susUNwzTQp7H046G1/gONdOhBAcXVIZI= | | Hydrate | 314,424 | 01b23e3e45ce5604b1643362323402c5e86b70049642e5be9891eb4a67d2e7b9 | sha256-AbI+PkXOVgSxZDNiMjQCxehrcASWQuW+mJHrSmfS57k= |

These local identities do not by themselves claim public availability. The immutable 1.0.0-rc.2 publication evidence remains in RELEASE_READINESS.md until the stable tag, package, CDN files, signatures, and provenance are independently recorded.

1.0.0 includes direct ordinary-element data-kit-if, first published in 1.0.0-rc.2. The immutable 1.0.0-rc.1 artifact still requires <template data-kit-if>.

More documentation

License

MIT © 2026 Huỳnh Nhân Quốc.