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

fornaxjs-debug

v0.0.1

Published

<div align="center">

Readme

GitHub Repo stars NPM Downloads GitHub package.json version GitHub last commit

Key Features 🔑

  • Custom Components 🧩: Define reusable UI elements using decorators and TypeScript classes.
  • Routing Made Easy 🗺️: Leverage a <router-outlet> and a straightforward routes.ts configuration for SPA navigation.
  • Flexible Styling Modes 🎨: Choose between scoped and global styling for your components.
  • TypeScript by Default 💻: Enjoy type safety and clean code with TypeScript integration.

Getting Started 🏁

Prerequisites ✅

Installation ⚙️

Create a new Fornax project:

bunx fnx generate project <project name>

OR just:

bunx fnx

Then follow the prompts to generate from schematics.

If adding Fornax to your existing Bun project:

bun add fornaxjs

Create a fornax.config.ts in your project’s root to configure directories, ports, custom plugins (style-loader is included by default for css imports), and extra entry points:

export default {
  Client: {
    srcDir: "./src/client",
    distDir: "./dist",
    port: 5000,
    plugins: [],
    entryPoints: [],
    alternateStyleLoader: null,
  },
  Server: {
    dir: "./src/server",
    port: 5500,
  },
};

Adjust as needed.

Project Structure 🗂️

A typical Fornax project might look like this:

project/
├─ src/
|  ├─ client/
│  |  ├─ index.html
│  |  ├─ routes.ts
│  |  ├─ app.component.ts
│  │  ├─ components/
│  │  │   ├─ some.component.ts
│  │  │   ├─ other.component.ts
│  │  ├─ assets/
|  |  ├─ services/
|  ├─ server/
|  |  ├─ controllers/
|  |  |   ├─ some.controller.ts
|  |  ├─ models/
|  |  |   ├─ some.ts
├─ fornax.config.ts
└─ main.ts
  • index.html: Your application’s HTML entry point.
  • main.ts: Dynamically generated entry that imports all components and routes.
  • routes.ts: Defines the application’s client-side routes.
  • app/components/: Store your custom components here.

Running the Dev Server 🔧

fnx dev

This starts:

  • Bun as a back-end/static server with watch mode.

Building for Production 🏗️

fnx build

Outputs bundled files into the dist directory.

Starting the App 🏃

After building, start the server without watch mode:

fnx start

Open http://localhost:5000 to view your application.


Styling Modes 🎨

Fornax supports two style modes for your components:

  • Scoped: <style> inside each component. Styles are encapsulated and don't leak globally.
  • Global: Allows global styles from index.html to affect components.

This is configured in the Component decorator.


Routing 🛣️

Define routes in routes.ts:

import { SomeComponent } from "./app/components/some.component";
import { OtherComponent } from "./app/components/other.component";

export const routes = [
  { path: "/", component: SomeComponent },
  { path: "/other", component: OtherComponent },
];

addRouter("some-selector", routes);

In your main component (app-component.ts):

@Component({
  selector: "app-component",
  template: `
    <nav>
      <a href="/">Some Component</a>
      <a href="/other">Other Component</a>
    </nav>
    <some-selector></some-selector>
  `,
})
export class AppComponent extends BaseComponent {}

Use client-side routing by preventing full page reloads and leveraging the <some-selector> to update views dynamically.


Components and Services 🧩

Components must extend BaseComponent and use the Component decorator (similar to Angular):

@Component({
  selector: "selector-goes-here",
  template: `html goes here`,
  style: `style goes here`,
})
export class SomeComponent extends BaseComponent {
  onInit(): void {
    // Lifecycle hooks inherited from BaseComponent
  }

  onDestroy(): void {
    // Lifecycle hooks inherited from BaseComponent
  }
}

You can import HTML or CSS into your component using Bun pre-configured loaders:

import { Component, BaseComponent } from "fornaxjs";
import html from "./some.component.html" with { type: "text" };
import styles from "./some.component.css";

@Component({
  selector: 'selector-goes-here',
  template: html,
  style: styles
})
export class SomeComponent extends BaseComponent {}

Services are lazily instantiated and then shared in a map across components via Context:

import { Service } from "fornaxjs";

@Service("ApiService")
export class ApiService {
  getData() {
    return "Welcome to Fornax!";
  }
}
import { Component, BaseComponent, Context } from "fornaxjs";
import { ApiService } from "../services/api.service";

@Component({
  selector: "hello-world",
  template: ` <p>{{ apiResponse }}</p> `,
})
export class HelloWorld extends BaseComponent {
  apiResponse = "Loading...";

  onInit(): void {
    const apiService: ApiService = Context.get("ApiService");
    this.apiResponse = apiService.getData();
  }
}

Any properties of the component that are featured in the template will cause a re-render when updated:

import { Component, BaseComponent } from "fornaxjs";

@Component({
  selector: "hello-world",
  template: ` <h1>Hello {{ name }}!</h1> `,
})
export class HelloWorld extends BaseComponent {
  name = "World";
  names: string[] = ["World", "GitHub", "Reddit", "Friends"];
  interval: any = setInterval(() => this.cycleNames(), 2000);

  cycleNames() {
    let name = this.names.shift() as string;
    this.names.push(name);
    this.name = name;
  }
}

Conditional and Iterative Rendering with *if and *for 🔀

Fornax provides powerful directives for conditionally rendering elements (*if) and iterating over collections (*for). These directives simplify dynamic UI updates while keeping your templates clean and declarative.

*if Directive ❓

The *if directive conditionally renders an element based on a boolean expression.

Syntax

<element *if="condition">Content</element>
  • condition: A boolean expression evaluated against the component's properties.

Example

<p *if="showText">This text is visible when 'showText' is true.</p>
<p *if="!showText">This text is visible when 'showText' is false.</p>

Component Code

@Component({...})
export class ExampleComponent extends BaseComponent {
  showText = true;

  toggleText() {
    this.showText = !this.showText;
  }
}

*for Directive 🔂

The *for directive iterates over a collection and renders the specified element for each item.

Syntax

<element *for="item of collection">{{ item }}</element>
  • item: The loop variable representing each element in the collection.
  • collection: The array or iterable to iterate over.

Example

<ul>
  <li *for="item of items">{{ item }}</li>
</ul>

Component Code

@Component({...})
export class ExampleComponent extends BaseComponent {
  items = ["Item 1", "Item 2", "Item 3"];
}

Combined Usage 🤝

The *if and *for directives can be used together for complex rendering logic.

Example

<ul *if="!itemsHidden">
  <li *for="item of items">{{ item }}</li>
</ul>
<p *if="itemsHidden">The items are hidden.</p>

Component Code

@Component({...})
export class ExampleComponent extends BaseComponent {
  itemsHidden = false;
  items = ["Item 1", "Item 2", "Item 3"];

  toggleItemsVisibility() {
    this.itemsHidden = !this.itemsHidden;
  }
}

Fornax API Framework

Fornax contains a lightweight, opinionated declarative API framework built on Bun and Hono with first-class support for TypeScript decorators, validation using Zod, and automatic OpenAPI documentation and Swagger generation. Simplify your API development with reusable models, robust validation, and seamless integration with Swagger.

Defining Models 🏗️

Use decorators like @String, @Number, and @ISODate to define your models with validation rules and OpenAPI metadata:

import { Model, String, Number, ISODate, OptionalISODate } from 'fornax';

@Model()
export class Event {
  @String({ example: '1', description: 'Unique identifier for the event' })
  id: string;

  @String({ example: 'Fornax Launch Party', description: 'Event name' })
  name: string;

  @ISODate({ example: '2023-12-21T15:30:00Z', description: 'Event start date and time' })
  startTime: string;

  @OptionalISODate({ example: '2023-12-22T15:30:00Z', description: 'Event end date and time' })
  endTime?: string;

  @Number({ example: 50, description: 'Number of attendees expected' })
  attendees: number;
}

Defining Controllers 🎛️

Define your controllers and routes using decorators like @Controller, @Get, and @Post. Secure your routes using the @Auth decorator.

Example Controller with Authentication

import { Controller, Get, Post } from 'fornax';
import { Auth } from './auth-decorators';
import { Event } from './models/Event';

@Controller('/events')
export class EventController {
  @Get('/:id', { params: Event }, Event)
  @Auth(async (ctx) => {
    const authHeader = ctx.req.headers.get('Authorization');
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw { message: 'Unauthorized', status: 401 };
    }

    const token = authHeader.replace('Bearer ', '');
    const user = verifyToken(token); // Replace with your token verification logic
    if (!user) {
      throw { message: 'Invalid token', status: 403 };
    }

    ctx.user = user;
  })
  async getEvent(ctx: any) {
    const { id } = ctx.req.valid('param');
    return ctx.json({
      id,
      name: 'Fornax Launch Party',
      startTime: '2023-12-21T15:30:00Z',
      attendees: 50,
    });
  }

  @Post('/', { body: Event }, Event)
  @Auth(async (ctx) => {
    const authHeader = ctx.req.headers.get('Authorization');
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw { message: 'Unauthorized', status: 401 };
    }

    const token = authHeader.replace('Bearer ', '');
    const user = verifyToken(token);
    if (!user || user.role !== 'Admin') {
      throw { message: 'Forbidden: Admin access required', status: 403 };
    }

    ctx.user = user;
  })
  async createEvent(ctx: any) {
    const event = ctx.req.valid('json');
    return ctx.json(event);
  }
}

Authentication Logic (WIP - NEEDS TESTING) 🔒

The @Auth decorator enables you to define custom authentication logic for each route. This logic can include:

  • Token-based authentication
  • Role-based access control
  • Session validation

Key Features

  • TypeScript Decorators: Simplify your API development with declarative decorators.
  • Validation: Built-in support for Zod schemas, including type-safe models and OpenAPI metadata.
  • Authentication: Secure your routes with customizable authentication logic using the @Auth decorator.
  • Automatic OpenAPI Documentation: Generate Swagger-compatible documentation effortlessly.
  • Fast and Lightweight: Built on Bun and Hono for high performance.

Start building APIs faster and smarter with Fornax!


Contributing 🤝

Fornax is a young project aiming for a simple, productive development experience in the Bun ecosystem.

  1. Fork and Clone 🔀:
    git clone https://github.com/TBosak/fornax.git
  2. Install Dependencies 📦:
    bun install
  3. Submit Pull Requests or Issues 🗣️: We'd love your feedback and contributions!

License ⚖️

Fornax is licensed under the MIT License. Feel free to use it in commercial and open-source projects.

Happy coding with Fornax!


TODO 📝

~~Parser - LRU Caching, deterministic & hierarchal ID generation~~ ~~Router - build on top of Vaadin router for now~~ & create replacement later ~~Services - add services & Injectable decorator...should there be a base service class?~~ Should there be a SubscriptionTracker baked into BaseComponent & we unsubscribe on disconnectedCallback? Implementing standalone components and Angular-like module system? Right now I'm just dumping everything into main. ~~Set up Vite for HMR when running dev script, with Bun handling prod build or can we just achieve live reloading with Bun? - https://bun.sh/guides/read-file/watch~~ ~~Finish Output decorator and handling event binding~~ ~~Fix full page reloads on routing~~ ~~Clean up dist folder chunks on build~~ ~~More granular builds to avoid replacing all files in dist on every code change~~ Configure CSS minification on build Test API framework middleware & auth decorators Create middleware registry Test CORS middleware Clean up folder structure - make it a little more intuitive Create example projects GraphQL support Build out a unit testing framework Default linter configurations to handle file imports SCHEMATICS Clean up this readme ...