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

@modyra/core

v0.4.0

Published

Framework-agnostic type-safe form engine — typed field trees, sync/async/cross-field validation, drafts and undo/redo over a minimal reactive contract.

Readme

@modyra/core

Framework-agnostic, type-safe form engine. Typed field trees and arrays, sync/async/cross-field validation, dirty/touched tracking, draft persistence, undo/redo and minimal-patch change tracking — with zero dependencies and no framework in sight.

import { createForm, field, group, required, min } from "@modyra/core";

const form = createForm({
  email: field("", [required()]),
  age: field<number | null>(null, [min(18)]),
  address: group({ city: field("Rome") }),
});

form.f.email.set("[email protected]");
form.f.email.errors(); // []
form.getValue().address.city; // typed — typos do not compile

Runs in Node, CLIs, workers and plain unit tests.

Feature tour

Typed field arrays — repeatable rows with compile-checked paths:

import { array, field, group, min } from "@modyra/core";

const form = createForm({
  items: array(group({ sku: field(""), qty: field<number>(1, [min(1)]) }), {
    initial: [{ sku: "TSHIRT-BLK-M", qty: 2 }],
  }),
});

form.f.items.push({ sku: "MUG-WHT", qty: 1 });
form.f.items.rows()[1].sku.errors();
form.f.items.move(0, 1);
form.getValue().items[0].qty; // number

Server-side async validation, done right — cancellable, cross-field, debounced, with timeout and preconditions:

import { field, serverValidator } from "@modyra/core";

coupon: field(
  "",
  [],
  serverValidator(
    async (code, ctx) => {
      if (!code) return null;
      const res = await api.check(code, ctx.form.fieldValue("country"), {
        signal: ctx.signal, // aborted when the run is superseded
      });
      return res.valid ? null : "Coupon not valid for your country";
    },
    { dependsOn: ["country"], debounceMs: 400, timeoutMs: 5000 },
  ),
);

Drafts, history, minimal patches:

const form = createForm(schema, {
  draft: { key: "checkout", exclude: ["iban"] }, // autosave/restore, TTL'd
  history: true, // undo()/redo()
});
form.getChanges(); // → typed minimal patch for your PATCH endpoint

Cross-field validation — form-level rules over the whole typed value, attributed to fields or to the form itself (path: null):

import { crossField } from "@modyra/core";

createForm(schema, {
  validators: [
    crossField(["passwordConfirm"], (v) =>
      v.password !== v.passwordConfirm ? "Passwords do not match" : null,
    ),
  ],
});

The reactive contract

The engine is written against four primitives — signal, computed, effect (with cleanup) and untracked — the common denominator of fine-grained reactivity (Solid, Preact Signals, Vue, Angular Signals, and the TC39 Signals proposal). It is not an Angular API: Angular is just one binding of the contract.

import { createForm, vanillaReactivity } from "@modyra/core";

// Node / tests / workers: the bundled graph
const form = createForm(schema); // reactivity defaults to vanillaReactivity()

Framework adapters (@modyra/angular, @modyra/react, @modyra/vue, @modyra/lit) pass their own implementation so form state participates natively in the host's change detection.

Security notes

  • Drafts are versioned envelopes with a 7-day TTL; File/Blob/BigInt values are refused, quota errors never crash the form, and prototype- pollution paths (__proto__ & co.) in tampered storage are discarded.
  • The framework-agnostic devtools panel masks sensitive-looking paths and escapes every rendered value.
  • Zero runtime dependencies, SSR-safe (no window/document access in the engine).

Documentation

License

MIT © Lorenzo Muscherà