npm package: @sirou/core
Description: Universal type-safe route schema, Trie matcher, guard system, and data loaders
Author: Sirou Contributors
Latest version: 1.1.3

# @sirou/core

The engine room of Sirou. This package provides the route schema definition, Trie-based matcher, universal router interface, guard system, data loaders, and all shared TypeScript types.

## Installation

```bash
npm install @sirou/core
```

---

## API Reference

### `defineRoutes(config)`

An identity function that provides full TypeScript inference for your route configuration. Always wrap your routes object with this.

```typescript
import { defineRoutes } from "@sirou/core";

export const routes = defineRoutes({
  home: { path: "/" },
  user: {
    path: "/user/:id",
    params: { id: "string" },
    meta: { title: "User Profile", requiresAuth: true },
    guards: ["auth"],
  },
  products: {
    path: "/products",
    children: {
      details: {
        path: "/:productId",
        params: { productId: "string" },
        loader: async ({ params }) => {
          return fetch(`/api/products/${params.productId}`).then((r) =>
            r.json(),
          );
        },
      },
    },
  },
  notFound: { path: "*" },
});
```

---

### `createRouter(config, adapter)`

Creates a platform-agnostic router instance. In most cases you'll use an adapter-specific factory (e.g., `SirouRouterProvider` in React), but you can use this directly.

```typescript
import { createRouter } from "@sirou/core";

const router = createRouter(routes, myAdapter);
```

---

### `SirouRouter` Interface

All Sirou routers implement this interface:

| Method                            | Description                                            |
| --------------------------------- | ------------------------------------------------------ |
| `go(routeName, params?)`          | Navigate to a route. Runs guards & loader first.       |
| `replace(routeName, params?)`     | Replace current history entry.                         |
| `back()`                          | Navigate back in history.                              |
| `current()`                       | Returns `RouteInfo` for the active route.              |
| `build(routeName, params?)`       | Build a URL string without navigating.                 |
| `subscribe(listener)`             | Subscribe to location changes. Returns unsubscribe fn. |
| `registerGuard(guard)`            | Register a named route guard.                          |
| `checkGuards(routeName, params?)` | Run guards without navigating.                         |
| `getHistory()`                    | Returns the navigation history array.                  |
| `getLoaderData(routeName)`        | Returns pre-fetched data for a route.                  |
| `getMeta(routeName)`              | Returns the metadata object for a route.               |
| `canGoBack()`                     | Returns `true` if there is history to go back to.      |

---

### Route Definition

Each route in your config can have the following properties:

```typescript
type RouteDefinition = {
  path: string; // URL path. Use :param for dynamic segments, * for wildcard.
  params?: {
    // Param type definitions for validation & inference.
    [key: string]:
      | "string"
      | "number"
      | "boolean"
      | "date"
      | {
          type: ParamType;
          optional?: boolean;
          validate?: (value: any) => boolean;
          transform?: (value: string) => any;
        };
  };
  meta?: Record<string, any>; // Arbitrary metadata (title, requiresAuth, etc.)
  guards?: string[]; // Names of guards to run before this route.
  loader?: (context: GuardContext) => Promise<any>; // Data fetcher.
  children?: RouteConfig; // Nested routes.
};
```

---

### Route Guards

Guards are async functions registered on the router. They run before navigation commits.

```typescript
router.registerGuard({
  name: "auth",
  execute: async ({ route, params, meta, context }) => {
    const token = localStorage.getItem("token");
    if (!token) {
      return { allowed: false, redirect: "/login" };
    }
    return { allowed: true };
  },
});
```

**Guard Context:**

```typescript
type GuardContext = {
  route: string; // The target route path
  params: Record<string, any>; // Resolved params
  meta?: Record<string, any>; // Route metadata
  context?: any; // Optional custom context
};
```

---

### Data Loaders

Add a `loader` to any route to fetch data before the component renders. The data is stored in the router and accessible via `router.getLoaderData(routeName)` or the `useRouteData()` hook in React.

```typescript
const routes = defineRoutes({
  products: {
    path: "/products",
    loader: async () => {
      const res = await fetch("/api/products");
      return res.json();
    },
  },
});
```

Loaders run **before** navigation commits, ensuring the component always has data on first render.

---

### Wildcard / 404 Routes

Use `path: '*'` to create a catch-all route. The Trie matcher uses priority order:

1. **Exact match** — `/about` matches `/about`
2. **Param match** — `/user/:id` matches `/user/123`
3. **Wildcard match** — `*` matches anything else

```typescript
const routes = defineRoutes({
  home: { path: "/" },
  notFound: { path: "*" },
});
```

---

### `SchemaValidator`

Validates a route config at runtime. Useful in CLI tools or dynamic configs.

```typescript
import { SchemaValidator } from "@sirou/core";

const validator = new SchemaValidator();
const result = validator.validate(routes);

if (!result.valid) {
  console.error(result.errors);
}
```

**Checks performed:**

- Duplicate paths
- Param name mismatches between path string and `params` config
- Invalid guard references

---

### `JSONExporter`

Serializes a route config to a plain JSON object. Used by the CLI `export` command and the Flutter bridge.

```typescript
import { JSONExporter } from "@sirou/core";

const exporter = new JSONExporter();
const json = exporter.export(routes);
fs.writeFileSync("routes.json", JSON.stringify(json, null, 2));
```

---

### `URLBuilder`

Builds URLs from a route definition and params object. Handles param interpolation.

```typescript
import { URLBuilder } from "@sirou/core";

const builder = new URLBuilder();
const url = builder.build({ path: "/user/:id" }, { id: "42" });
// => '/user/42'
```

---

## TypeScript Utilities

### `InferParams<Config>`

Infers the params object type from a `RouteParamConfig`.

```typescript
type MyParams = InferParams<{
  id: "string";
  page: { type: "number"; optional: true };
}>;
// => { id: string; page?: number }
```

### `FlatRouteMap<Config>`

Flattens a nested route config into a single-level map with dotted keys.

```typescript
type Routes = FlatRouteMap<typeof routes>;
// => { 'home': ..., 'products': ..., 'products.details': ... }
```

---

## License

MIT