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

@opencore-mes/juris-kit

v0.91.5

Published

A framework for server-rendered web apps whose pages stay live: no build step, no runtime dependency

Readme

Juris

A framework for server-rendered web apps whose pages stay live.

  • Every page is rendered whole on the server, so it can be read before any script runs; the browser then makes that same page interactive.
  • Live queries keep it up to date. A service says which queries it touches; after it runs, the server runs those queries again and sends each page that watches them only what changed.
  • No build step, no runtime dependency. The code a browser runs is the file on disk; a database driver is something the app passes in.
  • Layouts are plain objects, the same on both sides: { h1: { textContent: () => api.getState("title") } }.

Node 24 or later. Apache-2.0.

npm install @opencore-mes/juris-kit

A whole app

Five files: the app (the same on the server and in the browser), its services, the server, its start, and the browser's entry.

client/app.js: its routes and components.

export const routes = [
    { path: "/", name: "home", component: "Home", preload: () => [["greeting", "world"]], head: { title: "Hello" } },
    { path: "*", name: "missing", component: "NotFound", head: { title: "Not found" } },
];
export const title = { suffix: " · Hello", fallback: "Hello" };
export function register(juris) {
    juris.registerComponent("App", () => ({ main: { children: [{ RouterView: {} }] } }));
    juris.registerComponent("Home", (props, api) => {
        api.live("greeting", "greeting", "world");
        return { h1: { textContent: () => api.getState("greeting")?.text ?? "" } };
    });
    juris.registerComponent("NotFound", () => ({ p: { textContent: "Nothing here." } }));
}

services.js: what the app does. A refusal is written for the person reading it.

import { fail } from "@opencore-mes/juris-kit/errors.js";
export const services = {
    async greeting(name) {
        if (typeof name !== "string" || !name.trim()) fail("Say who to greet.", { fields: { name: "Required." } });
        return { text: `Hello, ${name.trim().slice(0, 60)}` };
    },
};

app.mjs and server.mjs: the server, and the one file that touches the process.

import { createJurisServer } from "@opencore-mes/juris-kit/server/kernel.js";
import { routes, register, title } from "./client/app.js";
export const createApp = ({ services, dev = false, build = "dev" }) => createJurisServer({
    root: new URL(".", import.meta.url), dev, build, services, routes, setup: register,
    api: { live: { public: true, queries: ["greeting"], touches: {} } },
    modules: { mounts: [{ url: "/client/", dir: "client" }], entry: "/client/boot.js" },
    page: { title },
});

// server.mjs
import { createApp } from "./app.mjs";
import { services } from "./services.js";
const app = await createApp({ services, dev: true });
const { url } = await app.listen({ port: 3000 });
console.log(`listening on ${url}`);

client/boot.js: the browser's entry. The framework's own files are served at /src/.

import { hydrate } from "/src/client.js";
import { routes, register, title } from "./app.js";
export const start = () => hydrate({ routes, register, title });

On a request for /, the server runs the route's preload calls, renders Home from their answers, and writes the page with the state it rendered from. In the browser, hydrate takes over that page, and api.live subscribes to greeting over the page's one event stream.

Where things are imported from

| Where | Import | For | | --- | --- | --- | | The browser's entry | /src/client.js | hydrate, the whole client boot | | Shared modules, when they need it | /src/forms.js, /src/clock.js, /src/live-data.js, /src/router.js, /src/errors.js, /src/storage.js | Plugins and helpers, in the browser | | The server | @opencore-mes/juris-kit/server/kernel.js (createJurisServer), .../server/process.js (startProcess) | The whole server, and starting it | | Services | @opencore-mes/juris-kit/errors.js | fail | | Beside the kernel | .../server/page-cache.js, .../server/bus/pg-outbox.js, .../server/db.js, .../server/dev-reload.js, .../server/http.js | A page cache, a change bus, a driver adapter, development reload, cookies |

A component reaches Juris through its api and imports nothing of it, so only an app's entry points name where Juris lives.

The full reference

REFERENCE.md is the framework's contract: everything an app may use, what the framework promises, what holds each promise, and what is not there yet. It ships in the package too (node_modules/@opencore-mes/juris-kit/REFERENCE.md).

Juris runs OpenCore MES, its largest user. Its tests are in tests/ (npm test). Issues and changes: github.com/opencore-mes/juris-kit.