astris-python 0.1.0__py3-none-any.whl

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.
astris/kernel.py ADDED
@@ -0,0 +1,230 @@
1
+ import importlib
2
+ import inspect
3
+ import pkgutil
4
+ import sys
5
+ from collections.abc import Callable, Coroutine, Sequence
6
+ from pathlib import Path
7
+ from typing import Any, Literal
8
+
9
+ from fastapi import APIRouter, FastAPI, HTTPException
10
+ from fastapi.exceptions import RequestValidationError
11
+ from fastapi.middleware.cors import CORSMiddleware
12
+ from pydantic import ValidationError
13
+ from starlette.exceptions import HTTPException as StarletteHTTPException
14
+ from starlette.middleware.sessions import SessionMiddleware
15
+ from starlette.staticfiles import StaticFiles
16
+
17
+ from astris.config import Settings, settings
18
+ from astris.database import db
19
+ from astris.http.static import PublicStaticMiddleware
20
+ from astris.inertia import share
21
+ from astris.inertia.exceptions import (
22
+ inertia_http_exception_handler,
23
+ inertia_validation_exception_handler,
24
+ )
25
+ from astris.inertia.shared import FlashMiddleware
26
+ from astris.security import CSRFMiddleware
27
+
28
+
29
+ class Astris:
30
+ """Core framework application class."""
31
+
32
+ def __init__(
33
+ self,
34
+ base_path: Path | None = None,
35
+ config: Settings | None = None,
36
+ title: str | None = None,
37
+ cors_origins: Sequence[str] | None = None,
38
+ enable_csrf: bool | None = None,
39
+ csrf_exempt_paths: Sequence[str] | None = None,
40
+ shared_props: dict[str, Any] | None = None,
41
+ database_url: str | None = None,
42
+ auto_create_tables: bool | None = None,
43
+ db_echo: bool | None = None,
44
+ secret_key: str | None = None,
45
+ session_cookie_name: str | None = None,
46
+ session_max_age: int | None = None,
47
+ session_https_only: bool | None = None,
48
+ session_same_site: Literal["lax", "strict", "none"] | None = None,
49
+ **fastapi_kwargs: Any,
50
+ ):
51
+ self.base_path = base_path or Path.cwd()
52
+ self.config = config or settings
53
+
54
+ self.cors_origins: Sequence[str] = (
55
+ cors_origins if cors_origins is not None else self.config.cors_origins
56
+ )
57
+ self.enable_csrf = (
58
+ enable_csrf if enable_csrf is not None else self.config.enable_csrf
59
+ )
60
+ self.csrf_exempt_paths: Sequence[str] = (
61
+ csrf_exempt_paths
62
+ if csrf_exempt_paths is not None
63
+ else self.config.csrf_exempt_paths
64
+ )
65
+ self.auto_create_tables = (
66
+ auto_create_tables
67
+ if auto_create_tables is not None
68
+ else self.config.auto_create_tables
69
+ )
70
+ self.secret_key = secret_key or self.config.app_key
71
+ self.session_cookie_name = (
72
+ session_cookie_name or self.config.session_cookie_name
73
+ )
74
+ self.session_max_age = (
75
+ session_max_age
76
+ if session_max_age is not None
77
+ else self.config.session_max_age
78
+ )
79
+ self.session_https_only = (
80
+ session_https_only
81
+ if session_https_only is not None
82
+ else self.config.session_https_only
83
+ )
84
+ self.session_same_site = (
85
+ session_same_site or self.config.session_same_site # type: ignore[assignment]
86
+ )
87
+
88
+ # Configure database engine
89
+ db.configure(
90
+ url=database_url or self.config.database_url,
91
+ echo=db_echo if db_echo is not None else self.config.db_echo,
92
+ base_path=self.base_path,
93
+ )
94
+
95
+ if shared_props:
96
+ share(shared_props)
97
+
98
+ # Ensure project root is in sys.path for dynamic imports
99
+ base_path_str = str(self.base_path)
100
+ if base_path_str not in sys.path:
101
+ sys.path.insert(0, base_path_str)
102
+
103
+ app_title = title or self.config.app_name
104
+ app_debug = self.config.app_debug
105
+ self.app = FastAPI(title=app_title, debug=app_debug, **fastapi_kwargs)
106
+ self._boot()
107
+
108
+ def _boot(self) -> None:
109
+ self._configure_middleware()
110
+ self._configure_exception_handlers()
111
+ self._mount_static()
112
+ self._discover_modules()
113
+ if self.auto_create_tables:
114
+ db.create_all()
115
+
116
+ def _configure_exception_handlers(self) -> None:
117
+ """Register Inertia validation and HTTP exception handlers."""
118
+ self.app.add_exception_handler(
119
+ RequestValidationError,
120
+ inertia_validation_exception_handler,
121
+ )
122
+ self.app.add_exception_handler(
123
+ ValidationError,
124
+ inertia_validation_exception_handler,
125
+ )
126
+ self.app.add_exception_handler(
127
+ HTTPException,
128
+ inertia_http_exception_handler,
129
+ )
130
+ self.app.add_exception_handler(
131
+ StarletteHTTPException,
132
+ inertia_http_exception_handler,
133
+ )
134
+
135
+ def _configure_middleware(self) -> None:
136
+ """Register middleware stack (configured innermost to outermost)."""
137
+ # 1. CSRF protection (innermost HTTP security)
138
+ if self.enable_csrf:
139
+ self.app.add_middleware(
140
+ CSRFMiddleware,
141
+ exempt_paths=self.csrf_exempt_paths,
142
+ )
143
+
144
+ # 2. Flash message persistence
145
+ self.app.add_middleware(FlashMiddleware)
146
+
147
+ # 3. Encrypted/signed cookie sessions
148
+ if self.secret_key:
149
+ self.app.add_middleware(
150
+ SessionMiddleware,
151
+ secret_key=self.secret_key,
152
+ session_cookie=self.session_cookie_name,
153
+ max_age=self.session_max_age,
154
+ https_only=self.session_https_only,
155
+ same_site=self.session_same_site,
156
+ )
157
+
158
+ # 4. Public static assets (serves /favicon.ico, /robots.txt, and public/ files directly)
159
+ public_dir = self.base_path / "public"
160
+ if public_dir.exists():
161
+ self.app.add_middleware(PublicStaticMiddleware, public_dir=public_dir)
162
+
163
+ # 5. CORS configuration (outermost to handle preflight)
164
+ self.app.add_middleware(
165
+ CORSMiddleware,
166
+ allow_origins=list(self.cors_origins),
167
+ allow_credentials=True,
168
+ allow_methods=["*"],
169
+ allow_headers=["*"],
170
+ expose_headers=["X-Inertia", "X-XSRF-TOKEN"],
171
+ )
172
+
173
+ def _mount_static(self) -> None:
174
+ """Mount compiled Vite frontend assets."""
175
+ build_dir = self.base_path / "public" / "build"
176
+ build_dir.mkdir(parents=True, exist_ok=True)
177
+ self.app.mount(
178
+ "/build",
179
+ StaticFiles(directory=str(build_dir), check_dir=False),
180
+ name="build",
181
+ )
182
+
183
+ def _discover_modules(self) -> None:
184
+ modules_dir = self.base_path / "app" / "modules"
185
+ if not modules_dir.exists():
186
+ return
187
+
188
+ registered_routers: set[int] = set()
189
+
190
+ for _, modname, ispkg in pkgutil.walk_packages(
191
+ [str(modules_dir)], prefix="app.modules."
192
+ ):
193
+ if ispkg:
194
+ continue
195
+
196
+ last_part = modname.split(".")[-1]
197
+
198
+ # Auto-import models to register SQLModel table metadata
199
+ if last_part.endswith(("_model", "_models")) or last_part in (
200
+ "models",
201
+ "model",
202
+ ):
203
+ try:
204
+ importlib.import_module(modname)
205
+ except ImportError:
206
+ pass
207
+
208
+ # Only scan controller files
209
+ if not last_part.endswith("_controller") and not last_part.startswith(
210
+ "controller"
211
+ ):
212
+ continue
213
+
214
+ module = importlib.import_module(modname)
215
+ for _, member in inspect.getmembers(module):
216
+ if (
217
+ isinstance(member, APIRouter)
218
+ and id(member) not in registered_routers
219
+ ):
220
+ self.app.include_router(member)
221
+ registered_routers.add(id(member))
222
+
223
+ async def __call__(
224
+ self,
225
+ scope: Any,
226
+ receive: Callable[..., Coroutine[Any, Any, Any]],
227
+ send: Callable[..., Coroutine[Any, Any, None]],
228
+ ) -> None:
229
+ """ASGI 3 interface."""
230
+ await self.app(scope, receive, send)
astris/py.typed ADDED
File without changes
@@ -0,0 +1,31 @@
1
+ from astris.routing.router import (
2
+ Body,
3
+ Controller,
4
+ Cookie,
5
+ Depends,
6
+ File,
7
+ Form,
8
+ Header,
9
+ Path,
10
+ PathParam,
11
+ Query,
12
+ Security,
13
+ UploadFile,
14
+ status,
15
+ )
16
+
17
+ __all__ = [
18
+ "Body",
19
+ "Controller",
20
+ "Cookie",
21
+ "Depends",
22
+ "File",
23
+ "Form",
24
+ "Header",
25
+ "Path",
26
+ "PathParam",
27
+ "Query",
28
+ "Security",
29
+ "UploadFile",
30
+ "status",
31
+ ]
@@ -0,0 +1,38 @@
1
+ from fastapi import (
2
+ APIRouter,
3
+ Body,
4
+ Cookie,
5
+ Depends,
6
+ File,
7
+ Form,
8
+ Header,
9
+ Path,
10
+ Query,
11
+ Security,
12
+ UploadFile,
13
+ status,
14
+ )
15
+
16
+ # Alias to avoid name collisions with standard library pathlib.Path
17
+ PathParam = Path
18
+
19
+
20
+ class Controller(APIRouter):
21
+ """Core Astris Controller router."""
22
+
23
+
24
+ __all__ = [
25
+ "Body",
26
+ "Controller",
27
+ "Cookie",
28
+ "Depends",
29
+ "File",
30
+ "Form",
31
+ "Header",
32
+ "Path",
33
+ "PathParam",
34
+ "Query",
35
+ "Security",
36
+ "UploadFile",
37
+ "status",
38
+ ]
@@ -0,0 +1,3 @@
1
+ from astris.security.csrf import CSRFMiddleware
2
+
3
+ __all__ = ["CSRFMiddleware"]
@@ -0,0 +1,100 @@
1
+ import secrets
2
+ from collections.abc import Sequence
3
+ from typing import ClassVar
4
+
5
+ from starlette.datastructures import MutableHeaders
6
+ from starlette.requests import Request
7
+ from starlette.responses import JSONResponse
8
+ from starlette.types import ASGIApp, Message, Receive, Scope, Send
9
+
10
+
11
+ class CSRFMiddleware:
12
+ """Cookie-to-header CSRF protection middleware.
13
+
14
+ Sets an XSRF-TOKEN cookie on responses and validates the incoming
15
+ X-XSRF-TOKEN (or X-CSRF-TOKEN) header against the cookie on state-changing requests.
16
+ """
17
+
18
+ SAFE_METHODS: ClassVar[frozenset[str]] = frozenset({"GET", "HEAD", "OPTIONS"})
19
+ COOKIE_NAME: ClassVar[str] = "XSRF-TOKEN"
20
+ HEADER_NAMES: ClassVar[tuple[str, ...]] = ("x-xsrf-token", "x-csrf-token")
21
+
22
+ def __init__(
23
+ self,
24
+ app: ASGIApp,
25
+ cookie_name: str = "XSRF-TOKEN",
26
+ cookie_path: str = "/",
27
+ cookie_domain: str | None = None,
28
+ cookie_secure: bool = False,
29
+ cookie_samesite: str = "lax",
30
+ exempt_paths: Sequence[str] = (),
31
+ ) -> None:
32
+ self.app = app
33
+ self.cookie_name = cookie_name
34
+ self.cookie_path = cookie_path
35
+ self.cookie_domain = cookie_domain
36
+ self.cookie_secure = cookie_secure
37
+ self.cookie_samesite = cookie_samesite
38
+ self.exempt_paths = exempt_paths
39
+
40
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
41
+ if scope["type"] != "http":
42
+ await self.app(scope, receive, send)
43
+ return
44
+
45
+ request = Request(scope, receive=receive)
46
+ cookie_token = request.cookies.get(self.cookie_name)
47
+ new_token: str | None = None
48
+
49
+ if not cookie_token:
50
+ cookie_token = secrets.token_urlsafe(32)
51
+ new_token = cookie_token
52
+
53
+ # Validate state-changing methods (POST, PUT, PATCH, DELETE)
54
+ if request.method.upper() not in self.SAFE_METHODS and not self._is_exempt(
55
+ request.url.path
56
+ ):
57
+ header_token = None
58
+ for header_name in self.HEADER_NAMES:
59
+ if header_name in request.headers:
60
+ header_token = request.headers[header_name]
61
+ break
62
+
63
+ if (
64
+ not header_token
65
+ or not cookie_token
66
+ or not secrets.compare_digest(header_token, cookie_token)
67
+ ):
68
+ response = JSONResponse(
69
+ status_code=419,
70
+ content={"message": "CSRF token mismatch or missing."},
71
+ )
72
+ await response(scope, receive, send)
73
+ return
74
+
75
+ if new_token is not None:
76
+ token_str: str = new_token
77
+
78
+ async def send_wrapper(message: Message) -> None:
79
+ if message["type"] == "http.response.start":
80
+ headers = MutableHeaders(scope=message)
81
+ cookie_parts = [
82
+ f"{self.cookie_name}={token_str}",
83
+ f"Path={self.cookie_path}",
84
+ f"SameSite={self.cookie_samesite.capitalize()}",
85
+ ]
86
+ if self.cookie_domain:
87
+ cookie_parts.append(f"Domain={self.cookie_domain}")
88
+ if self.cookie_secure:
89
+ cookie_parts.append("Secure")
90
+
91
+ headers.append("set-cookie", "; ".join(cookie_parts))
92
+
93
+ await send(message)
94
+
95
+ await self.app(scope, receive, send_wrapper)
96
+ else:
97
+ await self.app(scope, receive, send)
98
+
99
+ def _is_exempt(self, path: str) -> bool:
100
+ return any(path.startswith(exempt) for exempt in self.exempt_paths)
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.4
2
+ Name: astris-python
3
+ Version: 0.1.0
4
+ Summary: The modern full-stack web framework for Python. Everything you need to go from idea to orbit; for developers who want to build and ship at escape velocity.
5
+ Author: Felix Gomez
6
+ Author-email: Felix Gomez <felix@felixgomez.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Web Environment
11
+ Classifier: Framework :: AsyncIO
12
+ Classifier: Framework :: FastAPI
13
+ Classifier: Framework :: Pydantic
14
+ Classifier: Framework :: Pydantic :: 2
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Information Technology
17
+ Classifier: Intended Audience :: System Administrators
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Topic :: Internet
27
+ Classifier: Topic :: Internet :: WWW/HTTP
28
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
29
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
30
+ Classifier: Topic :: Software Development
31
+ Classifier: Topic :: Software Development :: Libraries
32
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
33
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
34
+ Classifier: Typing :: Typed
35
+ Requires-Dist: alembic>=1.19.1
36
+ Requires-Dist: fastapi>=0.141.1
37
+ Requires-Dist: itsdangerous>=2.2.0
38
+ Requires-Dist: pwdlib[argon2]>=0.3.1
39
+ Requires-Dist: pydantic>=2.13.4
40
+ Requires-Dist: pydantic-settings>=2.15.0
41
+ Requires-Dist: sqlmodel>=0.0.39
42
+ Requires-Dist: typer>=0.27.1
43
+ Requires-Dist: uvicorn[standard]>=0.52.4
44
+ Requires-Python: >=3.11
45
+ Description-Content-Type: text/markdown
46
+
47
+ <p align="center" style="padding: 20px 0 10px 0;">
48
+ <a href="https://github.com/TheFelixGomez/astris">
49
+ <img src="https://raw.githubusercontent.com/TheFelixGomez/astris/main/.github/assets/astris-logo-name.png" alt="Astris" width="380">
50
+ </a>
51
+ </p>
52
+
53
+ <p align="center">
54
+ <strong>The modern full-stack web framework for Python.</strong><br>
55
+ Full-stack simplicity with modern Python performance.
56
+ </p>
57
+
58
+ <p align="center">
59
+ <a href="https://pypi.org/project/astris-python/"><img src="https://img.shields.io/pypi/v/astris-python.svg" alt="PyPI version"></a>
60
+ <a href="https://pypi.org/project/astris-python/"><img src="https://img.shields.io/pypi/pyversions/astris-python.svg" alt="Python Versions"></a>
61
+ <a href="https://github.com/TheFelixGomez/astris/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
62
+ </p>
63
+
64
+ > ⚠️ **Early Alpha**: Astris is currently in active alpha development. APIs and features may undergo breaking changes until production-ready status is confirmed. Feedback, bug reports, and ideas are welcome!
65
+
66
+ ---
67
+
68
+ ## What is Astris?
69
+
70
+ **Astris** bridges the gap between the rapid, joyful developer experience of classic full-stack frameworks (like Laravel and Rails) and the raw performance, modern typing, and async concurrency of **FastAPI** and **Python 3.11+**.
71
+
72
+ By combining **FastAPI**, **Inertia.js**, **Vue 3**, **Tailwind CSS v4**, and **SQLModel**, Astris lets you build rich, dynamic single-page applications without the overhead of maintaining separate API layers or complex state synchronization.
73
+
74
+ ---
75
+
76
+ ## 🌟 Key Features
77
+
78
+ * **⚡ Lightning-Fast Core**: ASGI performance powered by FastAPI and Uvicorn with auto-generated OpenAPI & Swagger documentation.
79
+ * **🧩 Modern Monolith with Inertia.js**: Render Vue 3 components directly from FastAPI controllers with full server-side state hydration and zero REST boilerplate.
80
+ * **🛡️ Production-Ready Authentication**: Cryptographically signed cookie sessions, OWASP-standard **Argon2id** password hashing (`pwdlib`), and full-stack auth starter kit.
81
+ * **🗄️ SQLModel & Alembic Database Engine**: Unified declarative models, DTOs, and automatic schema migrations out-of-the-box.
82
+ * **⚙️ Type-Safe Centralized Configuration**: Powered by `pydantic-settings` for `.env` management, type casting, and fail-fast startup validation.
83
+ * **🪐 Orbit CLI**: An artisan developer CLI for scaffolding modules, generating migrations, and serving full-stack applications with hot-reloading.
84
+ * **🎨 Tailwind CSS v4 Pre-configured**: Instant zero-config styling powered by `@tailwindcss/vite`.
85
+
86
+ ---
87
+
88
+ ## 🚀 Quickstart
89
+
90
+ Create a new full-stack Astris application in seconds using `uvx` (or `pipx`):
91
+
92
+ ```bash
93
+ # 1. Create a new project
94
+ uvx --from astris-python astris new my_app
95
+
96
+ # Or install globally as a CLI tool:
97
+ # uv tool install astris-python
98
+ # astris new my_app
99
+
100
+ # 2. Enter directory and install frontend dependencies
101
+ cd my_app
102
+ npm install
103
+
104
+ # 3. Start full-stack development server (FastAPI + Vite HMR)
105
+ uv run orbit serve
106
+ ```
107
+
108
+ Open **`http://localhost:8000`** in your browser. Your full-stack app with authentication, Inertia.js, and SQLite is live!
109
+
110
+ ---
111
+
112
+ ## 💡 How It Feels
113
+
114
+ ### 1. Controllers & Inertia Rendering
115
+ Write clean, expressive controllers that return frontend views or JSON seamlessly:
116
+
117
+ ```python
118
+ from astris.auth import AuthUser, auth_required
119
+ from astris.inertia import InertiaResponse
120
+ from astris.routing import Controller
121
+
122
+ controller = Controller(prefix="/dashboard", dependencies=[auth_required])
123
+
124
+
125
+ @controller.get("/")
126
+ async def dashboard_page(request: Request, user: AuthUser) -> InertiaResponse:
127
+ # Props are passed directly to Vue 3 with $page.props
128
+ return InertiaResponse(request, "Dashboard", props={"username": user["name"]})
129
+ ```
130
+
131
+ ### 2. Unified SQLModel Models
132
+ Define database tables and validation schemas in a single declarative model:
133
+
134
+ ```python
135
+ from astris.database import Field, SQLModel
136
+
137
+
138
+ class ArticleBase(SQLModel):
139
+ title: str = Field(index=True)
140
+ content: str
141
+ is_published: bool = Field(default=False)
142
+
143
+
144
+ class Article(ArticleBase, table=True):
145
+ id: int | None = Field(default=None, primary_key=True)
146
+
147
+
148
+ class ArticleCreate(ArticleBase):
149
+ pass
150
+ ```
151
+
152
+ ### 3. Frontend Views (Vue 3 + Inertia)
153
+ Build dynamic reactive pages without configuring client-side routers:
154
+
155
+ ```vue
156
+ <script setup lang="ts">
157
+ import { Head, Link } from '@inertiajs/vue3'
158
+
159
+ defineProps<{
160
+ username: string
161
+ }>()
162
+ </script>
163
+
164
+ <template>
165
+ <Head title="Dashboard" />
166
+ <div class="min-h-screen bg-slate-950 text-slate-100 p-8">
167
+ <h1 class="text-3xl font-bold">Welcome back, {{ username }}!</h1>
168
+ </div>
169
+ </template>
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 🪐 The Orbit CLI
175
+
176
+ Astris comes equipped with **Orbit**, a developer toolkit for rapid development:
177
+
178
+ ```bash
179
+ # Start FastAPI backend + Vite frontend concurrently
180
+ uv run orbit serve
181
+
182
+ # Scaffold a new domain module (controller, service, model)
183
+ uv run orbit make:module billing
184
+
185
+ # Create and apply database migrations
186
+ uv run orbit make:migration "create_billing_table"
187
+ uv run orbit migrate
188
+
189
+ # Generate encryption keys
190
+ uv run orbit key:generate
191
+ ```
192
+
193
+ ---
194
+
195
+ ## 📂 Project Architecture
196
+
197
+ Astris structures projects with a modular, **Domain-Driven Design** that grows gracefully:
198
+
199
+ ```
200
+ my_app/
201
+ ├── app/
202
+ │ ├── core/
203
+ │ │ └── config.py # Centralized Pydantic settings & .env loading
204
+ │ └── modules/
205
+ │ ├── auth/ # Authentication domain (controller, service, model)
206
+ │ └── welcome/ # Welcome domain
207
+ ├── database/
208
+ │ └── migrations/ # Alembic database migration versions
209
+ ├── resources/
210
+ │ ├── css/
211
+ │ │ └── app.css # Tailwind CSS v4 styling
212
+ │ ├── js/
213
+ │ │ ├── Pages/ # Inertia Vue 3 views & components
214
+ │ │ └── app.ts # Vue entrypoint
215
+ │ └── views/
216
+ │ └── root.html # HTML shell template
217
+ ├── .env # App configuration & APP_KEY
218
+ └── pyproject.toml # Python dependencies
219
+ ```
220
+
221
+ ---
222
+
223
+ ## 🙏 Acknowledgments
224
+
225
+ Astris is built upon the work of giants. Huge gratitude to the incredible open-source projects and creators that make Astris possible:
226
+
227
+ * **[FastAPI](https://fastapi.tiangolo.com/) & [SQLModel](https://sqlmodel.tiangolo.com/)** by [Tiangolo](https://github.com/tiangolo) - for setting the standard in modern Python type safety, speed, and ergonomics.
228
+ * **[Inertia.js](https://inertiajs.com/)** by [Jonathan Reinink](https://github.com/reinink) - for the modern monolith architecture that bridges backend controllers and frontend SPAs without API boilerplate.
229
+ * **[Vue.js](https://vuejs.org/)** by [Evan You](https://github.com/yyx990803) - for the approachable and performant frontend component framework.
230
+ * **[Starlette](https://www.starlette.io/) & [Uvicorn](https://www.uvicorn.org/)** by [Encode](https://www.encode.io/) - for the lightning-fast ASGI toolkit and web server engine.
231
+ * **[Pydantic](https://docs.pydantic.dev/)** by [Samuel Colvin](https://github.com/samuelcolvin) & team - for rock-solid runtime data validation and centralized settings.
232
+ * **[Tailwind CSS](https://tailwindcss.com/)** by [Tailwind Labs](https://github.com/tailwindlabs) - for zero-config utility-first styling.
233
+ * **[Laravel](https://laravel.com/)** by [Taylor Otwell](https://github.com/taylorotwell) - for inspiring developer-first framework craftsmanship.
234
+
235
+ ---
236
+
237
+ ## 📄 License
238
+
239
+ The Astris framework is open source and licensed under the terms of the MIT license.
240
+
241
+ Built with ❤️ by [Felix Gomez](https://github.com/TheFelixGomez).
@@ -0,0 +1,29 @@
1
+ astris/__init__.py,sha256=D7FkbtDHx3Kui4UTTYjuG24jguofpRADh6tTzbGgn3c,113
2
+ astris/assets/favicon.ico,sha256=qcGguPAGFmUGh4IhE859vx8EM7Euo6OQan5XxZcrLhw,15086
3
+ astris/auth/__init__.py,sha256=8T1YUiPbyhXy25SH-4Ko6CzHis6ReD2-_LpVuRNlcbc,539
4
+ astris/auth/installer.py,sha256=ZyBQui6aA5yNBN8X9HUp6UTVuwvO5W3tDVsag6TJtLQ,21631
5
+ astris/auth/session.py,sha256=elFjFQxhM-NqvcNQv7OPue0WRY7RUiitFlP_kC42vu4,7955
6
+ astris/cli.py,sha256=3a4_dCYpx2zTe8t3wgCyi42BRFn4indFcGaNucQN2Os,13518
7
+ astris/config.py,sha256=P15SJD-1NsnjFYYtEVeeBEq67w_sOTsSabkxAk8wKGc,1260
8
+ astris/database/__init__.py,sha256=8FFa-LEhkcNYxshIBOnUOMB6rKcLlLBNHVwlH7_X3Dk,375
9
+ astris/database/migrations.py,sha256=xjbiMpRsAI8LCtzpTaWhQmmPjuVhO-szTiX7Xny2Rms,7045
10
+ astris/database/session.py,sha256=9JNeG-UA3df7TQ7AVtaA9PjBjf__mlZF_oAQxpDWf1E,3151
11
+ astris/http/__init__.py,sha256=oa2xsHdse6FGNxO0bAqP1p9iySl6i_NtVopxOnYUzlw,504
12
+ astris/http/static.py,sha256=x6ysvjXVJd2pm6VbNt96VC7fT6rNbolF8Y43VG0cMVI,1191
13
+ astris/inertia/__init__.py,sha256=220WVzFB8ppZahAmrKyMmcmsyotfiyGeOnrClInL5jA,163
14
+ astris/inertia/exceptions.py,sha256=W-4Xi3WeSBa8FP5UKhT4XYmu-eoze6GgTkaDPo018W4,4583
15
+ astris/inertia/response.py,sha256=P3jjUGfL5Bpz2e4Wy4f7kylJ7P3_QeMTY4OfSfX3qnw,3213
16
+ astris/inertia/shared.py,sha256=loVqwOPE0N4WztNNn4wNWhPlj240xdLgx65RRUepyeU,6095
17
+ astris/inertia/vite.py,sha256=cqtwqPmdxsvsAV0Ja0YrY1TPHZ14oKipS9c7UaNALIY,3211
18
+ astris/installer.py,sha256=S8tNo6SF1MVlEWwDulEsG13cP6FBRMFc4qZKkKVFAK8,22042
19
+ astris/kernel.py,sha256=qaIcnf080BiRNoauHIOB6TPso0mjc5Aqp7aYD9DqV2I,8278
20
+ astris/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ astris/routing/__init__.py,sha256=g1urCz-1r8CkPae-7UMbIYh7YS_VeIafa7cGtol9vV0,401
22
+ astris/routing/router.py,sha256=NkFxYcqZCJeBVu0ncGLdYjSKg1JpKDfhr-73XEn3Vgc,530
23
+ astris/security/__init__.py,sha256=RxdeOT3ooBox0IXFZCiM2vOnm6q0k-T9QZPt7PbPAbs,78
24
+ astris/security/csrf.py,sha256=sO_rJ3qYAChLwNO545-OpTy-McwyPu2Rhqa_0Rhy1RI,3683
25
+ astris_python-0.1.0.dist-info/licenses/LICENSE,sha256=em4jHkBMoTK2MVxCtwUUBKAKJYEn2tuXviBDN_Fbdrw,1077
26
+ astris_python-0.1.0.dist-info/WHEEL,sha256=4OL6Foqnnp3xRY5wMkjgc25_i5YJC6dKsC6LPcjqEoU,80
27
+ astris_python-0.1.0.dist-info/entry_points.txt,sha256=dPbhCstaZa3L5pAfgbqrqTH4T7zwf6oRvjxt3GZqtqE,135
28
+ astris_python-0.1.0.dist-info/METADATA,sha256=kxo4EtdyoAS0a2Y5jYhQ2cY_HT6P6jV84jUDNdZW_IU,9634
29
+ astris_python-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.12.5
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ astris = astris.installer:installer_cli
3
+ astris-python = astris.installer:installer_cli
4
+ orbit = astris.cli:orbit_cli
5
+