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

@kernhq/module-template

v0.2.6

Published

Apache-2.0 starting point for a Kern module — copy it, or read it as the reference for what a module is

Readme

@kernhq/module-template

The starting point for a Kern module. Apache-2.0, so what you build from it is yours to license however you like — including not at all.

npm create kern-module            # not yet; for now:
npx degit KernAIO/module-template my-module

A Kern module is one package. This one is a whole working module — a Note entity with list, create, delete and archive, its own Postgres schema, row-level security, permissions, capabilities, events, its own screens and its own strings — and a test that refuses to let the contract and the router drift apart.

The application holds no screens belonging to a module. Deleting your package removes your feature completely; that is the test of whether it is a module at all.

STRUCTURE.md is the map — what every directory is for, and the one part that is optional. In short: most modules are contract + server + client, and core hosts them. A module that needs its own process — an open socket, a queue it drains on its own clock — adds src/service/ and a Dockerfile, and hosts itself. Nothing else changes.

What is in here

| File | What it is | |---|---| | src/contract.ts | Zod models, the oRPC contract, events, permission keys, capabilities. Imported by both halves, so no Node code. | | src/server/schema.ts | Drizzle tables in mod_<id>. | | src/server/_impl.ts | The router. Separate from index.ts so the test can walk it without a kernel. | | src/server/index.ts | defineServerModule — schema, migrations, router, subscriptions. | | src/client/index.ts | The typed API client and module logic. Ships as source. | | src/module.test.ts | Contract-to-router parity and the authorisation guard. Keep it. | | migrations/0000_init.sql | Generated by pnpm db:generate. | | migrations/0001_rls.sql | Hand-written. Never generated. |

Copying it by hand

Each of these has been got wrong before.

  1. package.json — set name to @kernhq/module-<id>, and delete "private": true. A private package is skipped silently by changesets: the commit lands, CI is green, and nothing publishes.
  2. files must cover every directory ./client reaches — src/client and src/contract. The client ships as source, so a re-export the tarball omits breaks the consumer and nothing local notices, because the workspace resolves the file the package does not ship. pnpm check:pack catches it.
  3. The id agrees in four places: MODULE_ID, moduleSchema('<id>'), schemaFilter in drizzle.config.ts, and every permission and event prefix.
  4. Version comes from the package, never a literal: packageVersion(import.meta.url). A literal is not bumped by a release — chat once shipped as 0.2.0 while telling every admin it was 0.1.0, and that literal is what workspace_modules.installed_version recorded.
  5. Write the RLS migration. pnpm db:generate will not. Copy 0001_rls.sql and change the table names; rlsPolicySql from @kernhq/kernel emits the same text.
  6. Host it. A module nothing imports is invisible: its tests pass, it publishes, and every call 404s. Add it to featureModules in the core repo's src/service.ts, or to whichever service should hold it.
  7. Register the client. One line in the app's src/lib/modules/registry.ts: registerModule(crmClientModule), importing from @kernhq/module-crm/client. Together with step 6 that is the only wiring outside this package.

What a module can contribute

The server half declares tables, migrations, a router, procedures other modules call through kernel.call(), jobs, subscriptions, search indexers and lifecycle hooks.

The client half — src/client/module.ts in this package — declares nav, routes, commands, settingsPages, widgets for the dashboard, sidebar for the column beside the rail, presenters for rendering this module's objects inside somebody else's screen, and messages for its own strings. The shell renders whatever it finds; there are no route files in the application to keep in step. Read the kern-widget skill before writing a widget, and kern-module for the whole sequence.

What a screen may reach for

A module cannot import the application, so everything it needs from the shell comes from @kernhq/ui: session (who is signed in, what they may do, which capabilities the workspace has), navigation (where we are, go, describe), getHost (the API origin, whether the mock is running), t (this module's strings and the shared common bundle), the formatters, realtime, uploadFile, WidgetState, the design-system components and the charts.

Three things will compile while you are editing inside the app and fail the moment this package is built on its own — so they are worth knowing before you write them:

  • $app/state and $app/navigation do not exist here. A route component is passed workspaceId, workspaceSlug and params; anything else asks navigation.
  • $lib/* and $msg are the application's aliases. Your strings live in src/client/i18n.ts.
  • Importing this package's own barrel (./index.js) from inside it is a cycle. Name the file. The barrel re-exports the manifest, which reaches Svelte — so a pure-function test that goes through it fails with $state is not defined.

Before you call it done

pnpm typecheck && pnpm lint && pnpm test && pnpm build
pnpm check:pack        # the tarball contains what ./client imports
pnpm check:versions    # the manifest version matches package.json

Then use it through the interface, signed in, with the module enabled for a workspace. A module that has never served a request is not finished, whatever the type-checker says.