UNPKG

@remediator/core

Version:
235 lines (171 loc) 6.66 kB
# @remediator/core **A lightweight, CQRS-style mediator for modern JavaScript frameworks.** Focus on your business logic. Decouple your UI from your application layer with a simple, powerful, and type-safe mediator pattern. --- ## Features - **Type-Safe:** Full TypeScript support from end to end. - 🚀 **Zero Dependencies:** A tiny footprint for a fast experience. - ⚙️ **Optional Vite Plugin:** Powerful auto-registration for handlers and middleware. - 🛠️ **Framework Agnostic:** Works with Remix, Next.js, React Router, or any JS project. - 📦 **Manual & Automatic Modes:** Use the Vite plugin for convenience or register handlers manually for ultimate control. --- ## 🔧 Install ```bash npm install @remediator/core ``` --- ## 🚀 Two Ways to Use `reMediator` You can use this package in two modes: **Automatic** (with the Vite plugin) or **Manual**. ### Mode 1: Auto-Registration with Vite (Recommended) Let the Vite plugin discover and register your handlers and middleware automatically. #### 1. Configure the Plugin Add the plugin to your `vite.config.ts`. ```ts // vite.config.ts import { reMediatorPlugin } from "@remediator/core"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [ reMediatorPlugin({ // Options are optional, these are the defaults: // path: "app", // outputDir: "remediator", // includeFileNames: [ // "*.mediator.ts", // "*.mediator.server.ts", // "*.server.mediator.ts", // ], }), ], // ... other config }); ``` #### 2. Follow the Naming Conventions The plugin works by scanning for files and exports that follow specific naming conventions. - **Files:** Must end with `.mediator.ts`, `.mediator.server.ts`, or `.server.mediator.ts`. - **Handlers:** Must be an exported class named `XxxHandler`. - **Requests:** Must be an exported class named `XxxQuery` or `XxxCommand` that matches a handler. - **Middleware:** Must be an exported function named `xxxMiddleware`. ```ts // app/modules/users/getUser.mediator.ts // A "Request" class (Query or Command) export class GetUserQuery implements IRequest<User> { // This phantom property is crucial for TypeScript's type inference. readonly _response?: User; constructor(public id: string) {} } // A "Command" that returns nothing export class DeleteUserCommand implements IRequest<void> { readonly _response?: void; constructor(public userId: string) {} } // A "Handler" class with a matching name export class GetUserQueryHandler implements IRequestHandler<GetUserQuery, User> { async handle(query: GetUserQuery): Promise<User> { // Your business logic lives here return await getUserFromDb(query.id); } } // A "Middleware" function export const authMiddleware: Pipeline = async (req, ctx, next) => { if (!ctx.isAuthenticated) throw new Error("Unauthorized"); return next(); }; ``` #### 3. Import the Generated Client The plugin generates a `remediator/client.ts` file. **This is the only instance you should import in your application code.** ```ts // In your loader, action, or component import { reMediator } from "~/remediator/client"; // Use your project's path alias export async function loader({ request, params }: LoaderFunctionArgs) { const user = await reMediator.send(GetUserQuery, { id: params.id }, request); return json({ user }); } ``` ### Mode 2: Manual Registration (For other build tools or full control) If you aren't using Vite or prefer to be explicit, you can register everything manually. #### 1. Import from Core Import the `reMediatorInstance` and helper functions directly from the core package. ```ts // /app/remediator-setup.ts import { reMediatorInstance, registerHandler, registerMiddleware, } from "@remediator/core"; // Import your classes and functions import { GetUserQuery, GetUserQueryHandler, } from "./modules/users/getUser.mediator"; import { authMiddleware } from "./modules/auth/auth.middleware"; // Register everything registerHandler(GetUserQuery, new GetUserQueryHandler()); registerMiddleware(authMiddleware); // IMPORTANT: Import this setup file once in your app's entry point (e.g., main.ts) ``` #### 2. Use the Instance In the rest of your app, import the default instance from the core package. ```ts import reMediator from "@remediator/core"; export async function loader({ request, params }: LoaderFunctionArgs) { const user = await reMediator.send(GetUserQuery, { id: params.id }, request); return json({ user }); } ``` --- ## 📡 Dispatching Requests `reMediator.send()` has two convenient overloads: 1. **Constructor + data**: (Recommended) Instantiate and hydrate a request in one step. ```ts await reMediator.send( GetUserQuery, // Request class { id: "u123" }, // Properties to hydrate the class request, // Raw Request object [localMiddleware] // Optional, per-call middleware ); ``` 2. **Instance**: Use a pre-built request object. ```ts const command = new UpdateOrderCommand("o456", "shipped"); await reMediator.send(command, request); ``` --- ## 🛠️ Middleware Middleware are powerful functions that run **before** your handler. They are perfect for cross-cutting concerns like authentication, logging, or caching. ```ts // A middleware function must match the Pipeline signature export const authMiddleware: Pipeline = async (request, context, next) => { // 1. Read from the raw request or context const token = context.rawRequest.headers.get("Authorization"); // 2. Short-circuit the request if (!token) throw new UnauthorizedError(); // 3. Add to the context for downstream handlers context.user = await verifyToken(token); // 4. Continue to the next middleware or the handler return next(); }; ``` --- ## 🔧 Plugin Options You can customize the Vite plugin's behavior. ```ts reMediatorPlugin({ // Directory to search for mediator files. path: "app", // Default: 'app' // File patterns to include in the scan. includeFileNames: [ // Default: "*.mediator.ts", "*.mediator.server.ts", "*.server.mediator.ts", ], // Where to write the generated client.ts and manifest.json. outputDir: "remediator", // Default: 'remediator' }); ``` --- > Simple, explicit, and easy to test — perfect for clean frontend architecture. © 2025 @remediator/core