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
|
@@ -14,36 +14,10 @@ from litestar._openapi.parameters import ( # pyright: ignore[reportPrivateUsage
|
|
|
14
14
|
from litestar.handlers import HTTPRouteHandler
|
|
15
15
|
from litestar.routes import HTTPRoute
|
|
16
16
|
|
|
17
|
-
from litestar_vite.
|
|
17
|
+
from litestar_vite._codegen.ts import normalize_path, ts_type_from_openapi
|
|
18
18
|
|
|
19
19
|
_PATH_PARAM_EXTRACT_PATTERN = re.compile(r"\{([^:}]+)(?::([^}]+))?\}")
|
|
20
20
|
|
|
21
|
-
# HTTP methods in priority order for Inertia router integration
|
|
22
|
-
_HTTP_METHOD_PRIORITY = ["GET", "POST", "PUT", "PATCH", "DELETE"]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def pick_primary_method(methods: list[str]) -> str:
|
|
26
|
-
"""Pick the primary HTTP method for Inertia router integration.
|
|
27
|
-
|
|
28
|
-
When a route supports multiple HTTP methods, this picks the most
|
|
29
|
-
appropriate one for use with Inertia's router.visit() and form.submit().
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
methods: List of HTTP methods (e.g., ["GET", "HEAD", "OPTIONS"]).
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
The primary method in lowercase (e.g., "get", "post").
|
|
36
|
-
"""
|
|
37
|
-
for preferred in _HTTP_METHOD_PRIORITY:
|
|
38
|
-
if preferred in methods:
|
|
39
|
-
return preferred.lower()
|
|
40
|
-
# Fallback to first non-HEAD/OPTIONS method, or "get" if none
|
|
41
|
-
for method in methods:
|
|
42
|
-
if method not in {"HEAD", "OPTIONS"}:
|
|
43
|
-
return method.lower()
|
|
44
|
-
return "get"
|
|
45
|
-
|
|
46
|
-
|
|
47
21
|
_TS_SEMANTIC_ALIASES: dict[str, tuple[str, str]] = {
|
|
48
22
|
"UUID": ("UUID v4 string", "string"),
|
|
49
23
|
"DateTime": ("RFC 3339 date-time string", "string"),
|
|
@@ -57,7 +31,7 @@ _TS_SEMANTIC_ALIASES: dict[str, tuple[str, str]] = {
|
|
|
57
31
|
}
|
|
58
32
|
|
|
59
33
|
|
|
60
|
-
def
|
|
34
|
+
def _str_dict_factory() -> dict[str, str]:
|
|
61
35
|
"""Return an empty ``dict[str, str]`` (typed for pyright).
|
|
62
36
|
|
|
63
37
|
Returns:
|
|
@@ -73,13 +47,12 @@ class RouteMetadata:
|
|
|
73
47
|
name: str
|
|
74
48
|
path: str
|
|
75
49
|
methods: list[str]
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
query_params: dict[str, str] = field(default_factory=str_dict_factory)
|
|
50
|
+
params: dict[str, str] = field(default_factory=_str_dict_factory)
|
|
51
|
+
query_params: dict[str, str] = field(default_factory=_str_dict_factory)
|
|
79
52
|
component: "str | None" = None
|
|
80
53
|
|
|
81
54
|
|
|
82
|
-
def
|
|
55
|
+
def _extract_path_params(path: str) -> dict[str, str]:
|
|
83
56
|
"""Extract path parameters and their types from a route.
|
|
84
57
|
|
|
85
58
|
Args:
|
|
@@ -91,28 +64,22 @@ def extract_path_params(path: str) -> dict[str, str]:
|
|
|
91
64
|
return {match.group(1): "string" for match in _PATH_PARAM_EXTRACT_PATTERN.finditer(path)}
|
|
92
65
|
|
|
93
66
|
|
|
94
|
-
def
|
|
67
|
+
def _iter_route_handlers(app: Litestar) -> Generator[tuple["HTTPRoute", HTTPRouteHandler], None, None]:
|
|
95
68
|
"""Iterate over HTTP route handlers in an app.
|
|
96
69
|
|
|
97
|
-
Returns handlers in deterministic order, sorted by (route_path, handler_name)
|
|
98
|
-
to ensure consistent output across multiple runs.
|
|
99
|
-
|
|
100
70
|
Args:
|
|
101
71
|
app: The Litestar application.
|
|
102
72
|
|
|
103
73
|
Yields:
|
|
104
|
-
Tuples of (HTTPRoute, HTTPRouteHandler)
|
|
74
|
+
Tuples of (HTTPRoute, HTTPRouteHandler).
|
|
105
75
|
"""
|
|
106
|
-
handlers: list[tuple[HTTPRoute, HTTPRouteHandler]] = []
|
|
107
76
|
for route in app.routes:
|
|
108
77
|
if isinstance(route, HTTPRoute):
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
handlers.sort(key=lambda x: (str(x[0].path), x[1].handler_name or x[1].name or ""))
|
|
112
|
-
yield from handlers
|
|
78
|
+
for route_handler in route.route_handlers:
|
|
79
|
+
yield route, route_handler
|
|
113
80
|
|
|
114
81
|
|
|
115
|
-
def
|
|
82
|
+
def _extract_params_from_litestar(
|
|
116
83
|
handler: HTTPRouteHandler, http_route: "HTTPRoute", openapi_context: OpenAPIContext | None
|
|
117
84
|
) -> tuple[dict[str, str], dict[str, str]]:
|
|
118
85
|
"""Extract path and query parameters using Litestar's native OpenAPI generation.
|
|
@@ -159,7 +126,7 @@ def extract_params_from_litestar(
|
|
|
159
126
|
return path_params, query_params
|
|
160
127
|
|
|
161
128
|
|
|
162
|
-
def
|
|
129
|
+
def _make_unique_name(base_name: str, used_names: set[str], path: str, methods: list[str]) -> str:
|
|
163
130
|
"""Generate a unique route name, avoiding collisions.
|
|
164
131
|
|
|
165
132
|
Returns:
|
|
@@ -210,7 +177,7 @@ def extract_route_metadata(
|
|
|
210
177
|
with contextlib.suppress(AttributeError, TypeError, ValueError):
|
|
211
178
|
openapi_context = OpenAPIContext(openapi_config=app.openapi_config, plugins=app.plugins.openapi)
|
|
212
179
|
|
|
213
|
-
for http_route, route_handler in
|
|
180
|
+
for http_route, route_handler in _iter_route_handlers(app):
|
|
214
181
|
base_name = route_handler.name or route_handler.handler_name or str(route_handler)
|
|
215
182
|
methods = [method.upper() for method in route_handler.http_methods]
|
|
216
183
|
|
|
@@ -231,7 +198,7 @@ def extract_route_metadata(
|
|
|
231
198
|
else:
|
|
232
199
|
continue
|
|
233
200
|
|
|
234
|
-
route_name =
|
|
201
|
+
route_name = _make_unique_name(base_name, used_names, full_path, methods)
|
|
235
202
|
used_names.add(route_name)
|
|
236
203
|
|
|
237
204
|
if only and not any(pattern in route_name or pattern in full_path for pattern in only):
|
|
@@ -239,10 +206,10 @@ def extract_route_metadata(
|
|
|
239
206
|
if exclude and any(pattern in route_name or pattern in full_path for pattern in exclude):
|
|
240
207
|
continue
|
|
241
208
|
|
|
242
|
-
params, query_params =
|
|
209
|
+
params, query_params = _extract_params_from_litestar(route_handler, http_route, openapi_context)
|
|
243
210
|
|
|
244
211
|
if not params:
|
|
245
|
-
params =
|
|
212
|
+
params = _extract_path_params(full_path)
|
|
246
213
|
|
|
247
214
|
normalized_path = normalize_path(full_path)
|
|
248
215
|
|
|
@@ -254,7 +221,6 @@ def extract_route_metadata(
|
|
|
254
221
|
name=route_name,
|
|
255
222
|
path=normalized_path,
|
|
256
223
|
methods=methods,
|
|
257
|
-
method=pick_primary_method(methods),
|
|
258
224
|
params=params,
|
|
259
225
|
query_params=query_params,
|
|
260
226
|
component=cast("str | None", component),
|
|
@@ -274,31 +240,22 @@ def generate_routes_json(
|
|
|
274
240
|
) -> dict[str, Any]:
|
|
275
241
|
"""Generate Ziggy-compatible routes JSON.
|
|
276
242
|
|
|
277
|
-
The output is deterministic: routes are sorted by name to produce
|
|
278
|
-
byte-identical output for the same input data.
|
|
279
|
-
|
|
280
243
|
Returns:
|
|
281
|
-
A Ziggy-compatible routes payload as a dictionary
|
|
244
|
+
A Ziggy-compatible routes payload as a dictionary.
|
|
282
245
|
"""
|
|
283
246
|
routes_metadata = extract_route_metadata(app, only=only, exclude=exclude, openapi_schema=openapi_schema)
|
|
284
247
|
|
|
285
|
-
# Sort routes by name for deterministic output
|
|
286
|
-
sorted_routes = sorted(routes_metadata, key=lambda r: r.name)
|
|
287
|
-
|
|
288
248
|
routes_dict: dict[str, Any] = {}
|
|
289
249
|
|
|
290
|
-
for route in
|
|
291
|
-
route_data: dict[str, Any] = {"uri": route.path, "methods": route.methods
|
|
250
|
+
for route in routes_metadata:
|
|
251
|
+
route_data: dict[str, Any] = {"uri": route.path, "methods": route.methods}
|
|
292
252
|
|
|
293
253
|
if route.params:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
route_data["parameters"] = list(sorted_params.keys())
|
|
297
|
-
route_data["parameterTypes"] = sorted_params
|
|
254
|
+
route_data["parameters"] = list(route.params.keys())
|
|
255
|
+
route_data["parameterTypes"] = route.params
|
|
298
256
|
|
|
299
257
|
if route.query_params:
|
|
300
|
-
|
|
301
|
-
route_data["queryParameters"] = dict(sorted(route.query_params.items()))
|
|
258
|
+
route_data["queryParameters"] = route.query_params
|
|
302
259
|
|
|
303
260
|
if include_components and route.component:
|
|
304
261
|
route_data["component"] = route.component
|
|
@@ -330,7 +287,7 @@ _TS_TYPE_MAP: dict[str, str] = {
|
|
|
330
287
|
}
|
|
331
288
|
|
|
332
289
|
|
|
333
|
-
def
|
|
290
|
+
def _ts_type_for_param(param_type: str) -> str:
|
|
334
291
|
"""Map a parameter type string to TypeScript type.
|
|
335
292
|
|
|
336
293
|
Returns:
|
|
@@ -346,7 +303,7 @@ def ts_type_for_param(param_type: str) -> str:
|
|
|
346
303
|
return ts_type
|
|
347
304
|
|
|
348
305
|
|
|
349
|
-
def
|
|
306
|
+
def _is_type_required(param_type: str) -> bool:
|
|
350
307
|
"""Check if a parameter type indicates a required field.
|
|
351
308
|
|
|
352
309
|
Returns:
|
|
@@ -355,7 +312,7 @@ def is_type_required(param_type: str) -> bool:
|
|
|
355
312
|
return "undefined" not in param_type and not param_type.endswith("?")
|
|
356
313
|
|
|
357
314
|
|
|
358
|
-
def
|
|
315
|
+
def _escape_ts_string(s: str) -> str:
|
|
359
316
|
"""Escape a string for use in TypeScript string literals.
|
|
360
317
|
|
|
361
318
|
Returns:
|
|
@@ -370,53 +327,42 @@ def generate_routes_ts(
|
|
|
370
327
|
only: "list[str] | None" = None,
|
|
371
328
|
exclude: "list[str] | None" = None,
|
|
372
329
|
openapi_schema: dict[str, Any] | None = None,
|
|
373
|
-
global_route: bool = False,
|
|
374
330
|
) -> str:
|
|
375
331
|
"""Generate typed routes TypeScript file (Ziggy-style).
|
|
376
332
|
|
|
377
|
-
The output is deterministic: routes are sorted by name to produce
|
|
378
|
-
byte-identical output for the same input data.
|
|
379
|
-
|
|
380
333
|
Returns:
|
|
381
334
|
The generated TypeScript source.
|
|
382
335
|
"""
|
|
383
336
|
routes_metadata = extract_route_metadata(app, only=only, exclude=exclude, openapi_schema=openapi_schema)
|
|
384
337
|
|
|
385
|
-
# Sort routes by name for deterministic output
|
|
386
|
-
sorted_routes = sorted(routes_metadata, key=lambda r: r.name)
|
|
387
|
-
|
|
388
338
|
route_names: list[str] = []
|
|
389
339
|
path_params_entries: list[str] = []
|
|
390
340
|
query_params_entries: list[str] = []
|
|
391
341
|
routes_entries: list[str] = []
|
|
392
342
|
used_aliases: set[str] = set()
|
|
393
343
|
|
|
394
|
-
for route in
|
|
344
|
+
for route in routes_metadata:
|
|
395
345
|
route_name = route.name
|
|
396
346
|
route_names.append(route_name)
|
|
397
347
|
|
|
398
|
-
|
|
399
|
-
sorted_params = dict(sorted(route.params.items())) if route.params else {}
|
|
400
|
-
sorted_query_params = dict(sorted(route.query_params.items())) if route.query_params else {}
|
|
401
|
-
|
|
402
|
-
if sorted_params:
|
|
348
|
+
if route.params:
|
|
403
349
|
param_fields: list[str] = []
|
|
404
|
-
for param_name, param_type in
|
|
405
|
-
ts_type =
|
|
350
|
+
for param_name, param_type in route.params.items():
|
|
351
|
+
ts_type = _ts_type_for_param(param_type)
|
|
406
352
|
ts_type_clean = ts_type.replace(" | undefined", "")
|
|
407
|
-
used_aliases.update(
|
|
353
|
+
used_aliases.update(_collect_semantic_aliases(ts_type_clean))
|
|
408
354
|
param_fields.append(f" {param_name}: {ts_type_clean};")
|
|
409
355
|
path_params_entries.append(f" '{route_name}': {{\n" + "\n".join(param_fields) + "\n };")
|
|
410
356
|
else:
|
|
411
357
|
path_params_entries.append(f" '{route_name}': Record<string, never>;")
|
|
412
358
|
|
|
413
|
-
if
|
|
359
|
+
if route.query_params:
|
|
414
360
|
query_param_fields: list[str] = []
|
|
415
|
-
for param_name, param_type in
|
|
416
|
-
ts_type =
|
|
417
|
-
is_required =
|
|
361
|
+
for param_name, param_type in route.query_params.items():
|
|
362
|
+
ts_type = _ts_type_for_param(param_type)
|
|
363
|
+
is_required = _is_type_required(param_type)
|
|
418
364
|
ts_type_clean = ts_type.replace(" | undefined", "")
|
|
419
|
-
used_aliases.update(
|
|
365
|
+
used_aliases.update(_collect_semantic_aliases(ts_type_clean))
|
|
420
366
|
if is_required:
|
|
421
367
|
query_param_fields.append(f" {param_name}: {ts_type_clean};")
|
|
422
368
|
else:
|
|
@@ -425,37 +371,27 @@ def generate_routes_ts(
|
|
|
425
371
|
else:
|
|
426
372
|
query_params_entries.append(f" '{route_name}': Record<string, never>;")
|
|
427
373
|
|
|
428
|
-
methods_str = ", ".join(f"'{m}'" for m in
|
|
374
|
+
methods_str = ", ".join(f"'{m}'" for m in route.methods)
|
|
429
375
|
route_entry_lines = [
|
|
430
376
|
f" '{route_name}': {{",
|
|
431
|
-
f" path: '{
|
|
377
|
+
f" path: '{_escape_ts_string(route.path)}',",
|
|
432
378
|
f" methods: [{methods_str}] as const,",
|
|
433
|
-
f" method: '{route.method}',",
|
|
434
379
|
]
|
|
435
|
-
param_names_str = ", ".join(f"'{p}'" for p in
|
|
380
|
+
param_names_str = ", ".join(f"'{p}'" for p in route.params) if route.params else ""
|
|
436
381
|
route_entry_lines.append(f" pathParams: [{param_names_str}] as const,")
|
|
437
382
|
|
|
438
|
-
query_names_str = ", ".join(f"'{p}'" for p in
|
|
383
|
+
query_names_str = ", ".join(f"'{p}'" for p in route.query_params) if route.query_params else ""
|
|
439
384
|
route_entry_lines.append(f" queryParams: [{query_names_str}] as const,")
|
|
440
385
|
if route.component:
|
|
441
|
-
route_entry_lines.append(f" component: '{
|
|
386
|
+
route_entry_lines.append(f" component: '{_escape_ts_string(route.component)}',")
|
|
442
387
|
route_entry_lines.append(" },")
|
|
443
388
|
routes_entries.append("\n".join(route_entry_lines))
|
|
444
389
|
|
|
445
390
|
route_names_union = "\n | ".join(f"'{name}'" for name in route_names) if route_names else "never"
|
|
446
391
|
|
|
447
|
-
alias_block =
|
|
392
|
+
alias_block = _render_semantic_aliases(used_aliases)
|
|
448
393
|
alias_preamble = f"{alias_block}\n\n" if alias_block else ""
|
|
449
394
|
|
|
450
|
-
global_route_snippet = ""
|
|
451
|
-
if global_route:
|
|
452
|
-
global_route_snippet = (
|
|
453
|
-
"\n\n// Optionally register route() on window for global access\n"
|
|
454
|
-
"if (typeof window !== 'undefined') {\n"
|
|
455
|
-
" (window as any).route = route;\n"
|
|
456
|
-
"}\n"
|
|
457
|
-
)
|
|
458
|
-
|
|
459
395
|
return f"""// AUTO-GENERATED by litestar-vite. Do not edit.
|
|
460
396
|
/* eslint-disable */
|
|
461
397
|
|
|
@@ -521,9 +457,6 @@ type RoutesWithoutRequiredParams = Exclude<RouteName, RoutesWithRequiredParams>;
|
|
|
521
457
|
* route('books') // '/api/books'
|
|
522
458
|
* route('book_detail', {{ book_id: 123 }}) // '/api/books/123'
|
|
523
459
|
* route('search', {{ q: 'test', limit: 5 }}) // '/api/search?q=test&limit=5'
|
|
524
|
-
*
|
|
525
|
-
* // Access HTTP method from route definition when needed:
|
|
526
|
-
* routeDefinitions.login.method // 'post'
|
|
527
460
|
*/
|
|
528
461
|
export function route<T extends RoutesWithoutRequiredParams>(name: T): string;
|
|
529
462
|
export function route<T extends RoutesWithoutRequiredParams>(
|
|
@@ -702,14 +635,14 @@ export function isCurrentRoute(pattern: string): boolean {{
|
|
|
702
635
|
const regex = new RegExp(`^${{escaped.replace(/\\*/g, '.*')}}$`);
|
|
703
636
|
return regex.test(current);
|
|
704
637
|
}}
|
|
705
|
-
|
|
638
|
+
"""
|
|
706
639
|
|
|
707
640
|
|
|
708
|
-
def
|
|
641
|
+
def _collect_semantic_aliases(type_expr: str) -> set[str]:
|
|
709
642
|
return {alias for alias in _TS_SEMANTIC_ALIASES if alias in type_expr}
|
|
710
643
|
|
|
711
644
|
|
|
712
|
-
def
|
|
645
|
+
def _render_semantic_aliases(aliases: set[str]) -> str:
|
|
713
646
|
if not aliases:
|
|
714
647
|
return ""
|
|
715
648
|
|
|
@@ -56,31 +56,31 @@ def ts_type_from_openapi(schema_dict: dict[str, Any]) -> str:
|
|
|
56
56
|
|
|
57
57
|
if "anyOf" in schema_dict and isinstance(schema_dict["anyOf"], list) and schema_dict["anyOf"]:
|
|
58
58
|
schemas = cast("list[Any]", schema_dict["anyOf"])
|
|
59
|
-
union = {
|
|
60
|
-
return
|
|
59
|
+
union = {_ts_type_from_subschema(s) for s in schemas}
|
|
60
|
+
return _join_union(union)
|
|
61
61
|
|
|
62
62
|
result = "any"
|
|
63
63
|
match schema_dict:
|
|
64
64
|
case {"const": const} if const is not None:
|
|
65
|
-
result = "any" if const is False else
|
|
65
|
+
result = "any" if const is False else _ts_literal(const)
|
|
66
66
|
case {"enum": enum} if isinstance(enum, list) and enum:
|
|
67
67
|
enum_values = cast("list[Any]", enum)
|
|
68
|
-
result = " | ".join(
|
|
68
|
+
result = " | ".join(_ts_literal(v) for v in enum_values)
|
|
69
69
|
case {"oneOf": one_of} if isinstance(one_of, list) and one_of:
|
|
70
70
|
schemas = cast("list[Any]", one_of)
|
|
71
|
-
union = {
|
|
72
|
-
result =
|
|
71
|
+
union = {_ts_type_from_subschema(s) for s in schemas}
|
|
72
|
+
result = _join_union(union)
|
|
73
73
|
case {"allOf": all_of} if isinstance(all_of, list) and all_of:
|
|
74
74
|
schemas = cast("list[Any]", all_of)
|
|
75
|
-
parts = [
|
|
75
|
+
parts = [_wrap_union_for_intersection(_ts_type_from_subschema(s)) for s in schemas]
|
|
76
76
|
parts = [p for p in parts if p and p != "any"]
|
|
77
77
|
result = " & ".join(parts) if parts else "any"
|
|
78
78
|
case {"type": list()}:
|
|
79
79
|
type_entries_list: list[Any] = schema_dict["type"]
|
|
80
|
-
parts = [
|
|
81
|
-
result =
|
|
80
|
+
parts = [_ts_type_from_openapi_type_entry(t, schema_dict) for t in type_entries_list if isinstance(t, str)]
|
|
81
|
+
result = _join_union(set(parts)) if parts else "any"
|
|
82
82
|
case {"type": str() as schema_type}:
|
|
83
|
-
result =
|
|
83
|
+
result = _ts_type_from_openapi_type_entry(schema_type, schema_dict)
|
|
84
84
|
case _:
|
|
85
85
|
pass
|
|
86
86
|
|
|
@@ -146,13 +146,13 @@ def collect_ref_names(schema_dict: Any) -> set[str]:
|
|
|
146
146
|
return refs
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
def
|
|
149
|
+
def _ts_type_from_subschema(schema: Any) -> str:
|
|
150
150
|
if isinstance(schema, dict):
|
|
151
151
|
return ts_type_from_openapi(cast("dict[str, Any]", schema))
|
|
152
152
|
return "any"
|
|
153
153
|
|
|
154
154
|
|
|
155
|
-
def
|
|
155
|
+
def _ts_type_from_openapi_type_entry(type_name: str, schema_dict: dict[str, Any]) -> str:
|
|
156
156
|
primitive_types: dict[str, str] = {
|
|
157
157
|
"string": "string",
|
|
158
158
|
"integer": "number",
|
|
@@ -168,8 +168,8 @@ def ts_type_from_openapi_type_entry(type_name: str, schema_dict: dict[str, Any])
|
|
|
168
168
|
result = _OPENAPI_STRING_FORMAT_TO_TS_ALIAS.get(fmt, result)
|
|
169
169
|
if type_name == "array":
|
|
170
170
|
items = schema_dict.get("items")
|
|
171
|
-
item_type =
|
|
172
|
-
result = f"{
|
|
171
|
+
item_type = _ts_type_from_subschema(items) if isinstance(items, dict) else "unknown"
|
|
172
|
+
result = f"{_wrap_for_array(item_type)}[]"
|
|
173
173
|
elif type_name == "object":
|
|
174
174
|
properties = schema_dict.get("properties")
|
|
175
175
|
if not isinstance(properties, dict) or not properties:
|
|
@@ -182,7 +182,7 @@ def ts_type_from_openapi_type_entry(type_name: str, schema_dict: dict[str, Any])
|
|
|
182
182
|
|
|
183
183
|
lines: list[str] = ["{"]
|
|
184
184
|
for name, prop_schema in cast("dict[str, Any]", properties).items():
|
|
185
|
-
ts_type =
|
|
185
|
+
ts_type = _ts_type_from_subschema(prop_schema)
|
|
186
186
|
optional = "" if name in required else "?"
|
|
187
187
|
lines.append(f" {name}{optional}: {ts_type};")
|
|
188
188
|
lines.append("}")
|
|
@@ -191,7 +191,7 @@ def ts_type_from_openapi_type_entry(type_name: str, schema_dict: dict[str, Any])
|
|
|
191
191
|
return result
|
|
192
192
|
|
|
193
193
|
|
|
194
|
-
def
|
|
194
|
+
def _wrap_for_array(type_expr: str) -> str:
|
|
195
195
|
expr = type_expr.strip()
|
|
196
196
|
if not expr:
|
|
197
197
|
return "unknown"
|
|
@@ -203,7 +203,7 @@ def wrap_for_array(type_expr: str) -> str:
|
|
|
203
203
|
return expr
|
|
204
204
|
|
|
205
205
|
|
|
206
|
-
def
|
|
206
|
+
def _wrap_union_for_intersection(type_expr: str) -> str:
|
|
207
207
|
expr = type_expr.strip()
|
|
208
208
|
if not expr:
|
|
209
209
|
return "any"
|
|
@@ -214,7 +214,7 @@ def wrap_union_for_intersection(type_expr: str) -> str:
|
|
|
214
214
|
return expr
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
def
|
|
217
|
+
def _join_union(types: set[str]) -> str:
|
|
218
218
|
if not types:
|
|
219
219
|
return "any"
|
|
220
220
|
if len(types) == 1:
|
|
@@ -222,7 +222,7 @@ def join_union(types: set[str]) -> str:
|
|
|
222
222
|
return " | ".join(sorted(types))
|
|
223
223
|
|
|
224
224
|
|
|
225
|
-
def
|
|
225
|
+
def _ts_literal(value: Any) -> str:
|
|
226
226
|
if value is None:
|
|
227
227
|
return "null"
|
|
228
228
|
if isinstance(value, bool):
|