django-cfg 1.4.61__py3-none-any.whl → 1.4.63__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 django-cfg might be problematic. Click here for more details.

Files changed (179) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/services/otp_service.py +3 -14
  3. django_cfg/apps/centrifugo/__init__.py +57 -0
  4. django_cfg/apps/centrifugo/admin/__init__.py +13 -0
  5. django_cfg/apps/centrifugo/admin/centrifugo_log.py +249 -0
  6. django_cfg/apps/centrifugo/admin/config.py +82 -0
  7. django_cfg/apps/centrifugo/apps.py +31 -0
  8. django_cfg/apps/centrifugo/codegen/IMPLEMENTATION_SUMMARY.md +475 -0
  9. django_cfg/apps/centrifugo/codegen/README.md +242 -0
  10. django_cfg/apps/centrifugo/codegen/USAGE.md +616 -0
  11. django_cfg/apps/centrifugo/codegen/__init__.py +19 -0
  12. django_cfg/apps/centrifugo/codegen/discovery.py +246 -0
  13. django_cfg/apps/centrifugo/codegen/generators/go_thin/__init__.py +5 -0
  14. django_cfg/apps/centrifugo/codegen/generators/go_thin/generator.py +174 -0
  15. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/README.md.j2 +182 -0
  16. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/client.go.j2 +64 -0
  17. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/go.mod.j2 +10 -0
  18. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2 +300 -0
  19. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2.old +267 -0
  20. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/types.go.j2 +16 -0
  21. django_cfg/apps/centrifugo/codegen/generators/python_thin/__init__.py +7 -0
  22. django_cfg/apps/centrifugo/codegen/generators/python_thin/generator.py +241 -0
  23. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/README.md.j2 +128 -0
  24. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/__init__.py.j2 +22 -0
  25. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/client.py.j2 +73 -0
  26. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/models.py.j2 +19 -0
  27. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/requirements.txt.j2 +8 -0
  28. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/rpc_client.py.j2 +193 -0
  29. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/__init__.py +5 -0
  30. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/generator.py +124 -0
  31. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/README.md.j2 +38 -0
  32. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/client.ts.j2 +25 -0
  33. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/index.ts.j2 +12 -0
  34. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/package.json.j2 +13 -0
  35. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +137 -0
  36. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/tsconfig.json.j2 +14 -0
  37. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/types.ts.j2 +9 -0
  38. django_cfg/apps/centrifugo/codegen/utils/__init__.py +37 -0
  39. django_cfg/apps/centrifugo/codegen/utils/naming.py +155 -0
  40. django_cfg/apps/centrifugo/codegen/utils/type_converter.py +349 -0
  41. django_cfg/apps/centrifugo/decorators.py +137 -0
  42. django_cfg/apps/centrifugo/management/__init__.py +1 -0
  43. django_cfg/apps/centrifugo/management/commands/__init__.py +1 -0
  44. django_cfg/apps/centrifugo/management/commands/generate_centrifugo_clients.py +254 -0
  45. django_cfg/apps/centrifugo/managers/__init__.py +12 -0
  46. django_cfg/apps/centrifugo/managers/centrifugo_log.py +264 -0
  47. django_cfg/apps/centrifugo/migrations/0001_initial.py +164 -0
  48. django_cfg/apps/centrifugo/migrations/__init__.py +3 -0
  49. django_cfg/apps/centrifugo/models/__init__.py +11 -0
  50. django_cfg/apps/centrifugo/models/centrifugo_log.py +210 -0
  51. django_cfg/apps/centrifugo/registry.py +106 -0
  52. django_cfg/apps/centrifugo/router.py +125 -0
  53. django_cfg/apps/centrifugo/serializers/__init__.py +40 -0
  54. django_cfg/apps/centrifugo/serializers/admin_api.py +264 -0
  55. django_cfg/apps/centrifugo/serializers/channels.py +26 -0
  56. django_cfg/apps/centrifugo/serializers/health.py +17 -0
  57. django_cfg/apps/centrifugo/serializers/publishes.py +16 -0
  58. django_cfg/apps/centrifugo/serializers/stats.py +21 -0
  59. django_cfg/apps/centrifugo/services/__init__.py +12 -0
  60. django_cfg/apps/centrifugo/services/client/__init__.py +29 -0
  61. django_cfg/apps/centrifugo/services/client/client.py +577 -0
  62. django_cfg/apps/centrifugo/services/client/config.py +228 -0
  63. django_cfg/apps/centrifugo/services/client/exceptions.py +212 -0
  64. django_cfg/apps/centrifugo/services/config_helper.py +63 -0
  65. django_cfg/apps/centrifugo/services/dashboard_notifier.py +157 -0
  66. django_cfg/apps/centrifugo/services/logging.py +677 -0
  67. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +260 -0
  68. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +313 -0
  69. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +803 -0
  70. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +333 -0
  71. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +432 -0
  72. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +33 -0
  73. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +210 -0
  74. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +46 -0
  75. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +123 -0
  76. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +45 -0
  77. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +84 -0
  78. django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/stat_cards.html +23 -20
  79. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +91 -0
  80. django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/tab_navigation.html +15 -15
  81. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +415 -0
  82. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +61 -0
  83. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +58 -0
  84. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +48 -0
  85. django_cfg/apps/centrifugo/templatetags/__init__.py +1 -0
  86. django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +81 -0
  87. django_cfg/apps/centrifugo/urls.py +31 -0
  88. django_cfg/apps/{ipc → centrifugo}/urls_admin.py +4 -4
  89. django_cfg/apps/centrifugo/views/__init__.py +15 -0
  90. django_cfg/apps/centrifugo/views/admin_api.py +374 -0
  91. django_cfg/apps/centrifugo/views/dashboard.py +15 -0
  92. django_cfg/apps/centrifugo/views/monitoring.py +286 -0
  93. django_cfg/apps/centrifugo/views/testing_api.py +422 -0
  94. django_cfg/apps/support/utils/support_email_service.py +5 -18
  95. django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -2
  96. django_cfg/apps/urls.py +5 -5
  97. django_cfg/core/base/config_model.py +4 -44
  98. django_cfg/core/builders/apps_builder.py +2 -2
  99. django_cfg/core/generation/integration_generators/third_party.py +8 -8
  100. django_cfg/core/utils/__init__.py +5 -0
  101. django_cfg/core/utils/url_helpers.py +73 -0
  102. django_cfg/modules/base.py +7 -7
  103. django_cfg/modules/django_client/core/__init__.py +2 -1
  104. django_cfg/modules/django_client/core/config/config.py +8 -0
  105. django_cfg/modules/django_client/core/generator/__init__.py +42 -2
  106. django_cfg/modules/django_client/core/generator/go/__init__.py +14 -0
  107. django_cfg/modules/django_client/core/generator/go/client_generator.py +124 -0
  108. django_cfg/modules/django_client/core/generator/go/files_generator.py +133 -0
  109. django_cfg/modules/django_client/core/generator/go/generator.py +203 -0
  110. django_cfg/modules/django_client/core/generator/go/models_generator.py +304 -0
  111. django_cfg/modules/django_client/core/generator/go/naming.py +193 -0
  112. django_cfg/modules/django_client/core/generator/go/operations_generator.py +134 -0
  113. django_cfg/modules/django_client/core/generator/go/templates/Makefile.j2 +38 -0
  114. django_cfg/modules/django_client/core/generator/go/templates/README.md.j2 +55 -0
  115. django_cfg/modules/django_client/core/generator/go/templates/client.go.j2 +122 -0
  116. django_cfg/modules/django_client/core/generator/go/templates/enums.go.j2 +49 -0
  117. django_cfg/modules/django_client/core/generator/go/templates/errors.go.j2 +182 -0
  118. django_cfg/modules/django_client/core/generator/go/templates/go.mod.j2 +6 -0
  119. django_cfg/modules/django_client/core/generator/go/templates/main_client.go.j2 +60 -0
  120. django_cfg/modules/django_client/core/generator/go/templates/middleware.go.j2 +388 -0
  121. django_cfg/modules/django_client/core/generator/go/templates/models.go.j2 +28 -0
  122. django_cfg/modules/django_client/core/generator/go/templates/operations_client.go.j2 +142 -0
  123. django_cfg/modules/django_client/core/generator/go/templates/validation.go.j2 +217 -0
  124. django_cfg/modules/django_client/core/generator/go/type_mapper.py +380 -0
  125. django_cfg/modules/django_client/management/commands/generate_client.py +53 -3
  126. django_cfg/modules/django_client/system/generate_mjs_clients.py +3 -1
  127. django_cfg/modules/django_client/system/schema_parser.py +5 -1
  128. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +1 -0
  129. django_cfg/modules/django_twilio/sendgrid_service.py +7 -4
  130. django_cfg/modules/django_unfold/dashboard.py +25 -19
  131. django_cfg/pyproject.toml +1 -1
  132. django_cfg/registry/core.py +2 -0
  133. django_cfg/registry/modules.py +2 -2
  134. django_cfg/static/js/api/centrifugo/client.mjs +164 -0
  135. django_cfg/static/js/api/centrifugo/index.mjs +13 -0
  136. django_cfg/static/js/api/index.mjs +5 -5
  137. django_cfg/static/js/api/types.mjs +89 -26
  138. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/METADATA +1 -1
  139. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/RECORD +142 -68
  140. django_cfg/apps/ipc/README.md +0 -346
  141. django_cfg/apps/ipc/RPC_LOGGING.md +0 -321
  142. django_cfg/apps/ipc/TESTING.md +0 -539
  143. django_cfg/apps/ipc/__init__.py +0 -60
  144. django_cfg/apps/ipc/admin.py +0 -212
  145. django_cfg/apps/ipc/apps.py +0 -28
  146. django_cfg/apps/ipc/migrations/0001_initial.py +0 -137
  147. django_cfg/apps/ipc/migrations/__init__.py +0 -0
  148. django_cfg/apps/ipc/models.py +0 -221
  149. django_cfg/apps/ipc/serializers/__init__.py +0 -29
  150. django_cfg/apps/ipc/serializers/serializers.py +0 -343
  151. django_cfg/apps/ipc/services/__init__.py +0 -7
  152. django_cfg/apps/ipc/services/client/__init__.py +0 -23
  153. django_cfg/apps/ipc/services/client/client.py +0 -621
  154. django_cfg/apps/ipc/services/client/config.py +0 -214
  155. django_cfg/apps/ipc/services/client/exceptions.py +0 -201
  156. django_cfg/apps/ipc/services/logging.py +0 -239
  157. django_cfg/apps/ipc/services/monitor.py +0 -466
  158. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/main.mjs +0 -269
  159. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/overview.mjs +0 -259
  160. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/testing.mjs +0 -375
  161. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard.mjs.old +0 -441
  162. django_cfg/apps/ipc/templates/django_cfg_ipc/components/methods_content.html +0 -22
  163. django_cfg/apps/ipc/templates/django_cfg_ipc/components/notifications_content.html +0 -9
  164. django_cfg/apps/ipc/templates/django_cfg_ipc/components/overview_content.html +0 -9
  165. django_cfg/apps/ipc/templates/django_cfg_ipc/components/requests_content.html +0 -23
  166. django_cfg/apps/ipc/templates/django_cfg_ipc/components/system_status.html +0 -47
  167. django_cfg/apps/ipc/templates/django_cfg_ipc/components/testing_tools.html +0 -184
  168. django_cfg/apps/ipc/templates/django_cfg_ipc/layout/base.html +0 -71
  169. django_cfg/apps/ipc/templates/django_cfg_ipc/pages/dashboard.html +0 -56
  170. django_cfg/apps/ipc/urls.py +0 -23
  171. django_cfg/apps/ipc/views/__init__.py +0 -13
  172. django_cfg/apps/ipc/views/dashboard.py +0 -15
  173. django_cfg/apps/ipc/views/monitoring.py +0 -251
  174. django_cfg/apps/ipc/views/testing.py +0 -285
  175. django_cfg/static/js/api/ipc/client.mjs +0 -114
  176. django_cfg/static/js/api/ipc/index.mjs +0 -13
  177. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/WHEEL +0 -0
  178. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/entry_points.txt +0 -0
  179. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,124 @@
1
+ """
2
+ TypeScript thin wrapper client generator.
3
+ """
4
+
5
+ import logging
6
+ from pathlib import Path
7
+ from typing import List, Type
8
+ from pydantic import BaseModel
9
+ from jinja2 import Environment, FileSystemLoader, select_autoescape
10
+
11
+ from ...discovery import RPCMethodInfo
12
+ from ...utils import to_typescript_method_name, pydantic_to_typescript
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class TypeScriptThinGenerator:
18
+ """Generator for TypeScript thin wrapper clients."""
19
+
20
+ def __init__(
21
+ self,
22
+ methods: List[RPCMethodInfo],
23
+ models: List[Type[BaseModel]],
24
+ output_dir: Path,
25
+ ):
26
+ self.methods = methods
27
+ self.models = models
28
+ self.output_dir = Path(output_dir)
29
+
30
+ templates_dir = Path(__file__).parent / "templates"
31
+ self.jinja_env = Environment(
32
+ loader=FileSystemLoader(str(templates_dir)),
33
+ autoescape=select_autoescape(),
34
+ trim_blocks=True,
35
+ lstrip_blocks=True,
36
+ )
37
+
38
+ def generate(self):
39
+ """Generate all TypeScript files."""
40
+ self.output_dir.mkdir(parents=True, exist_ok=True)
41
+
42
+ self._generate_types()
43
+ self._generate_rpc_client()
44
+ self._generate_client()
45
+ self._generate_index()
46
+ self._generate_package_json()
47
+ self._generate_tsconfig()
48
+ self._generate_readme()
49
+
50
+ logger.info(f"✅ Generated TypeScript client in {self.output_dir}")
51
+
52
+ def _generate_types(self):
53
+ """Generate types.ts file."""
54
+ template = self.jinja_env.get_template("types.ts.j2")
55
+
56
+ types_data = []
57
+ for model in self.models:
58
+ ts_interface = pydantic_to_typescript(model)
59
+ types_data.append({
60
+ 'name': model.__name__,
61
+ 'code': ts_interface,
62
+ })
63
+
64
+ content = template.render(types=types_data)
65
+ (self.output_dir / "types.ts").write_text(content)
66
+
67
+ def _generate_rpc_client(self):
68
+ """Generate rpc-client.ts base class."""
69
+ template = self.jinja_env.get_template("rpc-client.ts.j2")
70
+ content = template.render()
71
+ (self.output_dir / "rpc-client.ts").write_text(content)
72
+
73
+ def _generate_client(self):
74
+ """Generate client.ts thin wrapper."""
75
+ template = self.jinja_env.get_template("client.ts.j2")
76
+
77
+ methods_data = []
78
+ for method in self.methods:
79
+ param_type = method.param_type.__name__ if method.param_type else "any"
80
+ return_type = method.return_type.__name__ if method.return_type else "any"
81
+ method_name_ts = to_typescript_method_name(method.name)
82
+
83
+ methods_data.append({
84
+ 'name': method.name,
85
+ 'name_ts': method_name_ts,
86
+ 'param_type': param_type,
87
+ 'return_type': return_type,
88
+ 'docstring': method.docstring or f"Call {method.name} RPC method",
89
+ })
90
+
91
+ model_names = [m.__name__ for m in self.models]
92
+
93
+ content = template.render(methods=methods_data, models=model_names)
94
+ (self.output_dir / "client.ts").write_text(content)
95
+
96
+ def _generate_index(self):
97
+ """Generate index.ts file."""
98
+ template = self.jinja_env.get_template("index.ts.j2")
99
+ model_names = [m.__name__ for m in self.models]
100
+ content = template.render(models=model_names)
101
+ (self.output_dir / "index.ts").write_text(content)
102
+
103
+ def _generate_package_json(self):
104
+ """Generate package.json file."""
105
+ template = self.jinja_env.get_template("package.json.j2")
106
+ content = template.render()
107
+ (self.output_dir / "package.json").write_text(content)
108
+
109
+ def _generate_tsconfig(self):
110
+ """Generate tsconfig.json file."""
111
+ template = self.jinja_env.get_template("tsconfig.json.j2")
112
+ content = template.render()
113
+ (self.output_dir / "tsconfig.json").write_text(content)
114
+
115
+ def _generate_readme(self):
116
+ """Generate README.md file."""
117
+ template = self.jinja_env.get_template("README.md.j2")
118
+ methods_data = [{'name': m.name, 'name_ts': to_typescript_method_name(m.name)} for m in self.methods[:3]]
119
+ model_names = [m.__name__ for m in self.models]
120
+ content = template.render(methods=methods_data, models=model_names)
121
+ (self.output_dir / "README.md").write_text(content)
122
+
123
+
124
+ __all__ = ['TypeScriptThinGenerator']
@@ -0,0 +1,38 @@
1
+ # Generated TypeScript Client
2
+
3
+ Auto-generated type-safe TypeScript client for Centrifugo WebSocket RPC.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install centrifuge
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { CentrifugoRPCClient, APIClient } from './client';
15
+
16
+ async function main() {
17
+ const rpc = new CentrifugoRPCClient(
18
+ 'ws://localhost:8000/connection/websocket',
19
+ 'your-jwt-token',
20
+ 'user-123'
21
+ );
22
+
23
+ await rpc.connect();
24
+
25
+ const api = new APIClient(rpc);
26
+
27
+ {% if methods %}
28
+ {% for method in methods %}
29
+ const result = await api.{{ method.name_ts }}(params);
30
+ {% endfor %}
31
+ {% endif %}
32
+
33
+ await rpc.disconnect();
34
+ }
35
+ ```
36
+
37
+ ---
38
+ **Generated by django_cfg.apps.centrifugo.codegen**
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Generated API Client
3
+ * Auto-generated thin wrapper - DO NOT EDIT
4
+ */
5
+
6
+ import { CentrifugoRPCClient } from './rpc-client';
7
+ {% if models %}
8
+ import type {
9
+ {% for model_name in models | unique | sort %} {{ model_name }},
10
+ {% endfor %}} from './types';
11
+ {% endif %}
12
+
13
+ export class APIClient {
14
+ constructor(private rpc: CentrifugoRPCClient) {}
15
+
16
+ {% for method in methods %}
17
+ /**
18
+ * {{ method.docstring.split('\n')[0] if method.docstring else 'No description' }}
19
+ */
20
+ async {{ method.name_ts }}(params: {{ method.param_type }}): Promise<{{ method.return_type }}> {
21
+ return this.rpc.call<{{ method.return_type }}>('{{ method.name }}', params);
22
+ }
23
+
24
+ {% endfor %}
25
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Generated TypeScript Client Package
3
+ * Auto-generated - DO NOT EDIT
4
+ */
5
+
6
+ export { CentrifugoRPCClient } from './rpc-client';
7
+ export { APIClient } from './client';
8
+ {% if models %}
9
+ export type {
10
+ {% for model_name in models | unique | sort %} {{ model_name }},
11
+ {% endfor %}} from './types';
12
+ {% endif %}
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "centrifugo-client",
3
+ "version": "1.0.0",
4
+ "description": "Generated Centrifugo WebSocket RPC client",
5
+ "main": "index.ts",
6
+ "types": "index.ts",
7
+ "dependencies": {
8
+ "centrifuge": "^5.0.0"
9
+ },
10
+ "devDependencies": {
11
+ "typescript": "^5.0.0"
12
+ }
13
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Base Centrifugo RPC Client
3
+ *
4
+ * Handles WebSocket connection and RPC call correlation.
5
+ * Auto-generated - DO NOT EDIT
6
+ */
7
+
8
+ import { Centrifuge } from 'centrifuge';
9
+
10
+ export class CentrifugoRPCClient {
11
+ private centrifuge: Centrifuge;
12
+ private subscription: any;
13
+ private pendingRequests: Map<string, { resolve: Function; reject: Function }> = new Map();
14
+ private readonly replyChannel: string;
15
+ private readonly timeout: number;
16
+
17
+ constructor(
18
+ url: string,
19
+ token: string,
20
+ userId: string,
21
+ timeout: number = 30000
22
+ ) {
23
+ this.replyChannel = `user#${userId}`;
24
+ this.timeout = timeout;
25
+
26
+ this.centrifuge = new Centrifuge(url, {
27
+ token,
28
+ });
29
+
30
+ this.centrifuge.on('connected', (ctx) => {
31
+ console.log('✅ Connected to Centrifugo');
32
+ });
33
+
34
+ this.centrifuge.on('disconnected', (ctx) => {
35
+ console.warn('Disconnected:', ctx);
36
+ // Reject all pending requests
37
+ this.pendingRequests.forEach(({ reject }) => {
38
+ reject(new Error('Disconnected from Centrifugo'));
39
+ });
40
+ this.pendingRequests.clear();
41
+ });
42
+ }
43
+
44
+ async connect(): Promise<void> {
45
+ return new Promise((resolve, reject) => {
46
+ this.centrifuge.connect();
47
+
48
+ // Subscribe to reply channel
49
+ this.subscription = this.centrifuge.newSubscription(this.replyChannel);
50
+
51
+ this.subscription.on('publication', (ctx: any) => {
52
+ this.handleResponse(ctx.data);
53
+ });
54
+
55
+ this.subscription.on('subscribed', () => {
56
+ resolve();
57
+ });
58
+
59
+ this.subscription.on('error', (ctx: any) => {
60
+ reject(new Error(ctx.error?.message || 'Subscription error'));
61
+ });
62
+
63
+ this.subscription.subscribe();
64
+ });
65
+ }
66
+
67
+ async disconnect(): Promise<void> {
68
+ if (this.subscription) {
69
+ this.subscription.unsubscribe();
70
+ }
71
+ this.centrifuge.disconnect();
72
+ }
73
+
74
+ async call<T = any>(method: string, params: any): Promise<T> {
75
+ const correlationId = this.generateCorrelationId();
76
+
77
+ const message = {
78
+ method,
79
+ params,
80
+ correlation_id: correlationId,
81
+ reply_to: this.replyChannel,
82
+ };
83
+
84
+ // Create promise for response
85
+ const promise = new Promise<T>((resolve, reject) => {
86
+ const timeoutId = setTimeout(() => {
87
+ this.pendingRequests.delete(correlationId);
88
+ reject(new Error(`RPC timeout: ${method}`));
89
+ }, this.timeout);
90
+
91
+ this.pendingRequests.set(correlationId, {
92
+ resolve: (result: T) => {
93
+ clearTimeout(timeoutId);
94
+ resolve(result);
95
+ },
96
+ reject: (error: Error) => {
97
+ clearTimeout(timeoutId);
98
+ reject(error);
99
+ },
100
+ });
101
+ });
102
+
103
+ // Publish request
104
+ await this.centrifuge.publish('rpc.requests', message);
105
+
106
+ console.log(`📤 RPC call: ${method} (${correlationId})`);
107
+
108
+ return promise;
109
+ }
110
+
111
+ private handleResponse(data: any): void {
112
+ const correlationId = data.correlation_id;
113
+ if (!correlationId) {
114
+ console.warn('Received response without correlation_id');
115
+ return;
116
+ }
117
+
118
+ const pending = this.pendingRequests.get(correlationId);
119
+ if (!pending) {
120
+ console.warn(`Received response for unknown correlation_id: ${correlationId}`);
121
+ return;
122
+ }
123
+
124
+ this.pendingRequests.delete(correlationId);
125
+
126
+ if (data.error) {
127
+ pending.reject(new Error(data.error.message || 'RPC error'));
128
+ } else {
129
+ console.log(`📥 RPC response: ${correlationId}`);
130
+ pending.resolve(data.result);
131
+ }
132
+ }
133
+
134
+ private generateCorrelationId(): string {
135
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
136
+ }
137
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "declaration": true,
11
+ "declarationMap": true,
12
+ "sourceMap": true
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generated TypeScript Types
3
+ * Auto-generated - DO NOT EDIT
4
+ */
5
+
6
+ {% for type in types %}
7
+ {{ type.code }}
8
+
9
+ {% endfor %}
@@ -0,0 +1,37 @@
1
+ """
2
+ Utilities for code generation.
3
+ """
4
+
5
+ from .naming import (
6
+ sanitize_method_name,
7
+ to_camel_case,
8
+ to_pascal_case,
9
+ to_python_method_name,
10
+ to_typescript_method_name,
11
+ to_go_method_name,
12
+ )
13
+ from .type_converter import (
14
+ pydantic_to_typescript,
15
+ pydantic_to_python,
16
+ pydantic_to_go,
17
+ generate_typescript_types,
18
+ generate_python_types,
19
+ generate_go_types,
20
+ )
21
+
22
+ __all__ = [
23
+ # Naming
24
+ "sanitize_method_name",
25
+ "to_camel_case",
26
+ "to_pascal_case",
27
+ "to_python_method_name",
28
+ "to_typescript_method_name",
29
+ "to_go_method_name",
30
+ # Type conversion
31
+ "pydantic_to_typescript",
32
+ "pydantic_to_python",
33
+ "pydantic_to_go",
34
+ "generate_typescript_types",
35
+ "generate_python_types",
36
+ "generate_go_types",
37
+ ]
@@ -0,0 +1,155 @@
1
+ """
2
+ Naming utilities for code generation.
3
+
4
+ Provides functions to convert RPC method names to valid identifiers
5
+ in different programming languages.
6
+ """
7
+
8
+
9
+ def sanitize_method_name(name: str) -> str:
10
+ """
11
+ Sanitize method name by replacing dots with underscores.
12
+
13
+ This handles namespaced method names (e.g., "workspace.file_changed")
14
+ and converts them to valid identifiers by replacing dots with underscores.
15
+
16
+ Args:
17
+ name: Original method name (may contain dots)
18
+
19
+ Returns:
20
+ Sanitized name with underscores instead of dots
21
+
22
+ Examples:
23
+ >>> sanitize_method_name("workspace.file_changed")
24
+ 'workspace_file_changed'
25
+ >>> sanitize_method_name("send_email")
26
+ 'send_email'
27
+ """
28
+ return name.replace('.', '_')
29
+
30
+
31
+ def to_camel_case(snake_str: str) -> str:
32
+ """
33
+ Convert snake_case to camelCase.
34
+
35
+ Args:
36
+ snake_str: String in snake_case format
37
+
38
+ Returns:
39
+ String in camelCase format
40
+
41
+ Examples:
42
+ >>> to_camel_case("workspace_file_changed")
43
+ 'workspaceFileChanged'
44
+ >>> to_camel_case("send_email")
45
+ 'sendEmail'
46
+ >>> to_camel_case("user_update_profile")
47
+ 'userUpdateProfile'
48
+ """
49
+ components = snake_str.split('_')
50
+ return components[0] + ''.join(x.title() for x in components[1:])
51
+
52
+
53
+ def to_typescript_method_name(rpc_name: str) -> str:
54
+ """
55
+ Convert RPC method name to valid TypeScript method name.
56
+
57
+ Handles namespaced methods by replacing dots with underscores,
58
+ then converts to camelCase.
59
+
60
+ Args:
61
+ rpc_name: Original RPC method name (may contain dots)
62
+
63
+ Returns:
64
+ Valid TypeScript method name in camelCase
65
+
66
+ Examples:
67
+ >>> to_typescript_method_name("workspace.file_changed")
68
+ 'workspaceFileChanged'
69
+ >>> to_typescript_method_name("session.message")
70
+ 'sessionMessage'
71
+ >>> to_typescript_method_name("send_email")
72
+ 'sendEmail'
73
+ """
74
+ sanitized = sanitize_method_name(rpc_name)
75
+ return to_camel_case(sanitized)
76
+
77
+
78
+ def to_python_method_name(rpc_name: str) -> str:
79
+ """
80
+ Convert RPC method name to valid Python method name.
81
+
82
+ Handles namespaced methods by replacing dots with underscores.
83
+ Python uses snake_case, so we just sanitize the name.
84
+
85
+ Args:
86
+ rpc_name: Original RPC method name (may contain dots)
87
+
88
+ Returns:
89
+ Valid Python method name in snake_case
90
+
91
+ Examples:
92
+ >>> to_python_method_name("workspace.file_changed")
93
+ 'workspace_file_changed'
94
+ >>> to_python_method_name("session.message")
95
+ 'session_message'
96
+ >>> to_python_method_name("send_email")
97
+ 'send_email'
98
+ """
99
+ return sanitize_method_name(rpc_name)
100
+
101
+
102
+ def to_pascal_case(snake_str: str) -> str:
103
+ """
104
+ Convert snake_case to PascalCase.
105
+
106
+ Args:
107
+ snake_str: String in snake_case format
108
+
109
+ Returns:
110
+ String in PascalCase format
111
+
112
+ Examples:
113
+ >>> to_pascal_case("workspace_file_changed")
114
+ 'WorkspaceFileChanged'
115
+ >>> to_pascal_case("send_email")
116
+ 'SendEmail'
117
+ >>> to_pascal_case("user_update_profile")
118
+ 'UserUpdateProfile'
119
+ """
120
+ return ''.join(word.capitalize() for word in snake_str.split('_'))
121
+
122
+
123
+ def to_go_method_name(rpc_name: str) -> str:
124
+ """
125
+ Convert RPC method name to valid Go method name.
126
+
127
+ Handles namespaced methods by replacing dots with underscores,
128
+ then converts to PascalCase (Go exported methods must start with capital).
129
+
130
+ Args:
131
+ rpc_name: Original RPC method name (may contain dots)
132
+
133
+ Returns:
134
+ Valid Go method name in PascalCase
135
+
136
+ Examples:
137
+ >>> to_go_method_name("workspace.file_changed")
138
+ 'WorkspaceFileChanged'
139
+ >>> to_go_method_name("session.message")
140
+ 'SessionMessage'
141
+ >>> to_go_method_name("send_email")
142
+ 'SendEmail'
143
+ """
144
+ sanitized = sanitize_method_name(rpc_name)
145
+ return to_pascal_case(sanitized)
146
+
147
+
148
+ __all__ = [
149
+ 'sanitize_method_name',
150
+ 'to_camel_case',
151
+ 'to_pascal_case',
152
+ 'to_typescript_method_name',
153
+ 'to_python_method_name',
154
+ 'to_go_method_name',
155
+ ]