jararaca 0.3.12a14__py3-none-any.whl → 0.3.12a16__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.
Potentially problematic release.
This version of jararaca might be problematic. Click here for more details.
- jararaca/__init__.py +7 -1
- jararaca/persistence/interceptors/aiosqa_interceptor.py +70 -27
- jararaca/tools/typescript/decorators.py +33 -0
- jararaca/tools/typescript/interface_parser.py +383 -63
- {jararaca-0.3.12a14.dist-info → jararaca-0.3.12a16.dist-info}/METADATA +1 -1
- {jararaca-0.3.12a14.dist-info → jararaca-0.3.12a16.dist-info}/RECORD +10 -10
- pyproject.toml +1 -1
- {jararaca-0.3.12a14.dist-info → jararaca-0.3.12a16.dist-info}/LICENSE +0 -0
- {jararaca-0.3.12a14.dist-info → jararaca-0.3.12a16.dist-info}/WHEEL +0 -0
- {jararaca-0.3.12a14.dist-info → jararaca-0.3.12a16.dist-info}/entry_points.txt +0 -0
jararaca/__init__.py
CHANGED
|
@@ -165,7 +165,11 @@ if TYPE_CHECKING:
|
|
|
165
165
|
from .presentation.websocket.websocket_interceptor import WebSocketInterceptor
|
|
166
166
|
from .scheduler.decorators import ScheduledAction
|
|
167
167
|
from .tools.app_config.interceptor import AppConfigurationInterceptor
|
|
168
|
-
from .tools.typescript.decorators import
|
|
168
|
+
from .tools.typescript.decorators import (
|
|
169
|
+
MutationEndpoint,
|
|
170
|
+
QueryEndpoint,
|
|
171
|
+
SplitInputOutput,
|
|
172
|
+
)
|
|
169
173
|
|
|
170
174
|
__all__ = [
|
|
171
175
|
"SetMetadata",
|
|
@@ -272,6 +276,7 @@ if TYPE_CHECKING:
|
|
|
272
276
|
"AppConfigurationInterceptor",
|
|
273
277
|
"QueryEndpoint",
|
|
274
278
|
"MutationEndpoint",
|
|
279
|
+
"SplitInputOutput",
|
|
275
280
|
"UseMiddleware",
|
|
276
281
|
"UseDependency",
|
|
277
282
|
"GlobalHttpErrorHandler",
|
|
@@ -501,6 +506,7 @@ _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
|
|
|
501
506
|
),
|
|
502
507
|
"QueryEndpoint": (__SPEC_PARENT__, "tools.typescript.decorators", None),
|
|
503
508
|
"MutationEndpoint": (__SPEC_PARENT__, "tools.typescript.decorators", None),
|
|
509
|
+
"SplitInputOutput": (__SPEC_PARENT__, "tools.typescript.decorators", None),
|
|
504
510
|
"UseMiddleware": (__SPEC_PARENT__, "presentation.decorators", None),
|
|
505
511
|
"UseDependency": (__SPEC_PARENT__, "presentation.decorators", None),
|
|
506
512
|
"GlobalHttpErrorHandler": (__SPEC_PARENT__, "rpc.http.decorators", None),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from contextlib import asynccontextmanager, contextmanager, suppress
|
|
2
2
|
from contextvars import ContextVar
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any, AsyncGenerator, Generator
|
|
4
|
+
from typing import Any, AsyncGenerator, Generator, Protocol
|
|
5
5
|
|
|
6
6
|
from sqlalchemy.ext.asyncio import (
|
|
7
7
|
AsyncSession,
|
|
@@ -18,6 +18,42 @@ from jararaca.persistence.interceptors.decorators import (
|
|
|
18
18
|
)
|
|
19
19
|
from jararaca.reflect.metadata import get_metadata_value
|
|
20
20
|
|
|
21
|
+
|
|
22
|
+
class SessionManager(Protocol):
|
|
23
|
+
def spawn_session(self, connection_name: str | None = None) -> AsyncSession: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
ctx_session_manager: ContextVar[SessionManager | None] = ContextVar(
|
|
27
|
+
"ctx_session_manager", default=None
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@contextmanager
|
|
32
|
+
def providing_session_manager(
|
|
33
|
+
session_manager: SessionManager,
|
|
34
|
+
) -> Generator[None, Any, None]:
|
|
35
|
+
"""
|
|
36
|
+
Context manager to provide a session manager for the current context.
|
|
37
|
+
"""
|
|
38
|
+
token = ctx_session_manager.set(session_manager)
|
|
39
|
+
try:
|
|
40
|
+
yield
|
|
41
|
+
finally:
|
|
42
|
+
with suppress(ValueError):
|
|
43
|
+
ctx_session_manager.reset(token)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def use_session_manager() -> SessionManager:
|
|
47
|
+
"""
|
|
48
|
+
Retrieve the current session manager from the context variable.
|
|
49
|
+
Raises ValueError if no session manager is set.
|
|
50
|
+
"""
|
|
51
|
+
session_manager = ctx_session_manager.get()
|
|
52
|
+
if session_manager is None:
|
|
53
|
+
raise ValueError("No session manager set in the context.")
|
|
54
|
+
return session_manager
|
|
55
|
+
|
|
56
|
+
|
|
21
57
|
ctx_default_connection_name: ContextVar[str] = ContextVar(
|
|
22
58
|
"ctx_default_connection_name", default=DEFAULT_CONNECTION_NAME
|
|
23
59
|
)
|
|
@@ -71,7 +107,8 @@ async def providing_new_session(
|
|
|
71
107
|
connection_name: str | None = None,
|
|
72
108
|
) -> AsyncGenerator[AsyncSession, None]:
|
|
73
109
|
|
|
74
|
-
|
|
110
|
+
session_manager = use_session_manager()
|
|
111
|
+
current_session = session_manager.spawn_session(connection_name)
|
|
75
112
|
|
|
76
113
|
async with AsyncSession(
|
|
77
114
|
current_session.bind,
|
|
@@ -131,7 +168,7 @@ class AIOSQAConfig:
|
|
|
131
168
|
self.inject_default = inject_default
|
|
132
169
|
|
|
133
170
|
|
|
134
|
-
class AIOSqlAlchemySessionInterceptor(AppInterceptor):
|
|
171
|
+
class AIOSqlAlchemySessionInterceptor(AppInterceptor, SessionManager):
|
|
135
172
|
|
|
136
173
|
def __init__(self, config: AIOSQAConfig):
|
|
137
174
|
self.config = config
|
|
@@ -148,27 +185,33 @@ class AIOSqlAlchemySessionInterceptor(AppInterceptor):
|
|
|
148
185
|
self, app_context: AppTransactionContext
|
|
149
186
|
) -> AsyncGenerator[None, None]:
|
|
150
187
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
188
|
+
with providing_session_manager(self):
|
|
189
|
+
uses_connection_metadata = get_metadata_value(
|
|
190
|
+
INJECT_PERSISTENCE_SESSION_METADATA_TEMPLATE.format(
|
|
191
|
+
connection_name=self.config.connection_name
|
|
192
|
+
),
|
|
193
|
+
self.config.inject_default,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if not uses_connection_metadata:
|
|
197
|
+
yield
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
async with self.sessionmaker() as session, session.begin() as tx:
|
|
201
|
+
token = ctx_default_connection_name.set(self.config.connection_name)
|
|
202
|
+
with providing_session(session, tx, self.config.connection_name):
|
|
203
|
+
try:
|
|
204
|
+
yield
|
|
205
|
+
if tx.is_active:
|
|
206
|
+
await tx.commit()
|
|
207
|
+
except Exception as e:
|
|
208
|
+
await tx.rollback()
|
|
209
|
+
raise e
|
|
210
|
+
finally:
|
|
211
|
+
with suppress(ValueError):
|
|
212
|
+
ctx_default_connection_name.reset(token)
|
|
213
|
+
|
|
214
|
+
def spawn_session(self, connection_name: str | None = None) -> AsyncSession:
|
|
215
|
+
connection_name = ensure_name(connection_name)
|
|
216
|
+
session = self.sessionmaker()
|
|
217
|
+
return session
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import Any, Callable, TypeVar, cast
|
|
2
2
|
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
3
5
|
DECORATED_FUNC = TypeVar("DECORATED_FUNC", bound=Callable[..., Any])
|
|
4
6
|
|
|
5
7
|
|
|
@@ -60,3 +62,34 @@ class MutationEndpoint:
|
|
|
60
62
|
Check if the function is marked as a mutation endpoint.
|
|
61
63
|
"""
|
|
62
64
|
return getattr(func, MutationEndpoint.METADATA_KEY, False)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
BASEMODEL_T = TypeVar("BASEMODEL_T", bound=BaseModel)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SplitInputOutput:
|
|
71
|
+
"""
|
|
72
|
+
Decorator to mark a Pydantic model to generate separate Input and Output TypeScript interfaces.
|
|
73
|
+
|
|
74
|
+
Input interface: Used for API inputs (mutations/queries), handles optional fields with defaults
|
|
75
|
+
Output interface: Used for API outputs, represents the complete object structure
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
METADATA_KEY = "__jararaca_split_input_output__"
|
|
79
|
+
|
|
80
|
+
def __init__(self) -> None:
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
def __call__(self, cls: type[BASEMODEL_T]) -> type[BASEMODEL_T]:
|
|
84
|
+
"""
|
|
85
|
+
Decorate the Pydantic model class to mark it for split interface generation.
|
|
86
|
+
"""
|
|
87
|
+
setattr(cls, self.METADATA_KEY, True)
|
|
88
|
+
return cls
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def is_split_model(cls: type) -> bool:
|
|
92
|
+
"""
|
|
93
|
+
Check if the Pydantic model is marked for split interface generation.
|
|
94
|
+
"""
|
|
95
|
+
return getattr(cls, SplitInputOutput.METADATA_KEY, False)
|
|
@@ -27,7 +27,7 @@ from fastapi import Request, Response, UploadFile
|
|
|
27
27
|
from fastapi.params import Body, Cookie, Depends, Header, Path, Query
|
|
28
28
|
from fastapi.security.http import HTTPBase
|
|
29
29
|
from pydantic import BaseModel, PlainValidator
|
|
30
|
-
from pydantic_core import
|
|
30
|
+
from pydantic_core import PydanticUndefined
|
|
31
31
|
|
|
32
32
|
from jararaca.microservice import Microservice
|
|
33
33
|
from jararaca.presentation.decorators import HttpMapping, RestController
|
|
@@ -35,7 +35,11 @@ from jararaca.presentation.websocket.decorators import RegisterWebSocketMessage
|
|
|
35
35
|
from jararaca.presentation.websocket.websocket_interceptor import (
|
|
36
36
|
WebSocketMessageWrapper,
|
|
37
37
|
)
|
|
38
|
-
from jararaca.tools.typescript.decorators import
|
|
38
|
+
from jararaca.tools.typescript.decorators import (
|
|
39
|
+
MutationEndpoint,
|
|
40
|
+
QueryEndpoint,
|
|
41
|
+
SplitInputOutput,
|
|
42
|
+
)
|
|
39
43
|
|
|
40
44
|
CONSTANT_PATTERN = re.compile(r"^[A-Z_]+$")
|
|
41
45
|
|
|
@@ -44,6 +48,152 @@ def is_constant(name: str) -> bool:
|
|
|
44
48
|
return CONSTANT_PATTERN.match(name) is not None
|
|
45
49
|
|
|
46
50
|
|
|
51
|
+
def should_exclude_field(
|
|
52
|
+
field_name: str, field_type: Any, basemodel_type: Type[Any]
|
|
53
|
+
) -> bool:
|
|
54
|
+
"""
|
|
55
|
+
Check if a field should be excluded from TypeScript interface generation.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
field_name: The name of the field
|
|
59
|
+
field_type: The type annotation of the field
|
|
60
|
+
basemodel_type: The BaseModel class containing the field
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
True if the field should be excluded, False otherwise
|
|
64
|
+
"""
|
|
65
|
+
# Check if field is private (starts with underscore)
|
|
66
|
+
if field_name.startswith("_"):
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
# Check if field has Pydantic Field annotation and is excluded via model_fields
|
|
70
|
+
if (
|
|
71
|
+
hasattr(basemodel_type, "model_fields")
|
|
72
|
+
and field_name in basemodel_type.model_fields
|
|
73
|
+
):
|
|
74
|
+
field_info = basemodel_type.model_fields[field_name]
|
|
75
|
+
|
|
76
|
+
# Check if field is excluded via Field(exclude=True)
|
|
77
|
+
if hasattr(field_info, "exclude") and field_info.exclude:
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
# Check if field is marked as private via Field(..., alias=None) pattern
|
|
81
|
+
if (
|
|
82
|
+
hasattr(field_info, "alias")
|
|
83
|
+
and field_info.alias is None
|
|
84
|
+
and field_name.startswith("_")
|
|
85
|
+
):
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
# Check for Annotated types with Field metadata
|
|
89
|
+
if get_origin(field_type) == Annotated:
|
|
90
|
+
for metadata in field_type.__metadata__:
|
|
91
|
+
# Check if this is a Pydantic Field by looking for expected attributes
|
|
92
|
+
if hasattr(metadata, "exclude") or hasattr(metadata, "alias"):
|
|
93
|
+
# Check if Field has exclude=True
|
|
94
|
+
if hasattr(metadata, "exclude") and metadata.exclude:
|
|
95
|
+
return True
|
|
96
|
+
# Check for private fields with alias=None
|
|
97
|
+
if (
|
|
98
|
+
hasattr(metadata, "alias")
|
|
99
|
+
and metadata.alias is None
|
|
100
|
+
and field_name.startswith("_")
|
|
101
|
+
):
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
# Check for Field instances assigned as default values
|
|
105
|
+
# This handles cases like: field_name: str = Field(exclude=True)
|
|
106
|
+
if (
|
|
107
|
+
hasattr(basemodel_type, "__annotations__")
|
|
108
|
+
and field_name in basemodel_type.__annotations__
|
|
109
|
+
):
|
|
110
|
+
# Check if there's a default value that's a Field instance
|
|
111
|
+
if hasattr(basemodel_type, field_name):
|
|
112
|
+
default_value = getattr(basemodel_type, field_name, None)
|
|
113
|
+
# Check if default value has Field-like attributes (duck typing approach)
|
|
114
|
+
if default_value is not None and hasattr(default_value, "exclude"):
|
|
115
|
+
if getattr(default_value, "exclude", False):
|
|
116
|
+
return True
|
|
117
|
+
# Check for private fields with alias=None in default Field
|
|
118
|
+
if (
|
|
119
|
+
default_value is not None
|
|
120
|
+
and hasattr(default_value, "alias")
|
|
121
|
+
and getattr(default_value, "alias", None) is None
|
|
122
|
+
and field_name.startswith("_")
|
|
123
|
+
):
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def has_default_value(
|
|
130
|
+
field_name: str, field_type: Any, basemodel_type: Type[Any]
|
|
131
|
+
) -> bool:
|
|
132
|
+
"""
|
|
133
|
+
Check if a field has a default value (making it optional in TypeScript).
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
field_name: The name of the field
|
|
137
|
+
field_type: The type annotation of the field
|
|
138
|
+
basemodel_type: The BaseModel class containing the field
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
True if the field has a default value, False otherwise
|
|
142
|
+
"""
|
|
143
|
+
# Skip literal types as they don't have defaults in the traditional sense
|
|
144
|
+
if get_origin(field_type) is Literal:
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
# Check if field has default in model_fields (standard Pydantic way)
|
|
148
|
+
if (
|
|
149
|
+
hasattr(basemodel_type, "model_fields")
|
|
150
|
+
and field_name in basemodel_type.model_fields
|
|
151
|
+
):
|
|
152
|
+
field_info = basemodel_type.model_fields[field_name]
|
|
153
|
+
if field_info.default is not PydanticUndefined:
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
# Check for Field instances assigned as default values
|
|
157
|
+
# This handles cases like: field_name: str = Field(default="value")
|
|
158
|
+
if (
|
|
159
|
+
hasattr(basemodel_type, "__annotations__")
|
|
160
|
+
and field_name in basemodel_type.__annotations__
|
|
161
|
+
):
|
|
162
|
+
if hasattr(basemodel_type, field_name):
|
|
163
|
+
default_value = getattr(basemodel_type, field_name, None)
|
|
164
|
+
# Check if it's a Field instance with a default
|
|
165
|
+
if default_value is not None and hasattr(default_value, "default"):
|
|
166
|
+
# Check if the Field has a default value set
|
|
167
|
+
field_default = getattr(default_value, "default", PydanticUndefined)
|
|
168
|
+
if field_default is not PydanticUndefined:
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
# Check for non-Field default values assigned directly to class attributes
|
|
172
|
+
# This handles cases like: field_name: str = "default_value"
|
|
173
|
+
if hasattr(basemodel_type, field_name):
|
|
174
|
+
default_value = getattr(basemodel_type, field_name, None)
|
|
175
|
+
# If it's not a Field instance but has a value, it's a default
|
|
176
|
+
if (
|
|
177
|
+
default_value is not None
|
|
178
|
+
and not hasattr(default_value, "exclude") # Not a Field instance
|
|
179
|
+
and not hasattr(default_value, "alias")
|
|
180
|
+
): # Not a Field instance
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
# Check for Annotated types with Field metadata that have defaults
|
|
184
|
+
if get_origin(field_type) == Annotated:
|
|
185
|
+
for metadata in field_type.__metadata__:
|
|
186
|
+
# Check if this is a Pydantic Field with a default
|
|
187
|
+
if hasattr(metadata, "default") and hasattr(
|
|
188
|
+
metadata, "exclude"
|
|
189
|
+
): # Ensure it's a Field
|
|
190
|
+
field_default = getattr(metadata, "default", PydanticUndefined)
|
|
191
|
+
if field_default is not PydanticUndefined:
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
|
|
47
197
|
class ParseContext:
|
|
48
198
|
def __init__(self) -> None:
|
|
49
199
|
self.mapped_types: set[Any] = set()
|
|
@@ -102,7 +252,14 @@ def parse_literal_value(value: Any) -> str:
|
|
|
102
252
|
return "unknown"
|
|
103
253
|
|
|
104
254
|
|
|
105
|
-
def get_field_type_for_ts(field_type: Any) -> Any:
|
|
255
|
+
def get_field_type_for_ts(field_type: Any, context_suffix: str = "") -> Any:
|
|
256
|
+
"""
|
|
257
|
+
Convert a Python type to its TypeScript equivalent.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
field_type: The Python type to convert
|
|
261
|
+
context_suffix: Suffix for split models (e.g., "Input", "Output")
|
|
262
|
+
"""
|
|
106
263
|
if field_type is Response:
|
|
107
264
|
return "unknown"
|
|
108
265
|
if field_type is Any:
|
|
@@ -130,17 +287,20 @@ def get_field_type_for_ts(field_type: Any) -> Any:
|
|
|
130
287
|
if field_type == Decimal:
|
|
131
288
|
return "number"
|
|
132
289
|
if get_origin(field_type) == ClassVar:
|
|
133
|
-
return get_field_type_for_ts(field_type.__args__[0])
|
|
290
|
+
return get_field_type_for_ts(field_type.__args__[0], context_suffix)
|
|
134
291
|
if get_origin(field_type) == tuple:
|
|
135
|
-
return f"[{', '.join([get_field_type_for_ts(field) for field in field_type.__args__])}]"
|
|
292
|
+
return f"[{', '.join([get_field_type_for_ts(field, context_suffix) for field in field_type.__args__])}]"
|
|
136
293
|
if get_origin(field_type) == list or get_origin(field_type) == frozenset:
|
|
137
|
-
return f"Array<{get_field_type_for_ts(field_type.__args__[0])}>"
|
|
294
|
+
return f"Array<{get_field_type_for_ts(field_type.__args__[0], context_suffix)}>"
|
|
138
295
|
if get_origin(field_type) == set:
|
|
139
|
-
return f"Array<{get_field_type_for_ts(field_type.__args__[0])}> // Set"
|
|
296
|
+
return f"Array<{get_field_type_for_ts(field_type.__args__[0], context_suffix)}> // Set"
|
|
140
297
|
if get_origin(field_type) == dict:
|
|
141
|
-
return f"{{[key: {get_field_type_for_ts(field_type.__args__[0])}]: {get_field_type_for_ts(field_type.__args__[1])}}}"
|
|
298
|
+
return f"{{[key: {get_field_type_for_ts(field_type.__args__[0], context_suffix)}]: {get_field_type_for_ts(field_type.__args__[1], context_suffix)}}}"
|
|
142
299
|
if inspect.isclass(field_type):
|
|
143
300
|
if not hasattr(field_type, "__pydantic_generic_metadata__"):
|
|
301
|
+
# Check if this is a split model and use appropriate suffix
|
|
302
|
+
if SplitInputOutput.is_split_model(field_type) and context_suffix:
|
|
303
|
+
return f"{field_type.__name__}{context_suffix}"
|
|
144
304
|
return field_type.__name__
|
|
145
305
|
pydantic_metadata = getattr(field_type, "__pydantic_generic_metadata__")
|
|
146
306
|
|
|
@@ -149,12 +309,17 @@ def get_field_type_for_ts(field_type: Any) -> Any:
|
|
|
149
309
|
if pydantic_metadata.get("origin") is not None
|
|
150
310
|
else field_type.__name__
|
|
151
311
|
)
|
|
312
|
+
|
|
313
|
+
# Check if this is a split model and use appropriate suffix
|
|
314
|
+
if SplitInputOutput.is_split_model(field_type) and context_suffix:
|
|
315
|
+
name = f"{field_type.__name__}{context_suffix}"
|
|
316
|
+
|
|
152
317
|
args = pydantic_metadata.get("args")
|
|
153
318
|
|
|
154
319
|
if len(args) > 0:
|
|
155
320
|
return "%s<%s>" % (
|
|
156
321
|
name,
|
|
157
|
-
", ".join([get_field_type_for_ts(arg) for arg in args]),
|
|
322
|
+
", ".join([get_field_type_for_ts(arg, context_suffix) for arg in args]),
|
|
158
323
|
)
|
|
159
324
|
|
|
160
325
|
return name
|
|
@@ -164,7 +329,9 @@ def get_field_type_for_ts(field_type: Any) -> Any:
|
|
|
164
329
|
if get_origin(field_type) == Literal:
|
|
165
330
|
return " | ".join([parse_literal_value(x) for x in field_type.__args__])
|
|
166
331
|
if get_origin(field_type) == UnionType or get_origin(field_type) == typing.Union:
|
|
167
|
-
return " | ".join(
|
|
332
|
+
return " | ".join(
|
|
333
|
+
[get_field_type_for_ts(x, context_suffix) for x in field_type.__args__]
|
|
334
|
+
)
|
|
168
335
|
if (get_origin(field_type) == Annotated) and (len(field_type.__args__) > 0):
|
|
169
336
|
if (
|
|
170
337
|
plain_validator := next(
|
|
@@ -172,8 +339,10 @@ def get_field_type_for_ts(field_type: Any) -> Any:
|
|
|
172
339
|
None,
|
|
173
340
|
)
|
|
174
341
|
) is not None:
|
|
175
|
-
return get_field_type_for_ts(
|
|
176
|
-
|
|
342
|
+
return get_field_type_for_ts(
|
|
343
|
+
plain_validator.json_schema_input_type, context_suffix
|
|
344
|
+
)
|
|
345
|
+
return get_field_type_for_ts(field_type.__args__[0], context_suffix)
|
|
177
346
|
return "unknown"
|
|
178
347
|
|
|
179
348
|
|
|
@@ -188,6 +357,59 @@ def get_generic_args(field_type: Any) -> Any:
|
|
|
188
357
|
def parse_type_to_typescript_interface(
|
|
189
358
|
basemodel_type: Type[Any],
|
|
190
359
|
) -> tuple[set[type], str]:
|
|
360
|
+
"""
|
|
361
|
+
Parse a Pydantic model into TypeScript interface(s).
|
|
362
|
+
|
|
363
|
+
If the model is decorated with @SplitInputOutput, it generates both Input and Output interfaces.
|
|
364
|
+
Otherwise, it generates a single interface.
|
|
365
|
+
"""
|
|
366
|
+
# Check if this model should be split into Input/Output interfaces
|
|
367
|
+
if SplitInputOutput.is_split_model(basemodel_type):
|
|
368
|
+
return parse_split_input_output_interfaces(basemodel_type)
|
|
369
|
+
|
|
370
|
+
return parse_single_typescript_interface(basemodel_type)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def parse_split_input_output_interfaces(
|
|
374
|
+
basemodel_type: Type[Any],
|
|
375
|
+
) -> tuple[set[type], str]:
|
|
376
|
+
"""
|
|
377
|
+
Generate both Input and Output TypeScript interfaces for a split model.
|
|
378
|
+
"""
|
|
379
|
+
mapped_types: set[type] = set()
|
|
380
|
+
combined_output = StringIO()
|
|
381
|
+
|
|
382
|
+
# Generate Input interface (with optional fields)
|
|
383
|
+
input_mapped_types, input_interface = parse_single_typescript_interface(
|
|
384
|
+
basemodel_type, interface_suffix="Input", force_optional_defaults=True
|
|
385
|
+
)
|
|
386
|
+
mapped_types.update(input_mapped_types)
|
|
387
|
+
combined_output.write(input_interface)
|
|
388
|
+
|
|
389
|
+
# Generate Output interface (all fields required as they come from the backend)
|
|
390
|
+
output_mapped_types, output_interface = parse_single_typescript_interface(
|
|
391
|
+
basemodel_type, interface_suffix="Output", force_optional_defaults=False
|
|
392
|
+
)
|
|
393
|
+
mapped_types.update(output_mapped_types)
|
|
394
|
+
combined_output.write(output_interface)
|
|
395
|
+
|
|
396
|
+
return mapped_types, combined_output.getvalue()
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def parse_single_typescript_interface(
|
|
400
|
+
basemodel_type: Type[Any],
|
|
401
|
+
interface_suffix: str = "",
|
|
402
|
+
force_optional_defaults: bool | None = None,
|
|
403
|
+
) -> tuple[set[type], str]:
|
|
404
|
+
"""
|
|
405
|
+
Generate a single TypeScript interface for a Pydantic model.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
basemodel_type: The Pydantic model class
|
|
409
|
+
interface_suffix: Suffix to add to the interface name (e.g., "Input", "Output")
|
|
410
|
+
force_optional_defaults: If True, fields with defaults are optional. If False, all fields are required.
|
|
411
|
+
If None, uses the default behavior (fields with defaults are optional).
|
|
412
|
+
"""
|
|
191
413
|
string_builder = StringIO()
|
|
192
414
|
mapped_types: set[type] = set()
|
|
193
415
|
|
|
@@ -214,7 +436,7 @@ def parse_type_to_typescript_interface(
|
|
|
214
436
|
enum_values = sorted([(x._name_, x.value) for x in basemodel_type])
|
|
215
437
|
return (
|
|
216
438
|
set(),
|
|
217
|
-
f"export enum {basemodel_type.__name__} {{\n"
|
|
439
|
+
f"export enum {basemodel_type.__name__}{interface_suffix} {{\n"
|
|
218
440
|
+ "\n ".join([f'\t{name} = "{value}",' for name, value in enum_values])
|
|
219
441
|
+ "\n}\n",
|
|
220
442
|
)
|
|
@@ -238,46 +460,46 @@ def parse_type_to_typescript_interface(
|
|
|
238
460
|
for inherited_class in valid_inherited_classes
|
|
239
461
|
}
|
|
240
462
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
463
|
+
# Modify inheritance for split interfaces
|
|
464
|
+
extends_expression = ""
|
|
465
|
+
if len(valid_inherited_classes) > 0:
|
|
466
|
+
extends_base_names = []
|
|
467
|
+
for inherited_class in valid_inherited_classes:
|
|
468
|
+
base_name = get_field_type_for_ts(inherited_class, interface_suffix)
|
|
469
|
+
# If the inherited class is also a split model, use the appropriate suffix
|
|
470
|
+
if SplitInputOutput.is_split_model(inherited_class) and interface_suffix:
|
|
471
|
+
base_name = f"{inherited_class.__name__}{interface_suffix}"
|
|
472
|
+
|
|
473
|
+
if inherited_classes_consts_conflict[inherited_class]:
|
|
474
|
+
base_name = "Omit<%s, %s>" % (
|
|
475
|
+
base_name,
|
|
476
|
+
" | ".join(
|
|
477
|
+
sorted(
|
|
478
|
+
[
|
|
479
|
+
'"%s"' % field_name
|
|
480
|
+
for field_name in inherited_classes_consts_conflict[
|
|
481
|
+
inherited_class
|
|
482
|
+
]
|
|
483
|
+
]
|
|
262
484
|
)
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
485
|
+
),
|
|
486
|
+
)
|
|
487
|
+
extends_base_names.append(base_name)
|
|
488
|
+
|
|
489
|
+
extends_expression = " extends %s" % ", ".join(
|
|
490
|
+
sorted(extends_base_names, key=lambda x: str(x))
|
|
268
491
|
)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
)
|
|
492
|
+
|
|
493
|
+
interface_name = f"{basemodel_type.__name__}{interface_suffix}"
|
|
272
494
|
|
|
273
495
|
if is_generic_type(basemodel_type):
|
|
274
496
|
generic_args = get_generic_args(basemodel_type)
|
|
275
497
|
string_builder.write(
|
|
276
|
-
f"export interface {
|
|
498
|
+
f"export interface {interface_name}<{', '.join(sorted([arg.__name__ for arg in generic_args]))}>{extends_expression} {{\n"
|
|
277
499
|
)
|
|
278
500
|
else:
|
|
279
501
|
string_builder.write(
|
|
280
|
-
f"export interface {
|
|
502
|
+
f"export interface {interface_name}{extends_expression} {{\n"
|
|
281
503
|
)
|
|
282
504
|
|
|
283
505
|
if hasattr(basemodel_type, "__annotations__"):
|
|
@@ -290,16 +512,23 @@ def parse_type_to_typescript_interface(
|
|
|
290
512
|
if field_name in cls_consts:
|
|
291
513
|
continue
|
|
292
514
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
515
|
+
# Check if field should be excluded (private or excluded via Field)
|
|
516
|
+
if should_exclude_field(field_name, field, basemodel_type):
|
|
517
|
+
continue
|
|
518
|
+
|
|
519
|
+
# Determine if field is optional based on the force_optional_defaults parameter
|
|
520
|
+
if force_optional_defaults is True:
|
|
521
|
+
# Input interface: fields with defaults are optional
|
|
522
|
+
is_optional = has_default_value(field_name, field, basemodel_type)
|
|
523
|
+
elif force_optional_defaults is False:
|
|
524
|
+
# Output interface: all fields are required (backend provides complete data)
|
|
525
|
+
is_optional = False
|
|
526
|
+
else:
|
|
527
|
+
# Default behavior: fields with defaults are optional
|
|
528
|
+
is_optional = has_default_value(field_name, field, basemodel_type)
|
|
529
|
+
|
|
301
530
|
string_builder.write(
|
|
302
|
-
f" {snake_to_camel(field_name) if not is_constant(field_name) else field_name}{'?' if
|
|
531
|
+
f" {snake_to_camel(field_name) if not is_constant(field_name) else field_name}{'?' if is_optional else ''}: {get_field_type_for_ts(field, interface_suffix)};\n"
|
|
303
532
|
)
|
|
304
533
|
mapped_types.update(extract_all_envolved_types(field))
|
|
305
534
|
mapped_types.add(field)
|
|
@@ -326,7 +555,7 @@ def parse_type_to_typescript_interface(
|
|
|
326
555
|
return_type = NoneType
|
|
327
556
|
|
|
328
557
|
string_builder.write(
|
|
329
|
-
f" {snake_to_camel(field_name)}: {get_field_type_for_ts(return_type)};\n"
|
|
558
|
+
f" {snake_to_camel(field_name)}: {get_field_type_for_ts(return_type, interface_suffix)};\n"
|
|
330
559
|
)
|
|
331
560
|
mapped_types.update(extract_all_envolved_types(return_type))
|
|
332
561
|
mapped_types.add(return_type)
|
|
@@ -515,7 +744,8 @@ def write_rest_controller_to_typescript_interface(
|
|
|
515
744
|
|
|
516
745
|
mapped_types.update(extract_all_envolved_types(return_type))
|
|
517
746
|
|
|
518
|
-
|
|
747
|
+
# For return types, use Output suffix if it's a split model
|
|
748
|
+
return_value_repr = get_field_type_for_ts(return_type, "Output")
|
|
519
749
|
|
|
520
750
|
arg_params_spec, parametes_mapped_types = extract_parameters(
|
|
521
751
|
member, rest_controller, mapping
|
|
@@ -732,32 +962,68 @@ def extract_parameters(
|
|
|
732
962
|
)
|
|
733
963
|
elif isinstance(annotated_type_hook, Body):
|
|
734
964
|
mapped_types.update(extract_all_envolved_types(parameter_type))
|
|
965
|
+
# For body parameters, use Input suffix if it's a split model
|
|
966
|
+
context_suffix = (
|
|
967
|
+
"Input"
|
|
968
|
+
if (
|
|
969
|
+
inspect.isclass(parameter_type)
|
|
970
|
+
and hasattr(parameter_type, "__dict__")
|
|
971
|
+
and SplitInputOutput.is_split_model(parameter_type)
|
|
972
|
+
)
|
|
973
|
+
else ""
|
|
974
|
+
)
|
|
735
975
|
parameters_list.append(
|
|
736
976
|
HttpParemeterSpec(
|
|
737
977
|
type_="body",
|
|
738
978
|
name=parameter_name,
|
|
739
979
|
required=True,
|
|
740
|
-
argument_type_str=get_field_type_for_ts(
|
|
980
|
+
argument_type_str=get_field_type_for_ts(
|
|
981
|
+
parameter_type, context_suffix
|
|
982
|
+
),
|
|
741
983
|
)
|
|
742
984
|
)
|
|
743
985
|
elif isinstance(annotated_type_hook, Query):
|
|
744
986
|
mapped_types.add(parameter_type)
|
|
987
|
+
# For query parameters, use Input suffix if it's a split model
|
|
988
|
+
context_suffix = (
|
|
989
|
+
"Input"
|
|
990
|
+
if (
|
|
991
|
+
inspect.isclass(parameter_type)
|
|
992
|
+
and hasattr(parameter_type, "__dict__")
|
|
993
|
+
and SplitInputOutput.is_split_model(parameter_type)
|
|
994
|
+
)
|
|
995
|
+
else ""
|
|
996
|
+
)
|
|
745
997
|
parameters_list.append(
|
|
746
998
|
HttpParemeterSpec(
|
|
747
999
|
type_="query",
|
|
748
1000
|
name=parameter_name,
|
|
749
1001
|
required=True,
|
|
750
|
-
argument_type_str=get_field_type_for_ts(
|
|
1002
|
+
argument_type_str=get_field_type_for_ts(
|
|
1003
|
+
parameter_type, context_suffix
|
|
1004
|
+
),
|
|
751
1005
|
)
|
|
752
1006
|
)
|
|
753
1007
|
elif isinstance(annotated_type_hook, Path):
|
|
754
1008
|
mapped_types.add(parameter_type)
|
|
1009
|
+
# For path parameters, use Input suffix if it's a split model
|
|
1010
|
+
context_suffix = (
|
|
1011
|
+
"Input"
|
|
1012
|
+
if (
|
|
1013
|
+
inspect.isclass(parameter_type)
|
|
1014
|
+
and hasattr(parameter_type, "__dict__")
|
|
1015
|
+
and SplitInputOutput.is_split_model(parameter_type)
|
|
1016
|
+
)
|
|
1017
|
+
else ""
|
|
1018
|
+
)
|
|
755
1019
|
parameters_list.append(
|
|
756
1020
|
HttpParemeterSpec(
|
|
757
1021
|
type_="path",
|
|
758
1022
|
name=parameter_name,
|
|
759
1023
|
required=True,
|
|
760
|
-
argument_type_str=get_field_type_for_ts(
|
|
1024
|
+
argument_type_str=get_field_type_for_ts(
|
|
1025
|
+
parameter_type, context_suffix
|
|
1026
|
+
),
|
|
761
1027
|
)
|
|
762
1028
|
)
|
|
763
1029
|
|
|
@@ -779,22 +1045,46 @@ def extract_parameters(
|
|
|
779
1045
|
re.search(f":{parameter_name}(?:/|$)", controller.path) is not None
|
|
780
1046
|
):
|
|
781
1047
|
mapped_types.add(annotated_type)
|
|
1048
|
+
# For path parameters, use Input suffix if it's a split model
|
|
1049
|
+
context_suffix = (
|
|
1050
|
+
"Input"
|
|
1051
|
+
if (
|
|
1052
|
+
inspect.isclass(annotated_type)
|
|
1053
|
+
and hasattr(annotated_type, "__dict__")
|
|
1054
|
+
and SplitInputOutput.is_split_model(annotated_type)
|
|
1055
|
+
)
|
|
1056
|
+
else ""
|
|
1057
|
+
)
|
|
782
1058
|
parameters_list.append(
|
|
783
1059
|
HttpParemeterSpec(
|
|
784
1060
|
type_="path",
|
|
785
1061
|
name=parameter_name,
|
|
786
1062
|
required=True,
|
|
787
|
-
argument_type_str=get_field_type_for_ts(
|
|
1063
|
+
argument_type_str=get_field_type_for_ts(
|
|
1064
|
+
annotated_type, context_suffix
|
|
1065
|
+
),
|
|
788
1066
|
)
|
|
789
1067
|
)
|
|
790
1068
|
else:
|
|
791
1069
|
mapped_types.add(annotated_type)
|
|
1070
|
+
# For default parameters (treated as query), use Input suffix if it's a split model
|
|
1071
|
+
context_suffix = (
|
|
1072
|
+
"Input"
|
|
1073
|
+
if (
|
|
1074
|
+
inspect.isclass(annotated_type)
|
|
1075
|
+
and hasattr(annotated_type, "__dict__")
|
|
1076
|
+
and SplitInputOutput.is_split_model(annotated_type)
|
|
1077
|
+
)
|
|
1078
|
+
else ""
|
|
1079
|
+
)
|
|
792
1080
|
parameters_list.append(
|
|
793
1081
|
HttpParemeterSpec(
|
|
794
1082
|
type_="query",
|
|
795
1083
|
name=parameter_name,
|
|
796
1084
|
required=True,
|
|
797
|
-
argument_type_str=get_field_type_for_ts(
|
|
1085
|
+
argument_type_str=get_field_type_for_ts(
|
|
1086
|
+
annotated_type, context_suffix
|
|
1087
|
+
),
|
|
798
1088
|
)
|
|
799
1089
|
)
|
|
800
1090
|
|
|
@@ -802,12 +1092,18 @@ def extract_parameters(
|
|
|
802
1092
|
parameter_type, BaseModel
|
|
803
1093
|
):
|
|
804
1094
|
mapped_types.update(extract_all_envolved_types(parameter_type))
|
|
1095
|
+
# For BaseModel parameters, use Input suffix if it's a split model
|
|
1096
|
+
context_suffix = (
|
|
1097
|
+
"Input" if SplitInputOutput.is_split_model(parameter_type) else ""
|
|
1098
|
+
)
|
|
805
1099
|
parameters_list.append(
|
|
806
1100
|
HttpParemeterSpec(
|
|
807
1101
|
type_="body",
|
|
808
1102
|
name=parameter_name,
|
|
809
1103
|
required=True,
|
|
810
|
-
argument_type_str=get_field_type_for_ts(
|
|
1104
|
+
argument_type_str=get_field_type_for_ts(
|
|
1105
|
+
parameter_type, context_suffix
|
|
1106
|
+
),
|
|
811
1107
|
)
|
|
812
1108
|
)
|
|
813
1109
|
elif (
|
|
@@ -816,22 +1112,46 @@ def extract_parameters(
|
|
|
816
1112
|
or re.search(f"{{{parameter_name}(:.*?)?}}", mapping.path) is not None
|
|
817
1113
|
):
|
|
818
1114
|
mapped_types.add(parameter_type)
|
|
1115
|
+
# For path parameters, use Input suffix if it's a split model
|
|
1116
|
+
context_suffix = (
|
|
1117
|
+
"Input"
|
|
1118
|
+
if (
|
|
1119
|
+
inspect.isclass(parameter_type)
|
|
1120
|
+
and hasattr(parameter_type, "__dict__")
|
|
1121
|
+
and SplitInputOutput.is_split_model(parameter_type)
|
|
1122
|
+
)
|
|
1123
|
+
else ""
|
|
1124
|
+
)
|
|
819
1125
|
parameters_list.append(
|
|
820
1126
|
HttpParemeterSpec(
|
|
821
1127
|
type_="path",
|
|
822
1128
|
name=parameter_name,
|
|
823
1129
|
required=True,
|
|
824
|
-
argument_type_str=get_field_type_for_ts(
|
|
1130
|
+
argument_type_str=get_field_type_for_ts(
|
|
1131
|
+
parameter_type, context_suffix
|
|
1132
|
+
),
|
|
825
1133
|
)
|
|
826
1134
|
)
|
|
827
1135
|
else:
|
|
828
1136
|
mapped_types.add(parameter_type)
|
|
1137
|
+
# For default parameters (treated as query), use Input suffix if it's a split model
|
|
1138
|
+
context_suffix = (
|
|
1139
|
+
"Input"
|
|
1140
|
+
if (
|
|
1141
|
+
inspect.isclass(parameter_type)
|
|
1142
|
+
and hasattr(parameter_type, "__dict__")
|
|
1143
|
+
and SplitInputOutput.is_split_model(parameter_type)
|
|
1144
|
+
)
|
|
1145
|
+
else ""
|
|
1146
|
+
)
|
|
829
1147
|
parameters_list.append(
|
|
830
1148
|
HttpParemeterSpec(
|
|
831
1149
|
type_="query",
|
|
832
1150
|
name=parameter_name,
|
|
833
1151
|
required=True,
|
|
834
|
-
argument_type_str=get_field_type_for_ts(
|
|
1152
|
+
argument_type_str=get_field_type_for_ts(
|
|
1153
|
+
parameter_type, context_suffix
|
|
1154
|
+
),
|
|
835
1155
|
)
|
|
836
1156
|
)
|
|
837
1157
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
2
2
|
README.md,sha256=2qMM__t_MoLKZr4IY9tXjo-Jn6LKjuHMb1qbyXpgL08,3401
|
|
3
|
-
pyproject.toml,sha256=
|
|
4
|
-
jararaca/__init__.py,sha256=
|
|
3
|
+
pyproject.toml,sha256=CJhUE0Llc5_AlQo4FUo5kjI7cblJBlzu4-YmrIZ0QCQ,2041
|
|
4
|
+
jararaca/__init__.py,sha256=vK3zyIVLckwZgj1FPX6jzSbzaSWmSy3wQ2KMwmpJnmg,22046
|
|
5
5
|
jararaca/__main__.py,sha256=-O3vsB5lHdqNFjUtoELDF81IYFtR-DSiiFMzRaiSsv4,67
|
|
6
6
|
jararaca/broker_backend/__init__.py,sha256=GzEIuHR1xzgCJD4FE3harNjoaYzxHMHoEL0_clUaC-k,3528
|
|
7
7
|
jararaca/broker_backend/mapper.py,sha256=vTsi7sWpNvlga1PWPFg0rCJ5joJ0cdzykkIc2Tuvenc,696
|
|
@@ -32,7 +32,7 @@ jararaca/observability/providers/otel.py,sha256=8N1F32W43t7c8cwmtTh6Yz9b7HyfGFSR
|
|
|
32
32
|
jararaca/persistence/base.py,sha256=xnGUbsLNz3gO-9iJt-Sn5NY13Yc9-misP8wLwQuGGoM,1024
|
|
33
33
|
jararaca/persistence/exports.py,sha256=Ghx4yoFaB4QVTb9WxrFYgmcSATXMNvrOvT8ybPNKXCA,62
|
|
34
34
|
jararaca/persistence/interceptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
jararaca/persistence/interceptors/aiosqa_interceptor.py,sha256=
|
|
35
|
+
jararaca/persistence/interceptors/aiosqa_interceptor.py,sha256=Cvr8wtCpRHE1Ddy9NvlZvXE2EVV0jBIibHCqj4pRkvc,6689
|
|
36
36
|
jararaca/persistence/interceptors/constants.py,sha256=o8g5RxDX9dSSnM9eXYlTJalGPfWxYm6-CAA8-FooUzE,36
|
|
37
37
|
jararaca/persistence/interceptors/decorators.py,sha256=p37r9ux5w_bvn8xPNPhScl3fjCc2enqTR0cJYLxMsrI,1627
|
|
38
38
|
jararaca/persistence/session.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -69,13 +69,13 @@ jararaca/tools/app_config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
69
69
|
jararaca/tools/app_config/decorators.py,sha256=-ckkMZ1dswOmECdo1rFrZ15UAku--txaNXMp8fd1Ndk,941
|
|
70
70
|
jararaca/tools/app_config/interceptor.py,sha256=HV8h4AxqUc_ACs5do4BSVlyxlRXzx7HqJtoVO9tfRnQ,2611
|
|
71
71
|
jararaca/tools/typescript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
|
-
jararaca/tools/typescript/decorators.py,sha256=
|
|
73
|
-
jararaca/tools/typescript/interface_parser.py,sha256=
|
|
72
|
+
jararaca/tools/typescript/decorators.py,sha256=y1zBq8mBZ8CBXlZ0nKy2RyIgCvP9kp4elACbaC6dptQ,2946
|
|
73
|
+
jararaca/tools/typescript/interface_parser.py,sha256=OMvz4vcsKD0eYPwH1hd3PJpLP2Bc_Ex0CefIhsJArf4,47306
|
|
74
74
|
jararaca/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
75
|
jararaca/utils/rabbitmq_utils.py,sha256=ytdAFUyv-OBkaVnxezuJaJoLrmN7giZgtKeet_IsMBs,10918
|
|
76
76
|
jararaca/utils/retry.py,sha256=DzPX_fXUvTqej6BQ8Mt2dvLo9nNlTBm7Kx2pFZ26P2Q,4668
|
|
77
|
-
jararaca-0.3.
|
|
78
|
-
jararaca-0.3.
|
|
79
|
-
jararaca-0.3.
|
|
80
|
-
jararaca-0.3.
|
|
81
|
-
jararaca-0.3.
|
|
77
|
+
jararaca-0.3.12a16.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
78
|
+
jararaca-0.3.12a16.dist-info/METADATA,sha256=d5M-tdj2etlzLG_d1OofoaoDIFYwLqgyhHda36VMTJU,4996
|
|
79
|
+
jararaca-0.3.12a16.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
80
|
+
jararaca-0.3.12a16.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
|
|
81
|
+
jararaca-0.3.12a16.dist-info/RECORD,,
|
pyproject.toml
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|