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

@hotreloads/crux-cli

v1.0.0

Published

CLI tool for scaffolding and managing the @hotreloads/crux actor system

Downloads

63

Readme

@hotreloads/crux-cli

CLI tool for scaffolding and managing the @hotreloads/crux actor system in React, Next.js, and SvelteKit projects.

Crux separates business logic from UI by putting it inside actors — isolated units that communicate through typed ask/reply and pub/sub. This CLI sets up the actor system in your project and generates typed actor boilerplate so you can focus on logic, not wiring.


Quick Start

# Initialize crux in your project
npx @hotreloads/crux-cli init

# Create your first actor
npx @hotreloads/crux-cli add actor auth

That's it. The CLI detects your framework, installs @hotreloads/crux, generates the actor system setup files, and wires everything together.


Requirements

  • Node.js >= 18
  • TypeScript 5.4+
  • One of: React + Vite, Next.js (App Router), or SvelteKit

Commands

crux-cli init

Initializes the crux actor system in your project.

npx @hotreloads/crux-cli init

What it does:

  1. Detects your framework from package.json — React + Vite, Next.js, or SvelteKit. If ambiguous, prompts you to choose.
  2. Asks where actor files should live (default: src/actors or src/lib/actors for Svelte).
  3. Asks whether to enable devtools in development mode.
  4. Installs @hotreloads/crux using your detected package manager (npm/yarn/pnpm/bun).
  5. Generates setup files based on your framework.
  6. Writes crux.config.json to your project root for subsequent commands.

Generated files for React + Vite:

src/actors/
  actor-address.ts       # Centralized actor address constants
  register-actors.ts     # ActorSystem creation + actor registration
  context.tsx            # ActorProvider + useActorRef hook
  index.ts               # Barrel export

Generated files for Next.js App Router:

Same structure as React + Vite, with:

  • context.tsx includes the "use client" directive
  • register-actors.ts guards devtools with typeof window !== "undefined"

Generated files for SvelteKit:

src/lib/actors/
  actor-address.ts       # Centralized actor address constants
  register-actors.ts     # ActorSystem creation + actor registration
  store.ts               # actorSubscribe / actorAsk wrappers
  index.ts               # Barrel export

After init — wrap your app:

React + Vite (main.tsx):

import { ActorProvider } from "./actors";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <ActorProvider>
    <App />
  </ActorProvider>
);

Next.js (app/layout.tsx):

import { ActorProvider } from "@/actors";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ActorProvider>{children}</ActorProvider>
      </body>
    </html>
  );
}

crux-cli add actor <name>

Creates a new actor with typed boilerplate and automatically wires it into your project.

npx @hotreloads/crux-cli add actor payments

What it generates:

src/actors/payments/
  payments-actor.ts          # Actor class with example ask
  payments-actor-types.ts    # Type-safe AskMap

payments-actor-types.ts:

import type { ActorAskMap } from "@hotreloads/crux";

export type PaymentsActorAsks = ActorAskMap & {
  // Example: define your ask types here
  getStatus: {
    payload: { id: string };
    result: { status: string };
  };
};

payments-actor.ts:

import { Actor } from "@hotreloads/crux";
import type { ActorResult } from "@hotreloads/crux";
import type { PaymentsActorAsks } from "./payments-actor-types";

export class PaymentsActor extends Actor {
  static ASKS = {
    GET_STATUS: "getStatus",
  } as const;

  static TOPICS = {
    STATUS: "payments_status",
  } as const;

  async ask(asktype: string, data: unknown): Promise<ActorResult> {
    switch (asktype) {
      case PaymentsActor.ASKS.GET_STATUS: {
        const payload = data as PaymentsActorAsks["getStatus"]["payload"];
        // TODO: implement your logic here
        this.publish(PaymentsActor.TOPICS.STATUS, { status: "active" }, true);
        return [{ status: "active" }, null];
      }
      default:
        return [null, this.errunsupportedask(asktype, data)];
    }
  }
}

Auto-wiring (via ts-morph AST manipulation):

The CLI automatically modifies three existing files:

  • actor-address.ts — adds PAYMENTS_ACTOR: "payments_actor" to the address object
  • register-actors.ts — adds the import and system.registerChild(...) call
  • index.ts — adds the barrel re-export

If the CLI can't find the expected code structure (e.g. you've heavily modified the files), it prints the exact lines to add manually instead of corrupting your code.

Using the actor in a component:

import { useActorRef } from "./actors";
import { ACTOR_ADDRESS } from "./actors";

function PaymentStatus({ id }: { id: string }) {
  const ref = useActorRef(ACTOR_ADDRESS.PAYMENTS_ACTOR);

  const handleCheck = async () => {
    const [result, err] = await ref.current!.ask("getStatus", { id });
    if (err) {
      console.error(err.errmsg);
      return;
    }
    console.log(result.status);
  };

  return <button onClick={handleCheck}>Check Status</button>;
}

Name conventions:

| Input | Class | Address Constant | Address Value | Directory | Files | |---|---|---|---|---|---| | payments | PaymentsActor | PAYMENTS_ACTOR | payments_actor | payments/ | payments-actor.ts | | bank-coupon | BankCouponActor | BANK_COUPON_ACTOR | bank_coupon_actor | bank-coupon/ | bank-coupon-actor.ts | | ratingAgency | RatingAgencyActor | RATING_AGENCY_ACTOR | rating_agency_actor | rating-agency/ | rating-agency-actor.ts |


crux-cli add middleware

Scaffolds a middleware for the actor system.

npx @hotreloads/crux-cli add middleware

Prompts you to choose a middleware type:

| Type | Description | |---|---| | Logging | Logs all asks with timing (performance.now()) | | Auth Guard | Blocks asks when not authenticated, with a configurable PUBLIC_ASKS set | | Retry | Retries NETWORK_ERROR failures with exponential backoff (3 retries, 500ms base) | | Custom | Blank template with before/after hooks |

Generates the file in src/actors/middleware/ and prints wiring instructions:

import { loggingMiddleware } from "./middleware/logging-middleware";
system.use(loggingMiddleware);

crux-cli add bridge

Sets up cross-tab or cross-worker actor communication.

npx @hotreloads/crux-cli add bridge

Prompts for:

  1. Transport — BroadcastChannel (cross-tab) or Custom (implement your own)
  2. Role — Host (actors live here) or Client (connects to host)

Generates the bridge setup file in your actors directory with usage examples.


crux-cli enhance <actor>

Upgrades an existing actor with advanced crux features.

# Add persistence (auto-save retained topics to localStorage)
npx @hotreloads/crux-cli enhance payments --add-persistence

# Add validation (Zod schema validation on ask payloads)
npx @hotreloads/crux-cli enhance payments --add-validation

--add-persistence changes the actor to extend PersistentActor and adds a setupPersistence() call in init().

--add-validation changes the actor to extend ValidatedActor and guides you to define Zod schemas.

If both flags are passed, the CLI warns about the base class conflict and prompts you to choose which to use as the primary approach.


crux-cli list

Shows all registered actors and their features.

npx @hotreloads/crux-cli list
  Registered Actors:
  ┌──────────────────┬─────────────────┬──────────┐
  │ Address          │ Class           │ Features │
  ├──────────────────┼─────────────────┼──────────┤
  │ auth_actor       │ AuthActor       │ -        │
  │ payments_actor   │ PaymentsActor   │ persist  │
  └──────────────────┴─────────────────┴──────────┘

Configuration

The CLI stores project configuration in crux.config.json at your project root:

{
  "framework": "react-vite",
  "actorsDir": "src/actors",
  "packageManager": "npm",
  "devtools": true,
  "middleware": ["logging"],
  "bridge": null
}

All commands (except init) read this file. If it's missing, they'll tell you to run init first.


Error Handling

| Scenario | What happens | |---|---| | No package.json in cwd | Error: "Not a Node.js project." | | crux.config.json missing | Error: "Run npx @hotreloads/crux-cli init first." | | Actor name already exists | Error: "Actor already exists. Use a different name." | | Auto-wiring can't find expected code | Warning with exact manual instructions | | @hotreloads/crux not installed | init installs it; other commands warn |


How It Works Under the Hood

  • File generation uses string templates — simple, debuggable, zero dependencies
  • Auto-wiring uses ts-morph for AST-based TypeScript file modification — finds the right insertion point in your code (import statements, object literals, function bodies) and adds exactly what's needed without breaking formatting or existing code
  • Framework detection reads your package.json dependencies and devDependencies
  • Package manager detection checks for lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb)

License

MIT


Built by Hotreloads Digital Private Limited

Looking for a technical partner? Contact [email protected]