@solid-stack/agnos-react
v1.0.0
Published
Next.js-style presentation framework for Agnos and React SPAs with Hash Routing and DI injection.
Readme
@solid-stack/agnos-react
Next.js-style presentation framework for Agnos and React SPAs with Hash Routing and Clean Architecture Dependency Injection.
🎯 Architecture Overview
@solid-stack/agnos-react abstracts away react-router-dom entirely, providing a Next.js App Router Developer Experience (DX) while compiling down to a purely static SPA that uses Hash Routing (mandatory for Capacitor and Tauri desktop/mobile builds). It also seamlessly bridges @solid-stack/di directly into route data loaders before React components even render.
@solid-stack/agnos-react/src
├── bootstrap/
│ └── loadReactPresentation.tsx # Vite entry point & DOM bootstrap
├── core/
│ ├── routing/
│ │ └── buildRouter.tsx # Glob parser, AST & tree builder
│ ├── di/
│ │ └── DiContext.tsx # DI Provider & useResolve hook
├── components/
│ └── Link.tsx # Next.js style <Link href="...">
├── hooks/
│ ├── useRouter.ts # Next.js style router (push, replace, back, forward)
│ ├── usePathname.ts # Next.js style usePathname
│ ├── useParams.ts # Strongly typed params extractor
│ └── useSearchParams.ts # Query parameter hook
├── types/
│ └── index.ts # Public loader/action types
└── index.ts # Strict public API surface🚀 Getting Started
1. Application Bootstrap (main.tsx)
import { Container } from "@solid-stack/di";
import { loadReactPresentation } from "@solid-stack/agnos-react";
const container = new Container();
await loadReactPresentation(container, {
rootElementId: "root",
providers: import.meta.glob("./features/**/DepsProvider.ts", { eager: true }),
pages: import.meta.glob("./pages/**/*.{tsx,jsx}"),
});🛠️ Developer Experience (Next.js Conventions)
1. Dynamic Layouts (pages/projects/[id]/layout.tsx)
import React from "react";
import { Link, useParams } from "@solid-stack/agnos-react";
export default function ProjectLayout({ children }: { children: React.ReactNode }) {
const { id } = useParams<{ id: string }>();
return (
<div className="layout">
<nav>
<Link href={`/projects/${id}/settings`}>Settings</Link>
</nav>
<main>{children}</main>
</div>
);
}2. Pages with DI-Injected Data Loaders (pages/projects/[id]/page.tsx)
import { useLoaderData } from "react-router-dom";
import { useResolve, useRouter, type AgnosLoaderArgs } from "@solid-stack/agnos-react";
import { ProjectApiService } from "@/features/projects/ProjectApiService";
export const loader = async ({ params, container }: AgnosLoaderArgs) => {
const api = container.resolve(ProjectApiService);
const project = await api.getProjectById(params.id!);
return { project };
};
export default function ProjectDetails() {
const { project } = useLoaderData() as any;
const router = useRouter();
return (
<div>
<h1>{project.name}</h1>
<button onClick={() => router.push("/projects")}>Back to List</button>
</div>
);
}3. Resolving DI Services in Components (useResolve)
import { useResolve } from "@solid-stack/agnos-react";
import { UserService } from "@/features/user/UserService";
export function UserBadge() {
const userService = useResolve(UserService);
return <span>{userService.getUserName()}</span>;
}4. Search Parameters (useSearchParams)
import { useSearchParams } from "@solid-stack/agnos-react";
export function SearchFilter() {
const searchParams = useSearchParams();
return (
<div>
<input
value={searchParams.get("q") ?? ""}
onChange={(e) => searchParams.setParams({ q: e.target.value })}
/>
</div>
);
}🧪 Testing Strategy
Run the test suite:
pnpm testIncludes 5 test suites covering:
- Hash-Routing Engine: File glob normalization, dynamic routes, catch-alls, layout wrapping with Outlet injection, error boundaries, and loader DI container injection.
- Dependency Injection: Stateless resolution, missing provider protection, memoization.
- Routing Hooks:
useParams,useSearchParams(reading and writing),usePathname,useRouter. - Components:
<Link>component navigation. - Bootstrap E2E: Provider execution, router initialization, and DOM mounting.
📦 Build & Typecheck
pnpm check📄 License
UNLICENSED © Solid Stack Digital
