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.
Files changed (154) hide show
  1. litestar_vite/__init__.py +54 -4
  2. litestar_vite/__metadata__.py +12 -7
  3. litestar_vite/_codegen/__init__.py +26 -0
  4. litestar_vite/_codegen/inertia.py +407 -0
  5. litestar_vite/_codegen/openapi.py +233 -0
  6. litestar_vite/_codegen/routes.py +653 -0
  7. litestar_vite/_codegen/ts.py +235 -0
  8. litestar_vite/_handler/__init__.py +8 -0
  9. litestar_vite/_handler/app.py +524 -0
  10. litestar_vite/_handler/routing.py +130 -0
  11. litestar_vite/cli.py +1147 -10
  12. litestar_vite/codegen.py +39 -0
  13. litestar_vite/commands.py +79 -0
  14. litestar_vite/config.py +1594 -70
  15. litestar_vite/deploy.py +355 -0
  16. litestar_vite/doctor.py +1179 -0
  17. litestar_vite/exceptions.py +78 -0
  18. litestar_vite/executor.py +316 -0
  19. litestar_vite/handler.py +9 -0
  20. litestar_vite/html_transform.py +426 -0
  21. litestar_vite/inertia/__init__.py +53 -0
  22. litestar_vite/inertia/_utils.py +114 -0
  23. litestar_vite/inertia/exception_handler.py +172 -0
  24. litestar_vite/inertia/helpers.py +1043 -0
  25. litestar_vite/inertia/middleware.py +54 -0
  26. litestar_vite/inertia/plugin.py +133 -0
  27. litestar_vite/inertia/request.py +286 -0
  28. litestar_vite/inertia/response.py +706 -0
  29. litestar_vite/inertia/types.py +316 -0
  30. litestar_vite/loader.py +462 -121
  31. litestar_vite/plugin.py +2160 -21
  32. litestar_vite/py.typed +0 -0
  33. litestar_vite/scaffolding/__init__.py +20 -0
  34. litestar_vite/scaffolding/generator.py +270 -0
  35. litestar_vite/scaffolding/templates.py +437 -0
  36. litestar_vite/templates/__init__.py +0 -0
  37. litestar_vite/templates/addons/tailwindcss/tailwind.css.j2 +1 -0
  38. litestar_vite/templates/angular/index.html.j2 +12 -0
  39. litestar_vite/templates/angular/openapi-ts.config.ts.j2 +18 -0
  40. litestar_vite/templates/angular/package.json.j2 +35 -0
  41. litestar_vite/templates/angular/src/app/app.component.css.j2 +3 -0
  42. litestar_vite/templates/angular/src/app/app.component.html.j2 +1 -0
  43. litestar_vite/templates/angular/src/app/app.component.ts.j2 +9 -0
  44. litestar_vite/templates/angular/src/app/app.config.ts.j2 +5 -0
  45. litestar_vite/templates/angular/src/main.ts.j2 +9 -0
  46. litestar_vite/templates/angular/src/styles.css.j2 +9 -0
  47. litestar_vite/templates/angular/tsconfig.app.json.j2 +34 -0
  48. litestar_vite/templates/angular/tsconfig.json.j2 +20 -0
  49. litestar_vite/templates/angular/vite.config.ts.j2 +21 -0
  50. litestar_vite/templates/angular-cli/.postcssrc.json.j2 +5 -0
  51. litestar_vite/templates/angular-cli/angular.json.j2 +36 -0
  52. litestar_vite/templates/angular-cli/openapi-ts.config.ts.j2 +18 -0
  53. litestar_vite/templates/angular-cli/package.json.j2 +27 -0
  54. litestar_vite/templates/angular-cli/proxy.conf.json.j2 +18 -0
  55. litestar_vite/templates/angular-cli/src/app/app.component.css.j2 +3 -0
  56. litestar_vite/templates/angular-cli/src/app/app.component.html.j2 +1 -0
  57. litestar_vite/templates/angular-cli/src/app/app.component.ts.j2 +9 -0
  58. litestar_vite/templates/angular-cli/src/app/app.config.ts.j2 +5 -0
  59. litestar_vite/templates/angular-cli/src/index.html.j2 +13 -0
  60. litestar_vite/templates/angular-cli/src/main.ts.j2 +6 -0
  61. litestar_vite/templates/angular-cli/src/styles.css.j2 +10 -0
  62. litestar_vite/templates/angular-cli/tailwind.config.js.j2 +4 -0
  63. litestar_vite/templates/angular-cli/tsconfig.app.json.j2 +16 -0
  64. litestar_vite/templates/angular-cli/tsconfig.json.j2 +26 -0
  65. litestar_vite/templates/angular-cli/tsconfig.spec.json.j2 +9 -0
  66. litestar_vite/templates/astro/astro.config.mjs.j2 +28 -0
  67. litestar_vite/templates/astro/openapi-ts.config.ts.j2 +15 -0
  68. litestar_vite/templates/astro/src/layouts/Layout.astro.j2 +63 -0
  69. litestar_vite/templates/astro/src/pages/index.astro.j2 +36 -0
  70. litestar_vite/templates/astro/src/styles/global.css.j2 +1 -0
  71. litestar_vite/templates/base/.gitignore.j2 +42 -0
  72. litestar_vite/templates/base/openapi-ts.config.ts.j2 +15 -0
  73. litestar_vite/templates/base/package.json.j2 +38 -0
  74. litestar_vite/templates/base/resources/vite-env.d.ts.j2 +1 -0
  75. litestar_vite/templates/base/tsconfig.json.j2 +37 -0
  76. litestar_vite/templates/htmx/src/main.js.j2 +8 -0
  77. litestar_vite/templates/htmx/templates/base.html.j2.j2 +56 -0
  78. litestar_vite/templates/htmx/templates/index.html.j2.j2 +13 -0
  79. litestar_vite/templates/htmx/vite.config.ts.j2 +40 -0
  80. litestar_vite/templates/nuxt/app.vue.j2 +29 -0
  81. litestar_vite/templates/nuxt/composables/useApi.ts.j2 +33 -0
  82. litestar_vite/templates/nuxt/nuxt.config.ts.j2 +31 -0
  83. litestar_vite/templates/nuxt/openapi-ts.config.ts.j2 +15 -0
  84. litestar_vite/templates/nuxt/pages/index.vue.j2 +54 -0
  85. litestar_vite/templates/react/index.html.j2 +13 -0
  86. litestar_vite/templates/react/src/App.css.j2 +56 -0
  87. litestar_vite/templates/react/src/App.tsx.j2 +19 -0
  88. litestar_vite/templates/react/src/main.tsx.j2 +10 -0
  89. litestar_vite/templates/react/vite.config.ts.j2 +39 -0
  90. litestar_vite/templates/react-inertia/index.html.j2 +14 -0
  91. litestar_vite/templates/react-inertia/package.json.j2 +46 -0
  92. litestar_vite/templates/react-inertia/resources/App.css.j2 +68 -0
  93. litestar_vite/templates/react-inertia/resources/main.tsx.j2 +17 -0
  94. litestar_vite/templates/react-inertia/resources/pages/Home.tsx.j2 +18 -0
  95. litestar_vite/templates/react-inertia/resources/ssr.tsx.j2 +19 -0
  96. litestar_vite/templates/react-inertia/vite.config.ts.j2 +59 -0
  97. litestar_vite/templates/react-router/index.html.j2 +12 -0
  98. litestar_vite/templates/react-router/src/App.css.j2 +17 -0
  99. litestar_vite/templates/react-router/src/App.tsx.j2 +7 -0
  100. litestar_vite/templates/react-router/src/main.tsx.j2 +10 -0
  101. litestar_vite/templates/react-router/vite.config.ts.j2 +39 -0
  102. litestar_vite/templates/react-tanstack/index.html.j2 +12 -0
  103. litestar_vite/templates/react-tanstack/openapi-ts.config.ts.j2 +18 -0
  104. litestar_vite/templates/react-tanstack/src/App.css.j2 +17 -0
  105. litestar_vite/templates/react-tanstack/src/main.tsx.j2 +21 -0
  106. litestar_vite/templates/react-tanstack/src/routeTree.gen.ts.j2 +7 -0
  107. litestar_vite/templates/react-tanstack/src/routes/__root.tsx.j2 +9 -0
  108. litestar_vite/templates/react-tanstack/src/routes/books.tsx.j2 +9 -0
  109. litestar_vite/templates/react-tanstack/src/routes/index.tsx.j2 +9 -0
  110. litestar_vite/templates/react-tanstack/vite.config.ts.j2 +39 -0
  111. litestar_vite/templates/svelte/index.html.j2 +13 -0
  112. litestar_vite/templates/svelte/src/App.svelte.j2 +30 -0
  113. litestar_vite/templates/svelte/src/app.css.j2 +45 -0
  114. litestar_vite/templates/svelte/src/main.ts.j2 +8 -0
  115. litestar_vite/templates/svelte/src/vite-env.d.ts.j2 +2 -0
  116. litestar_vite/templates/svelte/svelte.config.js.j2 +5 -0
  117. litestar_vite/templates/svelte/vite.config.ts.j2 +39 -0
  118. litestar_vite/templates/svelte-inertia/index.html.j2 +14 -0
  119. litestar_vite/templates/svelte-inertia/resources/app.css.j2 +21 -0
  120. litestar_vite/templates/svelte-inertia/resources/main.ts.j2 +11 -0
  121. litestar_vite/templates/svelte-inertia/resources/pages/Home.svelte.j2 +43 -0
  122. litestar_vite/templates/svelte-inertia/resources/vite-env.d.ts.j2 +2 -0
  123. litestar_vite/templates/svelte-inertia/svelte.config.js.j2 +5 -0
  124. litestar_vite/templates/svelte-inertia/vite.config.ts.j2 +37 -0
  125. litestar_vite/templates/sveltekit/openapi-ts.config.ts.j2 +15 -0
  126. litestar_vite/templates/sveltekit/src/app.css.j2 +40 -0
  127. litestar_vite/templates/sveltekit/src/app.html.j2 +12 -0
  128. litestar_vite/templates/sveltekit/src/hooks.server.ts.j2 +55 -0
  129. litestar_vite/templates/sveltekit/src/routes/+layout.svelte.j2 +12 -0
  130. litestar_vite/templates/sveltekit/src/routes/+page.svelte.j2 +34 -0
  131. litestar_vite/templates/sveltekit/svelte.config.js.j2 +12 -0
  132. litestar_vite/templates/sveltekit/tsconfig.json.j2 +14 -0
  133. litestar_vite/templates/sveltekit/vite.config.ts.j2 +31 -0
  134. litestar_vite/templates/vue/env.d.ts.j2 +7 -0
  135. litestar_vite/templates/vue/index.html.j2 +13 -0
  136. litestar_vite/templates/vue/src/App.vue.j2 +28 -0
  137. litestar_vite/templates/vue/src/main.ts.j2 +5 -0
  138. litestar_vite/templates/vue/src/style.css.j2 +45 -0
  139. litestar_vite/templates/vue/vite.config.ts.j2 +39 -0
  140. litestar_vite/templates/vue-inertia/env.d.ts.j2 +7 -0
  141. litestar_vite/templates/vue-inertia/index.html.j2 +14 -0
  142. litestar_vite/templates/vue-inertia/package.json.j2 +49 -0
  143. litestar_vite/templates/vue-inertia/resources/main.ts.j2 +18 -0
  144. litestar_vite/templates/vue-inertia/resources/pages/Home.vue.j2 +22 -0
  145. litestar_vite/templates/vue-inertia/resources/ssr.ts.j2 +21 -0
  146. litestar_vite/templates/vue-inertia/resources/style.css.j2 +21 -0
  147. litestar_vite/templates/vue-inertia/vite.config.ts.j2 +59 -0
  148. litestar_vite-0.15.0rc2.dist-info/METADATA +230 -0
  149. litestar_vite-0.15.0rc2.dist-info/RECORD +151 -0
  150. {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0rc2.dist-info}/WHEEL +1 -1
  151. litestar_vite/template_engine.py +0 -103
  152. litestar_vite-0.1.1.dist-info/METADATA +0 -68
  153. litestar_vite-0.1.1.dist-info/RECORD +0 -11
  154. {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)