vega-framework 0.1.35__tar.gz → 0.2.1__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.
- {vega_framework-0.1.35 → vega_framework-0.2.1}/PKG-INFO +10 -9
- {vega_framework-0.1.35 → vega_framework-0.2.1}/README.md +8 -7
- {vega_framework-0.1.35 → vega_framework-0.2.1}/pyproject.toml +28 -2
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/commands/add.py +9 -10
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/commands/generate.py +15 -15
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/commands/init.py +9 -8
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/commands/web.py +8 -7
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/main.py +4 -4
- vega_framework-0.2.1/vega/cli/scaffolds/__init__.py +13 -0
- vega_framework-0.2.1/vega/cli/scaffolds/vega_web.py +109 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/__init__.py +34 -8
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/components.py +29 -13
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/ARCHITECTURE.md.j2 +13 -13
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/README.md.j2 +5 -5
- vega_framework-0.2.1/vega/cli/templates/web/app.py.j2 +15 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/health_route.py.j2 +2 -2
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/main.py.j2 +2 -3
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/middleware.py.j2 +3 -3
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/router.py.j2 +2 -2
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/routes_init.py.j2 +3 -3
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/routes_init_autodiscovery.py.j2 +2 -2
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/users_route.py.j2 +2 -2
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/discovery/routes.py +13 -13
- vega_framework-0.2.1/vega/web/__init__.py +100 -0
- vega_framework-0.2.1/vega/web/application.py +234 -0
- vega_framework-0.2.1/vega/web/builtin_middlewares.py +288 -0
- vega_framework-0.2.1/vega/web/exceptions.py +151 -0
- vega_framework-0.2.1/vega/web/middleware.py +185 -0
- vega_framework-0.2.1/vega/web/request.py +120 -0
- vega_framework-0.2.1/vega/web/response.py +220 -0
- vega_framework-0.2.1/vega/web/route_middleware.py +266 -0
- vega_framework-0.2.1/vega/web/router.py +350 -0
- vega_framework-0.2.1/vega/web/routing.py +347 -0
- vega_framework-0.1.35/vega/cli/scaffolds/__init__.py +0 -9
- vega_framework-0.1.35/vega/cli/templates/web/app.py.j2 +0 -15
- {vega_framework-0.1.35 → vega_framework-0.2.1}/LICENSE +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/commands/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/commands/migrate.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/commands/update.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/scaffolds/fastapi.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/scaffolds/sqlalchemy.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/cli/command.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/cli/command_simple.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/cli/commands_init.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/domain/entity.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/domain/event.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/domain/event_handler.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/domain/interactor.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/domain/mediator.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/domain/repository_interface.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/domain/service_interface.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/infrastructure/model.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/infrastructure/repository_impl.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/infrastructure/service_impl.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/loader.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/.env.example +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/.gitignore +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/config.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/events_init.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/main.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/main_fastapi.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/main_standard.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/pyproject.toml.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/project/settings.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/sqlalchemy/alembic.ini.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/sqlalchemy/database_manager.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/sqlalchemy/env.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/sqlalchemy/script.py.mako +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/__init__.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/models_init.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/request_model.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/response_model.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/templates/web/user_models.py.j2 +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/utils/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/utils/async_support.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/utils/messages.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/utils/naming.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/cli/utils/validators.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/di/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/di/container.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/di/decorators.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/di/errors.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/di/scope.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/discovery/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/discovery/commands.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/discovery/events.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/events/README.md +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/events/SYNTAX_GUIDE.md +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/events/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/events/bus.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/events/decorators.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/events/event.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/events/middleware.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/patterns/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/patterns/interactor.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/patterns/mediator.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/patterns/repository.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/patterns/service.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/settings/__init__.py +0 -0
- {vega_framework-0.1.35 → vega_framework-0.2.1}/vega/settings/base.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: vega-framework
|
3
|
-
Version: 0.1
|
3
|
+
Version: 0.2.1
|
4
4
|
Summary: Enterprise-ready Python framework that enforces Clean Architecture for building maintainable and scalable applications.
|
5
5
|
License: MIT
|
6
6
|
License-File: LICENSE
|
@@ -19,10 +19,10 @@ Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
21
21
|
Requires-Dist: click (>=8.0,<9.0)
|
22
|
-
Requires-Dist: fastapi (>=0.109,<0.110)
|
23
22
|
Requires-Dist: jinja2 (>=3.1,<4.0)
|
24
23
|
Requires-Dist: pydantic (>=2.0,<3.0)
|
25
24
|
Requires-Dist: pydantic-settings (>=2.0,<3.0)
|
25
|
+
Requires-Dist: starlette (>=0.37,<0.38)
|
26
26
|
Requires-Dist: toml (>=0.10,<0.11)
|
27
27
|
Requires-Dist: uvicorn (>=0.27,<0.28)
|
28
28
|
Project-URL: Documentation, https://vega-framework.readthedocs.io
|
@@ -43,7 +43,7 @@ Traditional Python frameworks show you **how to build** but don't enforce **how
|
|
43
43
|
- ✅ **Business Logic First** - Pure, testable, framework-independent
|
44
44
|
- ✅ **CLI Scaffolding** - Generate entire projects and components
|
45
45
|
- ✅ **Async Support** - Full async/await for CLI and web
|
46
|
-
- ✅ **
|
46
|
+
- ✅ **Vega Web (Starlette) & SQLAlchemy** - Built-in integrations when needed
|
47
47
|
|
48
48
|
**[Read the Philosophy →](docs/explanation/philosophy.md)** to understand why architecture matters.
|
49
49
|
|
@@ -152,7 +152,7 @@ container = Container({
|
|
152
152
|
|
153
153
|
```bash
|
154
154
|
vega init my-app # Create new project
|
155
|
-
vega init my-api --template
|
155
|
+
vega init my-api --template web # Create with Vega Web
|
156
156
|
vega doctor # Validate architecture
|
157
157
|
vega update # Update framework
|
158
158
|
```
|
@@ -169,7 +169,7 @@ vega generate interactor CreateProduct
|
|
169
169
|
vega generate mediator CheckoutWorkflow
|
170
170
|
|
171
171
|
# Presentation layer
|
172
|
-
vega generate router Product #
|
172
|
+
vega generate router Product # Vega Web (requires: vega add web)
|
173
173
|
vega generate command create-product # CLI
|
174
174
|
|
175
175
|
# Infrastructure
|
@@ -179,7 +179,7 @@ vega generate model Product # SQLAlchemy (requires: vega add db)
|
|
179
179
|
### Add Features
|
180
180
|
|
181
181
|
```bash
|
182
|
-
vega add web # Add
|
182
|
+
vega add web # Add Vega Web support
|
183
183
|
vega add sqlalchemy # Add database support
|
184
184
|
```
|
185
185
|
|
@@ -232,6 +232,7 @@ await UserCreated(user_id="123", email="test@test.com").publish()
|
|
232
232
|
- [Patterns](docs/explanation/patterns/interactor.md) - Interactor, Mediator, Repository, Service
|
233
233
|
|
234
234
|
### Guides
|
235
|
+
- [Use Vega Web](docs/how-to/use-vega-web.md) - Build HTTP APIs with Vega's router and middleware
|
235
236
|
- [Building Domain Layer](docs/how-to/build-domain-layer.md) - Business logic first
|
236
237
|
- [CLI Reference](docs/reference/cli/overview.md) - All CLI commands
|
237
238
|
- [Events System](docs/explanation/events/overview.md) - Event-driven architecture
|
@@ -268,7 +269,7 @@ my-app/
|
|
268
269
|
│ └── services/ # API integrations
|
269
270
|
├── presentation/ # User interfaces
|
270
271
|
│ ├── cli/ # CLI commands
|
271
|
-
│ └── web/ #
|
272
|
+
│ └── web/ # Vega Web routes
|
272
273
|
├── config.py # Dependency injection
|
273
274
|
├── settings.py # Configuration
|
274
275
|
└── main.py # Entry point
|
@@ -289,7 +290,7 @@ async def create_order(request: Request):
|
|
289
290
|
```
|
290
291
|
|
291
292
|
**Problems:**
|
292
|
-
- Can't test without
|
293
|
+
- Can't test without the web framework, database, and Stripe
|
293
294
|
- Can't reuse for CLI or other interfaces
|
294
295
|
- Business rules are unclear
|
295
296
|
- Tightly coupled to specific technologies
|
@@ -309,7 +310,7 @@ class PlaceOrder(Interactor[Order]):
|
|
309
310
|
await payment_service.charge(...)
|
310
311
|
return await order_repo.save(order)
|
311
312
|
|
312
|
-
# ✅
|
313
|
+
# ✅ Vega Web route (Presentation) - just wiring
|
313
314
|
@router.post("/orders")
|
314
315
|
async def create_order_api(request: CreateOrderRequest):
|
315
316
|
return await PlaceOrder(...)
|
@@ -11,7 +11,7 @@ Traditional Python frameworks show you **how to build** but don't enforce **how
|
|
11
11
|
- ✅ **Business Logic First** - Pure, testable, framework-independent
|
12
12
|
- ✅ **CLI Scaffolding** - Generate entire projects and components
|
13
13
|
- ✅ **Async Support** - Full async/await for CLI and web
|
14
|
-
- ✅ **
|
14
|
+
- ✅ **Vega Web (Starlette) & SQLAlchemy** - Built-in integrations when needed
|
15
15
|
|
16
16
|
**[Read the Philosophy →](docs/explanation/philosophy.md)** to understand why architecture matters.
|
17
17
|
|
@@ -120,7 +120,7 @@ container = Container({
|
|
120
120
|
|
121
121
|
```bash
|
122
122
|
vega init my-app # Create new project
|
123
|
-
vega init my-api --template
|
123
|
+
vega init my-api --template web # Create with Vega Web
|
124
124
|
vega doctor # Validate architecture
|
125
125
|
vega update # Update framework
|
126
126
|
```
|
@@ -137,7 +137,7 @@ vega generate interactor CreateProduct
|
|
137
137
|
vega generate mediator CheckoutWorkflow
|
138
138
|
|
139
139
|
# Presentation layer
|
140
|
-
vega generate router Product #
|
140
|
+
vega generate router Product # Vega Web (requires: vega add web)
|
141
141
|
vega generate command create-product # CLI
|
142
142
|
|
143
143
|
# Infrastructure
|
@@ -147,7 +147,7 @@ vega generate model Product # SQLAlchemy (requires: vega add db)
|
|
147
147
|
### Add Features
|
148
148
|
|
149
149
|
```bash
|
150
|
-
vega add web # Add
|
150
|
+
vega add web # Add Vega Web support
|
151
151
|
vega add sqlalchemy # Add database support
|
152
152
|
```
|
153
153
|
|
@@ -200,6 +200,7 @@ await UserCreated(user_id="123", email="test@test.com").publish()
|
|
200
200
|
- [Patterns](docs/explanation/patterns/interactor.md) - Interactor, Mediator, Repository, Service
|
201
201
|
|
202
202
|
### Guides
|
203
|
+
- [Use Vega Web](docs/how-to/use-vega-web.md) - Build HTTP APIs with Vega's router and middleware
|
203
204
|
- [Building Domain Layer](docs/how-to/build-domain-layer.md) - Business logic first
|
204
205
|
- [CLI Reference](docs/reference/cli/overview.md) - All CLI commands
|
205
206
|
- [Events System](docs/explanation/events/overview.md) - Event-driven architecture
|
@@ -236,7 +237,7 @@ my-app/
|
|
236
237
|
│ └── services/ # API integrations
|
237
238
|
├── presentation/ # User interfaces
|
238
239
|
│ ├── cli/ # CLI commands
|
239
|
-
│ └── web/ #
|
240
|
+
│ └── web/ # Vega Web routes
|
240
241
|
├── config.py # Dependency injection
|
241
242
|
├── settings.py # Configuration
|
242
243
|
└── main.py # Entry point
|
@@ -257,7 +258,7 @@ async def create_order(request: Request):
|
|
257
258
|
```
|
258
259
|
|
259
260
|
**Problems:**
|
260
|
-
- Can't test without
|
261
|
+
- Can't test without the web framework, database, and Stripe
|
261
262
|
- Can't reuse for CLI or other interfaces
|
262
263
|
- Business rules are unclear
|
263
264
|
- Tightly coupled to specific technologies
|
@@ -277,7 +278,7 @@ class PlaceOrder(Interactor[Order]):
|
|
277
278
|
await payment_service.charge(...)
|
278
279
|
return await order_repo.save(order)
|
279
280
|
|
280
|
-
# ✅
|
281
|
+
# ✅ Vega Web route (Presentation) - just wiring
|
281
282
|
@router.post("/orders")
|
282
283
|
async def create_order_api(request: CreateOrderRequest):
|
283
284
|
return await PlaceOrder(...)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "vega-framework"
|
3
|
-
version = "0.1
|
3
|
+
version = "0.2.1"
|
4
4
|
description = "Enterprise-ready Python framework that enforces Clean Architecture for building maintainable and scalable applications."
|
5
5
|
authors = ["Roberto Ferro"]
|
6
6
|
license = "MIT"
|
@@ -43,7 +43,7 @@ click = "^8.0"
|
|
43
43
|
jinja2 = "^3.1"
|
44
44
|
toml = "^0.10"
|
45
45
|
uvicorn = "^0.27"
|
46
|
-
|
46
|
+
starlette = "^0.37"
|
47
47
|
|
48
48
|
[tool.poetry.group.dev.dependencies]
|
49
49
|
pytest = "^7.0"
|
@@ -53,6 +53,7 @@ black = "^23.0"
|
|
53
53
|
isort = "^5.0"
|
54
54
|
mypy = "^1.0"
|
55
55
|
ruff = "^0.1"
|
56
|
+
httpx = "^0.28.1"
|
56
57
|
|
57
58
|
[tool.poetry.scripts]
|
58
59
|
vega = "vega.cli.main:cli"
|
@@ -81,6 +82,31 @@ testpaths = ["tests"]
|
|
81
82
|
python_files = ["test_*.py"]
|
82
83
|
python_classes = ["Test*"]
|
83
84
|
python_functions = ["test_*"]
|
85
|
+
# Test markers
|
86
|
+
markers = [
|
87
|
+
"unit: Unit tests for individual components",
|
88
|
+
"functional: Functional tests for features and workflows",
|
89
|
+
"integration: Integration tests for component interactions",
|
90
|
+
"slow: Tests that take significant time to run",
|
91
|
+
"web: Web framework related tests",
|
92
|
+
"di: Dependency injection related tests",
|
93
|
+
"events: Event system related tests",
|
94
|
+
]
|
95
|
+
# Coverage settings
|
96
|
+
addopts = [
|
97
|
+
"--strict-markers",
|
98
|
+
"--tb=short",
|
99
|
+
"--cov=vega",
|
100
|
+
"--cov-report=term-missing",
|
101
|
+
"--cov-report=html:htmlcov",
|
102
|
+
"--cov-report=xml",
|
103
|
+
]
|
104
|
+
# Filter warnings
|
105
|
+
filterwarnings = [
|
106
|
+
"error",
|
107
|
+
"ignore::UserWarning",
|
108
|
+
"ignore::DeprecationWarning",
|
109
|
+
]
|
84
110
|
|
85
111
|
[tool.ruff]
|
86
112
|
line-length = 100
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
3
3
|
|
4
4
|
import click
|
5
5
|
|
6
|
-
from vega.cli.scaffolds import
|
6
|
+
from vega.cli.scaffolds import create_vega_web_scaffold, create_sqlalchemy_scaffold
|
7
7
|
|
8
8
|
|
9
9
|
@click.command()
|
@@ -13,7 +13,7 @@ def add(feature: str, path: str):
|
|
13
13
|
"""Add features to an existing Vega project
|
14
14
|
|
15
15
|
Features:
|
16
|
-
web - Add
|
16
|
+
web - Add Vega Web scaffold to the project
|
17
17
|
sqlalchemy - Add SQLAlchemy database support (alias: db)
|
18
18
|
db - Alias for sqlalchemy
|
19
19
|
|
@@ -40,13 +40,13 @@ def add(feature: str, path: str):
|
|
40
40
|
|
41
41
|
|
42
42
|
def add_web_feature(project_path: Path, project_name: str):
|
43
|
-
"""Add
|
44
|
-
click.echo(f"\n[*] Adding
|
43
|
+
"""Add Vega Web scaffold to existing project"""
|
44
|
+
click.echo(f"\n[*] Adding Vega Web scaffold to: {click.style(project_name, fg='green', bold=True)}\n")
|
45
45
|
|
46
46
|
# Check if presentation/web already exists
|
47
47
|
web_dir = project_path / "presentation" / "web"
|
48
48
|
if web_dir.exists() and (web_dir / "main.py").exists():
|
49
|
-
click.echo(click.style("WARNING:
|
49
|
+
click.echo(click.style("WARNING: Web scaffold already exists!", fg='yellow'))
|
50
50
|
if not click.confirm("Do you want to overwrite existing files?"):
|
51
51
|
click.echo("Aborted.")
|
52
52
|
return
|
@@ -60,13 +60,12 @@ def add_web_feature(project_path: Path, project_name: str):
|
|
60
60
|
presentation_dir.mkdir(parents=True, exist_ok=True)
|
61
61
|
click.echo(f" + Created presentation/")
|
62
62
|
|
63
|
-
# Create
|
64
|
-
|
63
|
+
# Create Vega Web scaffold
|
64
|
+
create_vega_web_scaffold(project_path, project_name, overwrite=overwrite)
|
65
65
|
|
66
|
-
click.echo(f"\n{click.style('SUCCESS:
|
66
|
+
click.echo(f"\n{click.style('SUCCESS: Vega Web scaffold added!', fg='green', bold=True)}\n")
|
67
67
|
click.echo("Next steps:")
|
68
|
-
click.echo(" 1.
|
69
|
-
click.echo(" poetry add fastapi uvicorn[standard]")
|
68
|
+
click.echo(" 1. Dependencies are already included in vega-framework")
|
70
69
|
click.echo(" 2. Run the server:")
|
71
70
|
click.echo(" vega web run --reload")
|
72
71
|
click.echo(" 3. Visit http://localhost:8000/api/health/status")
|
@@ -394,13 +394,13 @@ def _register_router_in_init(project_root: Path, resource_file: str, resource_na
|
|
394
394
|
|
395
395
|
|
396
396
|
def _generate_router(project_root: Path, project_name: str, name: str) -> None:
|
397
|
-
"""Generate a
|
397
|
+
"""Generate a Vega Web router for a resource"""
|
398
398
|
|
399
399
|
# Check if web folder exists
|
400
400
|
web_path = project_root / "presentation" / "web"
|
401
401
|
if not web_path.exists():
|
402
402
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
403
|
-
click.echo(" Router generation requires
|
403
|
+
click.echo(" Router generation requires Vega Web module")
|
404
404
|
click.echo(" Install it with: vega add web")
|
405
405
|
return
|
406
406
|
|
@@ -440,13 +440,13 @@ def _generate_router(project_root: Path, project_name: str, name: str) -> None:
|
|
440
440
|
|
441
441
|
|
442
442
|
def _generate_web_models(project_root: Path, project_name: str, name: str, is_request: bool, is_response: bool) -> None:
|
443
|
-
"""Generate Pydantic request or response model for
|
443
|
+
"""Generate Pydantic request or response model for Vega Web"""
|
444
444
|
|
445
445
|
# Check if web folder exists
|
446
446
|
web_path = project_root / "presentation" / "web"
|
447
447
|
if not web_path.exists():
|
448
448
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
449
|
-
click.echo(" Model generation requires
|
449
|
+
click.echo(" Model generation requires Vega Web module")
|
450
450
|
click.echo(" Install it with: vega add web")
|
451
451
|
return
|
452
452
|
|
@@ -531,13 +531,13 @@ def _generate_web_models(project_root: Path, project_name: str, name: str, is_re
|
|
531
531
|
|
532
532
|
|
533
533
|
def _generate_middleware(project_root: Path, project_name: str, class_name: str, file_name: str) -> None:
|
534
|
-
"""Generate a
|
534
|
+
"""Generate a Vega Web middleware"""
|
535
535
|
|
536
536
|
# Check if web folder exists
|
537
537
|
web_path = project_root / "presentation" / "web"
|
538
538
|
if not web_path.exists():
|
539
539
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
540
|
-
click.echo(" Middleware generation requires
|
540
|
+
click.echo(" Middleware generation requires Vega Web module")
|
541
541
|
click.echo(" Install it with: vega add web")
|
542
542
|
return
|
543
543
|
|
@@ -554,7 +554,7 @@ def _generate_middleware(project_root: Path, project_name: str, class_name: str,
|
|
554
554
|
# Check if __init__.py exists
|
555
555
|
init_file = middleware_path / "__init__.py"
|
556
556
|
if not init_file.exists():
|
557
|
-
init_file.write_text('"""
|
557
|
+
init_file.write_text('"""Vega Web Middlewares"""\n')
|
558
558
|
click.echo(f"+ Created {click.style(str(init_file.relative_to(project_root)), fg='green')}")
|
559
559
|
|
560
560
|
# Generate middleware file
|
@@ -589,8 +589,8 @@ def _register_middleware_in_app(project_root: Path, class_name: str, file_name:
|
|
589
589
|
click.echo(click.style(f'''
|
590
590
|
from .middleware.{file_name} import {class_name}Middleware
|
591
591
|
|
592
|
-
def create_app() ->
|
593
|
-
app =
|
592
|
+
def create_app() -> VegaApp:
|
593
|
+
app = VegaApp(...)
|
594
594
|
app.add_middleware({class_name}Middleware)
|
595
595
|
app.include_router(get_api_router())
|
596
596
|
return app
|
@@ -619,9 +619,9 @@ def create_app() -> FastAPI:
|
|
619
619
|
break
|
620
620
|
|
621
621
|
if not import_added:
|
622
|
-
# Fallback: add after
|
622
|
+
# Fallback: add after VegaApp import
|
623
623
|
for i, line in enumerate(lines):
|
624
|
-
if 'from
|
624
|
+
if 'from vega.web import' in line:
|
625
625
|
lines.insert(i + 1, middleware_import)
|
626
626
|
lines.insert(i + 2, '')
|
627
627
|
break
|
@@ -629,8 +629,8 @@ def create_app() -> FastAPI:
|
|
629
629
|
# Find create_app function and add middleware registration
|
630
630
|
middleware_added = False
|
631
631
|
for i, line in enumerate(lines):
|
632
|
-
if 'app =
|
633
|
-
# Find the end of
|
632
|
+
if 'app = VegaApp(' in line:
|
633
|
+
# Find the end of VegaApp initialization
|
634
634
|
j = i + 1
|
635
635
|
while j < len(lines) and not lines[j].strip().startswith('app.include_router'):
|
636
636
|
j += 1
|
@@ -649,8 +649,8 @@ def create_app() -> FastAPI:
|
|
649
649
|
click.echo(click.style(f'''
|
650
650
|
from .middleware.{file_name} import {class_name}Middleware
|
651
651
|
|
652
|
-
def create_app() ->
|
653
|
-
app =
|
652
|
+
def create_app() -> VegaApp:
|
653
|
+
app = VegaApp(...)
|
654
654
|
app.add_middleware({class_name}Middleware)
|
655
655
|
app.include_router(get_api_router())
|
656
656
|
return app
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
5
5
|
|
6
6
|
import click
|
7
7
|
|
8
|
-
from vega.cli.scaffolds import
|
8
|
+
from vega.cli.scaffolds import create_vega_web_scaffold
|
9
9
|
from vega.cli.templates.loader import render_template
|
10
10
|
import vega
|
11
11
|
|
@@ -104,14 +104,15 @@ def init_project(project_name: str, template: str, parent_path: str):
|
|
104
104
|
click.echo(f" + Created ARCHITECTURE.md")
|
105
105
|
|
106
106
|
# Create main.py based on template
|
107
|
-
|
108
|
-
|
109
|
-
|
107
|
+
# Support both "web" and "fastapi" (backward compat)
|
108
|
+
if template in ["web", "fastapi"]:
|
109
|
+
click.echo("\n[*] Adding Vega Web scaffold (presentation/web/)")
|
110
|
+
create_vega_web_scaffold(project_path, project_name)
|
110
111
|
|
111
|
-
# Create main.py for
|
112
|
+
# Create main.py for web project
|
112
113
|
main_content = render_template("main.py.j2", project_name=project_name, template="fastapi")
|
113
114
|
(project_path / "main.py").write_text(main_content)
|
114
|
-
click.echo(f" + Created main.py (
|
115
|
+
click.echo(f" + Created main.py (Vega Web entrypoint)")
|
115
116
|
else:
|
116
117
|
# Create standard main.py
|
117
118
|
main_content = render_template("main.py.j2", project_name=project_name, template="standard")
|
@@ -126,9 +127,9 @@ def init_project(project_name: str, template: str, parent_path: str):
|
|
126
127
|
click.echo(f" poetry install")
|
127
128
|
click.echo(f" cp .env.example .env")
|
128
129
|
|
129
|
-
if template
|
130
|
+
if template in ["web", "fastapi"]:
|
130
131
|
click.echo(f"\nRun commands:")
|
131
|
-
click.echo(f" vega web run # Start
|
132
|
+
click.echo(f" vega web run # Start Vega Web server (http://localhost:8000)")
|
132
133
|
click.echo(f" vega web run --reload # Start with auto-reload")
|
133
134
|
click.echo(f" python main.py hello # Run CLI command")
|
134
135
|
click.echo(f" python main.py --help # Show all commands")
|
@@ -1,4 +1,4 @@
|
|
1
|
-
"""Web command - Manage
|
1
|
+
"""Web command - Manage Vega Web server"""
|
2
2
|
import sys
|
3
3
|
from pathlib import Path
|
4
4
|
|
@@ -7,7 +7,7 @@ import click
|
|
7
7
|
|
8
8
|
@click.group()
|
9
9
|
def web():
|
10
|
-
"""Manage
|
10
|
+
"""Manage Vega Web server
|
11
11
|
|
12
12
|
Commands to manage the web server for your Vega project.
|
13
13
|
The web module must be added to the project first using 'vega add web'.
|
@@ -21,7 +21,7 @@ def web():
|
|
21
21
|
@click.option('--reload', is_flag=True, help='Enable auto-reload')
|
22
22
|
@click.option('--path', default='.', help='Path to Vega project (default: current directory)')
|
23
23
|
def run(host: str, port: int, reload: bool, path: str):
|
24
|
-
"""Start the
|
24
|
+
"""Start the Vega Web server
|
25
25
|
|
26
26
|
Examples:
|
27
27
|
vega web run
|
@@ -42,7 +42,7 @@ def run(host: str, port: int, reload: bool, path: str):
|
|
42
42
|
web_main = project_path / "presentation" / "web" / "main.py"
|
43
43
|
if not web_main.exists():
|
44
44
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
45
|
-
click.echo("\nThe
|
45
|
+
click.echo("\nThe Vega Web module is not available in this project.")
|
46
46
|
click.echo("Add it using:")
|
47
47
|
click.echo(click.style(" vega add web", fg='cyan', bold=True))
|
48
48
|
sys.exit(1)
|
@@ -56,8 +56,9 @@ def run(host: str, port: int, reload: bool, path: str):
|
|
56
56
|
import uvicorn
|
57
57
|
except ImportError:
|
58
58
|
click.echo(click.style("ERROR: uvicorn not installed", fg='red'))
|
59
|
-
click.echo("\
|
60
|
-
click.echo(
|
59
|
+
click.echo("\nUvicorn is required but not installed.")
|
60
|
+
click.echo("It should be included with vega-framework, but you can also install it with:")
|
61
|
+
click.echo(click.style(" poetry add uvicorn[standard]", fg='cyan', bold=True))
|
61
62
|
sys.exit(1)
|
62
63
|
|
63
64
|
# Initialize DI container first
|
@@ -73,7 +74,7 @@ def run(host: str, port: int, reload: bool, path: str):
|
|
73
74
|
try:
|
74
75
|
from presentation.web.main import app
|
75
76
|
except ImportError as e:
|
76
|
-
click.echo(click.style("ERROR: Failed to import
|
77
|
+
click.echo(click.style("ERROR: Failed to import Vega Web app", fg='red'))
|
77
78
|
click.echo(f"\nDetails: {e}")
|
78
79
|
click.echo("\nMake sure:")
|
79
80
|
click.echo(" 1. You are in the project directory or use --path")
|
@@ -34,7 +34,7 @@ def cli():
|
|
34
34
|
|
35
35
|
@cli.command()
|
36
36
|
@click.argument('project_name')
|
37
|
-
@click.option('--template', default='basic', help='Project template (basic,
|
37
|
+
@click.option('--template', default='basic', help='Project template (basic, web, ai-rag)')
|
38
38
|
@click.option('--path', default='.', help='Parent directory for project')
|
39
39
|
def init(project_name, template, path):
|
40
40
|
"""
|
@@ -49,7 +49,7 @@ def init(project_name, template, path):
|
|
49
49
|
|
50
50
|
Examples:
|
51
51
|
vega init my-app
|
52
|
-
vega init my-api --template=
|
52
|
+
vega init my-api --template=web
|
53
53
|
vega init my-ai --template=ai-rag --path=./projects
|
54
54
|
"""
|
55
55
|
init_project(project_name, template, path)
|
@@ -88,8 +88,8 @@ def generate(component_type, name, path, impl, request, response):
|
|
88
88
|
service - Service interface (domain layer)
|
89
89
|
interactor - Use case (business logic)
|
90
90
|
mediator - Workflow (orchestrates use cases)
|
91
|
-
router -
|
92
|
-
middleware -
|
91
|
+
router - Vega Web router (requires web module)
|
92
|
+
middleware - Vega Web middleware (requires web module)
|
93
93
|
webmodel - Pydantic request/response models (requires web module)
|
94
94
|
model - SQLAlchemy model (requires sqlalchemy module)
|
95
95
|
command - CLI command (async by default)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"""Scaffolding helpers for Vega CLI."""
|
2
|
+
|
3
|
+
from .vega_web import create_vega_web_scaffold
|
4
|
+
from .sqlalchemy import create_sqlalchemy_scaffold
|
5
|
+
|
6
|
+
# Backward compatibility alias
|
7
|
+
create_fastapi_scaffold = create_vega_web_scaffold
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
"create_vega_web_scaffold",
|
11
|
+
"create_fastapi_scaffold", # Deprecated: use create_vega_web_scaffold
|
12
|
+
"create_sqlalchemy_scaffold",
|
13
|
+
]
|
@@ -0,0 +1,109 @@
|
|
1
|
+
"""Vega Web scaffolding for new projects"""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Callable, Iterable
|
7
|
+
|
8
|
+
import click
|
9
|
+
|
10
|
+
from vega.cli.templates import (
|
11
|
+
render_vega_app,
|
12
|
+
render_vega_health_route,
|
13
|
+
render_vega_main,
|
14
|
+
render_vega_routes_init_autodiscovery,
|
15
|
+
render_vega_user_route,
|
16
|
+
render_pydantic_models_init,
|
17
|
+
render_pydantic_user_models,
|
18
|
+
render_web_package_init,
|
19
|
+
)
|
20
|
+
|
21
|
+
|
22
|
+
def create_vega_web_scaffold(
|
23
|
+
project_root: Path,
|
24
|
+
project_name: str,
|
25
|
+
*,
|
26
|
+
overwrite: bool = False,
|
27
|
+
echo: Callable[[str], None] | None = None,
|
28
|
+
) -> list[Path]:
|
29
|
+
"""
|
30
|
+
Create Vega Web scaffolding under the project presentation/web/ directory.
|
31
|
+
|
32
|
+
This creates a complete web application structure using Vega's built-in
|
33
|
+
web framework (built on Starlette).
|
34
|
+
|
35
|
+
Args:
|
36
|
+
project_root: Root directory of the project
|
37
|
+
project_name: Name of the project
|
38
|
+
overwrite: Whether to overwrite existing files
|
39
|
+
echo: Function to print messages (defaults to click.echo)
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
List of created file paths
|
43
|
+
"""
|
44
|
+
if echo is None:
|
45
|
+
echo = click.echo
|
46
|
+
|
47
|
+
created: list[Path] = []
|
48
|
+
web_dir = project_root / "presentation" / "web"
|
49
|
+
routes_dir = web_dir / "routes"
|
50
|
+
models_dir = web_dir / "models"
|
51
|
+
|
52
|
+
files: Iterable[tuple[Path, str]] = (
|
53
|
+
(web_dir / "__init__.py", render_web_package_init()),
|
54
|
+
(web_dir / "app.py", render_vega_app(project_name)),
|
55
|
+
(web_dir / "main.py", render_vega_main(project_name)),
|
56
|
+
(routes_dir / "__init__.py", render_vega_routes_init_autodiscovery()),
|
57
|
+
(routes_dir / "health.py", render_vega_health_route()),
|
58
|
+
(routes_dir / "users.py", render_vega_user_route()),
|
59
|
+
(models_dir / "__init__.py", render_pydantic_models_init()),
|
60
|
+
(models_dir / "user_models.py", render_pydantic_user_models()),
|
61
|
+
)
|
62
|
+
|
63
|
+
web_dir.mkdir(parents=True, exist_ok=True)
|
64
|
+
routes_dir.mkdir(parents=True, exist_ok=True)
|
65
|
+
models_dir.mkdir(parents=True, exist_ok=True)
|
66
|
+
|
67
|
+
for path, content in files:
|
68
|
+
rel_path = path.relative_to(project_root)
|
69
|
+
if path.exists() and not overwrite:
|
70
|
+
echo(
|
71
|
+
click.style(
|
72
|
+
f"WARNING: {rel_path} already exists. Skipping.",
|
73
|
+
fg="yellow",
|
74
|
+
)
|
75
|
+
)
|
76
|
+
continue
|
77
|
+
|
78
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
79
|
+
path.write_text(content, encoding="utf-8")
|
80
|
+
created.append(rel_path)
|
81
|
+
echo(f"+ Created {click.style(str(rel_path), fg='green')}")
|
82
|
+
|
83
|
+
echo("\n[TIP] Vega Web scaffold ready:")
|
84
|
+
echo(" 1. poetry install # sync dependencies (or poetry update)")
|
85
|
+
echo(" 2. poetry run vega web run --reload")
|
86
|
+
echo(" 3. Or: poetry run uvicorn presentation.web.main:app --reload")
|
87
|
+
|
88
|
+
return created
|
89
|
+
|
90
|
+
|
91
|
+
def _ensure_dependency_line(lines: list[str], name: str, spec: str) -> bool:
|
92
|
+
"""Insert dependency assignment into [tool.poetry.dependencies] if missing."""
|
93
|
+
header = "[tool.poetry.dependencies]"
|
94
|
+
try:
|
95
|
+
start = next(i for i, line in enumerate(lines) if line.strip() == header)
|
96
|
+
except StopIteration:
|
97
|
+
return False
|
98
|
+
|
99
|
+
end = start + 1
|
100
|
+
while end < len(lines) and not lines[end].startswith("["):
|
101
|
+
end += 1
|
102
|
+
|
103
|
+
block = lines[start + 1:end]
|
104
|
+
if any(line.strip().startswith(f"{name} =") for line in block):
|
105
|
+
return False
|
106
|
+
|
107
|
+
insertion = f"{name} = \"{spec}\"\n"
|
108
|
+
lines.insert(end, insertion)
|
109
|
+
return True
|