django-cfg 1.4.10__py3-none-any.whl → 1.4.13__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.
- django_cfg/apps/agents/management/commands/create_agent.py +1 -1
- django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
- django_cfg/apps/newsletter/serializers.py +40 -3
- django_cfg/apps/newsletter/views/campaigns.py +12 -3
- django_cfg/apps/newsletter/views/emails.py +14 -3
- django_cfg/apps/newsletter/views/subscriptions.py +12 -2
- django_cfg/apps/payments/views/api/currencies.py +49 -6
- django_cfg/apps/payments/views/api/webhooks.py +72 -7
- django_cfg/apps/payments/views/overview/serializers.py +34 -1
- django_cfg/apps/payments/views/overview/views.py +2 -1
- django_cfg/apps/payments/views/serializers/payments.py +6 -6
- django_cfg/apps/urls.py +106 -45
- django_cfg/core/base/config_model.py +2 -2
- django_cfg/core/constants.py +1 -1
- django_cfg/core/generation/integration_generators/__init__.py +1 -1
- django_cfg/core/generation/integration_generators/api.py +73 -49
- django_cfg/core/integration/display/startup.py +30 -22
- django_cfg/core/integration/url_integration.py +15 -16
- django_cfg/management/commands/check_endpoints.py +11 -160
- django_cfg/management/commands/check_settings.py +13 -348
- django_cfg/management/commands/clear_constance.py +13 -201
- django_cfg/management/commands/create_token.py +13 -321
- django_cfg/management/commands/generate_clients.py +23 -0
- django_cfg/management/commands/list_urls.py +13 -306
- django_cfg/management/commands/migrate_all.py +13 -126
- django_cfg/management/commands/migrator.py +13 -396
- django_cfg/management/commands/rundramatiq.py +15 -247
- django_cfg/management/commands/rundramatiq_simulator.py +12 -429
- django_cfg/management/commands/runserver_ngrok.py +15 -160
- django_cfg/management/commands/script.py +12 -488
- django_cfg/management/commands/show_config.py +12 -215
- django_cfg/management/commands/show_urls.py +12 -342
- django_cfg/management/commands/superuser.py +15 -295
- django_cfg/management/commands/task_clear.py +14 -217
- django_cfg/management/commands/task_status.py +13 -248
- django_cfg/management/commands/test_email.py +15 -86
- django_cfg/management/commands/test_telegram.py +14 -61
- django_cfg/management/commands/test_twilio.py +15 -105
- django_cfg/management/commands/tree.py +13 -383
- django_cfg/management/commands/validate_openapi.py +10 -0
- django_cfg/middleware/README.md +1 -1
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/__init__.py +2 -2
- django_cfg/models/api/drf/spectacular.py +6 -6
- django_cfg/models/django/__init__.py +2 -2
- django_cfg/models/django/openapi.py +162 -0
- django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
- django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
- django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
- django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
- django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
- django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
- django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
- django_cfg/modules/django_admin/management/commands/script.py +496 -0
- django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
- django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
- django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
- django_cfg/modules/django_admin/management/commands/tree.py +390 -0
- django_cfg/modules/django_client/__init__.py +20 -0
- django_cfg/modules/django_client/apps.py +35 -0
- django_cfg/modules/django_client/core/__init__.py +56 -0
- django_cfg/modules/django_client/core/archive/__init__.py +11 -0
- django_cfg/modules/django_client/core/archive/manager.py +134 -0
- django_cfg/modules/django_client/core/cli/__init__.py +12 -0
- django_cfg/modules/django_client/core/cli/main.py +235 -0
- django_cfg/modules/django_client/core/config/__init__.py +18 -0
- django_cfg/modules/django_client/core/config/config.py +208 -0
- django_cfg/modules/django_client/core/config/group.py +101 -0
- django_cfg/modules/django_client/core/config/service.py +209 -0
- django_cfg/modules/django_client/core/generator/__init__.py +115 -0
- django_cfg/modules/django_client/core/generator/base.py +838 -0
- django_cfg/modules/django_client/core/generator/python/__init__.py +16 -0
- django_cfg/modules/django_client/core/generator/python/async_client_gen.py +174 -0
- django_cfg/modules/django_client/core/generator/python/files_generator.py +180 -0
- django_cfg/modules/django_client/core/generator/python/generator.py +182 -0
- django_cfg/modules/django_client/core/generator/python/models_generator.py +318 -0
- django_cfg/modules/django_client/core/generator/python/operations_generator.py +278 -0
- django_cfg/modules/django_client/core/generator/python/sync_client_gen.py +102 -0
- django_cfg/modules/django_client/core/generator/python/templates/__init__.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +153 -0
- django_cfg/modules/django_client/core/generator/python/templates/app_init.py.jinja +6 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/app_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/flat_client.py.jinja +38 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/main_client.py.jinja +68 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/main_client_file.py.jinja +14 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/operation_method.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sub_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_main_client.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_operation_method.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_sub_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +52 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/app_models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/enum_class.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/enums.py.jinja +8 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/schema_class.py.jinja +21 -0
- django_cfg/modules/django_client/core/generator/python/templates/pyproject.toml.jinja +55 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/logger.py.jinja +255 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/retry.py.jinja +271 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/schema.py.jinja +12 -0
- django_cfg/modules/django_client/core/generator/typescript/__init__.py +14 -0
- django_cfg/modules/django_client/core/generator/typescript/client_generator.py +165 -0
- django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +428 -0
- django_cfg/modules/django_client/core/generator/typescript/files_generator.py +207 -0
- django_cfg/modules/django_client/core/generator/typescript/generator.py +432 -0
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +536 -0
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py +245 -0
- django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +298 -0
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +329 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/api_instance.ts.jinja +131 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/app_index.ts.jinja +2 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/app_client.ts.jinja +18 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/client.ts.jinja +403 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/flat_client.ts.jinja +109 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/main_client_file.ts.jinja +10 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/operation.ts.jinja +61 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/sub_client.ts.jinja +15 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +45 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/index.ts.jinja +30 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/index.ts.jinja +5 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +268 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/app_models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/enums.ts.jinja +4 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/package.json.jinja +52 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/schemas/index.ts.jinja +21 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/schemas/schema.ts.jinja +24 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/tsconfig.json.jinja +20 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/errors.ts.jinja +116 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/http.ts.jinja +98 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/logger.ts.jinja +259 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/retry.ts.jinja +175 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/schema.ts.jinja +7 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/storage.ts.jinja +158 -0
- django_cfg/modules/django_client/core/groups/__init__.py +13 -0
- django_cfg/modules/django_client/core/groups/detector.py +178 -0
- django_cfg/modules/django_client/core/groups/manager.py +314 -0
- django_cfg/modules/django_client/core/ir/__init__.py +57 -0
- django_cfg/modules/django_client/core/ir/context.py +387 -0
- django_cfg/modules/django_client/core/ir/operation.py +518 -0
- django_cfg/modules/django_client/core/ir/schema.py +353 -0
- django_cfg/modules/django_client/core/parser/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/base.py +648 -0
- django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/models/base.py +212 -0
- django_cfg/modules/django_client/core/parser/models/components.py +160 -0
- django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
- django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
- django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
- django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
- django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
- django_cfg/modules/django_client/core/validation/__init__.py +22 -0
- django_cfg/modules/django_client/core/validation/checker.py +134 -0
- django_cfg/modules/django_client/core/validation/fixer.py +216 -0
- django_cfg/modules/django_client/core/validation/reporter.py +480 -0
- django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
- django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
- django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
- django_cfg/modules/django_client/core/validation/safety.py +266 -0
- django_cfg/modules/django_client/management/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +427 -0
- django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
- django_cfg/modules/django_client/pytest.ini +30 -0
- django_cfg/modules/django_client/spectacular/__init__.py +10 -0
- django_cfg/modules/django_client/spectacular/async_detection.py +187 -0
- django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
- django_cfg/modules/django_client/urls.py +72 -0
- django_cfg/{dashboard → modules/django_dashboard}/DEBUG_README.md +2 -2
- django_cfg/{dashboard → modules/django_dashboard}/REFACTORING_SUMMARY.md +1 -1
- django_cfg/modules/django_dashboard/management/__init__.py +0 -0
- django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
- django_cfg/{dashboard → modules/django_dashboard}/management/commands/debug_dashboard.py +5 -5
- django_cfg/modules/django_dashboard/sections/documentation.py +391 -0
- django_cfg/modules/django_email/management/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/test_email.py +93 -0
- django_cfg/modules/django_logging/LOGGING_GUIDE.md +1 -1
- django_cfg/modules/django_logging/django_logger.py +6 -6
- django_cfg/modules/django_ngrok/management/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
- django_cfg/modules/django_tasks/management/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
- django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
- django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
- django_cfg/modules/django_telegram/management/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
- django_cfg/modules/django_twilio/management/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
- django_cfg/modules/django_unfold/callbacks/main.py +21 -10
- django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
- django_cfg/pyproject.toml +2 -6
- django_cfg/registry/third_party.py +5 -7
- django_cfg/routing/callbacks.py +1 -1
- django_cfg/static/admin/css/prose-unfold.css +666 -0
- django_cfg/templates/admin/index.html +8 -0
- django_cfg/templates/admin/index_new.html +13 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
- django_cfg/templates/admin/sections/documentation_section.html +172 -0
- django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/METADATA +2 -2
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/RECORD +224 -74
- django_cfg/management/commands/generate.py +0 -107
- /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
- /django_cfg/{dashboard → modules/django_admin}/management/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_admin}/management/commands/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/components.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/debug.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/base.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/commands.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/overview.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/stats.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/system.py +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
"""
|
2
|
+
TypeScript Files Generator - Generates utility files (index, http, errors, logger, etc.).
|
3
|
+
"""
|
4
|
+
|
5
|
+
from __future__ import annotations
|
6
|
+
|
7
|
+
from jinja2 import Environment
|
8
|
+
from ...ir import IROperationObject
|
9
|
+
from ..base import GeneratedFile
|
10
|
+
|
11
|
+
|
12
|
+
class FilesGenerator:
|
13
|
+
"""Generates TypeScript utility files."""
|
14
|
+
|
15
|
+
def __init__(self, jinja_env: Environment, context, base):
|
16
|
+
self.jinja_env = jinja_env
|
17
|
+
self.context = context
|
18
|
+
self.base = base
|
19
|
+
self.openapi_schema = getattr(base, 'openapi_schema', None)
|
20
|
+
|
21
|
+
def generate_index_file(self):
|
22
|
+
"""Generate index.ts with exports."""
|
23
|
+
|
24
|
+
template = self.jinja_env.get_template('index.ts.jinja')
|
25
|
+
content = template.render(
|
26
|
+
has_enums=bool(self.base.get_enum_schemas())
|
27
|
+
)
|
28
|
+
|
29
|
+
return GeneratedFile(
|
30
|
+
path="index.ts",
|
31
|
+
content=content,
|
32
|
+
description="Module exports",
|
33
|
+
)
|
34
|
+
|
35
|
+
def generate_app_index_file(self, tag: str, operations: list[IROperationObject]):
|
36
|
+
"""Generate index.ts for a specific app."""
|
37
|
+
from ..base import GeneratedFile
|
38
|
+
|
39
|
+
template = self.jinja_env.get_template('app_index.ts.jinja')
|
40
|
+
content = template.render()
|
41
|
+
|
42
|
+
folder_name = self.base.tag_and_app_to_folder_name(tag, operations)
|
43
|
+
return GeneratedFile(
|
44
|
+
path=f"{folder_name}/index.ts",
|
45
|
+
content=content,
|
46
|
+
description=f"Module exports for {tag}",
|
47
|
+
)
|
48
|
+
|
49
|
+
def generate_main_index_file(self):
|
50
|
+
"""Generate main index.ts with API class and JWT management."""
|
51
|
+
|
52
|
+
ops_by_tag = self.base.group_operations_by_tag()
|
53
|
+
tags = sorted(ops_by_tag.keys())
|
54
|
+
|
55
|
+
# Prepare tags data for template
|
56
|
+
tags_data = [
|
57
|
+
{
|
58
|
+
"class_name": self.base.tag_to_class_name(tag, suffix=""),
|
59
|
+
"property": self.base.tag_to_property_name(tag),
|
60
|
+
"slug": self.base.tag_and_app_to_folder_name(tag, ops_by_tag[tag]),
|
61
|
+
}
|
62
|
+
for tag in tags
|
63
|
+
]
|
64
|
+
|
65
|
+
# Check if we have enums
|
66
|
+
all_schemas = self.context.schemas
|
67
|
+
all_enums = self.base._collect_enums_from_schemas(all_schemas)
|
68
|
+
|
69
|
+
template = self.jinja_env.get_template('main_index.ts.jinja')
|
70
|
+
content = template.render(
|
71
|
+
api_title=self.context.openapi_info.title,
|
72
|
+
tags=tags_data,
|
73
|
+
has_enums=bool(all_enums),
|
74
|
+
generate_zod_schemas=getattr(self.base, 'generate_zod_schemas', False),
|
75
|
+
generate_fetchers=getattr(self.base, 'generate_fetchers', False),
|
76
|
+
generate_swr_hooks=getattr(self.base, 'generate_swr_hooks', False),
|
77
|
+
)
|
78
|
+
|
79
|
+
return GeneratedFile(
|
80
|
+
path="index.ts",
|
81
|
+
content=content,
|
82
|
+
description="Main index with API class and JWT management",
|
83
|
+
)
|
84
|
+
|
85
|
+
def generate_http_adapter_file(self):
|
86
|
+
"""Generate http.ts with HttpClient adapter interface."""
|
87
|
+
|
88
|
+
template = self.jinja_env.get_template('utils/http.ts.jinja')
|
89
|
+
content = template.render()
|
90
|
+
|
91
|
+
return GeneratedFile(
|
92
|
+
path="http.ts",
|
93
|
+
content=content,
|
94
|
+
description="HTTP client adapter interface and implementations",
|
95
|
+
)
|
96
|
+
|
97
|
+
def generate_errors_file(self):
|
98
|
+
"""Generate errors.ts with APIError class."""
|
99
|
+
|
100
|
+
template = self.jinja_env.get_template('utils/errors.ts.jinja')
|
101
|
+
content = template.render()
|
102
|
+
|
103
|
+
return GeneratedFile(
|
104
|
+
path="errors.ts",
|
105
|
+
content=content,
|
106
|
+
description="API error classes",
|
107
|
+
)
|
108
|
+
|
109
|
+
def generate_storage_file(self):
|
110
|
+
"""Generate storage.ts with StorageAdapter implementations."""
|
111
|
+
|
112
|
+
template = self.jinja_env.get_template('utils/storage.ts.jinja')
|
113
|
+
content = template.render()
|
114
|
+
|
115
|
+
return GeneratedFile(
|
116
|
+
path="storage.ts",
|
117
|
+
content=content,
|
118
|
+
description="Storage adapters for cross-platform support",
|
119
|
+
)
|
120
|
+
|
121
|
+
def generate_logger_file(self):
|
122
|
+
"""Generate logger.ts with Consola integration."""
|
123
|
+
|
124
|
+
template = self.jinja_env.get_template('utils/logger.ts.jinja')
|
125
|
+
content = template.render()
|
126
|
+
|
127
|
+
return GeneratedFile(
|
128
|
+
path="logger.ts",
|
129
|
+
content=content,
|
130
|
+
description="API Logger with Consola",
|
131
|
+
)
|
132
|
+
|
133
|
+
def generate_retry_file(self):
|
134
|
+
"""Generate retry.ts with p-retry integration."""
|
135
|
+
|
136
|
+
template = self.jinja_env.get_template('utils/retry.ts.jinja')
|
137
|
+
content = template.render()
|
138
|
+
|
139
|
+
return GeneratedFile(
|
140
|
+
path="retry.ts",
|
141
|
+
content=content,
|
142
|
+
description="Retry utilities with p-retry",
|
143
|
+
)
|
144
|
+
|
145
|
+
def generate_api_instance_file(self):
|
146
|
+
"""Generate api-instance.ts with global singleton."""
|
147
|
+
|
148
|
+
template = self.jinja_env.get_template('api_instance.ts.jinja')
|
149
|
+
content = template.render()
|
150
|
+
|
151
|
+
return GeneratedFile(
|
152
|
+
path="api-instance.ts",
|
153
|
+
content=content,
|
154
|
+
description="Global API singleton for universal configuration",
|
155
|
+
)
|
156
|
+
|
157
|
+
def generate_package_json_file(self, package_config: dict = None):
|
158
|
+
"""Generate package.json for npm publishing."""
|
159
|
+
if package_config is None:
|
160
|
+
package_config = {}
|
161
|
+
|
162
|
+
# Default configuration
|
163
|
+
defaults = {
|
164
|
+
"package_name": package_config.get("name", "api-client"),
|
165
|
+
"version": package_config.get("version", "1.0.0"),
|
166
|
+
"description": package_config.get("description") or f"Auto-generated TypeScript client for {self.context.openapi_info.title}",
|
167
|
+
"author": package_config.get("author"),
|
168
|
+
"license": package_config.get("license", "MIT"),
|
169
|
+
"repository_url": package_config.get("repository_url"),
|
170
|
+
"keywords": package_config.get("keywords", ["api", "client", "typescript", "openapi"]),
|
171
|
+
"private": package_config.get("private", False),
|
172
|
+
}
|
173
|
+
|
174
|
+
# Add Zod flag
|
175
|
+
defaults["generate_zod_schemas"] = self.base.generate_zod_schemas
|
176
|
+
|
177
|
+
template = self.jinja_env.get_template('package.json.jinja')
|
178
|
+
content = template.render(**defaults)
|
179
|
+
|
180
|
+
return GeneratedFile(
|
181
|
+
path="package.json",
|
182
|
+
content=content,
|
183
|
+
description="NPM package configuration",
|
184
|
+
)
|
185
|
+
|
186
|
+
def generate_tsconfig_file(self):
|
187
|
+
"""Generate tsconfig.json for TypeScript compilation."""
|
188
|
+
template = self.jinja_env.get_template('tsconfig.json.jinja')
|
189
|
+
content = template.render()
|
190
|
+
|
191
|
+
return GeneratedFile(
|
192
|
+
path="tsconfig.json",
|
193
|
+
content=content,
|
194
|
+
description="TypeScript compiler configuration",
|
195
|
+
)
|
196
|
+
|
197
|
+
def generate_schema_file(self):
|
198
|
+
"""Generate schema.ts with OpenAPI schema as const."""
|
199
|
+
|
200
|
+
template = self.jinja_env.get_template('utils/schema.ts.jinja')
|
201
|
+
content = template.render(schema=self.openapi_schema)
|
202
|
+
|
203
|
+
return GeneratedFile(
|
204
|
+
path="schema.ts",
|
205
|
+
content=content,
|
206
|
+
description="OpenAPI Schema",
|
207
|
+
)
|
@@ -0,0 +1,432 @@
|
|
1
|
+
"""
|
2
|
+
TypeScript Generator - Generates TypeScript client (Fetch API).
|
3
|
+
|
4
|
+
This generator creates a complete TypeScript API client from IR:
|
5
|
+
- TypeScript interfaces (Request/Response/Patch splits)
|
6
|
+
- Enum types from x-enum-varnames
|
7
|
+
- Fetch API for HTTP
|
8
|
+
- Django CSRF/session handling
|
9
|
+
- Type-safe
|
10
|
+
"""
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
|
14
|
+
import pathlib
|
15
|
+
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
16
|
+
|
17
|
+
from ..base import BaseGenerator, GeneratedFile
|
18
|
+
from ...ir import IROperationObject, IRSchemaObject
|
19
|
+
|
20
|
+
from .models_generator import ModelsGenerator
|
21
|
+
from .operations_generator import OperationsGenerator
|
22
|
+
from .client_generator import ClientGenerator
|
23
|
+
from .files_generator import FilesGenerator
|
24
|
+
from .schemas_generator import SchemasGenerator
|
25
|
+
from .fetchers_generator import FetchersGenerator
|
26
|
+
from .hooks_generator import HooksGenerator
|
27
|
+
|
28
|
+
|
29
|
+
class TypeScriptGenerator(BaseGenerator):
|
30
|
+
"""
|
31
|
+
TypeScript client generator.
|
32
|
+
|
33
|
+
Generates:
|
34
|
+
- models.ts: TypeScript interfaces (User, UserRequest, PatchedUser)
|
35
|
+
- enums.ts: Enum types (StatusEnum, RoleEnum)
|
36
|
+
- client.ts: APIClient class with all operations
|
37
|
+
- index.ts: Module exports
|
38
|
+
"""
|
39
|
+
|
40
|
+
def __init__(self, *args, **kwargs):
|
41
|
+
super().__init__(*args, **kwargs)
|
42
|
+
|
43
|
+
# Setup Jinja2 environment
|
44
|
+
templates_dir = pathlib.Path(__file__).parent / "templates"
|
45
|
+
self.jinja_env = Environment(
|
46
|
+
loader=FileSystemLoader(str(templates_dir)),
|
47
|
+
autoescape=select_autoescape(['html', 'xml']),
|
48
|
+
trim_blocks=True,
|
49
|
+
lstrip_blocks=True,
|
50
|
+
)
|
51
|
+
|
52
|
+
# Initialize sub-generators
|
53
|
+
self.models_gen = ModelsGenerator(self.jinja_env, self.context, self)
|
54
|
+
self.operations_gen = OperationsGenerator(self.jinja_env, self.context, self)
|
55
|
+
self.client_gen = ClientGenerator(self.jinja_env, self.context, self, self.operations_gen)
|
56
|
+
self.files_gen = FilesGenerator(self.jinja_env, self.context, self)
|
57
|
+
self.schemas_gen = SchemasGenerator(self.jinja_env, self.context, self)
|
58
|
+
self.fetchers_gen = FetchersGenerator(self.jinja_env, self.context, self)
|
59
|
+
self.hooks_gen = HooksGenerator(self.jinja_env, self.context, self)
|
60
|
+
|
61
|
+
def generate(self) -> list[GeneratedFile]:
|
62
|
+
"""Generate all TypeScript client files."""
|
63
|
+
files = []
|
64
|
+
|
65
|
+
if self.client_structure == "namespaced":
|
66
|
+
# Generate per-app folders
|
67
|
+
ops_by_tag = self.group_operations_by_tag()
|
68
|
+
|
69
|
+
for tag, operations in sorted(ops_by_tag.items()):
|
70
|
+
# Generate app folder (models.ts, client.ts, index.ts)
|
71
|
+
files.extend(self._generate_app_folder(tag, operations))
|
72
|
+
|
73
|
+
# Generate shared enums.ts (Variant 2: all enums in root)
|
74
|
+
all_schemas = self.context.schemas
|
75
|
+
all_enums = self._collect_enums_from_schemas(all_schemas)
|
76
|
+
if all_enums:
|
77
|
+
files.append(self.models_gen.generate_shared_enums_file(all_enums))
|
78
|
+
|
79
|
+
# Generate main client.ts
|
80
|
+
files.append(self.client_gen.generate_main_client_file(ops_by_tag))
|
81
|
+
|
82
|
+
# Generate main index.ts
|
83
|
+
files.append(self.files_gen.generate_main_index_file())
|
84
|
+
|
85
|
+
# Generate http.ts with HttpClientAdapter
|
86
|
+
files.append(self.files_gen.generate_http_adapter_file())
|
87
|
+
|
88
|
+
# Generate errors.ts with APIError
|
89
|
+
files.append(self.files_gen.generate_errors_file())
|
90
|
+
|
91
|
+
# Generate storage.ts with StorageAdapter
|
92
|
+
files.append(self.files_gen.generate_storage_file())
|
93
|
+
|
94
|
+
# Generate logger.ts with Consola
|
95
|
+
files.append(self.files_gen.generate_logger_file())
|
96
|
+
|
97
|
+
# Generate retry.ts with p-retry
|
98
|
+
files.append(self.files_gen.generate_retry_file())
|
99
|
+
|
100
|
+
# Generate api-instance.ts singleton (needed for fetchers/hooks)
|
101
|
+
if self.generate_fetchers:
|
102
|
+
files.append(self.files_gen.generate_api_instance_file())
|
103
|
+
|
104
|
+
# Generate schema.ts with OpenAPI schema
|
105
|
+
if self.openapi_schema:
|
106
|
+
files.append(self.files_gen.generate_schema_file())
|
107
|
+
|
108
|
+
# Generate Zod schemas if requested
|
109
|
+
if self.generate_zod_schemas:
|
110
|
+
files.extend(self._generate_zod_schemas())
|
111
|
+
|
112
|
+
# Generate fetchers if requested
|
113
|
+
if self.generate_fetchers:
|
114
|
+
if not self.generate_zod_schemas:
|
115
|
+
print("⚠️ Warning: Fetchers require Zod schemas. Enable generate_zod_schemas.")
|
116
|
+
else:
|
117
|
+
files.extend(self._generate_fetchers())
|
118
|
+
|
119
|
+
# Generate SWR hooks if requested
|
120
|
+
if self.generate_swr_hooks:
|
121
|
+
if not self.generate_fetchers:
|
122
|
+
print("⚠️ Warning: SWR hooks require fetchers. Enable generate_fetchers.")
|
123
|
+
else:
|
124
|
+
files.extend(self._generate_swr_hooks())
|
125
|
+
else:
|
126
|
+
# Flat structure (original logic)
|
127
|
+
files.append(self.models_gen.generate_models_file())
|
128
|
+
|
129
|
+
enum_schemas = self.get_enum_schemas()
|
130
|
+
if enum_schemas:
|
131
|
+
files.append(self.models_gen.generate_enums_file())
|
132
|
+
|
133
|
+
files.append(self.client_gen.generate_client_file())
|
134
|
+
files.append(self.files_gen.generate_index_file())
|
135
|
+
|
136
|
+
# Generate storage.ts with StorageAdapter
|
137
|
+
files.append(self.files_gen.generate_storage_file())
|
138
|
+
|
139
|
+
# Generate logger.ts with Consola
|
140
|
+
files.append(self.files_gen.generate_logger_file())
|
141
|
+
|
142
|
+
# Generate retry.ts with p-retry
|
143
|
+
files.append(self.files_gen.generate_retry_file())
|
144
|
+
|
145
|
+
# Generate api-instance.ts singleton (needed for fetchers/hooks)
|
146
|
+
if self.generate_fetchers:
|
147
|
+
files.append(self.files_gen.generate_api_instance_file())
|
148
|
+
|
149
|
+
# Generate schema.ts with OpenAPI schema
|
150
|
+
if self.openapi_schema:
|
151
|
+
files.append(self.files_gen.generate_schema_file())
|
152
|
+
|
153
|
+
# Generate Zod schemas if requested
|
154
|
+
if self.generate_zod_schemas:
|
155
|
+
files.extend(self._generate_zod_schemas())
|
156
|
+
|
157
|
+
# Generate fetchers if requested
|
158
|
+
if self.generate_fetchers:
|
159
|
+
if not self.generate_zod_schemas:
|
160
|
+
print("⚠️ Warning: Fetchers require Zod schemas. Enable generate_zod_schemas.")
|
161
|
+
else:
|
162
|
+
files.extend(self._generate_fetchers())
|
163
|
+
|
164
|
+
# Generate SWR hooks if requested
|
165
|
+
if self.generate_swr_hooks:
|
166
|
+
if not self.generate_fetchers:
|
167
|
+
print("⚠️ Warning: SWR hooks require fetchers. Enable generate_fetchers.")
|
168
|
+
else:
|
169
|
+
files.extend(self._generate_swr_hooks())
|
170
|
+
|
171
|
+
# Generate package files if requested
|
172
|
+
if self.generate_package_files:
|
173
|
+
files.append(self.files_gen.generate_package_json_file(self.package_config))
|
174
|
+
files.append(self.files_gen.generate_tsconfig_file())
|
175
|
+
|
176
|
+
return files
|
177
|
+
|
178
|
+
# ===== Delegation Methods (for backward compatibility with tests) =====
|
179
|
+
|
180
|
+
def generate_schema(self, schema: IRSchemaObject) -> str:
|
181
|
+
"""Generate TypeScript interface for schema."""
|
182
|
+
return self.models_gen.generate_schema(schema)
|
183
|
+
|
184
|
+
def generate_enum(self, schema: IRSchemaObject) -> str:
|
185
|
+
"""Generate TypeScript enum from x-enum-varnames."""
|
186
|
+
return self.models_gen.generate_enum(schema)
|
187
|
+
|
188
|
+
def generate_operation(self, operation: IROperationObject, remove_tag_prefix: bool = False, in_subclient: bool = False) -> str:
|
189
|
+
"""Generate async method for operation."""
|
190
|
+
return self.operations_gen.generate_operation(operation, remove_tag_prefix, in_subclient)
|
191
|
+
|
192
|
+
def _to_camel_case(self, snake_str: str) -> str:
|
193
|
+
"""Convert snake_case to camelCase (delegate to operations generator)."""
|
194
|
+
return self.operations_gen._to_camel_case(snake_str)
|
195
|
+
|
196
|
+
# ===== Per-App Folder Generation (Namespaced Structure) =====
|
197
|
+
|
198
|
+
def _generate_app_folder(self, tag: str, operations: list[IROperationObject]) -> list[GeneratedFile]:
|
199
|
+
"""Generate folder for a specific app (tag)."""
|
200
|
+
files = []
|
201
|
+
|
202
|
+
# Get schemas used by this app
|
203
|
+
app_schemas = self._get_schemas_for_operations(operations)
|
204
|
+
|
205
|
+
# Generate models.ts for this app
|
206
|
+
files.append(self.models_gen.generate_app_models_file(tag, app_schemas, operations))
|
207
|
+
|
208
|
+
# Generate client.ts for this app
|
209
|
+
files.append(self.client_gen.generate_app_client_file(tag, operations))
|
210
|
+
|
211
|
+
# Generate index.ts for this app
|
212
|
+
files.append(self.files_gen.generate_app_index_file(tag, operations))
|
213
|
+
|
214
|
+
return files
|
215
|
+
|
216
|
+
def _get_schemas_for_operations(self, operations: list[IROperationObject]) -> dict[str, IRSchemaObject]:
|
217
|
+
"""
|
218
|
+
Get all schemas used by given operations.
|
219
|
+
|
220
|
+
This method recursively resolves all schema dependencies ($ref) to ensure
|
221
|
+
that nested schemas (e.g., APIKeyList referenced by PaginatedAPIKeyListList)
|
222
|
+
are included in the generated models file.
|
223
|
+
"""
|
224
|
+
schemas = {}
|
225
|
+
|
226
|
+
for operation in operations:
|
227
|
+
# Request body schemas
|
228
|
+
if operation.request_body and operation.request_body.schema_name:
|
229
|
+
schema_name = operation.request_body.schema_name
|
230
|
+
if schema_name in self.context.schemas:
|
231
|
+
schemas[schema_name] = self.context.schemas[schema_name]
|
232
|
+
|
233
|
+
# Patch request body schemas
|
234
|
+
if operation.patch_request_body and operation.patch_request_body.schema_name:
|
235
|
+
schema_name = operation.patch_request_body.schema_name
|
236
|
+
if schema_name in self.context.schemas:
|
237
|
+
schemas[schema_name] = self.context.schemas[schema_name]
|
238
|
+
|
239
|
+
# Response schemas
|
240
|
+
for status_code, response in operation.responses.items():
|
241
|
+
if response.schema_name:
|
242
|
+
if response.schema_name in self.context.schemas:
|
243
|
+
schemas[response.schema_name] = self.context.schemas[response.schema_name]
|
244
|
+
|
245
|
+
# Recursively resolve all nested schema dependencies
|
246
|
+
schemas = self._resolve_nested_schemas(schemas)
|
247
|
+
|
248
|
+
return schemas
|
249
|
+
|
250
|
+
def _resolve_nested_schemas(self, initial_schemas: dict[str, IRSchemaObject]) -> dict[str, IRSchemaObject]:
|
251
|
+
"""
|
252
|
+
Recursively resolve all nested schema dependencies ($ref).
|
253
|
+
|
254
|
+
This ensures that if SchemaA references SchemaB (e.g., via a property or array items),
|
255
|
+
SchemaB is also included in the output, even if it's not directly used in operations.
|
256
|
+
|
257
|
+
Example:
|
258
|
+
PaginatedAPIKeyListList has:
|
259
|
+
results: Array<APIKeyList> ← $ref to APIKeyList
|
260
|
+
|
261
|
+
This method will find APIKeyList and include it.
|
262
|
+
|
263
|
+
Args:
|
264
|
+
initial_schemas: Schemas directly used by operations
|
265
|
+
|
266
|
+
Returns:
|
267
|
+
All schemas including nested dependencies
|
268
|
+
"""
|
269
|
+
resolved = dict(initial_schemas)
|
270
|
+
queue = list(initial_schemas.values())
|
271
|
+
seen = set(initial_schemas.keys())
|
272
|
+
|
273
|
+
while queue:
|
274
|
+
schema = queue.pop(0)
|
275
|
+
|
276
|
+
# Check properties for $ref and nested items
|
277
|
+
if schema.properties:
|
278
|
+
for prop in schema.properties.values():
|
279
|
+
# Direct $ref on property
|
280
|
+
if prop.ref and prop.ref not in seen:
|
281
|
+
if prop.ref in self.context.schemas:
|
282
|
+
resolved[prop.ref] = self.context.schemas[prop.ref]
|
283
|
+
queue.append(self.context.schemas[prop.ref])
|
284
|
+
seen.add(prop.ref)
|
285
|
+
|
286
|
+
# $ref inside array items (CRITICAL for PaginatedXList patterns!)
|
287
|
+
if prop.items and prop.items.ref:
|
288
|
+
if prop.items.ref not in seen:
|
289
|
+
if prop.items.ref in self.context.schemas:
|
290
|
+
resolved[prop.items.ref] = self.context.schemas[prop.items.ref]
|
291
|
+
queue.append(self.context.schemas[prop.items.ref])
|
292
|
+
seen.add(prop.items.ref)
|
293
|
+
|
294
|
+
# Check array items for $ref at schema level
|
295
|
+
if schema.items and schema.items.ref:
|
296
|
+
if schema.items.ref not in seen:
|
297
|
+
if schema.items.ref in self.context.schemas:
|
298
|
+
resolved[schema.items.ref] = self.context.schemas[schema.items.ref]
|
299
|
+
queue.append(self.context.schemas[schema.items.ref])
|
300
|
+
seen.add(schema.items.ref)
|
301
|
+
|
302
|
+
return resolved
|
303
|
+
|
304
|
+
# ===== Zod Schemas Generation =====
|
305
|
+
|
306
|
+
def _generate_zod_schemas(self) -> list[GeneratedFile]:
|
307
|
+
"""
|
308
|
+
Generate Zod validation schemas for all models.
|
309
|
+
|
310
|
+
Creates:
|
311
|
+
- schemas/User.schema.ts
|
312
|
+
- schemas/UserRequest.schema.ts
|
313
|
+
- schemas/PaginatedUser.schema.ts
|
314
|
+
- schemas/index.ts
|
315
|
+
"""
|
316
|
+
files = []
|
317
|
+
schema_names = []
|
318
|
+
|
319
|
+
# Get all schemas that should have Zod validation
|
320
|
+
all_schemas = {**self.context.schemas}
|
321
|
+
|
322
|
+
# Track refs to resolve dependencies
|
323
|
+
schema_refs = {} # schema_name -> set of referenced schemas
|
324
|
+
for schema_name, schema in all_schemas.items():
|
325
|
+
refs = self._get_schema_refs(schema)
|
326
|
+
schema_refs[schema_name] = refs
|
327
|
+
|
328
|
+
# Generate individual schema files
|
329
|
+
for schema_name, schema in sorted(all_schemas.items()):
|
330
|
+
# Skip enum schemas (they use z.nativeEnum from enums.ts)
|
331
|
+
if schema.enum:
|
332
|
+
continue
|
333
|
+
|
334
|
+
# Generate Zod schema file
|
335
|
+
refs = schema_refs.get(schema_name, set())
|
336
|
+
files.append(self.schemas_gen.generate_schema_file(schema, refs))
|
337
|
+
schema_names.append(schema_name)
|
338
|
+
|
339
|
+
# Generate index.ts
|
340
|
+
if schema_names:
|
341
|
+
files.append(self.schemas_gen.generate_schemas_index_file(schema_names))
|
342
|
+
|
343
|
+
return files
|
344
|
+
|
345
|
+
def _get_schema_refs(self, schema: IRSchemaObject) -> set[str]:
|
346
|
+
"""
|
347
|
+
Get all schemas referenced by this schema.
|
348
|
+
|
349
|
+
Returns set of schema names that are directly referenced.
|
350
|
+
"""
|
351
|
+
refs = set()
|
352
|
+
|
353
|
+
if schema.properties:
|
354
|
+
for prop in schema.properties.values():
|
355
|
+
if prop.ref and prop.ref in self.context.schemas:
|
356
|
+
# Don't include enum refs (they're handled separately)
|
357
|
+
if not self.context.schemas[prop.ref].enum:
|
358
|
+
refs.add(prop.ref)
|
359
|
+
|
360
|
+
if prop.items and prop.items.ref:
|
361
|
+
if prop.items.ref in self.context.schemas:
|
362
|
+
if not self.context.schemas[prop.items.ref].enum:
|
363
|
+
refs.add(prop.items.ref)
|
364
|
+
|
365
|
+
if schema.items and schema.items.ref:
|
366
|
+
if schema.items.ref in self.context.schemas:
|
367
|
+
if not self.context.schemas[schema.items.ref].enum:
|
368
|
+
refs.add(schema.items.ref)
|
369
|
+
|
370
|
+
return refs
|
371
|
+
|
372
|
+
# ===== Fetchers Generation =====
|
373
|
+
|
374
|
+
def _generate_fetchers(self) -> list[GeneratedFile]:
|
375
|
+
"""
|
376
|
+
Generate typed fetcher functions for all operations.
|
377
|
+
|
378
|
+
Creates:
|
379
|
+
- _utils/fetchers/users.ts
|
380
|
+
- _utils/fetchers/posts.ts
|
381
|
+
- _utils/fetchers/index.ts
|
382
|
+
"""
|
383
|
+
files = []
|
384
|
+
module_names = []
|
385
|
+
|
386
|
+
# Group operations by tag
|
387
|
+
ops_by_tag = self.group_operations_by_tag()
|
388
|
+
|
389
|
+
# Generate fetchers for each tag
|
390
|
+
for tag, operations in sorted(ops_by_tag.items()):
|
391
|
+
folder_name = self.tag_and_app_to_folder_name(tag, operations)
|
392
|
+
|
393
|
+
# Generate fetchers file for this tag
|
394
|
+
files.append(self.fetchers_gen.generate_tag_fetchers_file(tag, operations))
|
395
|
+
module_names.append(folder_name)
|
396
|
+
|
397
|
+
# Generate index.ts
|
398
|
+
if module_names:
|
399
|
+
files.append(self.fetchers_gen.generate_fetchers_index_file(module_names))
|
400
|
+
|
401
|
+
return files
|
402
|
+
|
403
|
+
# ===== SWR Hooks Generation =====
|
404
|
+
|
405
|
+
def _generate_swr_hooks(self) -> list[GeneratedFile]:
|
406
|
+
"""
|
407
|
+
Generate SWR hooks for all operations.
|
408
|
+
|
409
|
+
Creates:
|
410
|
+
- _utils/hooks/shop_products.ts
|
411
|
+
- _utils/hooks/shop_orders.ts
|
412
|
+
- _utils/hooks/index.ts
|
413
|
+
"""
|
414
|
+
files = []
|
415
|
+
module_names = []
|
416
|
+
|
417
|
+
# Group operations by tag
|
418
|
+
ops_by_tag = self.group_operations_by_tag()
|
419
|
+
|
420
|
+
# Generate hooks for each tag
|
421
|
+
for tag, operations in sorted(ops_by_tag.items()):
|
422
|
+
folder_name = self.tag_and_app_to_folder_name(tag, operations)
|
423
|
+
|
424
|
+
# Generate hooks file for this tag
|
425
|
+
files.append(self.hooks_gen.generate_tag_hooks_file(tag, operations))
|
426
|
+
module_names.append(folder_name)
|
427
|
+
|
428
|
+
# Generate index.ts
|
429
|
+
if module_names:
|
430
|
+
files.append(self.hooks_gen.generate_hooks_index_file(module_names))
|
431
|
+
|
432
|
+
return files
|