Amoxi – a TypeScript framework where wiring mistakes fail the build Amoxi, a new full-stack TypeScript framework for Node.js and AI agents from the karawash-projects GitHub organization, validates the entire dependency graph at build time so that missing or ambiguous beans, dependency cycles, bad routes, misnamed islands, server code imported into the browser, and invalid validation rules stop `amoxi build` with a file and line. The framework uses compile-time dependency injection modeled on Micronaut and generates plain wiring code with no runtime reflection, starting in about 10 ms, while server-rendered JSX pages with islands ship roughly 9 kB of gzipped JavaScript in the example app. Amoxi runs durable streaming AI agents that survive restarts, pause for human approval, and expose tools over MCP and A2A, requires Node.js 22 or newer, and is scaffolded via `npx create-amoxi-app@latest`, with starter templates licensed 0BSD. Website and documentation: karawash-projects.github.io/amoxi https://karawash-projects.github.io/amoxi/ A full-stack TypeScript framework for Node.js and AI agents. Compile-time dependency injection modelled on Micronaut, server-rendered JSX pages with islands of interactivity, durable streaming AI agents with generative UI, and the production plumbing a real deployment needs. - Compile-time wiring. amoxi build reads your decorators and types with the TypeScript compiler, validates the whole dependency graph, and generates plain wiring code. No reflection at runtime; the framework starts in about 10 ms. - Fast pages, little JavaScript. Pages are JSX rendered on the server straight to HTML strings. Interactive parts are islands : small components bundled separately and loaded only where used. The example app's signal-based islands come to about 9 kB of JavaScript gzipped in total; React islands are available where you want React libraries. - Feels like a single-page app. Links navigate without full reloads and prefetch on hover; slow sections stream in; responses can be cached with stale-while-revalidate. - Ready for AI agents. Agents run as durable background work that survives restarts, stream token by token, pause for human approval, show your own components generative UI , remember and search your documents. Their tools' schemas are generated from your TypeScript types. Every page is also readable by AI as markdown or JSON, with /llms.txt , and your tools and agents can be offered to other agents over MCP and A2A. - Mistakes fail the build. Missing or ambiguous beans, dependency cycles, bad routes, misnamed islands, server code imported into the browser, and invalid validation rules all stop amoxi build with a file and line. npx create-amoxi-app@latest my-app cd my-app npm run dev http://localhost:8080 Like npx create-next-app@latest . The generator asks for a template web : pages, islands and a JSON API; agent : the same plus an AI agent with a streaming chat; api : JSON API only; web is used with --yes or when it isn't run in a terminal , installs dependencies and makes an initial git commit. Options: --template , --no-install , --no-git , --yes . Each command is on its own line so the steps work the same in PowerShell, Command Prompt, bash and zsh. Requires Node.js 22 or newer; works on Windows, macOS and Linux. No environment variables are needed, in development or production. Code generated by create-amoxi-app is yours: the starter templates are licensed 0BSD, with no attribution required. | | Next.js | Amoxi | |---|---|---| | Create an app | npx create-next-app@latest my-app | npx create-amoxi-app@latest my-app | | Package | next | amoxi | | Import | import ... from 'next' | import ... from 'amoxi' | | Develop / build / run | next dev / next build / next start | amoxi dev / amoxi build / amoxi start | To work on Amoxi itself, or try unreleased changes, see CONTRIBUTING.md https://github.com/karawash-projects/amoxi/blob/main/CONTRIBUTING.md . BUILD TIME amoxi build RUNTIME ┌─────────────────────────────────────────┐ ┌────────────────────────────────┐ │ your .ts/.tsx decorators are markers │ │ AmoxiApplication.run module │ │ │ │ │ ApplicationContext: │ │ TypeScript compiler + type checker │ │ instances id ??= │ │ │ │ emits │ beans id .create r │ │ analyzer → beans, routes, pages, │ ───── │ HTTP pipeline: security, │ │ body schemas, islands │ │ CSRF, middleware, cache, │ │ validator → missing/ambiguous/cycles │ │ validation, compression │ │ codegen → wiring + validators + routes │ │ Pages: JSX → HTML strings │ │ esbuild → island bundles, CSS hashed │ │ Browser: islands + router │ └─────────────────────────────────────────┘ └────────────────────────────────┘ Decorators are no-op functions at runtime: no reflect-metadata , no emitDecoratorMetadata . Everything they describe is turned into ordinary code in src/generated/amoxi.generated.ts , which tsc then type-checks a second time. @Singleton export class UserService { constructor private readonly repository: UserRepository, // interface, resolved at build time private readonly notifications: NotificationService, @Value '${users.max-count:1000}' private readonly maxUsers: number, {} } @Singleton @Named 'email' export class EmailNotifier implements Notifier { / ... / } @Singleton export class NotificationService { constructor private readonly notifiers: readonly Notifier , // every Notifier @Named 'log' private readonly audit: Notifier, // exactly one {} } @Factory export class InfrastructureFactory { @Bean clock : Clock { return new SystemClock ; } // for classes you can't decorate } Available: @Singleton , @Prototype , @Eager , @Primary , @Named , @Factory + @Bean , @Value , @PostConstruct , @PreDestroy , optional param?: T and collection T injection, and injecting ApplicationContext itself. The generated code for a bean is a plain constructor call: beans.push { id: 7, name: "NotificationService", / ... / create: r: BeanResolver = new C5 r.get 5 , r.get 6 , r.get 6 } ; @Controller '/users' export class UserController { @Get '/:id' get @PathParam id: number { ... } // "abc" → 400, coerced by generated code @Get @Cached 30, { staleWhileRevalidate: 60 } list @QueryParam limit?: number { ... } @Post create @Body body: NewUser { ... } // validated from the NewUser type } @Middleware 0 export class ServerTimingMiddleware implements HttpMiddleware { async handle ctx: RequestContext, next: = Promise