litestar-vite 0.1.1__py3-none-any.whl → 0.15.0rc2__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.
- litestar_vite/__init__.py +54 -4
- litestar_vite/__metadata__.py +12 -7
- litestar_vite/_codegen/__init__.py +26 -0
- litestar_vite/_codegen/inertia.py +407 -0
- litestar_vite/_codegen/openapi.py +233 -0
- litestar_vite/_codegen/routes.py +653 -0
- litestar_vite/_codegen/ts.py +235 -0
- litestar_vite/_handler/__init__.py +8 -0
- litestar_vite/_handler/app.py +524 -0
- litestar_vite/_handler/routing.py +130 -0
- litestar_vite/cli.py +1147 -10
- litestar_vite/codegen.py +39 -0
- litestar_vite/commands.py +79 -0
- litestar_vite/config.py +1594 -70
- litestar_vite/deploy.py +355 -0
- litestar_vite/doctor.py +1179 -0
- litestar_vite/exceptions.py +78 -0
- litestar_vite/executor.py +316 -0
- litestar_vite/handler.py +9 -0
- litestar_vite/html_transform.py +426 -0
- litestar_vite/inertia/__init__.py +53 -0
- litestar_vite/inertia/_utils.py +114 -0
- litestar_vite/inertia/exception_handler.py +172 -0
- litestar_vite/inertia/helpers.py +1043 -0
- litestar_vite/inertia/middleware.py +54 -0
- litestar_vite/inertia/plugin.py +133 -0
- litestar_vite/inertia/request.py +286 -0
- litestar_vite/inertia/response.py +706 -0
- litestar_vite/inertia/types.py +316 -0
- litestar_vite/loader.py +462 -121
- litestar_vite/plugin.py +2160 -21
- litestar_vite/py.typed +0 -0
- litestar_vite/scaffolding/__init__.py +20 -0
- litestar_vite/scaffolding/generator.py +270 -0
- litestar_vite/scaffolding/templates.py +437 -0
- litestar_vite/templates/__init__.py +0 -0
- litestar_vite/templates/addons/tailwindcss/tailwind.css.j2 +1 -0
- litestar_vite/templates/angular/index.html.j2 +12 -0
- litestar_vite/templates/angular/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular/package.json.j2 +35 -0
- litestar_vite/templates/angular/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular/src/main.ts.j2 +9 -0
- litestar_vite/templates/angular/src/styles.css.j2 +9 -0
- litestar_vite/templates/angular/tsconfig.app.json.j2 +34 -0
- litestar_vite/templates/angular/tsconfig.json.j2 +20 -0
- litestar_vite/templates/angular/vite.config.ts.j2 +21 -0
- litestar_vite/templates/angular-cli/.postcssrc.json.j2 +5 -0
- litestar_vite/templates/angular-cli/angular.json.j2 +36 -0
- litestar_vite/templates/angular-cli/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular-cli/package.json.j2 +27 -0
- litestar_vite/templates/angular-cli/proxy.conf.json.j2 +18 -0
- litestar_vite/templates/angular-cli/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular-cli/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular-cli/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular-cli/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular-cli/src/index.html.j2 +13 -0
- litestar_vite/templates/angular-cli/src/main.ts.j2 +6 -0
- litestar_vite/templates/angular-cli/src/styles.css.j2 +10 -0
- litestar_vite/templates/angular-cli/tailwind.config.js.j2 +4 -0
- litestar_vite/templates/angular-cli/tsconfig.app.json.j2 +16 -0
- litestar_vite/templates/angular-cli/tsconfig.json.j2 +26 -0
- litestar_vite/templates/angular-cli/tsconfig.spec.json.j2 +9 -0
- litestar_vite/templates/astro/astro.config.mjs.j2 +28 -0
- litestar_vite/templates/astro/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/astro/src/layouts/Layout.astro.j2 +63 -0
- litestar_vite/templates/astro/src/pages/index.astro.j2 +36 -0
- litestar_vite/templates/astro/src/styles/global.css.j2 +1 -0
- litestar_vite/templates/base/.gitignore.j2 +42 -0
- litestar_vite/templates/base/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/base/package.json.j2 +38 -0
- litestar_vite/templates/base/resources/vite-env.d.ts.j2 +1 -0
- litestar_vite/templates/base/tsconfig.json.j2 +37 -0
- litestar_vite/templates/htmx/src/main.js.j2 +8 -0
- litestar_vite/templates/htmx/templates/base.html.j2.j2 +56 -0
- litestar_vite/templates/htmx/templates/index.html.j2.j2 +13 -0
- litestar_vite/templates/htmx/vite.config.ts.j2 +40 -0
- litestar_vite/templates/nuxt/app.vue.j2 +29 -0
- litestar_vite/templates/nuxt/composables/useApi.ts.j2 +33 -0
- litestar_vite/templates/nuxt/nuxt.config.ts.j2 +31 -0
- litestar_vite/templates/nuxt/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/nuxt/pages/index.vue.j2 +54 -0
- litestar_vite/templates/react/index.html.j2 +13 -0
- litestar_vite/templates/react/src/App.css.j2 +56 -0
- litestar_vite/templates/react/src/App.tsx.j2 +19 -0
- litestar_vite/templates/react/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-inertia/index.html.j2 +14 -0
- litestar_vite/templates/react-inertia/package.json.j2 +46 -0
- litestar_vite/templates/react-inertia/resources/App.css.j2 +68 -0
- litestar_vite/templates/react-inertia/resources/main.tsx.j2 +17 -0
- litestar_vite/templates/react-inertia/resources/pages/Home.tsx.j2 +18 -0
- litestar_vite/templates/react-inertia/resources/ssr.tsx.j2 +19 -0
- litestar_vite/templates/react-inertia/vite.config.ts.j2 +59 -0
- litestar_vite/templates/react-router/index.html.j2 +12 -0
- litestar_vite/templates/react-router/src/App.css.j2 +17 -0
- litestar_vite/templates/react-router/src/App.tsx.j2 +7 -0
- litestar_vite/templates/react-router/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react-router/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-tanstack/index.html.j2 +12 -0
- litestar_vite/templates/react-tanstack/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/react-tanstack/src/App.css.j2 +17 -0
- litestar_vite/templates/react-tanstack/src/main.tsx.j2 +21 -0
- litestar_vite/templates/react-tanstack/src/routeTree.gen.ts.j2 +7 -0
- litestar_vite/templates/react-tanstack/src/routes/__root.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/books.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/index.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte/index.html.j2 +13 -0
- litestar_vite/templates/svelte/src/App.svelte.j2 +30 -0
- litestar_vite/templates/svelte/src/app.css.j2 +45 -0
- litestar_vite/templates/svelte/src/main.ts.j2 +8 -0
- litestar_vite/templates/svelte/src/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte-inertia/index.html.j2 +14 -0
- litestar_vite/templates/svelte-inertia/resources/app.css.j2 +21 -0
- litestar_vite/templates/svelte-inertia/resources/main.ts.j2 +11 -0
- litestar_vite/templates/svelte-inertia/resources/pages/Home.svelte.j2 +43 -0
- litestar_vite/templates/svelte-inertia/resources/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte-inertia/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte-inertia/vite.config.ts.j2 +37 -0
- litestar_vite/templates/sveltekit/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/sveltekit/src/app.css.j2 +40 -0
- litestar_vite/templates/sveltekit/src/app.html.j2 +12 -0
- litestar_vite/templates/sveltekit/src/hooks.server.ts.j2 +55 -0
- litestar_vite/templates/sveltekit/src/routes/+layout.svelte.j2 +12 -0
- litestar_vite/templates/sveltekit/src/routes/+page.svelte.j2 +34 -0
- litestar_vite/templates/sveltekit/svelte.config.js.j2 +12 -0
- litestar_vite/templates/sveltekit/tsconfig.json.j2 +14 -0
- litestar_vite/templates/sveltekit/vite.config.ts.j2 +31 -0
- litestar_vite/templates/vue/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue/index.html.j2 +13 -0
- litestar_vite/templates/vue/src/App.vue.j2 +28 -0
- litestar_vite/templates/vue/src/main.ts.j2 +5 -0
- litestar_vite/templates/vue/src/style.css.j2 +45 -0
- litestar_vite/templates/vue/vite.config.ts.j2 +39 -0
- litestar_vite/templates/vue-inertia/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue-inertia/index.html.j2 +14 -0
- litestar_vite/templates/vue-inertia/package.json.j2 +49 -0
- litestar_vite/templates/vue-inertia/resources/main.ts.j2 +18 -0
- litestar_vite/templates/vue-inertia/resources/pages/Home.vue.j2 +22 -0
- litestar_vite/templates/vue-inertia/resources/ssr.ts.j2 +21 -0
- litestar_vite/templates/vue-inertia/resources/style.css.j2 +21 -0
- litestar_vite/templates/vue-inertia/vite.config.ts.j2 +59 -0
- litestar_vite-0.15.0rc2.dist-info/METADATA +230 -0
- litestar_vite-0.15.0rc2.dist-info/RECORD +151 -0
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0rc2.dist-info}/WHEEL +1 -1
- litestar_vite/template_engine.py +0 -103
- litestar_vite-0.1.1.dist-info/METADATA +0 -68
- litestar_vite-0.1.1.dist-info/RECORD +0 -11
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
3
|
+
|
|
4
|
+
from litestar import MediaType
|
|
5
|
+
from litestar.connection import Request
|
|
6
|
+
from litestar.connection.base import AuthT, StateT, UserT
|
|
7
|
+
from litestar.exceptions import (
|
|
8
|
+
HTTPException,
|
|
9
|
+
ImproperlyConfiguredException,
|
|
10
|
+
InternalServerException,
|
|
11
|
+
NotAuthorizedException,
|
|
12
|
+
NotFoundException,
|
|
13
|
+
PermissionDeniedException,
|
|
14
|
+
)
|
|
15
|
+
from litestar.exceptions.responses import (
|
|
16
|
+
create_debug_response, # pyright: ignore[reportUnknownVariableType]
|
|
17
|
+
create_exception_response, # pyright: ignore[reportUnknownVariableType]
|
|
18
|
+
)
|
|
19
|
+
from litestar.plugins.flash import flash
|
|
20
|
+
from litestar.repository.exceptions import (
|
|
21
|
+
ConflictError, # pyright: ignore[reportUnknownVariableType,reportAttributeAccessIssue]
|
|
22
|
+
NotFoundError, # pyright: ignore[reportUnknownVariableType,reportAttributeAccessIssue]
|
|
23
|
+
RepositoryError, # pyright: ignore[reportUnknownVariableType,reportAttributeAccessIssue]
|
|
24
|
+
)
|
|
25
|
+
from litestar.response import Response
|
|
26
|
+
from litestar.status_codes import (
|
|
27
|
+
HTTP_400_BAD_REQUEST,
|
|
28
|
+
HTTP_401_UNAUTHORIZED,
|
|
29
|
+
HTTP_404_NOT_FOUND,
|
|
30
|
+
HTTP_405_METHOD_NOT_ALLOWED,
|
|
31
|
+
HTTP_409_CONFLICT,
|
|
32
|
+
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
33
|
+
HTTP_500_INTERNAL_SERVER_ERROR,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
from litestar_vite.inertia.helpers import error
|
|
37
|
+
from litestar_vite.inertia.request import InertiaRequest
|
|
38
|
+
from litestar_vite.inertia.response import InertiaBack, InertiaRedirect, InertiaResponse
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from litestar.connection import Request
|
|
42
|
+
from litestar.connection.base import AuthT, StateT, UserT
|
|
43
|
+
from litestar.response import Response
|
|
44
|
+
|
|
45
|
+
from litestar_vite.inertia.plugin import InertiaPlugin
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
FIELD_ERR_RE = re.compile(r"field `(.+)`$")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class _HTTPConflictException(HTTPException):
|
|
52
|
+
"""Request conflict with the current state of the target resource."""
|
|
53
|
+
|
|
54
|
+
status_code: int = HTTP_409_CONFLICT
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def exception_to_http_response(request: "Request[UserT, AuthT, StateT]", exc: "Exception") -> "Response[Any]":
|
|
58
|
+
"""Handler for all exceptions subclassed from HTTPException.
|
|
59
|
+
|
|
60
|
+
Inertia detection:
|
|
61
|
+
|
|
62
|
+
- For InertiaRequest instances, uses the request's derived flags (route component + headers).
|
|
63
|
+
- For plain Request instances (e.g., before routing/when middleware didn't run), falls back
|
|
64
|
+
to checking the ``X-Inertia`` header.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
request: The request object.
|
|
68
|
+
exc: The exception to handle.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
The response object.
|
|
72
|
+
"""
|
|
73
|
+
is_inertia_header = request.headers.get("x-inertia", "").lower() == "true"
|
|
74
|
+
if isinstance(request, InertiaRequest):
|
|
75
|
+
inertia_enabled = request.inertia_enabled or request.is_inertia or is_inertia_header
|
|
76
|
+
else:
|
|
77
|
+
inertia_enabled = is_inertia_header
|
|
78
|
+
|
|
79
|
+
if not inertia_enabled:
|
|
80
|
+
if isinstance(exc, HTTPException):
|
|
81
|
+
return cast("Response[Any]", create_exception_response(request, exc))
|
|
82
|
+
if isinstance(exc, NotFoundError):
|
|
83
|
+
http_exc = NotFoundException
|
|
84
|
+
elif isinstance(exc, (RepositoryError, ConflictError)):
|
|
85
|
+
http_exc = _HTTPConflictException # type: ignore[assignment]
|
|
86
|
+
else:
|
|
87
|
+
http_exc = InternalServerException # type: ignore[assignment]
|
|
88
|
+
if request.app.debug and http_exc not in {PermissionDeniedException, NotFoundError}:
|
|
89
|
+
return cast("Response[Any]", create_debug_response(request, exc))
|
|
90
|
+
return cast("Response[Any]", create_exception_response(request, http_exc(detail=str(exc.__cause__)))) # pyright: ignore[reportUnknownArgumentType]
|
|
91
|
+
return create_inertia_exception_response(request, exc)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def create_inertia_exception_response(request: "Request[UserT, AuthT, StateT]", exc: "Exception") -> "Response[Any]":
|
|
95
|
+
"""Create the inertia exception response.
|
|
96
|
+
|
|
97
|
+
This function handles exceptions for Inertia-enabled routes, returning appropriate
|
|
98
|
+
responses based on the exception type and status code.
|
|
99
|
+
|
|
100
|
+
Note:
|
|
101
|
+
This function uses defensive programming techniques to handle edge cases:
|
|
102
|
+
- Type-safe handling of exception ``extra`` attribute (may be string, list, dict, or None)
|
|
103
|
+
- Graceful handling when InertiaPlugin is not registered
|
|
104
|
+
- Broad exception handling for flash() calls (non-critical operation)
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
request: The request object.
|
|
108
|
+
exc: The exception to handle.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
The response object, either an InertiaResponse, InertiaRedirect, or InertiaBack.
|
|
112
|
+
"""
|
|
113
|
+
is_inertia_header = request.headers.get("x-inertia", "").lower() == "true"
|
|
114
|
+
is_inertia = request.is_inertia if isinstance(request, InertiaRequest) else is_inertia_header
|
|
115
|
+
|
|
116
|
+
status_code = exc.status_code if isinstance(exc, HTTPException) else HTTP_500_INTERNAL_SERVER_ERROR
|
|
117
|
+
preferred_type = MediaType.HTML if not is_inertia else MediaType.JSON
|
|
118
|
+
detail = exc.detail if isinstance(exc, HTTPException) else str(exc)
|
|
119
|
+
extras: Any = None
|
|
120
|
+
if isinstance(exc, HTTPException):
|
|
121
|
+
try:
|
|
122
|
+
extras = exc.extra # pyright: ignore[reportUnknownMemberType]
|
|
123
|
+
except AttributeError:
|
|
124
|
+
extras = None
|
|
125
|
+
content: dict[str, Any] = {"status_code": status_code, "message": detail}
|
|
126
|
+
|
|
127
|
+
inertia_plugin: "InertiaPlugin | None"
|
|
128
|
+
try:
|
|
129
|
+
inertia_plugin = request.app.plugins.get("InertiaPlugin")
|
|
130
|
+
except KeyError:
|
|
131
|
+
inertia_plugin = None
|
|
132
|
+
|
|
133
|
+
if extras:
|
|
134
|
+
content.update({"extra": extras})
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
if detail:
|
|
138
|
+
flash(request, detail, category="error")
|
|
139
|
+
except (AttributeError, KeyError, RuntimeError, ImproperlyConfiguredException):
|
|
140
|
+
request.logger.warning("Unable to set flash message", exc_info=True)
|
|
141
|
+
|
|
142
|
+
if extras and isinstance(extras, (list, tuple)) and len(extras) >= 1: # pyright: ignore[reportUnknownArgumentType]
|
|
143
|
+
first_extra = extras[0] # pyright: ignore[reportUnknownVariableType]
|
|
144
|
+
if isinstance(first_extra, dict):
|
|
145
|
+
message: dict[str, str] = cast("dict[str, str]", first_extra)
|
|
146
|
+
key_value = message.get("key")
|
|
147
|
+
default_field = f"root.{key_value}" if key_value is not None else "root"
|
|
148
|
+
error_detail = str(message.get("message", detail) or detail)
|
|
149
|
+
match = FIELD_ERR_RE.search(error_detail)
|
|
150
|
+
field = match.group(1) if match else default_field
|
|
151
|
+
error(request, field, error_detail or detail)
|
|
152
|
+
|
|
153
|
+
if status_code in {HTTP_422_UNPROCESSABLE_ENTITY, HTTP_400_BAD_REQUEST}:
|
|
154
|
+
return InertiaBack(request)
|
|
155
|
+
if isinstance(exc, PermissionDeniedException):
|
|
156
|
+
return InertiaBack(request)
|
|
157
|
+
|
|
158
|
+
if inertia_plugin is None:
|
|
159
|
+
return InertiaResponse[Any](media_type=preferred_type, content=content, status_code=status_code)
|
|
160
|
+
|
|
161
|
+
if (status_code == HTTP_401_UNAUTHORIZED or isinstance(exc, NotAuthorizedException)) and (
|
|
162
|
+
inertia_plugin.config.redirect_unauthorized_to is not None
|
|
163
|
+
and request.url.path != inertia_plugin.config.redirect_unauthorized_to
|
|
164
|
+
):
|
|
165
|
+
return InertiaRedirect(request, redirect_to=inertia_plugin.config.redirect_unauthorized_to)
|
|
166
|
+
|
|
167
|
+
if status_code in {HTTP_404_NOT_FOUND, HTTP_405_METHOD_NOT_ALLOWED} and (
|
|
168
|
+
inertia_plugin.config.redirect_404 is not None and request.url.path != inertia_plugin.config.redirect_404
|
|
169
|
+
):
|
|
170
|
+
return InertiaRedirect(request, redirect_to=inertia_plugin.config.redirect_404)
|
|
171
|
+
|
|
172
|
+
return InertiaResponse[Any](media_type=preferred_type, content=content, status_code=status_code)
|