xania 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. xania-0.1.2/LICENSE +22 -0
  2. xania-0.1.2/PKG-INFO +485 -0
  3. xania-0.1.2/README.md +461 -0
  4. xania-0.1.2/pyproject.toml +41 -0
  5. xania-0.1.2/setup.cfg +4 -0
  6. xania-0.1.2/xania/__init__.py +23 -0
  7. xania-0.1.2/xania/__main__.py +8 -0
  8. xania-0.1.2/xania/cli.py +330 -0
  9. xania-0.1.2/xania/engine/runtime.py +50 -0
  10. xania-0.1.2/xania/engine/serializer.py +60 -0
  11. xania-0.1.2/xania/example/counter.py +49 -0
  12. xania-0.1.2/xania/renderer/__init__.py +12 -0
  13. xania-0.1.2/xania/renderer/component.py +46 -0
  14. xania-0.1.2/xania/renderer/elements.py +323 -0
  15. xania-0.1.2/xania/renderer/index.html +64 -0
  16. xania-0.1.2/xania/renderer/registry.py +34 -0
  17. xania-0.1.2/xania/renderer/render.py +12 -0
  18. xania-0.1.2/xania/renderer/state.py +154 -0
  19. xania-0.1.2/xania/renderer/test.html +511 -0
  20. xania-0.1.2/xania/runtime/__init__.py +22 -0
  21. xania-0.1.2/xania/runtime/compiler.py +95 -0
  22. xania-0.1.2/xania/runtime/spa.py +119 -0
  23. xania-0.1.2/xania/static/app.js +1115 -0
  24. xania-0.1.2/xania/static/runtime.js +113 -0
  25. xania-0.1.2/xania/static/spa_runtime.js +271 -0
  26. xania-0.1.2/xania/web/app.py +40 -0
  27. xania-0.1.2/xania/web/auth.py +239 -0
  28. xania-0.1.2/xania/web/config.py +43 -0
  29. xania-0.1.2/xania/web/ratelimit.py +46 -0
  30. xania-0.1.2/xania/web/routes.py +248 -0
  31. xania-0.1.2/xania/web/schemas.py +24 -0
  32. xania-0.1.2/xania/web/serve.py +61 -0
  33. xania-0.1.2/xania.egg-info/PKG-INFO +485 -0
  34. xania-0.1.2/xania.egg-info/SOURCES.txt +36 -0
  35. xania-0.1.2/xania.egg-info/dependency_links.txt +1 -0
  36. xania-0.1.2/xania.egg-info/entry_points.txt +2 -0
  37. xania-0.1.2/xania.egg-info/requires.txt +8 -0
  38. xania-0.1.2/xania.egg-info/top_level.txt +1 -0
xania-0.1.2/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
xania-0.1.2/PKG-INFO ADDED
@@ -0,0 +1,485 @@
1
+ Metadata-Version: 2.4
2
+ Name: xania
3
+ Version: 0.1.2
4
+ Summary: A minimal React-like Python UI framework on FastAPI (CSR runtime + component model).
5
+ License: MIT
6
+ Keywords: xania,ui,spa,fastapi,vdom,framework
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Framework :: FastAPI
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Python: >=3.12
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: click>=8.3.3
17
+ Requires-Dist: fastapi>=0.136.1
18
+ Requires-Dist: pydantic>=2.13.3
19
+ Requires-Dist: uvicorn>=0.46.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: build>=1.5.0; extra == "dev"
22
+ Requires-Dist: twine>=6.2.0; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # Xania — Python UI Framework for SPAs
26
+
27
+ **Build fast, interactive web apps entirely in Python. No JavaScript required.**
28
+
29
+ Xania compiles Python code to standalone Single-Page Applications (SPAs) that run 100% in the browser.
30
+
31
+ ## Features
32
+
33
+ - **Write UI in Python** – VDOM elements, components, state management
34
+ - **Compile to JavaScript** – Your Python spec → `index.html` + `app.js`
35
+ - **Deploy anywhere** – Static SPA with no server needed
36
+ - **Production-ready** – Built on FastAPI with auth, rate limiting, CSRF protection
37
+ - **Zero client-side code** – Framework handles all routing, state, and rendering
38
+
39
+ ## Quick Start
40
+
41
+ ### Requirements
42
+ - Python **3.12+**
43
+
44
+ ### 1. Create a new project
45
+
46
+ ```bash
47
+ pip install xania
48
+ xania init my_website
49
+ cd my_website
50
+ ```
51
+
52
+ ### 2. Edit your site
53
+
54
+ Edit `app.py` to add pages and content:
55
+
56
+ ```python
57
+ from xania import SpaApp, StaticPage, Div, H1, Button
58
+
59
+ app = SpaApp(name="MySite")
60
+
61
+ app.route(
62
+ "/",
63
+ StaticPage(
64
+ title="Home",
65
+ html='<h1>Welcome!</h1><p>Built with Xania</p>'
66
+ )
67
+ )
68
+ ```
69
+
70
+ ### 3. Compile
71
+
72
+ ```bash
73
+ python app.py
74
+ # Generates: index.html + static/app.js + static/spa_runtime.js
75
+ ```
76
+
77
+ ### 4. Run
78
+
79
+ ```bash
80
+ xania serve .
81
+ # 🚀 Serving SPA from /path/to/my_website
82
+ # 📍 http://127.0.0.1:8000
83
+ ```
84
+
85
+ Open your browser to `http://127.0.0.1:8000`
86
+
87
+ ### 5. Deploy
88
+
89
+ Copy the generated files to any static host:
90
+
91
+ ```bash
92
+ # Upload to Netlify, Vercel, GitHub Pages, S3, etc.
93
+ cp -r . /path/to/deployment/
94
+ ```
95
+
96
+ ## How It Works
97
+
98
+ ### SPA Mode (Recommended)
99
+
100
+ 1. **Define your app in Python** – Use `SpaApp` with routes and pages
101
+ 2. **Compile** – Xania generates JavaScript that runs entirely in the browser
102
+ 3. **Deploy** – Copy `index.html` + `static/` to any static host
103
+
104
+ No server needed for the SPA itself. Client-side routing via History API.
105
+
106
+ ```python
107
+ from xania import SpaApp, StaticPage
108
+
109
+ app = SpaApp(name="MySite")
110
+ app.route("/", StaticPage(title="Home", html="..."))
111
+ app.route("/about", StaticPage(title="About", html="..."))
112
+
113
+ # Then: python app.py → generates index.html + app.js
114
+ ```
115
+
116
+ ### Server Mode (Advanced)
117
+
118
+ If you need a backend, integrate with FastAPI:
119
+
120
+ ```python
121
+ from fastapi import FastAPI
122
+ from xania import mount_spa
123
+
124
+ app = FastAPI()
125
+
126
+ # Add API routes
127
+ @app.get("/api/data")
128
+ def get_data():
129
+ return {"items": [...]}
130
+
131
+ # Mount compiled SPA
132
+ mount_spa(app, ".")
133
+ ```
134
+
135
+ Then: `xania serve .` or `uvicorn app:app`
136
+
137
+ ---
138
+
139
+ ## Two Modes Explained
140
+
141
+ **SPA Mode** (Default)
142
+ - HTML shell + JavaScript SPA
143
+ - Client-side routing (History API)
144
+ - No server needed for UI
145
+ - Perfect for: static sites, blogs, documentation, dashboards
146
+ - Deploy to: Netlify, Vercel, GitHub Pages, S3
147
+
148
+ **Server Mode** (Optional)
149
+ - FastAPI backend + SPA frontend
150
+ - Add custom APIs, auth, data
151
+ - Still compiles to static files
152
+ - Perfect for: full-stack apps with complex backends
153
+ - Deploy to: Heroku, Railway, AWS, DigitalOcean
154
+
155
+ ---
156
+
157
+ ## Core Concepts
158
+
159
+ **VDOM Elements** – Python classes representing HTML:
160
+
161
+ ```python
162
+ from xania import Div, H1, Button, Input
163
+
164
+ Div(
165
+ H1("Welcome"),
166
+ Input(type="text", placeholder="Enter name"),
167
+ Button("Click me", onclick="App.dispatch('Counter', 'click')"),
168
+ class_name="flex flex-col gap-4"
169
+ )
170
+ ```
171
+
172
+ **Components** – Stateful UI units:
173
+
174
+ ```python
175
+ from xania import Component, Div, Span, Button
176
+
177
+ class Counter(Component):
178
+ def initial_state(self):
179
+ return {"count": 0}
180
+
181
+ def render(self, state):
182
+ return Div(
183
+ Span(str(state.count)),
184
+ Button("+", onclick="App.dispatch('Counter', 'inc')")
185
+ )
186
+
187
+ def on_inc(self, state, payload):
188
+ state.count += 1
189
+ ```
190
+
191
+ **Pages** – Routes in your SPA:
192
+
193
+ ```python
194
+ from xania import SpaApp, StaticPage, TemplatePage, JsExpr
195
+
196
+ app = SpaApp()
197
+
198
+ # Static page
199
+ app.route("/", StaticPage(
200
+ title="Home",
201
+ html="<h1>Welcome</h1>"
202
+ ))
203
+
204
+ # Template page with placeholders
205
+ app.route("/user/{id}", TemplatePage(
206
+ title="User",
207
+ template="<h1>User {name}</h1>",
208
+ placeholders={"name": "John"}
209
+ ))
210
+ ```
211
+
212
+ ---
213
+
214
+ ## Project Structure
215
+
216
+ **Core Modules:**
217
+ - `renderer/` – VDOM elements, components, state management
218
+ - `runtime/` – SPA compiler (Python → JavaScript)
219
+ - `web/` – FastAPI integration, serving, auth
220
+ - `static/` – Browser runtime (`spa_runtime.js`)
221
+ - `cli.py` – Command-line interface
222
+
223
+ **See also:** [documentation/](documentation/) for full tutorial SPA built with Xania
224
+
225
+ ---
226
+
227
+ ## Examples
228
+
229
+ ### Counter with State
230
+
231
+ ```python
232
+ from xania import Component, Div, Button, Span
233
+
234
+ class Counter(Component):
235
+ def initial_state(self):
236
+ return {"count": 0}
237
+
238
+ def render(self, state):
239
+ return Div(
240
+ Span(f"Count: {state.count}"),
241
+ Button("+", onclick="App.dispatch('Counter', 'inc')"),
242
+ Button("-", onclick="App.dispatch('Counter', 'dec')"),
243
+ )
244
+
245
+ def on_inc(self, state, payload):
246
+ state.count += 1
247
+
248
+ def on_dec(self, state, payload):
249
+ state.count -= 1
250
+ ```
251
+
252
+ ### Multi-Page SPA
253
+
254
+ ```python
255
+ from xania import SpaApp, StaticPage, TemplatePage
256
+
257
+ app = SpaApp(name="MyBlog")
258
+
259
+ # Home page
260
+ app.route("/", StaticPage(
261
+ title="Home",
262
+ html="<h1>Welcome to My Blog</h1>"
263
+ ))
264
+
265
+ # About page
266
+ app.route("/about", StaticPage(
267
+ title="About",
268
+ html="<h1>About Me</h1><p>I build with Xania.</p>"
269
+ ))
270
+
271
+ # Post page with placeholders
272
+ app.route("/post/{id}", TemplatePage(
273
+ title="Post",
274
+ template="<h1>{title}</h1><p>{content}</p>",
275
+ placeholders={"title": "My First Post", "content": "..."}
276
+ ))
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Deployment
282
+
283
+ ### Option 1: Static Hosting (Simplest)
284
+
285
+ ```bash
286
+ # Compile
287
+ python app.py
288
+
289
+ # Upload everything to your host
290
+ # - Netlify (drag & drop)
291
+ # - Vercel (git integration)
292
+ # - GitHub Pages
293
+ # - S3 + CloudFront
294
+ ```
295
+
296
+ ### Option 2: FastAPI Server
297
+
298
+ ```python
299
+ # serve.py
300
+ from fastapi import FastAPI
301
+ from xania import mount_spa
302
+
303
+ app = FastAPI()
304
+
305
+ @app.get("/api/hello")
306
+ def hello():
307
+ return {"message": "Hello from API!"}
308
+
309
+ mount_spa(app, ".")
310
+ ```
311
+
312
+ ```bash
313
+ # Local: uvicorn serve:app --reload
314
+ # Production: gunicorn serve:app (on Heroku, Railway, etc.)
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Development
320
+
321
+ Clone and install:
322
+
323
+ ```bash
324
+ git clone https://github.com/xania/framework.git
325
+ cd xania
326
+ python -m venv .venv
327
+ source .venv/bin/activate
328
+ pip install -e ".[dev]"
329
+ ```
330
+
331
+ Run examples:
332
+
333
+ ```bash
334
+ xania serve documentation # Full tutorial
335
+ xania dev # Dev server
336
+ ```
337
+
338
+ ---
339
+
340
+ ## Contributing
341
+
342
+ PRs welcome! See [GitHub](https://github.com/xania/framework)
343
+ - DOM events calling `App.dispatch('Counter', 'increment')`
344
+
345
+ ## Tags / Elements (VDOM)
346
+
347
+ Xania’s “tags” are Python functions/classes that build a VDOM tree in `renderer/elements.py`.
348
+
349
+ - **Use built-in tag helpers**: `Div(...)`, `Span(...)`, `Button(...)`, `H1(...)`, etc.
350
+ - **Use `Element(tag, ...)` for any HTML tag** (even if there is no helper yet):
351
+
352
+ ```python
353
+ from renderer.elements import Element
354
+
355
+ node = Element("section", "Hello")
356
+ ```
357
+
358
+ ### Children
359
+ - **Text children**: strings (they are HTML-escaped by `engine/serializer.py`)
360
+ - **Element children**: nested `Element` instances
361
+ - **Lists**: spread them with `*items` when building children
362
+
363
+ ### Attributes (“props”)
364
+ Attributes are passed as keyword args:
365
+
366
+ - **`class_name` → `class`**
367
+ - **`for_` → `for`**
368
+ - **`http_equiv` → `http-equiv`**
369
+ - **Any `_` becomes `-`** (e.g. `data_component="Counter"` → `data-component="Counter"`)
370
+ - **Booleans**:
371
+ - `disabled=True` renders `disabled`
372
+ - `disabled=False` omits it
373
+
374
+ Example:
375
+
376
+ ```python
377
+ from renderer.elements import Button
378
+
379
+ Button(
380
+ "Click",
381
+ class_name="px-3 py-2 rounded",
382
+ data_component="Counter",
383
+ disabled=False,
384
+ )
385
+ ```
386
+
387
+ ## Registering components
388
+
389
+ In `app.py`:
390
+
391
+ ```python
392
+ from renderer.registry import ComponentRegistry
393
+ from example.counter import Counter
394
+
395
+ ComponentRegistry.register("Counter", Counter(id="counter"))
396
+ ```
397
+
398
+ The string `"Counter"` must match what the client dispatches:
399
+
400
+ ```html
401
+ onclick="App.dispatch('Counter','increment')"
402
+ ```
403
+
404
+ ## Web API
405
+
406
+ ### `GET /`
407
+ Returns the HTML shell (mount points + `<script src="/static/runtime.js">`).
408
+
409
+ ### `POST /event`
410
+ Request (`web/schemas.py`):
411
+
412
+ ```json
413
+ {
414
+ "component": "Counter",
415
+ "action": "increment",
416
+ "payload": {}
417
+ }
418
+ ```
419
+
420
+ Response:
421
+
422
+ ```json
423
+ {
424
+ "updates": [
425
+ { "id": "counter", "html": "<div>...</div>" }
426
+ ]
427
+ }
428
+ ```
429
+
430
+ The client applies updates by doing `document.getElementById(id).innerHTML = html`.
431
+
432
+ ## Publish readiness (honest checklist)
433
+
434
+ This codebase is **a solid architectural baseline**, but it is **not ready to publish as a production framework** yet. Here’s what’s missing / risky:
435
+
436
+ - **Session/user isolation**: `ComponentRegistry` stores **singletons** in-process.
437
+ - In production you need per-user (cookie/session) component instances or a state store.
438
+ - **Concurrency & scaling**:
439
+ - Multiple workers/processes will not share component state.
440
+ - Need a storage layer (Redis/DB) or stateless model (client-owned state + patches).
441
+ - **Security**:
442
+ - `onclick="App.dispatch(...)"` is safe here because HTML is generated server-side, but any user-provided text/attrs must be carefully validated.
443
+ - Consider CSRF protection for `/event`.
444
+ - Demo auth exists for stress-testing, but production needs strong secrets + HTTPS-only cookies + real user storage.
445
+ - **Performance**:
446
+ - Current updates replace `innerHTML` of the whole component.
447
+ - Next step: **diffing + patching** (you already have a base architecture for it).
448
+ - **Testing**:
449
+ - No unit tests or integration tests yet.
450
+ - **Packaging polish**:
451
+ - Ensure `pyproject.toml` metadata is correct (name/urls/authors).
452
+ - Add a changelog and decide versioning strategy.
453
+
454
+ ## Roadmap (recommended next steps)
455
+
456
+ - Add per-session registry/state store (cookie -> component state)
457
+ - Add diff/patch engine (update minimal DOM, not full `innerHTML`)
458
+ - Add router + multipage CSR navigation
459
+ - Add test suite + CI
460
+ - Add docs site and examples gallery
461
+
462
+ ## Publish checklist (minimal)
463
+
464
+ - **Package identity**: confirm `pyproject.toml` name/version/description are correct.
465
+ - **License**: ensure `LICENSE` exists and matches `pyproject.toml` (`MIT` here).
466
+ - **Versioning**: adopt SemVer (`0.x` while APIs are changing quickly).
467
+ - **README**: keep Quick start + minimal example runnable.
468
+ - **Security**: decide on CSRF/auth strategy for `/event` before public deploys.
469
+ - **State model**: decide per-user state persistence (sessions/Redis) before scaling.
470
+ - **CI**: add at least `python -m py_compile` + basic tests on every push.
471
+
472
+ ## Demo auth (stress testing only)
473
+
474
+ This repo includes a minimal cookie-session auth demo used by the SPA stress page:
475
+
476
+ - Login endpoint: `POST /api/auth/login` (demo user: `admin/admin`)
477
+ - Logout endpoint: `POST /api/auth/logout`
478
+ - Current user: `GET /api/auth/me`
479
+ - Protected stress endpoints: `GET /api/private/big-json`, `POST /api/private/write-echo`
480
+
481
+ Environment variables:
482
+
483
+ - `XANIA_ENV=dev|prod` (default `dev`)
484
+ - `XANIA_SECRET_KEY=...` (required when `XANIA_ENV` is not dev)
485
+ - `XANIA_COOKIE_SECURE=true|false` (defaults to true outside dev)