Metadata-Version: 2.4
Name: flaxon-fyr
Version: 0.1.1
Summary: Fyr.js reactive frontend integration plugin for Flaxon framework
Author-email: Aldane Hutchinson <aldanehutchinson5@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/aldanedev-create/flaxon-fyr
Project-URL: Repository, https://github.com/aldanedev-create/flaxon-fyr
Project-URL: Issues, https://github.com/aldanedev-create/flaxon-fyr/issues
Project-URL: Documentation, https://fyrjsorg.vercel.app/
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flaxon>=0.1.8
Requires-Dist: jinja2>=3.1.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Requires-Dist: mypy>=1.8.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.30.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Provides-Extra: standard
Requires-Dist: flaxon[standard]; extra == "standard"
Dynamic: license-file

#🍳 Flaxon Fyr.js

Author of Fyr.js: ALdane Hutchinson
Author of Flaxon : Aldane Hutchinson




 <p align="center">
  <img src="https://raw.githubusercontent.com/aldanedev-create/Flaxon-Backend-Framework/main/assets/flaxon.png" alt="flaxon Logo"
   width="200"/>
</p>

 
  <p align="center">
  <a href="https://pypi.org/project/flaxon/"><img src="https://img.shields.io/pypi/v/flaxon.svg" alt="PyPI version"></a>
  <a href="https://github.com/aldanedev-create/Flaxon-Backend-Framework/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
  <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/badge/code%20style-ruff-000000.svg" alt="Code style: ruff"></a>
</p>
 



 <p align="center">
  <img src="https://raw.githubusercontent.com/aldanedev-create/Fyr-Framework-SC-/main/assets/fyr.png" alt="fyr Logo"
   width="200"/>
</p>






## Table of Contents

* [What is Fyr.js?](#what-is-fyrjs)
* [Features](#features)
* [Installation](#installation)
* [Quick Start](#quick-start)

  * [1. Load the Plugin](#1-load-the-plugin)
  * [2. Create a Route with a Fyr App](#2-create-a-route-with-a-fyr-app)
  * [3. Define a Fyr Controller (JavaScript)](#3-define-a-fyr-controller-javascript)
  * [4. HTML Template](#4-html-template)
* [Server Actions](#server-actions)
* [Configuration](#configuration)

  * [Environment Variables](#environment-variables)
  * [With Flaxon Config](#with-flaxon-config)
* [Advanced Usage](#advanced-usage)

  * [State Hydration](#state-hydration)
  * [Custom Templates](#custom-templates)
  * [Multiple Fyr Apps](#multiple-fyr-apps)
  * [CSRF Protection](#csrf-protection)
  * [Local Asset Serving](#local-asset-serving)
* [Fyr CDN Assets](#fyr-cdn-assets)
* [Testing](#testing)
* [Project Structure](#project-structure)
* [Security Best Practices](#security-best-practices)
* [Fyr Directives Reference](#fyr-directives-reference)
* [Roadmap](#roadmap)
* [Related Plugins](#related-plugins)
* [Contributing](#contributing)
* [License](#license)
* [Support](#support) 




**Fyr.js** reactive frontend integration plugin for Flaxon framework.

## What is Fyr.js?

Fyr.js is a CDN-only, HTML-first reactive JavaScript framework. It lets you build interactive web applications without installing Node.js, npm, or any build tools. Just add a script tag and use HTML directives.

**Website:** [https://fyrjsorg.vercel.app/](https://fyrjsorg.vercel.app/)

## Features

- 🚀 **CDN-only** — No installation required, just script tags
- 📦 **Reactive state** — Automatic DOM updates when state changes
- 🎯 **Server actions** — Call backend endpoints with `Fyr.action`
- 🔄 **State hydration** — Pass server data to Fyr frontend
- 🛡️ **CSRF protection** — Built-in CSRF for actions
- 🔌 **Plugin integration** — Seamless Flaxon plugin loading
- 📝 **Template rendering** — Render Fyr HTML with Jinja2
- 🎨 **Flexible assets** — CDN or local asset serving

## Installation

```bash
pip install flaxon-fyr
```

## Quick Start

### 1. Load the Plugin

```python
from flaxon import Flaxon
from flaxon_fyr import FyrPlugin

app = Flaxon("my-app")

await app.plugins.load_plugin(FyrPlugin(
    cdn_version="0.1.2",
))
```

### 2. Create a Route with a Fyr App

`FyrPlugin` doesn't export a standalone `fyr_app()` function — call `render_app()` on the plugin instance instead. From inside a route handler, the current app is available as `request.app`, so the plugin is reachable at `request.app.state.fyr`:

```python
@app.get("/")
async def home(request):
    return request.app.state.fyr.render_app(
        "counter",
        state={"count": 0},
        template="templates/counter.html"
    )
```

### 3. Define a Fyr Controller (JavaScript)

```javascript
// app.js
Fyr.createApp("counter", {
    state: { count: 0 },
    methods: {
        increment() {
            this.state.count += 1;
        },
        decrement() {
            this.state.count -= 1;
        }
    }
});

Fyr.start("counter");
```

### 4. HTML Template

```html
<!-- templates/counter.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Counter</title>
    <script defer src="{{ fyr_cdn_url }}"></script>
    <script defer src="/app.js"></script>
</head>
<body>
    <main fyr-app="counter">
        <h1>Counter: <span fyr-text="count"></span></h1>
        <button fyr-click="increment()">+</button>
        <button fyr-click="decrement()">-</button>
    </main>
</body>
</html>
```

## Server Actions

Define backend actions that Fyr can call. `@fyr_action` registers the handler into a global registry that `FyrPlugin` picks up automatically when it's constructed — so make sure your action modules are imported before you build the plugin instance:

```python
from flaxon_fyr import fyr_action

@fyr_action("todos.create")
async def create_todo(data, request):
    todo = await create_todo_in_db(data["text"])
    return {"success": True, "data": todo}

@fyr_action("todos.list")
async def list_todos(data, request):
    todos = await get_all_todos()
    return {"data": todos}
```

Every action handler must accept `(data, request)`, even if it doesn't use `data`.

Call from frontend:

```javascript
const result = await Fyr.action.call("todos.create", { text: "Learn Fyr" });
if (result.success) {
    console.log(result.data);
}
```

You can also register actions on an existing plugin instance directly:

```python
async def create_todo(data, request):
    todo = await create_todo_in_db(data["text"])
    return {"success": True, "data": todo}

plugin = FyrPlugin()
plugin.register_action("todos.create", create_todo)
await app.plugins.load_plugin(plugin)
```

## Configuration

### Environment Variables

```bash
# Fyr CDN version
FYR_CDN_VERSION=0.1.2

# Action prefix
FYR_ACTION_PREFIX=/_fyr/actions

# CSRF enabled
FYR_CSRF_ENABLED=true

# Use local assets
FYR_USE_LOCAL_ASSETS=false

# Debug mode
FYR_DEBUG=false
```

### With Flaxon Config

```python
app = Flaxon("my-app", config={
    "FYR_CDN_VERSION": "0.1.2",
    "FYR_ACTION_PREFIX": "/api/actions",
    "FYR_CSRF_ENABLED": True,
})

plugin = FyrPlugin.from_config(app.config)
await app.plugins.load_plugin(plugin)
```

## Advanced Usage

### State Hydration

Pass server data to Fyr frontend:

```python
from flaxon_fyr import hydrate

@app.get("/dashboard")
async def dashboard(request):
    user = await get_current_user(request)
    tasks = await get_user_tasks(user.id)
    
    state = hydrate({
        "user": {"id": user.id, "name": user.name},
        "tasks": [{"id": t.id, "text": t.text, "done": t.done} for t in tasks],
        "flash": request.session.pop("flash", {}),
    })
    
    return request.app.state.fyr.render_app("dashboard", state)
```

### Custom Templates

```python
@app.get("/custom")
async def custom(request):
    return request.app.state.fyr.render_app(
        "custom-app",
        state={"message": "Hello from server!"},
        template="templates/custom.html",
        context={
            "page_title": "Custom Page",
            "user": request.session.get("user"),
        }
    )
```

### Multiple Fyr Apps

```python
@app.get("/multi")
async def multi_apps(request):
    return request.app.state.fyr.render_app(
        "multi",
        state={
            "counter": {"count": 0},
            "todo": {"items": [], "draft": ""},
        }
    )
```

```html
<main fyr-app="multi">
    <section>
        <h2>Counter</h2>
        <span fyr-text="counter.count"></span>
        <button fyr-click="counter.count++">+</button>
    </section>
    
    <section>
        <h2>Todo</h2>
        <input fyr-model="todo.draft">
        <button fyr-click="todo.items.push({id: Date.now(), text: todo.draft})">
            Add
        </button>
        <template fyr-for="item in todo.items" fyr-key="item.id">
            <p fyr-text="item.text"></p>
        </template>
    </section>
</main>
```

### CSRF Protection

CSRF is on by default. The cookie is issued automatically on the first response and validated (via the `X-CSRFToken` header against the cookie) on every action request:

```python
# Enabled by default
await app.plugins.load_plugin(FyrPlugin(
    csrf_enabled=True,
))

# Disable for development
await app.plugins.load_plugin(FyrPlugin(
    csrf_enabled=False,
))
```

`FyrPlugin.__init__` doesn't take `csrf_cookie_name`/`csrf_header_name` directly — to change those, build a `FyrPluginConfig` and pass it as `config=`:

```python
from flaxon_fyr import FyrPlugin
from flaxon_fyr.plugin import FyrPluginConfig

await app.plugins.load_plugin(FyrPlugin(
    config=FyrPluginConfig(
        csrf_enabled=True,
        csrf_cookie_name="csrf_token",
        csrf_header_name="X-CSRFToken",
    ),
))
```

The current CSRF token for the request is available at `request._csrf_token` inside a route handler, if you need to render it into a form manually.

### Local Asset Serving

```python
# Serve Fyr assets from local static directory
await app.plugins.load_plugin(FyrPlugin(
    use_local_assets=True,
    assets_path="static/fyr",
))

# The plugin will serve:
# /fyr/assets/fyr.js
# /fyr/assets/fyr.min.js
# /fyr/assets/fyr-python.js
# etc.
```

## Fyr CDN Assets

| Asset | URL |
|---|---|
| Core | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr.min.js` |
| ESM | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr.esm.js` |
| Router | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-router.min.js` |
| Python | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-python.min.js` |
| WASM | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-wasm.min.js` |
| Socket | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-socket.min.js` |
| UI | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-ui.min.js` |
| UI CSS | `https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-ui.css` |

## Testing

```bash
# Run all tests
pytest

# Run with coverage
pytest --cov=flaxon_fyr

# Run specific test
pytest tests/test_actions.py -v
```

## Project Structure

```text
flaxon-fyr/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── flaxon_fyr/
│       ├── __init__.py      # Public API exports
│       ├── plugin.py        # FyrPlugin class
│       ├── renderer.py      # Fyr template rendering
│       ├── actions.py       # Server action handler
│       ├── assets.py        # Asset serving (CDN/local)
│       ├── hydration.py     # Server-to-client state hydration
│       ├── middleware.py    # Fyr middleware (CSRF, etc.)
│       └── types.py         # Type definitions
└── tests/
    ├── test_plugin.py
    ├── test_renderer.py
    ├── test_actions.py
    └── test_integration.py
```

## Security Best Practices

✅ Use CSRF protection in production

✅ Validate all action inputs on server

✅ Authenticate actions with session

✅ Use HTTPS in production

✅ Keep Fyr version pinned

✅ Use Subresource Integrity for CDN assets

✅ Sanitize data passed to `fyr-html`

✅ Never put secrets in frontend code

## Fyr Directives Reference

| Directive | Purpose | Example |
|---|---|---|
| `fyr-app` | Mark application root | `fyr-app="todo"` |
| `fyr-controller` | Attach state and methods | `fyr-controller="cart"` |
| `fyr-text` | Render escaped text | `fyr-text="user.name"` |
| `fyr-html` | Render trusted HTML | `fyr-html="trustedContent"` |
| `fyr-model` | Two-way form binding | `fyr-model="form.email"` |
| `fyr-click` | Handle click | `fyr-click="save()"` |
| `fyr-on:event` | Handle any event | `fyr-on:input="search()"` |
| `fyr-show` | Toggle visibility | `fyr-show="loggedIn"` |
| `fyr-if` | Create/remove content | `fyr-if="items.length"` |
| `fyr-for` | Repeat template | `fyr-for="item in items"` |
| `fyr-submit` | Handle form submit | `fyr-submit="login()"` |
| `fyr-bind:*` | Bind attribute/property | `fyr-bind:disabled="busy"` |
| `fyr-class` | Bind CSS classes | `fyr-class="{ active: selected }"` |
| `fyr-style` | Bind styles | `fyr-style="{ width: progress + '%' }"` |
| `fyr-ref` | Store DOM reference | `fyr-ref="emailInput"` |
| `fyr-init` | Run startup method | `fyr-init="load()"` |
| `fyr-cloak` | Hide before initialization | `fyr-cloak` |
| `fyr-transition` | Apply transition hooks | `fyr-transition="fade"` |

## Roadmap

| Version | Features |
|---|---|
| 0.1.0 | Basic Fyr integration, CDN serving |
| 0.2.0 | Server actions with CSRF |
| 0.3.0 | State hydration and session integration |
| 0.4.0 | Component rendering |
| 0.5.0 | Asset serving (local) |
| 0.6.0 | Fyr router integration |

## Related Plugins

- flaxon-jinax - Jinja2 template integration
- flaxon-sentry - Sentry error tracking
- flaxon-oauth-google - Google OAuth
- flaxon-inertia - Inertia.js integration

## Contributing

1. Fork the repository
2. Create a feature branch
3. Add tests for new features
4. Ensure all tests pass
5. Submit a pull request

## License

MIT License - See LICENSE file for details.

## Support

- 📚 Documentation
- 🐛 Issue Tracker
- 💬 Discussions
- 🌐 [Fyr.js Website](https://fyrjsorg.vercel.app/)
