https://github.com/TBosak/fornax
Build Faster, Code Smarter, With Fornax β The π₯Bun-Powered π₯Full-Stack πΈοΈWeb Framework
https://github.com/TBosak/fornax
bun bunjs contributions-welcome custom-elements fornax framework frontend javascript typescript web-components web-framework
Last synced: 23 days ago
JSON representation
Build Faster, Code Smarter, With Fornax β The π₯Bun-Powered π₯Full-Stack πΈοΈWeb Framework
- Host: GitHub
- URL: https://github.com/TBosak/fornax
- Owner: TBosak
- License: mit
- Created: 2024-12-05T03:52:16.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2024-12-29T17:31:20.000Z (4 months ago)
- Last Synced: 2025-04-05T12:45:19.545Z (24 days ago)
- Topics: bun, bunjs, contributions-welcome, custom-elements, fornax, framework, frontend, javascript, typescript, web-components, web-framework
- Language: TypeScript
- Homepage:
- Size: 392 KB
- Stars: 10
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
- fucking-awesome-angular - fornax - A lightweight, opinionated, and highly customizable Bun-powered full-stack web framework designed to simplify building single-page applications with custom components, routing, and flexible styling options. (Table of contents / Third Party Components)
- awesome-angular - fornax - A lightweight, opinionated, and highly customizable Bun-powered full-stack web framework designed to simplify building single-page applications with custom components, routing, and flexible styling options. (Table of contents / Third Party Components)
README
![]()
Fornax




Fornax is a lightweight, opinionated, and highly customizable Bun-powered full-stack web framework designed to simplify building single-page applications with custom components, routing, and flexible styling options.
**Key Features** π
- **Custom Components** π§©: Define reusable UI elements using decorators and TypeScript classes.
- **Routing Made Easy** πΊοΈ: Leverage a `` 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 β
- **Bun** π: Install Bun from [https://bun.sh/](https://bun.sh/)### Installation βοΈ
Create a new Fornax project:
```bash
bunx fnx generate project
```
OR just:
```bash
bunx fnx
```
Then follow the prompts to generate from schematics.If adding Fornax to your existing Bun project:
```bash
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:
```typescript
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 π§
```bash
fnx dev
```This starts:
- Bun as a back-end/static server with watch mode.
### Building for Production ποΈ
```bash
fnx build
```Outputs bundled files into the `dist` directory.
### Starting the App π
After building, start the server without watch mode:
```bash
fnx start
```Open `http://localhost:5000` to view your application.
---
## Styling Modes π¨
Fornax supports two style modes for your components:
- **Scoped:** `` 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`:
```typescript
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`):
```typescript
@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):
```typescript
@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:
```typescript
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:
```typescript
import { Service } from "fornaxjs";@Service("ApiService")
export class ApiService {
getData() {
return "Welcome to Fornax!";
}
}
``````typescript
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:
```typescript
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
```html
<element *if="condition">Content</element>
```- `condition`: A boolean expression evaluated against the component's properties.
#### Example
```html
<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
```typescript
@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
```html
<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
```html
<ul>
<li *for="item of items">{{ item }}</li>
</ul>
```#### Component Code
```typescript
@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
```html
<ul *if="!itemsHidden">
<li *for="item of items">{{ item }}</li>
</ul>
<p *if="itemsHidden">The items are hidden.</p>
```#### Component Code
```typescript
@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:
```typescript
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
```typescript
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** π:
```bash
git clone https://github.com/TBosak/fornax.git
```
2. **Install Dependencies** π¦:
```bash
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~~ <br>
~~Router - build on top of Vaadin router for now~~ & create replacement later <br>
~~Services - add services & Injectable decorator...should there be a base service class?~~ <br>
Should there be a SubscriptionTracker baked into BaseComponent & we unsubscribe on disconnectedCallback? <br>
Implementing standalone components and Angular-like module system? Right now I'm just dumping everything into main. <br>
~~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~~<br>
~~Finish Output decorator and handling event binding~~ <br>
~~Fix full page reloads on routing~~ <br>
~~Clean up dist folder chunks on build~~ <br>
~~More granular builds to avoid replacing all files in dist on every code change~~ <br>
Configure CSS minification on build <br>
Test API framework middleware & auth decorators <br>
Create middleware registry <br>
Test CORS middleware <br>
Clean up folder structure - make it a little more intuitive <br>
Create example projects <br>
GraphQL support <br>
Build out a unit testing framework <br>
Default linter configurations to handle file imports<br>
~~SCHEMATICS~~ <br>
Clean up this readme <br>
...