litestar-vite 0.15.0__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/_codegen/__init__.py +26 -0
- litestar_vite/_codegen/inertia.py +407 -0
- litestar_vite/{codegen/_openapi.py → _codegen/openapi.py} +11 -58
- litestar_vite/{codegen/_routes.py → _codegen/routes.py} +43 -110
- litestar_vite/{codegen/_ts.py → _codegen/ts.py} +19 -19
- litestar_vite/_handler/__init__.py +8 -0
- litestar_vite/{handler/_app.py → _handler/app.py} +29 -117
- litestar_vite/cli.py +254 -155
- litestar_vite/codegen.py +39 -0
- litestar_vite/commands.py +6 -0
- litestar_vite/{config/__init__.py → config.py} +726 -99
- litestar_vite/deploy.py +3 -14
- litestar_vite/doctor.py +6 -8
- litestar_vite/executor.py +1 -45
- litestar_vite/handler.py +9 -0
- litestar_vite/html_transform.py +5 -148
- litestar_vite/inertia/__init__.py +0 -24
- litestar_vite/inertia/_utils.py +0 -5
- litestar_vite/inertia/exception_handler.py +16 -22
- litestar_vite/inertia/helpers.py +18 -546
- litestar_vite/inertia/plugin.py +11 -77
- litestar_vite/inertia/request.py +0 -48
- litestar_vite/inertia/response.py +17 -113
- litestar_vite/inertia/types.py +0 -19
- litestar_vite/loader.py +7 -7
- litestar_vite/plugin.py +2184 -0
- litestar_vite/templates/angular/package.json.j2 +1 -2
- litestar_vite/templates/angular-cli/package.json.j2 +1 -2
- litestar_vite/templates/base/package.json.j2 +1 -2
- litestar_vite/templates/react-inertia/package.json.j2 +1 -2
- litestar_vite/templates/vue-inertia/package.json.j2 +1 -2
- {litestar_vite-0.15.0.dist-info → litestar_vite-0.15.0rc2.dist-info}/METADATA +5 -5
- {litestar_vite-0.15.0.dist-info → litestar_vite-0.15.0rc2.dist-info}/RECORD +36 -49
- litestar_vite/codegen/__init__.py +0 -48
- litestar_vite/codegen/_export.py +0 -229
- litestar_vite/codegen/_inertia.py +0 -619
- litestar_vite/codegen/_utils.py +0 -141
- litestar_vite/config/_constants.py +0 -97
- litestar_vite/config/_deploy.py +0 -70
- litestar_vite/config/_inertia.py +0 -241
- litestar_vite/config/_paths.py +0 -63
- litestar_vite/config/_runtime.py +0 -235
- litestar_vite/config/_spa.py +0 -93
- litestar_vite/config/_types.py +0 -94
- litestar_vite/handler/__init__.py +0 -9
- litestar_vite/inertia/precognition.py +0 -274
- litestar_vite/plugin/__init__.py +0 -687
- litestar_vite/plugin/_process.py +0 -185
- litestar_vite/plugin/_proxy.py +0 -689
- litestar_vite/plugin/_proxy_headers.py +0 -244
- litestar_vite/plugin/_static.py +0 -37
- litestar_vite/plugin/_utils.py +0 -489
- /litestar_vite/{handler/_routing.py → _handler/routing.py} +0 -0
- {litestar_vite-0.15.0.dist-info → litestar_vite-0.15.0rc2.dist-info}/WHEEL +0 -0
- {litestar_vite-0.15.0.dist-info → litestar_vite-0.15.0rc2.dist-info}/licenses/LICENSE +0 -0
litestar_vite/plugin/_utils.py
DELETED
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
"""Utilities for logging, environment setup, and route detection."""
|
|
2
|
-
|
|
3
|
-
__all__ = (
|
|
4
|
-
"configure_proxy_logging",
|
|
5
|
-
"console",
|
|
6
|
-
"create_proxy_client",
|
|
7
|
-
"get_litestar_route_prefixes",
|
|
8
|
-
"infer_port_from_argv",
|
|
9
|
-
"is_litestar_route",
|
|
10
|
-
"is_non_serving_assets_cli",
|
|
11
|
-
"is_proxy_debug",
|
|
12
|
-
"log_fail",
|
|
13
|
-
"log_info",
|
|
14
|
-
"log_success",
|
|
15
|
-
"log_warn",
|
|
16
|
-
"normalize_prefix",
|
|
17
|
-
"pick_free_port",
|
|
18
|
-
"resolve_litestar_version",
|
|
19
|
-
"set_app_environment",
|
|
20
|
-
"set_environment",
|
|
21
|
-
"static_not_found_handler",
|
|
22
|
-
"vite_not_found_handler",
|
|
23
|
-
"write_runtime_config_file",
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
import importlib.metadata
|
|
27
|
-
import logging
|
|
28
|
-
import os
|
|
29
|
-
import sys
|
|
30
|
-
from pathlib import Path
|
|
31
|
-
from typing import TYPE_CHECKING, Any, Protocol, cast, overload
|
|
32
|
-
|
|
33
|
-
from rich.console import Console
|
|
34
|
-
|
|
35
|
-
from litestar_vite.codegen import write_if_changed as _write_if_changed
|
|
36
|
-
from litestar_vite.config import InertiaConfig, TypeGenConfig
|
|
37
|
-
|
|
38
|
-
if TYPE_CHECKING:
|
|
39
|
-
import httpx
|
|
40
|
-
from litestar import Litestar, Response
|
|
41
|
-
from litestar.connection import Request
|
|
42
|
-
from litestar.exceptions import NotFoundException
|
|
43
|
-
|
|
44
|
-
from litestar_vite.config import ViteConfig
|
|
45
|
-
|
|
46
|
-
_TICK = "[bold green]✓[/]"
|
|
47
|
-
_INFO = "[cyan]•[/]"
|
|
48
|
-
_WARN = "[yellow]![/]"
|
|
49
|
-
_FAIL = "[red]x[/]"
|
|
50
|
-
|
|
51
|
-
console = Console()
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
_vite_proxy_debug: bool | None = None
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def is_proxy_debug() -> bool:
|
|
58
|
-
"""Check if VITE_PROXY_DEBUG is enabled (cached).
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
True if VITE_PROXY_DEBUG is set to a truthy value, else False.
|
|
62
|
-
"""
|
|
63
|
-
global _vite_proxy_debug # noqa: PLW0603
|
|
64
|
-
if _vite_proxy_debug is None:
|
|
65
|
-
_vite_proxy_debug = os.environ.get("VITE_PROXY_DEBUG", "").lower() in {"1", "true", "yes"}
|
|
66
|
-
return _vite_proxy_debug
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def configure_proxy_logging() -> None:
|
|
70
|
-
"""Suppress verbose proxy-related logging unless debug is enabled.
|
|
71
|
-
|
|
72
|
-
Suppresses INFO-level logs from:
|
|
73
|
-
- httpx: logs every HTTP request
|
|
74
|
-
- websockets: logs connection events
|
|
75
|
-
- uvicorn.protocols.websockets: logs "connection open/closed"
|
|
76
|
-
|
|
77
|
-
Only show these logs when VITE_PROXY_DEBUG is enabled.
|
|
78
|
-
"""
|
|
79
|
-
|
|
80
|
-
if not is_proxy_debug():
|
|
81
|
-
for logger_name in ("httpx", "websockets", "uvicorn.protocols.websockets"):
|
|
82
|
-
logging.getLogger(logger_name).setLevel(logging.WARNING)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
configure_proxy_logging()
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# Cache HTTP/2 availability check result
|
|
89
|
-
_h2_available: bool | None = None
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _check_h2_available() -> bool:
|
|
93
|
-
"""Check if the h2 package is installed for HTTP/2 support.
|
|
94
|
-
|
|
95
|
-
Returns:
|
|
96
|
-
True if h2 is installed, False otherwise.
|
|
97
|
-
"""
|
|
98
|
-
global _h2_available # noqa: PLW0603
|
|
99
|
-
if _h2_available is None:
|
|
100
|
-
try:
|
|
101
|
-
import h2 # noqa: F401 # pyright: ignore[reportMissingImports,reportUnusedImport]
|
|
102
|
-
|
|
103
|
-
_h2_available = True
|
|
104
|
-
except ImportError:
|
|
105
|
-
_h2_available = False
|
|
106
|
-
return _h2_available
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def create_proxy_client(
|
|
110
|
-
http2: bool = True,
|
|
111
|
-
timeout: float = 30.0,
|
|
112
|
-
max_keepalive: int = 20,
|
|
113
|
-
max_connections: int = 40,
|
|
114
|
-
keepalive_expiry: float = 60.0,
|
|
115
|
-
) -> "httpx.AsyncClient":
|
|
116
|
-
"""Create an httpx.AsyncClient with connection pooling for proxy use.
|
|
117
|
-
|
|
118
|
-
This factory function creates a shared HTTP client with optimized settings
|
|
119
|
-
for proxying requests to Vite dev servers or SSR frameworks. The client
|
|
120
|
-
uses connection pooling for better performance.
|
|
121
|
-
|
|
122
|
-
Args:
|
|
123
|
-
http2: Enable HTTP/2 support (requires h2 package).
|
|
124
|
-
timeout: Request timeout in seconds.
|
|
125
|
-
max_keepalive: Maximum number of keep-alive connections per host.
|
|
126
|
-
max_connections: Maximum total concurrent connections.
|
|
127
|
-
keepalive_expiry: Idle timeout before closing keep-alive connections.
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
A configured httpx.AsyncClient with connection pooling.
|
|
131
|
-
"""
|
|
132
|
-
import httpx
|
|
133
|
-
|
|
134
|
-
http2_enabled = http2 and _check_h2_available()
|
|
135
|
-
limits = httpx.Limits(
|
|
136
|
-
max_keepalive_connections=max_keepalive, max_connections=max_connections, keepalive_expiry=keepalive_expiry
|
|
137
|
-
)
|
|
138
|
-
return httpx.AsyncClient(limits=limits, timeout=httpx.Timeout(timeout), http2=http2_enabled)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def infer_port_from_argv() -> str | None:
|
|
142
|
-
"""Best-effort extraction of `--port/-p` from process argv.
|
|
143
|
-
|
|
144
|
-
Returns:
|
|
145
|
-
The port as a string if found, else None.
|
|
146
|
-
"""
|
|
147
|
-
|
|
148
|
-
argv = sys.argv[1:]
|
|
149
|
-
for i, arg in enumerate(argv):
|
|
150
|
-
if arg in {"-p", "--port"} and i + 1 < len(argv) and argv[i + 1].isdigit():
|
|
151
|
-
return argv[i + 1]
|
|
152
|
-
if arg.startswith("--port="):
|
|
153
|
-
_, _, value = arg.partition("=")
|
|
154
|
-
if value.isdigit():
|
|
155
|
-
return value
|
|
156
|
-
return None
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def is_non_serving_assets_cli() -> bool:
|
|
160
|
-
"""Return True when running CLI assets commands that don't start a server.
|
|
161
|
-
|
|
162
|
-
This suppresses dev-proxy setup/logging for commands like `assets build`
|
|
163
|
-
where only a Vite build is performed and no proxy should be initialized.
|
|
164
|
-
|
|
165
|
-
Returns:
|
|
166
|
-
True when the current process is running a non-serving `litestar assets ...` command, otherwise False.
|
|
167
|
-
"""
|
|
168
|
-
|
|
169
|
-
argv_str = " ".join(sys.argv)
|
|
170
|
-
non_serving_commands = (
|
|
171
|
-
" assets build",
|
|
172
|
-
" assets install",
|
|
173
|
-
" assets deploy",
|
|
174
|
-
" assets doctor",
|
|
175
|
-
" assets generate-types",
|
|
176
|
-
" assets export-routes",
|
|
177
|
-
" assets status",
|
|
178
|
-
" assets init",
|
|
179
|
-
)
|
|
180
|
-
return any(cmd in argv_str for cmd in non_serving_commands)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def log_success(message: str) -> None:
|
|
184
|
-
"""Print a success message with consistent styling."""
|
|
185
|
-
|
|
186
|
-
console.print(f"{_TICK} {message}")
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def log_info(message: str) -> None:
|
|
190
|
-
"""Print an informational message with consistent styling."""
|
|
191
|
-
|
|
192
|
-
console.print(f"{_INFO} {message}")
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def log_warn(message: str) -> None:
|
|
196
|
-
"""Print a warning message with consistent styling."""
|
|
197
|
-
|
|
198
|
-
console.print(f"{_WARN} {message}")
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def log_fail(message: str) -> None:
|
|
202
|
-
"""Print an error message with consistent styling."""
|
|
203
|
-
|
|
204
|
-
console.print(f"{_FAIL} {message}")
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
@overload
|
|
208
|
-
def write_runtime_config_file(config: "ViteConfig", *, asset_url_override: str | None = None) -> str: ...
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
@overload
|
|
212
|
-
def write_runtime_config_file(
|
|
213
|
-
config: "ViteConfig", *, asset_url_override: str | None = None, return_status: bool
|
|
214
|
-
) -> tuple[str, bool]: ...
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def write_runtime_config_file(
|
|
218
|
-
config: "ViteConfig", *, asset_url_override: str | None = None, return_status: bool = False
|
|
219
|
-
) -> str | tuple[str, bool]:
|
|
220
|
-
"""Write a JSON handoff file for the Vite plugin and return its path.
|
|
221
|
-
|
|
222
|
-
The runtime config file is read by the JS plugin. We serialize with Litestar's JSON encoder for
|
|
223
|
-
consistency and format output deterministically for easier debugging.
|
|
224
|
-
|
|
225
|
-
Returns:
|
|
226
|
-
The path to the written config file.
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
|
-
root = config.root_dir or Path.cwd()
|
|
230
|
-
path = Path(root) / ".litestar.json"
|
|
231
|
-
types = config.types if isinstance(config.types, TypeGenConfig) else None
|
|
232
|
-
resource_dir = config.resource_dir
|
|
233
|
-
resource_dir_value = str(resource_dir)
|
|
234
|
-
bundle_dir_value = str(config.bundle_dir)
|
|
235
|
-
ssr_out_dir_value = str(config.ssr_output_dir) if config.ssr_output_dir else None
|
|
236
|
-
|
|
237
|
-
litestar_version = os.environ.get("LITESTAR_VERSION") or resolve_litestar_version()
|
|
238
|
-
|
|
239
|
-
deploy_asset_url = None
|
|
240
|
-
deploy = config.deploy_config
|
|
241
|
-
if deploy is not None and deploy.asset_url:
|
|
242
|
-
deploy_asset_url = deploy.asset_url
|
|
243
|
-
|
|
244
|
-
payload = {
|
|
245
|
-
"assetUrl": config.asset_url,
|
|
246
|
-
"deployAssetUrl": deploy_asset_url,
|
|
247
|
-
"bundleDir": bundle_dir_value,
|
|
248
|
-
"hotFile": config.hot_file,
|
|
249
|
-
"resourceDir": resource_dir_value,
|
|
250
|
-
"staticDir": str(config.static_dir),
|
|
251
|
-
"manifest": config.manifest_name,
|
|
252
|
-
"mode": config.mode,
|
|
253
|
-
"proxyMode": config.proxy_mode,
|
|
254
|
-
"port": config.port,
|
|
255
|
-
"host": config.host,
|
|
256
|
-
"ssrOutDir": ssr_out_dir_value,
|
|
257
|
-
"types": {
|
|
258
|
-
"enabled": True,
|
|
259
|
-
"output": str(types.output),
|
|
260
|
-
"openapiPath": str(types.openapi_path),
|
|
261
|
-
"routesPath": str(types.routes_path),
|
|
262
|
-
"pagePropsPath": str(types.page_props_path),
|
|
263
|
-
"generateZod": types.generate_zod,
|
|
264
|
-
"generateSdk": types.generate_sdk,
|
|
265
|
-
"generateRoutes": types.generate_routes,
|
|
266
|
-
"generatePageProps": types.generate_page_props,
|
|
267
|
-
"globalRoute": types.global_route,
|
|
268
|
-
}
|
|
269
|
-
if types
|
|
270
|
-
else None,
|
|
271
|
-
"logging": {
|
|
272
|
-
"level": config.logging_config.level,
|
|
273
|
-
"showPathsAbsolute": config.logging_config.show_paths_absolute,
|
|
274
|
-
"suppressNpmOutput": config.logging_config.suppress_npm_output,
|
|
275
|
-
"suppressViteBanner": config.logging_config.suppress_vite_banner,
|
|
276
|
-
"timestamps": config.logging_config.timestamps,
|
|
277
|
-
},
|
|
278
|
-
"spa": {"useScriptElement": config.inertia.use_script_element}
|
|
279
|
-
if isinstance(config.inertia, InertiaConfig)
|
|
280
|
-
else None,
|
|
281
|
-
"executor": config.runtime.executor,
|
|
282
|
-
"litestarVersion": litestar_version,
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
import msgspec
|
|
286
|
-
from litestar.serialization import encode_json
|
|
287
|
-
|
|
288
|
-
content = msgspec.json.format(encode_json(payload), indent=2)
|
|
289
|
-
changed = _write_if_changed(path, content)
|
|
290
|
-
if return_status:
|
|
291
|
-
return str(path), changed
|
|
292
|
-
return str(path)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
def set_environment(config: "ViteConfig", asset_url_override: str | None = None) -> None:
|
|
296
|
-
"""Configure environment variables for Vite integration.
|
|
297
|
-
|
|
298
|
-
Sets environment variables that can be used by both the Python backend
|
|
299
|
-
and the Vite frontend during development.
|
|
300
|
-
|
|
301
|
-
Args:
|
|
302
|
-
config: The Vite configuration.
|
|
303
|
-
asset_url_override: Optional asset URL to force (e.g., CDN base during build).
|
|
304
|
-
"""
|
|
305
|
-
litestar_version = os.environ.get("LITESTAR_VERSION") or resolve_litestar_version()
|
|
306
|
-
asset_url = asset_url_override or config.asset_url
|
|
307
|
-
if asset_url:
|
|
308
|
-
os.environ.setdefault("ASSET_URL", asset_url)
|
|
309
|
-
if config.base_url:
|
|
310
|
-
os.environ.setdefault("VITE_BASE_URL", config.base_url)
|
|
311
|
-
os.environ.setdefault("VITE_ALLOW_REMOTE", str(True))
|
|
312
|
-
|
|
313
|
-
backend_host = os.environ.get("LITESTAR_HOST") or "127.0.0.1"
|
|
314
|
-
backend_port = os.environ.get("LITESTAR_PORT") or os.environ.get("PORT") or infer_port_from_argv() or "8000"
|
|
315
|
-
os.environ["LITESTAR_HOST"] = backend_host
|
|
316
|
-
os.environ["LITESTAR_PORT"] = str(backend_port)
|
|
317
|
-
os.environ.setdefault("APP_URL", f"http://{backend_host}:{backend_port}")
|
|
318
|
-
|
|
319
|
-
os.environ.setdefault("VITE_PROTOCOL", config.protocol)
|
|
320
|
-
if config.proxy_mode is not None:
|
|
321
|
-
os.environ.setdefault("VITE_PROXY_MODE", config.proxy_mode)
|
|
322
|
-
|
|
323
|
-
os.environ.setdefault("VITE_HOST", config.host)
|
|
324
|
-
os.environ.setdefault("VITE_PORT", str(config.port))
|
|
325
|
-
os.environ.setdefault("NUXT_HOST", config.host)
|
|
326
|
-
os.environ.setdefault("NUXT_PORT", str(config.port))
|
|
327
|
-
os.environ.setdefault("NITRO_HOST", config.host)
|
|
328
|
-
os.environ.setdefault("NITRO_PORT", str(config.port))
|
|
329
|
-
os.environ.setdefault("HOST", config.host)
|
|
330
|
-
os.environ.setdefault("PORT", str(config.port))
|
|
331
|
-
|
|
332
|
-
os.environ["LITESTAR_VERSION"] = litestar_version
|
|
333
|
-
os.environ.setdefault("LITESTAR_VITE_RUNTIME", config.runtime.executor or "node")
|
|
334
|
-
os.environ.setdefault("LITESTAR_VITE_INSTALL_CMD", " ".join(config.install_command))
|
|
335
|
-
|
|
336
|
-
if config.is_dev_mode:
|
|
337
|
-
os.environ.setdefault("VITE_DEV_MODE", str(config.is_dev_mode))
|
|
338
|
-
|
|
339
|
-
config_path = write_runtime_config_file(config, asset_url_override=asset_url_override)
|
|
340
|
-
os.environ["LITESTAR_VITE_CONFIG_PATH"] = config_path
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
def set_app_environment(app: "Litestar") -> None:
|
|
344
|
-
"""Set environment variables derived from the Litestar app instance.
|
|
345
|
-
|
|
346
|
-
This is called after set_environment() once the app is available,
|
|
347
|
-
to export app-specific configuration like OpenAPI paths.
|
|
348
|
-
|
|
349
|
-
Args:
|
|
350
|
-
app: The Litestar application instance.
|
|
351
|
-
"""
|
|
352
|
-
openapi_config = app.openapi_config
|
|
353
|
-
if openapi_config is not None and isinstance(openapi_config.path, str) and openapi_config.path:
|
|
354
|
-
os.environ.setdefault("LITESTAR_OPENAPI_PATH", openapi_config.path)
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
def resolve_litestar_version() -> str:
|
|
358
|
-
"""Return the installed Litestar version string.
|
|
359
|
-
|
|
360
|
-
Returns:
|
|
361
|
-
The installed Litestar version, or "unknown" when unavailable.
|
|
362
|
-
"""
|
|
363
|
-
try:
|
|
364
|
-
return importlib.metadata.version("litestar")
|
|
365
|
-
except importlib.metadata.PackageNotFoundError:
|
|
366
|
-
return "unknown"
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
def pick_free_port() -> int:
|
|
370
|
-
import socket
|
|
371
|
-
|
|
372
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
373
|
-
sock.bind(("127.0.0.1", 0))
|
|
374
|
-
return sock.getsockname()[1]
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
def normalize_prefix(prefix: str) -> str:
|
|
378
|
-
if not prefix.startswith("/"):
|
|
379
|
-
prefix = f"/{prefix}"
|
|
380
|
-
if not prefix.endswith("/"):
|
|
381
|
-
prefix = f"{prefix}/"
|
|
382
|
-
return prefix
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
class _RoutePrefixesState(Protocol):
|
|
386
|
-
litestar_vite_route_prefixes: tuple[str, ...]
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
def get_litestar_route_prefixes(app: "Litestar") -> tuple[str, ...]:
|
|
390
|
-
"""Build a cached list of Litestar route prefixes for the given app.
|
|
391
|
-
|
|
392
|
-
This function collects all registered route paths from the Litestar application
|
|
393
|
-
and caches them for efficient lookup. The cache is stored in app.state to ensure
|
|
394
|
-
it's automatically cleaned up when the app is garbage collected.
|
|
395
|
-
|
|
396
|
-
Includes:
|
|
397
|
-
- All registered Litestar route paths
|
|
398
|
-
- OpenAPI schema path (customizable via openapi_config.path)
|
|
399
|
-
- Common API prefixes as fallback (/api, /schema, /docs)
|
|
400
|
-
|
|
401
|
-
Args:
|
|
402
|
-
app: The Litestar application instance.
|
|
403
|
-
|
|
404
|
-
Returns:
|
|
405
|
-
A tuple of route prefix strings (without trailing slashes).
|
|
406
|
-
"""
|
|
407
|
-
state = cast("_RoutePrefixesState", app.state)
|
|
408
|
-
try:
|
|
409
|
-
return state.litestar_vite_route_prefixes
|
|
410
|
-
except AttributeError:
|
|
411
|
-
pass
|
|
412
|
-
|
|
413
|
-
prefixes: list[str] = []
|
|
414
|
-
for route in app.routes:
|
|
415
|
-
prefix = route.path.rstrip("/")
|
|
416
|
-
if prefix:
|
|
417
|
-
prefixes.append(prefix)
|
|
418
|
-
|
|
419
|
-
openapi_config = app.openapi_config
|
|
420
|
-
if openapi_config is not None:
|
|
421
|
-
schema_path = openapi_config.path
|
|
422
|
-
if schema_path:
|
|
423
|
-
prefixes.append(schema_path.rstrip("/"))
|
|
424
|
-
|
|
425
|
-
prefixes.extend(["/api", "/schema", "/docs"])
|
|
426
|
-
|
|
427
|
-
unique_prefixes = sorted(set(prefixes), key=len, reverse=True)
|
|
428
|
-
result = tuple(unique_prefixes)
|
|
429
|
-
|
|
430
|
-
state.litestar_vite_route_prefixes = result
|
|
431
|
-
|
|
432
|
-
if is_proxy_debug():
|
|
433
|
-
console.print(f"[dim][route-detection] Cached prefixes: {result}[/]")
|
|
434
|
-
|
|
435
|
-
return result
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
def is_litestar_route(path: str, app: "Litestar") -> bool:
|
|
439
|
-
"""Check if a path matches a registered Litestar route.
|
|
440
|
-
|
|
441
|
-
This function determines if a request path should be handled by Litestar
|
|
442
|
-
rather than proxied to the Vite dev server or served as SPA content.
|
|
443
|
-
|
|
444
|
-
A path matches if it equals a registered prefix or starts with prefix + "/".
|
|
445
|
-
|
|
446
|
-
Args:
|
|
447
|
-
path: The request path to check (e.g., "/schema", "/api/users").
|
|
448
|
-
app: The Litestar application instance.
|
|
449
|
-
|
|
450
|
-
Returns:
|
|
451
|
-
True if the path matches a Litestar route, False otherwise.
|
|
452
|
-
"""
|
|
453
|
-
excluded = get_litestar_route_prefixes(app)
|
|
454
|
-
return any(path == prefix or path.startswith(f"{prefix}/") for prefix in excluded)
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
def static_not_found_handler(
|
|
458
|
-
_request: "Request[Any, Any, Any]", _exc: "NotFoundException"
|
|
459
|
-
) -> "Response[bytes]": # pragma: no cover - trivial
|
|
460
|
-
"""Return an empty 404 response for static files routing misses.
|
|
461
|
-
|
|
462
|
-
Returns:
|
|
463
|
-
An empty 404 response.
|
|
464
|
-
"""
|
|
465
|
-
from litestar import Response
|
|
466
|
-
|
|
467
|
-
return Response(status_code=404, content=b"")
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
def vite_not_found_handler(request: "Request[Any, Any, Any]", exc: "NotFoundException") -> "Response[Any]":
|
|
471
|
-
"""Return a consistent 404 response for missing static assets / routes.
|
|
472
|
-
|
|
473
|
-
Inertia requests are delegated to the Inertia exception handler to support
|
|
474
|
-
redirect_404 configuration.
|
|
475
|
-
|
|
476
|
-
Args:
|
|
477
|
-
request: Incoming request.
|
|
478
|
-
exc: NotFound exception raised by routing.
|
|
479
|
-
|
|
480
|
-
Returns:
|
|
481
|
-
Response instance for the 404.
|
|
482
|
-
"""
|
|
483
|
-
from litestar import Response
|
|
484
|
-
|
|
485
|
-
if request.headers.get("x-inertia", "").lower() == "true":
|
|
486
|
-
from litestar_vite.inertia.exception_handler import exception_to_http_response
|
|
487
|
-
|
|
488
|
-
return exception_to_http_response(request, exc)
|
|
489
|
-
return Response(status_code=404, content=b"")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|