npm package: @piyushagade/flexirouter
Description: A lightweight, feature-rich SPA router supporting Hash/Path modes, dynamic params, and an event bus.
Author: Piyush Agade
Latest version: 1.0.2

# @piyushagade/flexirouter 🚀

A lightweight, feature-loaded Single Page Application (SPA) router for jQuery-powered projects. FlexiRouter transforms your traditional multi-page website into a modern, fluid experience with zero-config hash routing or clean Path URLs.

---

## ✨ Features

* **Dual Mode:** Switch between clean Path URLs (History API) and Zero-Config Hash URLs (`#/path`).
* **Dynamic Parameters:** Pattern matching to extract variables from URLs (e.g., `/user/:id`).
* **Lifecycle Hooks:** Granular control with `onLoad`, `onClose`, and `onRefresh`.
* **Lazy Loading:** Dynamic imports for modules, loading code only when the route is active.
* **Route Guards:** Global `authGuard` and per-route `beforeEnter` middleware.
* **Event Bus:** A built-in Pub/Sub system for decoupled module communication.
* **Headless Breadcrumbs:** Returns an array of "Steps" on every route change for custom UI rendering.
* **Loading States:** Built-in management for async loading overlays.

## ✨ New in v1.0.1

- ✅ **Auto-Queueing:** Call `router.navigate()` inside `onLoad` without `setTimeout`. The router now queues nested calls automatically.
- 🎣 **Lifecycle Fixes:** `onClose` and `onRefresh` now fire reliably with standardized arguments.
- 🛡️ **MIME Safety:** Documentation updated to address nested path MIME type errors via absolute paths.

## 📦 Installation

```bash
npm install @piyushagade/flexirouter
```

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@piyushagade/flexirouter@latest/FlexiRouter.min.js"></script>
```

## 🚯 Quick Start

### 1. Define your HTML
Mark your views with a specific class or data attribute (default is `[data-route-view]`). The dafault data attribute for navigation links is `[data-link]`. Both attribute names can be configured.

```html
<!-- Navigation -->
<nav>
  <a href="/dashboard" data-link>Dashboard</a>
  <a href="/settings" data-link>Settings</a>
</nav>

<!-- Views -->
<div id="view-dashboard" data-route-view class="hidden">Dashboard Content</div>
<div id="view-settings" data-route-view class="hidden">Settings Content</div>

<!-- Optional Loader -->
<div id="router-loader" style="display:none;">Loading...</div>
```

### 2. Initialize the Router
```javascript
import FlexiRouter from '@piyushagade/flexirouter';

new FlexiRouter({
    useHash: true,
    routes: {
        '/': { redirectTo: '/dashboard' },
        '/dashboard': { 
            target: 'view-dashboard',
            steps: [{ name: 'Home', url: '/' }, { name: 'Dashboard', url: '/dashboard' }]
        },
        '/user/:username': {
            target: 'view-profile',
            module: './ProfileManager.js',
            initMethod: 'init'
        },
        '/printer/:id': {
            onLoad: ({ params }) => {
                console.log("Entering printer", params.id);
            },
            onClose: () => console.log("Exiting printer")
        }
    }
});
```

Another example:
```javascript
const router = new FlexiRouter({
    routes: {
        '/': {
            onLoad: (router) => router.navigate('/dashboard')
        },
        '/profile/:id': {
            onLoad: (router, { params }) => console.log("Entering", params.id),
            onClose: () => console.log("Cleaning up")
        }
    }
});
```

## 🛡️ Route Middleware & Guards

```javascript
const router = new FlexiRouter({
    authGuard: async () => {
        return !!localStorage.getItem('token');
    },
    routes: {
        '/admin': {
            target: 'admin-view',
            protected: true,
            beforeEnter: async (params) => confirm("Enter restricted area?")
        }
    }
});
```

## 🎣 Lifecycle Hooks

If you do not wish to use FlexiRouter as described in `Define your HTML` section above, you can manually control DOM by bypassing the `target` property and using the following functions instead:

- **onLoad:** Triggered when entering a route.
- **onClose:** Triggered when leaving a route for a new one.
- **onRefresh:** Triggered when navigating to the already active route.

```javascript
const router = new FlexiRouter({
    authGuard: async () => {
        return !!localStorage.getItem('token');
    },
    routes: {
        '/': { 
            onLoad: (router, { params }) => {
                if (!auth.loggedin) router.navigate('/login');
                else router.navigate('/profile/' + auth.userId)
            },
            onClose: (router, { params }) => {
                console.log('Leaving login view');
            },
        },
        '/profile/:id': {
            onLoad: (router, { params }) => {
                console.log('User profile loaded for: ' + auth.userId);

                // DOM manipulation
                $('#login-overlay').fadeOut(200);
                $('#member-overlay').fadeIn(100);

                loadUserData(auth.userId);
            },

        }
    }
});
```


## 📢 Event Bus

```javascript
router.on('user-login', (user) => console.log(user.name));
router.emit('user-login', { name: 'Piyush' });
```

## 🌐 Server Configurations

For **Path Mode** (useHash: false), your server must serve `index.html` for all routes to allow FlexiRouter to take over routing.

### Express.js
```javascript
// Serve static files first, then fallback to index.html for non-API routes
app.use(express.static('public'));
app.get(/^(?!\/api).+/, (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
```

### Flask (Python)
```python
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    return render_template('index.html')
```

### Nginx
```nginx
location / {
    try_files $uri $uri/ /index.html;
}
```

## 🛠️ Migration Guide

1. **Consolidate:** Move body content into `index.html` within `data-route-view` divs.
2. **Links:** Add `data-link` to anchor tags.
3. **Logic:** Move jQuery logic into ES Modules.
4. **Server:** For Path mode, redirect 404s to `index.html`.


## 📄 License
MIT © [Piyush Agade](https://github.com/piyushagade)