Metadata-Version: 2.4
Name: flaxon-fyr
Version: 0.1.0
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://flaxon.io/docs/plugins/fyr
Classifier: Development Status :: 3 - Alpha
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

**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, fyr_app

app = Flaxon("my-app")

app.plugins.load_plugin(FyrPlugin(
    cdn_version="0.1.2",
))
2. Create a Route with Fyr App
python
@app.get("/")
async def home(request):
    return fyr_app(
        "counter",
        state={"count": 0},
        template="templates/counter.html"
    )
3. Define 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:

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(request):
    todos = await get_all_todos()
    return {"data": todos}
Call from frontend:

javascript
const result = await Fyr.action.call("todos.create", { text: "Learn Fyr" });
if (result.success) {
    console.log(result.data);
}
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)
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 fyr_app("dashboard", state)
Custom Templates
python
@app.get("/custom")
async def custom(request):
    return fyr_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 fyr_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
python
# Enabled by default
app.plugins.load_plugin(FyrPlugin(
    csrf_enabled=True,
    csrf_cookie_name="csrf_token",
    csrf_header_name="X-CSRFToken",
))

# Disable for development
app.plugins.load_plugin(FyrPlugin(
    csrf_enabled=False,
))
Local Asset Serving
python
# Serve Fyr assets from local static directory
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
Fork the repository

Create a feature branch

Add tests for new features

Ensure all tests pass

Submit a pull request

License
MIT License - See LICENSE file for details.

Support
📚 Documentation

🐛 Issue Tracker

💬 Discussions

🌐 Fyr.js Website
