fastapi-voyager 0.16.0a3__tar.gz → 0.17.0__tar.gz

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.
Files changed (116) hide show
  1. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/PKG-INFO +32 -20
  2. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/README.md +31 -19
  3. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/docs/changelog.md +17 -3
  4. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/setup-litestar.sh +2 -2
  5. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/adapters/common.py +40 -1
  6. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/adapters/litestar_adapter.py +1 -1
  7. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/er_diagram.py +17 -8
  8. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/render.py +7 -2
  9. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/render_style.py +13 -0
  10. fastapi_voyager-0.17.0/src/fastapi_voyager/templates/html/schema_header.j2 +1 -0
  11. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/type.py +1 -0
  12. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/type_helper.py +20 -0
  13. fastapi_voyager-0.17.0/src/fastapi_voyager/version.py +2 -0
  14. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/voyager.py +28 -11
  15. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/index.html +13 -6
  16. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/store.js +5 -0
  17. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/vue-main.js +17 -3
  18. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/litestar/demo.py +4 -1
  19. fastapi_voyager-0.17.0/tests/litestar/embedding.py +44 -0
  20. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_embedding_litestar.py +3 -3
  21. fastapi_voyager-0.16.0a3/src/fastapi_voyager/templates/html/schema_header.j2 +0 -2
  22. fastapi_voyager-0.16.0a3/src/fastapi_voyager/version.py +0 -2
  23. fastapi_voyager-0.16.0a3/tests/litestar/embedding.py +0 -53
  24. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.githooks/README.md +0 -0
  25. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.githooks/pre-commit +0 -0
  26. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  27. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  28. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.github/workflows/publish.yml +0 -0
  29. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.gitignore +0 -0
  30. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.prettierignore +0 -0
  31. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.prettierrc +0 -0
  32. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/.python-version +0 -0
  33. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/CONTRIBUTING.md +0 -0
  34. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/LICENSE +0 -0
  35. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/docs/claude/0_REFACTORING_RENDER_NOTES.md +0 -0
  36. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/docs/idea.md +0 -0
  37. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/package-lock.json +0 -0
  38. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/pyproject.toml +0 -0
  39. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/release.md +0 -0
  40. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/setup-django-ninja.sh +0 -0
  41. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/setup-fastapi.sh +0 -0
  42. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/setup-hooks.sh +0 -0
  43. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/__init__.py +0 -0
  44. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/adapters/__init__.py +0 -0
  45. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/adapters/base.py +0 -0
  46. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/adapters/django_ninja_adapter.py +0 -0
  47. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/adapters/fastapi_adapter.py +0 -0
  48. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/cli.py +0 -0
  49. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/filter.py +0 -0
  50. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/introspectors/__init__.py +0 -0
  51. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/introspectors/base.py +0 -0
  52. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/introspectors/detector.py +0 -0
  53. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/introspectors/django_ninja.py +0 -0
  54. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/introspectors/fastapi.py +0 -0
  55. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/introspectors/litestar.py +0 -0
  56. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/module.py +0 -0
  57. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/pydantic_resolve_util.py +0 -0
  58. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/server.py +0 -0
  59. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/cluster.j2 +0 -0
  60. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/cluster_container.j2 +0 -0
  61. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/digraph.j2 +0 -0
  62. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/er_diagram.j2 +0 -0
  63. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/link.j2 +0 -0
  64. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/route_node.j2 +0 -0
  65. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/schema_node.j2 +0 -0
  66. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/dot/tag_node.j2 +0 -0
  67. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/html/colored_text.j2 +0 -0
  68. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/html/pydantic_meta.j2 +0 -0
  69. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/html/schema_field_row.j2 +0 -0
  70. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/templates/html/schema_table.j2 +0 -0
  71. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/component/demo.js +0 -0
  72. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/component/render-graph.js +0 -0
  73. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
  74. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/component/schema-code-display.js +0 -0
  75. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/graph-ui.js +0 -0
  76. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
  77. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/graphviz.svg.js +0 -0
  78. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
  79. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
  80. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
  81. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
  82. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
  83. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/icon/favicon.ico +0 -0
  84. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/icon/site.webmanifest +0 -0
  85. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/quasar.min.css +0 -0
  86. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/src/fastapi_voyager/web/quasar.min.js +0 -0
  87. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/README.md +0 -0
  88. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/__init__.py +0 -0
  89. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/django_ninja/__init__.py +0 -0
  90. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/django_ninja/demo.py +0 -0
  91. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/django_ninja/embedding.py +0 -0
  92. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/django_ninja/settings.py +0 -0
  93. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/django_ninja/urls.py +0 -0
  94. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/embedding_test_utils.py +0 -0
  95. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/fastapi/__init__.py +0 -0
  96. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/fastapi/demo.py +0 -0
  97. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/fastapi/demo_anno.py +0 -0
  98. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/fastapi/embedding.py +0 -0
  99. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/litestar/__init__.py +0 -0
  100. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/service/__init__.py +0 -0
  101. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/service/schema/__init__.py +0 -0
  102. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/service/schema/base_entity.py +0 -0
  103. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/service/schema/extra.py +0 -0
  104. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/service/schema/schema.py +0 -0
  105. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_adapter_interface.py +0 -0
  106. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_analysis.py +0 -0
  107. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_embedding_django_ninja.py +0 -0
  108. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_embedding_fastapi.py +0 -0
  109. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_filter.py +0 -0
  110. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_generic.py +0 -0
  111. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_import.py +0 -0
  112. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_module.py +0 -0
  113. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_resolve_util_impl.py +0 -0
  114. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/tests/test_type_helper.py +0 -0
  115. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/uv.lock +0 -0
  116. {fastapi_voyager-0.16.0a3 → fastapi_voyager-0.17.0}/voyager.jpg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.16.0a3
3
+ Version: 0.17.0
4
4
  Summary: Visualize FastAPI application's routing tree and dependencies
5
5
  Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
6
6
  Project-URL: Source, https://github.com/allmonday/fastapi-voyager
@@ -205,34 +205,44 @@ uvicorn your_app:application --reload
205
205
 
206
206
  ### Litestar
207
207
 
208
+ Litestar doesn't support mounting to an existing app like FastAPI. The recommended pattern is to export `ROUTE_HANDLERS` from your main app:
209
+
208
210
  ```python
209
- from litestar import Litestar, get
210
- from fastapi_voyager import create_voyager
211
+ # In your main app file (e.g., app.py)
212
+ from litestar import Litestar, Controller
211
213
 
212
- # Create Litestar app
213
- app = Litestar()
214
+ class MyController(Controller):
215
+ # ... your routes ...
214
216
 
215
- @get("/hello")
216
- def hello() -> dict:
217
- return {"message": "Hello World"}
217
+ ROUTE_HANDLERS = [MyController] # Export for extension
218
+ app = Litestar(route_handlers=ROUTE_HANDLERS)
219
+ ```
218
220
 
219
- # Create voyager app (returns a Litestar app)
220
- voyager_app = create_voyager(app)
221
+ Then create voyager by reusing `ROUTE_HANDLERS`:
221
222
 
222
- # Create ASGI application that routes between main app and voyager
223
- async def asgi_app(scope, receive, send):
224
- if scope["type"] == "http" and scope["path"].startswith("/voyager"):
225
- # Remove /voyager prefix for voyager app
226
- new_scope = dict(scope)
227
- new_scope["path"] = scope["path"][8:] or "/"
228
- await voyager_app(new_scope, receive, send)
229
- else:
230
- await app(scope, receive, send)
223
+ ```python
224
+ # In your voyager embedding file
225
+ from typing import Any, Awaitable, Callable
226
+ from litestar import Litestar, asgi
227
+ from fastapi_voyager import create_voyager
228
+ from your_app import ROUTE_HANDLERS, app as your_app
229
+
230
+ voyager_app = create_voyager(your_app)
231
+
232
+ @asgi("/voyager", is_mount=True, copy_scope=True)
233
+ async def voyager_mount(
234
+ scope: dict[str, Any],
235
+ receive: Callable[[], Awaitable[dict[str, Any]]],
236
+ send: Callable[[dict[str, Any]], Awaitable[None]]
237
+ ) -> None:
238
+ await voyager_app(scope, receive, send)
239
+
240
+ app = Litestar(route_handlers=ROUTE_HANDLERS + [voyager_mount])
231
241
  ```
232
242
 
233
243
  Start with:
234
244
  ```bash
235
- uvicorn your_app:asgi_app --reload
245
+ uvicorn your_app:app --reload
236
246
  # Visit http://localhost:8000/voyager
237
247
  ```
238
248
 
@@ -335,6 +345,8 @@ voyager -m tests.demo --server --port=8002
335
345
  voyager -m tests.demo --server --app my_app
336
346
  ```
337
347
 
348
+ > **Note**: Server mode does not support ER diagram or pydantic-resolve metadata configuration. Use `create_voyager()` in your code with `er_diagram` and `enable_pydantic_resolve_meta` parameters to enable these features.
349
+
338
350
  ### Generate DOT File
339
351
 
340
352
  ```bash
@@ -154,34 +154,44 @@ uvicorn your_app:application --reload
154
154
 
155
155
  ### Litestar
156
156
 
157
+ Litestar doesn't support mounting to an existing app like FastAPI. The recommended pattern is to export `ROUTE_HANDLERS` from your main app:
158
+
157
159
  ```python
158
- from litestar import Litestar, get
159
- from fastapi_voyager import create_voyager
160
+ # In your main app file (e.g., app.py)
161
+ from litestar import Litestar, Controller
160
162
 
161
- # Create Litestar app
162
- app = Litestar()
163
+ class MyController(Controller):
164
+ # ... your routes ...
163
165
 
164
- @get("/hello")
165
- def hello() -> dict:
166
- return {"message": "Hello World"}
166
+ ROUTE_HANDLERS = [MyController] # Export for extension
167
+ app = Litestar(route_handlers=ROUTE_HANDLERS)
168
+ ```
167
169
 
168
- # Create voyager app (returns a Litestar app)
169
- voyager_app = create_voyager(app)
170
+ Then create voyager by reusing `ROUTE_HANDLERS`:
170
171
 
171
- # Create ASGI application that routes between main app and voyager
172
- async def asgi_app(scope, receive, send):
173
- if scope["type"] == "http" and scope["path"].startswith("/voyager"):
174
- # Remove /voyager prefix for voyager app
175
- new_scope = dict(scope)
176
- new_scope["path"] = scope["path"][8:] or "/"
177
- await voyager_app(new_scope, receive, send)
178
- else:
179
- await app(scope, receive, send)
172
+ ```python
173
+ # In your voyager embedding file
174
+ from typing import Any, Awaitable, Callable
175
+ from litestar import Litestar, asgi
176
+ from fastapi_voyager import create_voyager
177
+ from your_app import ROUTE_HANDLERS, app as your_app
178
+
179
+ voyager_app = create_voyager(your_app)
180
+
181
+ @asgi("/voyager", is_mount=True, copy_scope=True)
182
+ async def voyager_mount(
183
+ scope: dict[str, Any],
184
+ receive: Callable[[], Awaitable[dict[str, Any]]],
185
+ send: Callable[[dict[str, Any]], Awaitable[None]]
186
+ ) -> None:
187
+ await voyager_app(scope, receive, send)
188
+
189
+ app = Litestar(route_handlers=ROUTE_HANDLERS + [voyager_mount])
180
190
  ```
181
191
 
182
192
  Start with:
183
193
  ```bash
184
- uvicorn your_app:asgi_app --reload
194
+ uvicorn your_app:app --reload
185
195
  # Visit http://localhost:8000/voyager
186
196
  ```
187
197
 
@@ -284,6 +294,8 @@ voyager -m tests.demo --server --port=8002
284
294
  voyager -m tests.demo --server --app my_app
285
295
  ```
286
296
 
297
+ > **Note**: Server mode does not support ER diagram or pydantic-resolve metadata configuration. Use `create_voyager()` in your code with `er_diagram` and `enable_pydantic_resolve_meta` parameters to enable these features.
298
+
287
299
  ### Generate DOT File
288
300
 
289
301
  ```bash
@@ -183,14 +183,28 @@
183
183
  - [x] fix import error
184
184
  - 0.16.0alpha-3
185
185
  - [x] fix voyager cli, add web parameter
186
+ - 0.16.1
187
+ - [x] improve litestar support
186
188
 
187
189
  ## 0.17, enhance er diagram
188
190
  - 0.17.0
189
- - [ ] show loader name
190
- - [ ] show relationship list when double click entity in er diagram
191
- - [ ] highlight entity in use case
191
+ - [x] 1.different theme color for frameworks
192
+ - fastapi, keep current
193
+ - django-ninja, #4cae4f
194
+ - litestar, rgb(237, 182, 65)
195
+ - [x] 2.highight entity classes
196
+ - enable if er diagram is enabled
197
+ - entities in er diagram should be labeled as "Entity" after the title, and title should be bold
198
+ - [x] 3.click esc to cancel search
199
+ - 0.17.1
200
+ - [ ] 1.show loader name
201
+ - [ ] 2.show relationship list when double click entity in er diagram
202
+ - [ ] 3.highlight entity in use case
203
+ - [ ] 4.change cli -m param, use `path.to.module:app` instead.
192
204
 
193
205
  ## 1.0, release
194
206
  - [ ] add tests
195
207
 
208
+ ## 1.1 future
209
+
196
210
 
@@ -39,10 +39,10 @@ echo ""
39
39
 
40
40
  # Start Litestar server
41
41
  echo "🌟 Starting Litestar Voyager server..."
42
- echo " App: tests.litestar.embedding:asgi_app"
42
+ echo " App: tests.litestar.embedding:app"
43
43
  echo " URL: http://127.0.0.1:8000"
44
44
  echo ""
45
45
  echo "Press Ctrl+C to stop the server"
46
46
  echo ""
47
47
 
48
- uv run uvicorn tests.litestar.embedding:asgi_app --reload --host 127.0.0.1 --port 8000
48
+ uv run uvicorn tests.litestar.embedding:app --reload --host 127.0.0.1 --port 8000
@@ -9,7 +9,9 @@ from typing import Any
9
9
  from pydantic_resolve import ErDiagram
10
10
 
11
11
  from fastapi_voyager.er_diagram import VoyagerErDiagram
12
+ from fastapi_voyager.introspectors.detector import FrameworkType, detect_framework
12
13
  from fastapi_voyager.render import Renderer
14
+ from fastapi_voyager.render_style import RenderConfig
13
15
  from fastapi_voyager.type import CoreData, SchemaNode, Tag
14
16
  from fastapi_voyager.type_helper import get_source, get_vscode_link
15
17
  from fastapi_voyager.version import __version__
@@ -23,6 +25,7 @@ STATIC_FILES_PATH = "/fastapi-voyager-static"
23
25
  GA_PLACEHOLDER = "<!-- GA_SNIPPET -->"
24
26
  VERSION_PLACEHOLDER = "<!-- VERSION_PLACEHOLDER -->"
25
27
  STATIC_PATH_PLACEHOLDER = "<!-- STATIC_PATH -->"
28
+ THEME_COLOR_PLACEHOLDER = "<!-- THEME_COLOR -->"
26
29
 
27
30
 
28
31
  def build_ga_snippet(ga_id: str | None) -> str:
@@ -70,13 +73,45 @@ class VoyagerContext:
70
73
  self.ga_id = ga_id
71
74
  self.er_diagram = er_diagram
72
75
  self.enable_pydantic_resolve_meta = enable_pydantic_resolve_meta
73
- self.framework_name = framework_name or "API"
76
+
77
+ # Detect and store framework type (single source of truth)
78
+ self._framework_type = detect_framework(target_app)
79
+ # Display name for frontend (backward compatible)
80
+ self.framework_name = framework_name or self._get_display_name()
81
+
82
+ def _get_display_name(self) -> str:
83
+ """Get display name for the detected framework type."""
84
+ display_names = {
85
+ FrameworkType.FASTAPI: "FastAPI",
86
+ FrameworkType.DJANGO_NINJA: "Django Ninja",
87
+ FrameworkType.LITESTAR: "Litestar",
88
+ }
89
+ return display_names.get(self._framework_type, "API")
90
+
91
+ def _get_theme_color(self) -> str:
92
+ """Get theme color for the current framework."""
93
+ config = RenderConfig()
94
+ return config.colors.get_framework_color(self._framework_type)
95
+
96
+ def _get_entity_class_names(self) -> set[str] | None:
97
+ """Extract entity class names from er_diagram."""
98
+ if not self.er_diagram:
99
+ return None
100
+
101
+ from fastapi_voyager.type_helper import full_class_name
102
+
103
+ return {
104
+ full_class_name(entity.kls)
105
+ for entity in self.er_diagram.configs
106
+ }
74
107
 
75
108
  def get_voyager(self, **kwargs) -> Voyager:
76
109
  """Create a Voyager instance with common configuration."""
77
110
  config = {
78
111
  "module_color": self.module_color,
79
112
  "show_pydantic_resolve_meta": self.enable_pydantic_resolve_meta,
113
+ "theme_color": self._get_theme_color(),
114
+ "entity_class_names": self._get_entity_class_names(),
80
115
  }
81
116
  config.update(kwargs)
82
117
  return Voyager(**config)
@@ -179,6 +214,7 @@ class VoyagerContext:
179
214
  show_fields=core_data.show_fields,
180
215
  module_color=core_data.module_color,
181
216
  schema=core_data.schema,
217
+ theme_color=self._get_theme_color(),
182
218
  )
183
219
  return renderer.render_dot(
184
220
  core_data.tags, core_data.routes, core_data.nodes, core_data.links
@@ -191,6 +227,7 @@ class VoyagerContext:
191
227
  self.er_diagram,
192
228
  show_fields=payload.get("show_fields", "object"),
193
229
  show_module=payload.get("show_module", True),
230
+ theme_color=self._get_theme_color(),
194
231
  ).render_dot()
195
232
  return ""
196
233
 
@@ -203,6 +240,8 @@ class VoyagerContext:
203
240
  content = content.replace(VERSION_PLACEHOLDER, f"?v={__version__}")
204
241
  # Replace static files path placeholder with actual path (without leading slash)
205
242
  content = content.replace(STATIC_PATH_PLACEHOLDER, STATIC_FILES_PATH.lstrip("/"))
243
+ # Replace theme color placeholder with framework-specific color
244
+ content = content.replace(THEME_COLOR_PLACEHOLDER, self._get_theme_color())
206
245
  return content
207
246
  # fallback simple page if index.html missing
208
247
  return """
@@ -53,7 +53,7 @@ class LitestarAdapter(VoyagerAdapter):
53
53
  from litestar import Litestar, MediaType, Request, Response, get, post
54
54
  from litestar.static_files import create_static_files_router
55
55
 
56
- @get("/er-diagram")
56
+ @post("/er-diagram")
57
57
  async def get_er_diagram(request: Request) -> str:
58
58
  payload = await request.json()
59
59
  return self.ctx.get_er_diagram_dot(payload)
@@ -38,13 +38,15 @@ class DiagramRenderer(Renderer):
38
38
  self,
39
39
  *,
40
40
  show_fields: FieldType = 'single',
41
- show_module: bool = True
41
+ show_module: bool = True,
42
+ theme_color: str | None = None
42
43
  ) -> None:
43
44
  # Initialize parent Renderer with shared config
44
45
  super().__init__(
45
46
  show_fields=show_fields,
46
47
  show_module=show_module,
47
- config=RenderConfig() # Use unified style configuration
48
+ config=RenderConfig(), # Use unified style configuration
49
+ theme_color=theme_color,
48
50
  )
49
51
  logger.info(f'show_module: {self.show_module}')
50
52
 
@@ -99,10 +101,11 @@ class DiagramRenderer(Renderer):
99
101
 
100
102
 
101
103
  class VoyagerErDiagram:
102
- def __init__(self,
103
- er_diagram: ErDiagram,
104
+ def __init__(self,
105
+ er_diagram: ErDiagram,
104
106
  show_fields: FieldType = 'single',
105
- show_module: bool = False):
107
+ show_module: bool = False,
108
+ theme_color: str | None = None):
106
109
 
107
110
  self.er_diagram = er_diagram
108
111
  self.nodes: list[SchemaNode] = []
@@ -115,6 +118,7 @@ class VoyagerErDiagram:
115
118
 
116
119
  self.show_field = show_fields
117
120
  self.show_module = show_module
121
+ self.theme_color = theme_color
118
122
 
119
123
  def generate_node_head(self, link_name: str):
120
124
  return f'{link_name}::{PK}'
@@ -164,10 +168,11 @@ class VoyagerErDiagram:
164
168
  if full_name not in self.node_set:
165
169
  # skip meta info for normal queries
166
170
  self.node_set[full_name] = SchemaNode(
167
- id=full_name,
171
+ id=full_name,
168
172
  module=schema.__module__,
169
173
  name=schema.__name__,
170
- fields=get_fields(schema, fk_set)
174
+ fields=get_fields(schema, fk_set),
175
+ is_entity=False # Don't mark in ER diagram
171
176
  )
172
177
  return full_name
173
178
 
@@ -209,7 +214,11 @@ class VoyagerErDiagram:
209
214
 
210
215
  for entity in self.er_diagram.configs:
211
216
  self.analysis_entity(entity)
212
- renderer = DiagramRenderer(show_fields=self.show_field, show_module=self.show_module)
217
+ renderer = DiagramRenderer(
218
+ show_fields=self.show_field,
219
+ show_module=self.show_module,
220
+ theme_color=self.theme_color
221
+ )
213
222
  return renderer.render_dot(list(self.node_set.values()), self.links)
214
223
 
215
224
 
@@ -63,6 +63,7 @@ class Renderer:
63
63
  show_module: bool = True,
64
64
  show_pydantic_resolve_meta: bool = False,
65
65
  config: RenderConfig | None = None,
66
+ theme_color: str | None = None,
66
67
  ) -> None:
67
68
  self.show_fields = show_fields if show_fields in ('single', 'object', 'all') else 'single'
68
69
  self.module_color = module_color or {}
@@ -75,6 +76,9 @@ class Renderer:
75
76
  self.colors = self.config.colors
76
77
  self.style = self.config.style
77
78
 
79
+ # Framework theme color (overrides default primary color)
80
+ self.theme_color = theme_color or self.colors.primary
81
+
78
82
  # Initialize template renderer
79
83
  self.template_renderer = TemplateRenderer()
80
84
 
@@ -228,7 +232,7 @@ class Renderer:
228
232
  rows.append(self._render_schema_field(field))
229
233
 
230
234
  # Determine header color
231
- default_color = self.colors.primary if color is None else color
235
+ default_color = self.theme_color if color is None else color
232
236
  header_color = self.colors.highlight if node.id == self.schema else default_color
233
237
 
234
238
  # Render header
@@ -236,7 +240,8 @@ class Renderer:
236
240
  'html/schema_header.j2',
237
241
  text=node.name,
238
242
  bg_color=header_color,
239
- port=PK
243
+ port=PK,
244
+ is_entity=node.is_entity
240
245
  )
241
246
 
242
247
  # Render complete table
@@ -3,11 +3,20 @@ Style constants and configuration for rendering DOT graphs and HTML tables.
3
3
  """
4
4
  from dataclasses import dataclass, field
5
5
 
6
+ from fastapi_voyager.introspectors.detector import FrameworkType
7
+
6
8
 
7
9
  @dataclass
8
10
  class ColorScheme:
9
11
  """Color scheme for graph visualization."""
10
12
 
13
+ # Framework-specific theme colors (single source of truth)
14
+ FRAMEWORK_COLORS: dict[FrameworkType, str] = field(default_factory=lambda: {
15
+ FrameworkType.FASTAPI: '#009485',
16
+ FrameworkType.DJANGO_NINJA: '#4cae4f',
17
+ FrameworkType.LITESTAR: '#edb641',
18
+ })
19
+
11
20
  # Node colors
12
21
  primary: str = '#009485'
13
22
  highlight: str = 'tomato'
@@ -30,6 +39,10 @@ class ColorScheme:
30
39
  # Text colors
31
40
  text_gray: str = '#999'
32
41
 
42
+ def get_framework_color(self, framework_type: FrameworkType) -> str:
43
+ """Get theme color for a specific framework type."""
44
+ return self.FRAMEWORK_COLORS.get(framework_type, self.primary)
45
+
33
46
 
34
47
  @dataclass
35
48
  class GraphvizStyle:
@@ -0,0 +1 @@
1
+ <tr><td cellpadding="6" bgcolor="{{ bg_color }}" align="center" colspan="1" {% if port %}port="{{ port }}"{% endif %}><font color="white">{% if is_entity %}<b>{{ text }} (E)</b>{% else %}{{ text }}{% endif %}</font></td></tr>
@@ -48,6 +48,7 @@ class ModuleRoute:
48
48
  class SchemaNode(NodeBase):
49
49
  module: str
50
50
  fields: list[FieldInfo] = field(default_factory=list)
51
+ is_entity: bool = False # Mark if this is an ER diagram entity
51
52
 
52
53
  @dataclass
53
54
  class ModuleNode:
@@ -29,6 +29,26 @@ def full_class_name(cls):
29
29
  return f"{cls.__module__}.{cls.__qualname__}"
30
30
 
31
31
 
32
+ def is_base_entity_subclass(schema, entity_class_names: set[str] | None = None) -> bool:
33
+ """
34
+ Check if a schema is a pydantic-resolve BaseEntity entity.
35
+
36
+ Checks if the class's full name is in the entity_class_names set.
37
+
38
+ Args:
39
+ schema: The schema class to check
40
+ entity_class_names: Optional set of full class names from er_diagram.configs
41
+
42
+ Returns:
43
+ True if the schema is an entity, False otherwise
44
+ """
45
+ if not entity_class_names:
46
+ return False
47
+
48
+ schema_full_name = full_class_name(schema)
49
+ return schema_full_name in entity_class_names
50
+
51
+
32
52
  def get_core_types(tp):
33
53
  """
34
54
  - get the core type
@@ -0,0 +1,2 @@
1
+ __all__ = ["__version__"]
2
+ __version__ = "0.17.0"
@@ -16,6 +16,7 @@ from fastapi_voyager.type_helper import (
16
16
  get_core_types,
17
17
  get_pydantic_fields,
18
18
  get_type_name,
19
+ is_base_entity_subclass,
19
20
  is_inheritance_of_pydantic_base,
20
21
  is_non_pydantic_type,
21
22
  update_forward_refs,
@@ -24,8 +25,8 @@ from fastapi_voyager.type_helper import (
24
25
 
25
26
  class Voyager:
26
27
  def __init__(
27
- self,
28
- schema: str | None = None,
28
+ self,
29
+ schema: str | None = None,
29
30
  schema_field: str | None = None,
30
31
  show_fields: FieldType = 'single',
31
32
  include_tags: list[str] | None = None,
@@ -34,6 +35,8 @@ class Voyager:
34
35
  hide_primitive_route: bool = False,
35
36
  show_module: bool = True,
36
37
  show_pydantic_resolve_meta: bool = False,
38
+ theme_color: str | None = None,
39
+ entity_class_names: set[str] | None = None,
37
40
  ):
38
41
 
39
42
  self.routes: list[Route] = []
@@ -57,6 +60,8 @@ class Voyager:
57
60
  self.hide_primitive_route = hide_primitive_route
58
61
  self.show_module = show_module
59
62
  self.show_pydantic_resolve_meta = show_pydantic_resolve_meta
63
+ self.theme_color = theme_color
64
+ self.entity_class_names = entity_class_names
60
65
 
61
66
  def _get_introspector(self, app) -> AppIntrospector:
62
67
  """
@@ -189,7 +194,7 @@ class Voyager:
189
194
  """
190
195
  full_name = full_class_name(schema)
191
196
  bases_fields = get_bases_fields([s for s in schema.__bases__ if is_inheritance_of_pydantic_base(s)])
192
-
197
+
193
198
  subset_reference = getattr(schema, const.ENSURE_SUBSET_REFERENCE, None)
194
199
  if subset_reference and is_inheritance_of_pydantic_base(subset_reference):
195
200
  bases_fields.update(get_bases_fields([subset_reference]))
@@ -197,10 +202,11 @@ class Voyager:
197
202
  if full_name not in self.node_set:
198
203
  # skip meta info for normal queries
199
204
  self.node_set[full_name] = SchemaNode(
200
- id=full_name,
205
+ id=full_name,
201
206
  module=schema.__module__,
202
207
  name=schema.__name__,
203
- fields=get_pydantic_fields(schema, bases_fields)
208
+ fields=get_pydantic_fields(schema, bases_fields),
209
+ is_entity=is_base_entity_subclass(schema, self.entity_class_names)
204
210
  )
205
211
  return full_name
206
212
 
@@ -351,14 +357,15 @@ class Voyager:
351
357
 
352
358
  renderer = Renderer(
353
359
  show_fields=self.show_fields,
354
- module_color=self.module_color,
355
- schema=self.schema,
360
+ module_color=self.module_color,
361
+ schema=self.schema,
356
362
  show_module=self.show_module,
357
- show_pydantic_resolve_meta=self.show_pydantic_resolve_meta)
363
+ show_pydantic_resolve_meta=self.show_pydantic_resolve_meta,
364
+ theme_color=self.theme_color)
358
365
 
359
366
  _tags, _routes, _links = self.handle_hide(_tags, _routes, _links)
360
367
  return renderer.render_dot(_tags, _routes, _nodes, _links)
361
-
368
+
362
369
 
363
370
  def render_tag_level_brief_dot(self, module_prefix: str | None = None):
364
371
  _tags, _routes, _nodes, _links = filter_graph(
@@ -379,7 +386,12 @@ class Voyager:
379
386
  links=_links,
380
387
  )
381
388
 
382
- renderer = Renderer(show_fields=self.show_fields, module_color=self.module_color, schema=self.schema, show_module=self.show_module)
389
+ renderer = Renderer(
390
+ show_fields=self.show_fields,
391
+ module_color=self.module_color,
392
+ schema=self.schema,
393
+ show_module=self.show_module,
394
+ theme_color=self.theme_color)
383
395
 
384
396
  _tags, _routes, _links = self.handle_hide(_tags, _routes, _links)
385
397
  return renderer.render_dot(_tags, _routes, _nodes, _links, True)
@@ -403,7 +415,12 @@ class Voyager:
403
415
  links=_links,
404
416
  )
405
417
 
406
- renderer = Renderer(show_fields=self.show_fields, module_color=self.module_color, schema=self.schema, show_module=self.show_module)
418
+ renderer = Renderer(
419
+ show_fields=self.show_fields,
420
+ module_color=self.module_color,
421
+ schema=self.schema,
422
+ show_module=self.show_module,
423
+ theme_color=self.theme_color)
407
424
 
408
425
  _tags, _routes, _links = self.handle_hide(_tags, _routes, _links)
409
426
  return renderer.render_dot(_tags, _routes, _nodes, _links, True)
@@ -35,6 +35,9 @@
35
35
  </head>
36
36
 
37
37
  <style>
38
+ :root {
39
+ --q-primary: <!-- THEME_COLOR -->;
40
+ }
38
41
  html,
39
42
  body {
40
43
  height: 100%;
@@ -112,7 +115,7 @@
112
115
  width: 32px;
113
116
  height: 32px;
114
117
  border-radius: 50%;
115
- background: #009485;
118
+ background: var(--q-primary);
116
119
  color: white;
117
120
  cursor: pointer;
118
121
  display: flex;
@@ -122,7 +125,7 @@
122
125
  transition: all 0.2s ease;
123
126
  }
124
127
  .tag-navigator-collapse-btn-right:hover {
125
- background: #007a6d;
128
+ filter: brightness(0.9);
126
129
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
127
130
  }
128
131
 
@@ -144,7 +147,7 @@
144
147
  Helvetica,
145
148
  Arial,
146
149
  sans-serif;
147
- color: #009485;
150
+ color: var(--q-primary);
148
151
  }
149
152
  .loading-text {
150
153
  font-size: 14px;
@@ -153,7 +156,7 @@
153
156
  width: 20px;
154
157
  height: 20px;
155
158
  border: 2px solid rgba(0, 148, 133, 0.2);
156
- border-top-color: #009485;
159
+ border-top-color: var(--q-primary);
157
160
  border-radius: 50%;
158
161
  animation: frv-spin 0.8s linear infinite;
159
162
  }
@@ -171,6 +174,10 @@
171
174
  }
172
175
  </style>
173
176
  <body class="app-loading">
177
+ <script>
178
+ // Framework theme color for JS components
179
+ window.FRAMEWORK_THEME_COLOR = "<!-- THEME_COLOR -->"
180
+ </script>
174
181
  <!-- App boot loading overlay: shown until JS initializes -->
175
182
  <div id="app-loading-overlay" aria-busy="true" aria-live="polite">
176
183
  <div class="spinner" aria-hidden="true"></div>
@@ -181,7 +188,7 @@
181
188
  <q-header bordered class="bg-primary text-white">
182
189
  <q-toolbar
183
190
  class="row text-grey-9 bg-white"
184
- style="width: 100%; border-bottom: 2px solid #009485"
191
+ style="width: 100%; border-bottom: 2px solid var(--q-primary)"
185
192
  >
186
193
  <div
187
194
  class="col-auto text-primary"
@@ -313,7 +320,7 @@
313
320
  height="52"
314
321
  viewBox="0 0 250 250"
315
322
  style="
316
- fill: #009485;
323
+ fill: <!-- THEME_COLOR -->;
317
324
  color: #fff;
318
325
  position: absolute;
319
326
  top: 0;
@@ -521,6 +521,11 @@ const actions = {
521
521
  */
522
522
  resetSearchState() {
523
523
  state.search.mode = false
524
+ // Clear search schema and field selection
525
+ state.search.schemaName = null
526
+ state.search.fieldName = null
527
+ state.search.fieldOptions = []
528
+
524
529
  const hadPreviousValue = state.previousTagRoute.hasValue
525
530
 
526
531
  if (hadPreviousValue) {
@@ -5,7 +5,7 @@ import RenderGraph from "./component/render-graph.js"
5
5
  import { GraphUI } from "./graph-ui.js"
6
6
  import { store } from "./store.js"
7
7
 
8
- const { createApp, onMounted, ref, watch } = window.Vue
8
+ const { createApp, onMounted, onUnmounted, ref, watch } = window.Vue
9
9
 
10
10
  // Load toggle states from localStorage
11
11
  function loadToggleState(key, defaultValue = false) {
@@ -296,6 +296,19 @@ const app = createApp({
296
296
  document.title = `${store.state.framework_name} Voyager`
297
297
  }
298
298
  // Reveal app content only after initial JS/data is ready
299
+
300
+ // Add keyboard event listener for ESC to cancel search
301
+ const handleKeyDown = (event) => {
302
+ if (event.key === "Escape" && store.state.search.mode) {
303
+ resetSearch()
304
+ }
305
+ }
306
+ document.addEventListener("keydown", handleKeyDown)
307
+
308
+ // Cleanup on unmount
309
+ onUnmounted(() => {
310
+ document.removeEventListener("keydown", handleKeyDown)
311
+ })
299
312
  })
300
313
 
301
314
  return {
@@ -324,9 +337,10 @@ const app = createApp({
324
337
 
325
338
  app.use(window.Quasar)
326
339
 
327
- // Set Quasar primary theme color to green
340
+ // Set Quasar primary theme color from framework configuration
328
341
  if (window.Quasar && typeof window.Quasar.setCssVar === "function") {
329
- window.Quasar.setCssVar("primary", "#009485")
342
+ const themeColor = window.FRAMEWORK_THEME_COLOR || "#009485"
343
+ window.Quasar.setCssVar("primary", themeColor)
330
344
  }
331
345
 
332
346
  app.component("schema-code-display", SchemaCodeDisplay) // double click to see node details
@@ -132,7 +132,10 @@ class DemoController(Controller):
132
132
  return True
133
133
 
134
134
 
135
+ # Export route handlers for extension (e.g., adding voyager)
136
+ ROUTE_HANDLERS = [DemoController]
137
+
135
138
  # Create a Litestar app instance - this is the main app that can be run directly
136
139
  app = Litestar(
137
- route_handlers=[DemoController]
140
+ route_handlers=ROUTE_HANDLERS
138
141
  )
@@ -0,0 +1,44 @@
1
+ """
2
+ Litestar embedding example for fastapi-voyager.
3
+
4
+ This module demonstrates how to integrate voyager with a Litestar application.
5
+
6
+ Unlike FastAPI, Litestar doesn't support mounting to an existing app after creation.
7
+ The recommended pattern is to reuse the ROUTE_HANDLERS from demo.py.
8
+ """
9
+ from typing import Any, Awaitable, Callable
10
+
11
+ from litestar import Litestar, asgi
12
+
13
+ from fastapi_voyager import create_voyager
14
+ from tests.litestar.demo import ROUTE_HANDLERS, app as demo_app, diagram
15
+
16
+ # Create voyager app (returns a Litestar app)
17
+ voyager_app = create_voyager(
18
+ demo_app,
19
+ er_diagram=diagram,
20
+ module_color={"tests.service": "purple"},
21
+ module_prefix="tests.service",
22
+ swagger_url="/schema/swagger",
23
+ initial_page_policy='first',
24
+ ga_id='G-R64S7Q49VL',
25
+ online_repo_url="https://github.com/allmonday/fastapi-voyager/blob/main",
26
+ enable_pydantic_resolve_meta=True
27
+ )
28
+
29
+ # Mount voyager using Litestar's @asgi() decorator
30
+ @asgi("/voyager", is_mount=True, copy_scope=True)
31
+ async def voyager_mount(
32
+ scope: dict[str, Any],
33
+ receive: Callable[[], Awaitable[dict[str, Any]]],
34
+ send: Callable[[dict[str, Any]], Awaitable[None]]
35
+ ) -> None:
36
+ await voyager_app(scope, receive, send)
37
+
38
+ # Create combined app by reusing ROUTE_HANDLERS from demo.py
39
+ # This is the recommended pattern for Litestar
40
+ app = Litestar(route_handlers=ROUTE_HANDLERS + [voyager_mount])
41
+
42
+ # Exports
43
+ # - Use `uvicorn tests.litestar.embedding:app --reload` for combined app
44
+ # - Use `uvicorn tests.litestar.embedding:demo_app --reload` for demo only
@@ -24,11 +24,11 @@ def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:
24
24
  @pytest_asyncio.fixture
25
25
  async def async_client() -> AsyncGenerator[httpx.AsyncClient, None]:
26
26
  """Create an async HTTP client for testing Litestar embedding."""
27
- # Import the ASGI application from tests.litestar.embedding
28
- from tests.litestar.embedding import asgi_app
27
+ # Import the combined app from tests.litestar.embedding
28
+ from tests.litestar.embedding import app
29
29
 
30
30
  # Use ASGITransport for testing ASGI apps with httpx
31
- transport = httpx.ASGITransport(app=asgi_app)
31
+ transport = httpx.ASGITransport(app=app)
32
32
  async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
33
33
  yield client
34
34
 
@@ -1,2 +0,0 @@
1
- <tr><td cellpadding="6" bgcolor="{{ bg_color }}" align="center" colspan="1" {% if port %}port="{{ port }}"{% endif %}>
2
- <font color="white"> {{ text }} </font></td></tr>
@@ -1,2 +0,0 @@
1
- __all__ = ["__version__"]
2
- __version__ = "0.16.0alpha-3"
@@ -1,53 +0,0 @@
1
- """
2
- Litestar embedding example for fastapi-voyager.
3
-
4
- This module demonstrates how to integrate voyager with a Litestar application.
5
- """
6
- from fastapi_voyager import create_voyager
7
- from tests.litestar.demo import app, diagram
8
-
9
- # Create voyager app for visualization
10
- # Note: create_voyager automatically detects Litestar and returns a Litestar app
11
- voyager_app = create_voyager(
12
- app,
13
- er_diagram=diagram,
14
- module_color={"tests.service": "purple"},
15
- module_prefix="tests.service",
16
- swagger_url="/schema/swagger",
17
- initial_page_policy='first',
18
- ga_id='G-R64S7Q49VL',
19
- online_repo_url="https://github.com/allmonday/fastapi-voyager/blob/main",
20
- enable_pydantic_resolve_meta=True
21
- )
22
-
23
- # ASGI app that routes between main app and voyager
24
- # This allows voyager to be accessed at /voyager while the main app handles other routes
25
- async def asgi_app(scope, receive, send):
26
- """
27
- ASGI app that routes between main app and voyager.
28
-
29
- Usage:
30
- uvicorn tests.litestar.embedding:asgi_app --reload
31
-
32
- Then access:
33
- - http://localhost:8000/demo/* for the main app
34
- - http://localhost:8000/voyager for voyager UI
35
- """
36
- if scope["type"] == "http" and scope["path"].startswith("/voyager"):
37
- # Forward to voyager app
38
- # Remove /voyager prefix for the voyager app (it expects root path)
39
- new_scope = dict(scope)
40
- new_scope["path"] = scope["path"][8:] # Remove '/voyager'
41
- if new_scope["path"] == "":
42
- new_scope["path"] = "/"
43
- if "raw_path" in new_scope:
44
- new_scope["raw_path"] = scope["raw_path"][8:]
45
- await voyager_app(new_scope, receive, send)
46
- else:
47
- # Forward to main app
48
- await app(scope, receive, send)
49
-
50
- # Exports
51
- # - Use `uvicorn tests.litestar.embedding:asgi_app --reload` for combined app (main + voyager at /voyager)
52
- # - Use `uvicorn tests.litestar.embedding:app --reload` for main app only
53
- # - Use `uvicorn tests.litestar.embedding:voyager_app --reload` for voyager only