{"id":27098855,"url":"https://github.com/TBosak/fornax","last_synced_at":"2025-04-06T12:02:53.391Z","repository":{"id":268192732,"uuid":"898798113","full_name":"TBosak/fornax","owner":"TBosak","description":"Build Faster,  Code Smarter, With Fornax – The 🥖Bun-Powered 🥞Full-Stack 🕸️Web Framework","archived":false,"fork":false,"pushed_at":"2024-12-29T17:31:20.000Z","size":401,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T12:45:19.545Z","etag":null,"topics":["bun","bunjs","contributions-welcome","custom-elements","fornax","framework","frontend","javascript","typescript","web-components","web-framework"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TBosak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["TBosak"]}},"created_at":"2024-12-05T03:52:16.000Z","updated_at":"2025-04-04T22:01:02.000Z","dependencies_parsed_at":"2024-12-15T04:17:33.096Z","dependency_job_id":"24c9a7bf-158e-4080-98fb-1559b16682b8","html_url":"https://github.com/TBosak/fornax","commit_stats":null,"previous_names":["tbosak/fornax"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TBosak%2Ffornax","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TBosak%2Ffornax/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TBosak%2Ffornax/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TBosak%2Ffornax/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TBosak","download_url":"https://codeload.github.com/TBosak/fornax/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247478309,"owners_count":20945266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","bunjs","contributions-welcome","custom-elements","fornax","framework","frontend","javascript","typescript","web-components","web-framework"],"created_at":"2025-04-06T12:01:13.373Z","updated_at":"2025-04-06T12:02:53.377Z","avatar_url":"https://github.com/TBosak.png","language":"TypeScript","funding_links":["https://github.com/sponsors/TBosak"],"categories":["Angular-Inspired Solutions"],"sub_categories":["Wrappers"],"readme":"\u003cdiv align=\"center\"\u003e\n  \n\u003cimg width=\"150px\" src=\"https://github.com/user-attachments/assets/cbe98a21-31f1-4209-af64-8a43f058f3cf\"\u003e\n\n\u003ch1\u003eFornax\n\n![GitHub Repo stars](https://img.shields.io/github/stars/tbosak/fornax)\n![NPM Downloads](https://img.shields.io/npm/dw/fornaxjs)\n![GitHub package.json version](https://img.shields.io/github/package-json/v/tbosak/fornax)\n![GitHub last commit](https://img.shields.io/github/last-commit/tbosak/fornax)\n\n\u003c/h1\u003e\n\u003cbr\u003e\n\u003c/div\u003e\nFornax 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.\n\u003cbr\u003e\n\n**Key Features** 🔑\n- **Custom Components** 🧩: Define reusable UI elements using decorators and TypeScript classes.\n- **Routing Made Easy** 🗺️: Leverage a `\u003crouter-outlet\u003e` and a straightforward `routes.ts` configuration for SPA navigation.\n- **Flexible Styling Modes** 🎨: Choose between scoped and global styling for your components.\n- **TypeScript by Default** 💻: Enjoy type safety and clean code with TypeScript integration.\n\n---\n\n## Getting Started 🏁\n\n### Prerequisites ✅\n- **Bun** 🍞: Install Bun from [https://bun.sh/](https://bun.sh/)\n\n### Installation ⚙️\nCreate a new Fornax project:\n```bash\nbunx fnx generate project \u003cproject name\u003e\n```\nOR just:\n```bash\nbunx fnx\n```\nThen follow the prompts to generate from schematics.\n\nIf adding Fornax to your existing Bun project:\n\n```bash\nbun add fornaxjs\n```\n\nCreate 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:\n\n```typescript\nexport default {\n  Client: {\n    srcDir: \"./src/client\",\n    distDir: \"./dist\",\n    port: 5000,\n    plugins: [],\n    entryPoints: [],\n    alternateStyleLoader: null,\n  },\n  Server: {\n    dir: \"./src/server\",\n    port: 5500,\n  },\n};\n\n```\n\nAdjust as needed.\n\n### Project Structure 🗂️\n\nA typical Fornax project might look like this:\n\n```\nproject/\n├─ src/\n|  ├─ client/\n│  |  ├─ index.html\n│  |  ├─ routes.ts\n│  |  ├─ app.component.ts\n│  │  ├─ components/\n│  │  │   ├─ some.component.ts\n│  │  │   ├─ other.component.ts\n│  │  ├─ assets/\n|  |  ├─ services/\n|  ├─ server/\n|  |  ├─ controllers/\n|  |  |   ├─ some.controller.ts\n|  |  ├─ models/\n|  |  |   ├─ some.ts\n├─ fornax.config.ts\n└─ main.ts\n```\n\n- `index.html`: Your application’s HTML entry point.\n- `main.ts`: Dynamically generated entry that imports all components and routes.\n- `routes.ts`: Defines the application’s client-side routes.\n- `app/components/`: Store your custom components here.\n\n### Running the Dev Server 🔧\n\n```bash\nfnx dev\n```\n\nThis starts:\n\n- Bun as a back-end/static server with watch mode.\n\n### Building for Production 🏗️\n\n```bash\nfnx build\n```\n\nOutputs bundled files into the `dist` directory.\n\n### Starting the App 🏃\n\nAfter building, start the server without watch mode:\n\n```bash\nfnx start\n```\n\nOpen `http://localhost:5000` to view your application.\n\n---\n\n## Styling Modes 🎨\n\nFornax supports two style modes for your components:\n\n- **Scoped:** `\u003cstyle\u003e` inside each component. Styles are encapsulated and don't leak globally.\n- **Global:** Allows global styles from `index.html` to affect components.\n\nThis is configured in the Component decorator.\n\n---\n\n## Routing 🛣️\n\nDefine routes in `routes.ts`:\n\n```typescript\nimport { SomeComponent } from \"./app/components/some.component\";\nimport { OtherComponent } from \"./app/components/other.component\";\n\nexport const routes = [\n  { path: \"/\", component: SomeComponent },\n  { path: \"/other\", component: OtherComponent },\n];\n\naddRouter(\"some-selector\", routes);\n```\n\nIn your main component (`app-component.ts`):\n\n```typescript\n@Component({\n  selector: \"app-component\",\n  template: `\n    \u003cnav\u003e\n      \u003ca href=\"/\"\u003eSome Component\u003c/a\u003e\n      \u003ca href=\"/other\"\u003eOther Component\u003c/a\u003e\n    \u003c/nav\u003e\n    \u003csome-selector\u003e\u003c/some-selector\u003e\n  `,\n})\nexport class AppComponent extends BaseComponent {}\n```\n\nUse client-side routing by preventing full page reloads and leveraging the `\u003csome-selector\u003e` to update views dynamically.\n\n---\n\n## Components and Services 🧩\n\nComponents must extend `BaseComponent` and use the `Component` decorator (similar to Angular):\n\n```typescript\n@Component({\n  selector: \"selector-goes-here\",\n  template: `html goes here`,\n  style: `style goes here`,\n})\nexport class SomeComponent extends BaseComponent {\n  onInit(): void {\n    // Lifecycle hooks inherited from BaseComponent\n  }\n\n  onDestroy(): void {\n    // Lifecycle hooks inherited from BaseComponent\n  }\n}\n```\n\nYou can import HTML or CSS into your component using Bun pre-configured loaders:\n\n```typescript\nimport { Component, BaseComponent } from \"fornaxjs\";\nimport html from \"./some.component.html\" with { type: \"text\" };\nimport styles from \"./some.component.css\";\n\n@Component({\n  selector: 'selector-goes-here',\n  template: html,\n  style: styles\n})\nexport class SomeComponent extends BaseComponent {}\n```\n\nServices are lazily instantiated and then shared in a map across components via Context:\n\n```typescript\nimport { Service } from \"fornaxjs\";\n\n@Service(\"ApiService\")\nexport class ApiService {\n  getData() {\n    return \"Welcome to Fornax!\";\n  }\n}\n```\n\n```typescript\nimport { Component, BaseComponent, Context } from \"fornaxjs\";\nimport { ApiService } from \"../services/api.service\";\n\n@Component({\n  selector: \"hello-world\",\n  template: ` \u003cp\u003e{{ apiResponse }}\u003c/p\u003e `,\n})\nexport class HelloWorld extends BaseComponent {\n  apiResponse = \"Loading...\";\n\n  onInit(): void {\n    const apiService: ApiService = Context.get(\"ApiService\");\n    this.apiResponse = apiService.getData();\n  }\n}\n```\n\nAny properties of the component that are featured in the template will cause a re-render when updated:\n\n```typescript\nimport { Component, BaseComponent } from \"fornaxjs\";\n\n@Component({\n  selector: \"hello-world\",\n  template: ` \u003ch1\u003eHello {{ name }}!\u003c/h1\u003e `,\n})\nexport class HelloWorld extends BaseComponent {\n  name = \"World\";\n  names: string[] = [\"World\", \"GitHub\", \"Reddit\", \"Friends\"];\n  interval: any = setInterval(() =\u003e this.cycleNames(), 2000);\n\n  cycleNames() {\n    let name = this.names.shift() as string;\n    this.names.push(name);\n    this.name = name;\n  }\n}\n```\n\n---\n\n## Conditional and Iterative Rendering with `*if` and `*for` 🔀\n\nFornax 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.\n\n### `*if` Directive ❓\n\nThe `*if` directive conditionally renders an element based on a boolean expression.\n\n#### Syntax\n```html\n\u003celement *if=\"condition\"\u003eContent\u003c/element\u003e\n```\n\n- `condition`: A boolean expression evaluated against the component's properties.\n\n#### Example\n```html\n\u003cp *if=\"showText\"\u003eThis text is visible when 'showText' is true.\u003c/p\u003e\n\u003cp *if=\"!showText\"\u003eThis text is visible when 'showText' is false.\u003c/p\u003e\n```\n\n#### Component Code\n```typescript\n@Component({...})\nexport class ExampleComponent extends BaseComponent {\n  showText = true;\n\n  toggleText() {\n    this.showText = !this.showText;\n  }\n}\n```\n\n---\n\n### `*for` Directive 🔂\n\nThe `*for` directive iterates over a collection and renders the specified element for each item.\n\n#### Syntax\n```html\n\u003celement *for=\"item of collection\"\u003e{{ item }}\u003c/element\u003e\n```\n\n- `item`: The loop variable representing each element in the collection.\n- `collection`: The array or iterable to iterate over.\n\n#### Example\n```html\n\u003cul\u003e\n  \u003cli *for=\"item of items\"\u003e{{ item }}\u003c/li\u003e\n\u003c/ul\u003e\n```\n\n#### Component Code\n```typescript\n@Component({...})\nexport class ExampleComponent extends BaseComponent {\n  items = [\"Item 1\", \"Item 2\", \"Item 3\"];\n}\n```\n\n---\n\n### Combined Usage 🤝\n\nThe `*if` and `*for` directives can be used together for complex rendering logic.\n\n#### Example\n```html\n\u003cul *if=\"!itemsHidden\"\u003e\n  \u003cli *for=\"item of items\"\u003e{{ item }}\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp *if=\"itemsHidden\"\u003eThe items are hidden.\u003c/p\u003e\n```\n\n#### Component Code\n```typescript\n@Component({...})\nexport class ExampleComponent extends BaseComponent {\n  itemsHidden = false;\n  items = [\"Item 1\", \"Item 2\", \"Item 3\"];\n\n  toggleItemsVisibility() {\n    this.itemsHidden = !this.itemsHidden;\n  }\n}\n```\n\n---\n\n# **Fornax API Framework** ⚡\n\nFornax 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.\n\n### **Defining Models** 🏗️\n\nUse decorators like `@String`, `@Number`, and `@ISODate` to define your models with validation rules and OpenAPI metadata:\n\n```typescript\nimport { Model, String, Number, ISODate, OptionalISODate } from 'fornax';\n\n@Model()\nexport class Event {\n  @String({ example: '1', description: 'Unique identifier for the event' })\n  id: string;\n\n  @String({ example: 'Fornax Launch Party', description: 'Event name' })\n  name: string;\n\n  @ISODate({ example: '2023-12-21T15:30:00Z', description: 'Event start date and time' })\n  startTime: string;\n\n  @OptionalISODate({ example: '2023-12-22T15:30:00Z', description: 'Event end date and time' })\n  endTime?: string;\n\n  @Number({ example: 50, description: 'Number of attendees expected' })\n  attendees: number;\n}\n```\n\n---\n\n### **Defining Controllers** 🎛️\n\nDefine your controllers and routes using decorators like `@Controller`, `@Get`, and `@Post`. Secure your routes using the `@Auth` decorator.\n\n#### Example Controller with Authentication\n\n```typescript\nimport { Controller, Get, Post } from 'fornax';\nimport { Auth } from './auth-decorators';\nimport { Event } from './models/Event';\n\n@Controller('/events')\nexport class EventController {\n  @Get('/:id', { params: Event }, Event)\n  @Auth(async (ctx) =\u003e {\n    const authHeader = ctx.req.headers.get('Authorization');\n    if (!authHeader || !authHeader.startsWith('Bearer ')) {\n      throw { message: 'Unauthorized', status: 401 };\n    }\n\n    const token = authHeader.replace('Bearer ', '');\n    const user = verifyToken(token); // Replace with your token verification logic\n    if (!user) {\n      throw { message: 'Invalid token', status: 403 };\n    }\n\n    ctx.user = user;\n  })\n  async getEvent(ctx: any) {\n    const { id } = ctx.req.valid('param');\n    return ctx.json({\n      id,\n      name: 'Fornax Launch Party',\n      startTime: '2023-12-21T15:30:00Z',\n      attendees: 50,\n    });\n  }\n\n  @Post('/', { body: Event }, Event)\n  @Auth(async (ctx) =\u003e {\n    const authHeader = ctx.req.headers.get('Authorization');\n    if (!authHeader || !authHeader.startsWith('Bearer ')) {\n      throw { message: 'Unauthorized', status: 401 };\n    }\n\n    const token = authHeader.replace('Bearer ', '');\n    const user = verifyToken(token);\n    if (!user || user.role !== 'Admin') {\n      throw { message: 'Forbidden: Admin access required', status: 403 };\n    }\n\n    ctx.user = user;\n  })\n  async createEvent(ctx: any) {\n    const event = ctx.req.valid('json');\n    return ctx.json(event);\n  }\n}\n```\n\n---\n\n### **Authentication Logic (WIP - NEEDS TESTING)** 🔒\n\nThe `@Auth` decorator enables you to define custom authentication logic for each route. This logic can include:\n\n- Token-based authentication\n- Role-based access control\n- Session validation\n\n---\n\n### **Key Features** ✨\n\n- **TypeScript Decorators:** Simplify your API development with declarative decorators.\n- **Validation:** Built-in support for Zod schemas, including type-safe models and OpenAPI metadata.\n- **Authentication:** Secure your routes with customizable authentication logic using the `@Auth` decorator.\n- **Automatic OpenAPI Documentation:** Generate Swagger-compatible documentation effortlessly.\n- **Fast and Lightweight:** Built on **Bun** and **Hono** for high performance.\n\nStart building APIs faster and smarter with Fornax!\n\n---\n\n## Contributing 🤝\n\nFornax is a young project aiming for a simple, productive development experience in the Bun ecosystem.\n\n1. **Fork and Clone** 🔀:\n   ```bash\n   git clone https://github.com/TBosak/fornax.git\n   ```\n2. **Install Dependencies** 📦:\n   ```bash\n   bun install\n   ```\n3. **Submit Pull Requests or Issues** 🗣️:\n   We'd love your feedback and contributions!\n\n---\n\n## License ⚖️\n\nFornax is licensed under the MIT License. Feel free to use it in commercial and open-source projects.\n\n**Happy coding with Fornax!** ✨\n\n---\n\n## TODO 📝\n\n~~Parser - LRU Caching, deterministic \u0026 hierarchal ID generation~~ \u003cbr\u003e\n~~Router - build on top of Vaadin router for now~~ \u0026 create replacement later \u003cbr\u003e\n~~Services - add services \u0026 Injectable decorator...should there be a base service class?~~ \u003cbr\u003e\nShould there be a SubscriptionTracker baked into BaseComponent \u0026 we unsubscribe on disconnectedCallback? \u003cbr\u003e\nImplementing standalone components and Angular-like module system? Right now I'm just dumping everything into main. \u003cbr\u003e\n~~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~~\u003cbr\u003e\n~~Finish Output decorator and handling event binding~~ \u003cbr\u003e\n~~Fix full page reloads on routing~~ \u003cbr\u003e\n~~Clean up dist folder chunks on build~~ \u003cbr\u003e\n~~More granular builds to avoid replacing all files in dist on every code change~~ \u003cbr\u003e\nConfigure CSS minification on build \u003cbr\u003e\nTest API framework middleware \u0026 auth decorators \u003cbr\u003e\nCreate middleware registry \u003cbr\u003e\nTest CORS middleware \u003cbr\u003e\nClean up folder structure - make it a little more intuitive \u003cbr\u003e\nCreate example projects \u003cbr\u003e\nGraphQL support \u003cbr\u003e\nBuild out a unit testing framework \u003cbr\u003e\nDefault linter configurations to handle file imports\u003cbr\u003e\n~~SCHEMATICS~~ \u003cbr\u003e\nClean up this readme \u003cbr\u003e\n...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTBosak%2Ffornax","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTBosak%2Ffornax","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTBosak%2Ffornax/lists"}