nyxa 0.1.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.

Potentially problematic release.


This version of nyxa might be problematic. Click here for more details.

nyxa-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,397 @@
1
+ Metadata-Version: 2.3
2
+ Name: nyxa
3
+ Version: 0.1.0
4
+ Summary: Batteries-included DX for Python API backends on FastAPI — convention-based CLI and runtime bases
5
+ Keywords: fastapi,artisan,cli,scaffold,backend,api,nyxa
6
+ Author: Al-Amin Islam Nerob
7
+ Author-email: Al-Amin Islam Nerob <alamin@aincoder.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Framework :: FastAPI
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
19
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
+ Classifier: Typing :: Typed
21
+ Requires-Dist: fastapi>=0.115.0
22
+ Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: pydantic-settings>=2.0
24
+ Requires-Dist: python-dotenv>=1.0.0
25
+ Requires-Dist: typer>=0.12.0
26
+ Requires-Dist: uvicorn>=0.30.0
27
+ Requires-Python: >=3.11
28
+ Project-URL: Homepage, https://nyxadev.com
29
+ Project-URL: Repository, https://github.com/NyxaDev/nyxa
30
+ Project-URL: Documentation, https://nyxadev.com
31
+ Description-Content-Type: text/markdown
32
+
33
+ # nyxa
34
+
35
+ Batteries-included DX for **Python API backends** on FastAPI.
36
+
37
+ **Nyxa** is pronounced *Nik-sah* (`/ˈnɪksə/`). Site: [nyxadev.com](https://nyxadev.com) · Repo: [NyxaDev/nyxa](https://github.com/NyxaDev/nyxa).
38
+
39
+ Nyxa gives you a convention-based CLI (`nyxa`, with an `artisan` alias), project layout, central `routes.py` + controllers, and small runtime bases (`AppError`, abort helpers, `register`, events). It is **backend/API only** — no server-side templates, Vite, or frontend stack.
40
+
41
+ > Long-term plans: see the workspace [`roadmap/`](../../roadmap/).
42
+
43
+ ---
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ uv add nyxa
49
+ # or
50
+ pip install nyxa
51
+ ```
52
+
53
+ The import and CLI stay **`nyxa`**:
54
+
55
+ ```python
56
+ from nyxa import AppError, Route, register
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Quick start
62
+
63
+ ```bash
64
+ uvx --from nyxa nyxa new myapp
65
+ cd myapp
66
+ uv sync
67
+ uv run dev
68
+ ```
69
+
70
+ Interactive mode (TTY) prompts for database, auth, and optional packs. For CI:
71
+
72
+ ```bash
73
+ nyxa new myapp --database sqlite --auth none --no-input
74
+ ```
75
+
76
+ Cheat sheet: [`docs/cheat-sheet.md`](../../docs/cheat-sheet.md).
77
+ Adapter upgrades: [`docs/upgrading.md`](../../docs/upgrading.md).
78
+
79
+ ### CORS and logging
80
+
81
+ Optional; off by default. Configure in `nyxa.toml` or env — applied inside `register()`:
82
+
83
+ ```toml
84
+ [cors]
85
+ origins = ["http://localhost:3000"]
86
+
87
+ [logging]
88
+ json = true
89
+ level = "INFO"
90
+ ```
91
+
92
+ ```bash
93
+ CORS_ORIGINS=http://localhost:3000
94
+ NYXA_LOG_JSON=1
95
+ ```
96
+
97
+ Or call `configure_cors(app, origins=[...])` / `configure_logging()` manually.
98
+
99
+ Scaffold into the current directory:
100
+
101
+ ```bash
102
+ mkdir myapp && cd myapp
103
+ uvx --from nyxa nyxa init
104
+ ```
105
+
106
+ Monorepo / unpublished local checkout:
107
+
108
+ ```bash
109
+ uv run nyxa new myapp --nyxa-path /path/to/packages/nyxa
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Routing
115
+
116
+ Define routes in `src/{app}/routes.py`. Nyxa **compiles** them at startup into normal FastAPI endpoints (no Route stack on the request hot path).
117
+
118
+ | What you need | Nyxa |
119
+ | --- | --- |
120
+ | Map a path to a controller action | `Route.get("/users", [UserController, "index"])` |
121
+ | Group routes under a prefix | `with Route.prefix("/api"):` |
122
+ | Name a route | `.name("users.index")` |
123
+ | RESTful resource routes | `Route.resource("posts", PostController)` |
124
+ | List compiled routes | `nyxa route:list` |
125
+
126
+ ```python
127
+ # src/myapp/routes.py
128
+ from nyxa.routing import Route
129
+ from myapp.http.controllers.user_controller import UserController
130
+
131
+ with Route.prefix("/api"):
132
+ Route.resource("users", UserController)
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Stable CLI (0.1)
138
+
139
+ Entry points: `nyxa`, `artisan` (alias), and `dev`.
140
+
141
+ Generated scaffolds include **Google-style docstrings** (`Args` / `Returns`) on modules, classes, and functions.
142
+
143
+ | Command | Purpose |
144
+ | --- | --- |
145
+ | `nyxa new` / `nyxa init [name]` | Scaffold a new API app (`--database`, `--auth`, `--no-input`, `--queue`/`--cache`/`--mail`/`--storage`, `--force`, `--nyxa-path`) |
146
+ | `nyxa make:controller` | Controller + auto route; shorts `-a`/`-m`/`-r`/`-R`/`-e`/`-s` |
147
+ | `nyxa make:exc` | Error + handler; nested paths (`user/NotFound`); status heuristic + `--status` |
148
+ | `nyxa make:req` / `make:res` | FormRequest / response models |
149
+ | `nyxa make:policy` | Policy class (`nyxa-auth`) |
150
+ | `nyxa make:middleware` | Middleware class |
151
+ | `nyxa make:provider` | Service provider (`register` / `boot`) |
152
+ | `nyxa make:service` | Service class |
153
+ | `nyxa make:dependency` | FastAPI dependency |
154
+ | `nyxa make:schema` / `make:enum` / `make:config` | Schema, enum, settings |
155
+ | `nyxa make:event` / `make:listener` / `make:job` | Events, listeners (`--event` required), jobs (`Job` class when `nyxa-queue` installed) |
156
+ | `nyxa queue work` | Run a job once in-process (`nyxa-queue`) |
157
+ | `nyxa cache clear` | Flush cache store (`nyxa-cache`) |
158
+ | `nyxa mail test` | Send sample mail via configured mailer (`nyxa-mail`) |
159
+ | `nyxa make:test` | Pytest stub |
160
+ | `nyxa make:resource` | Full CRUD slice + `Route.resource` stub |
161
+ | `nyxa route:list` / `list:routes` | List routes (method, URI, name, action) |
162
+ | `dev` | Run uvicorn using `nyxa.toml` `[dev]` |
163
+
164
+ ---
165
+
166
+ ## Stable public API (0.1)
167
+
168
+ Supported imports for application code:
169
+
170
+ ```python
171
+ from nyxa import (
172
+ AppError,
173
+ Controller,
174
+ EventDispatcher,
175
+ ForbiddenError,
176
+ FormRequest,
177
+ Route,
178
+ ServiceProvider,
179
+ abort,
180
+ bind,
181
+ check,
182
+ config,
183
+ context,
184
+ current_user,
185
+ dispatcher,
186
+ env,
187
+ guest,
188
+ no_content,
189
+ not_found,
190
+ register,
191
+ route,
192
+ settings,
193
+ success,
194
+ user,
195
+ )
196
+ ```
197
+
198
+ | Symbol | Role |
199
+ | --- | --- |
200
+ | `AppError` | Base application error (override via `[bases].error`); default JSON handler on `register` |
201
+ | `UnauthorizedError` / `ForbiddenError` / `NotFoundError` / … | Core HTTP-mapped `AppError` subclasses (401–429) |
202
+ | `abort` / `unauthorized` / `forbidden` / `not_found` / … | Raise helpers (`NoReturn`) for common exits |
203
+ | `success` / `created` / `accepted` / `no_content` / `json_response` | JSON envelope / empty-body helpers |
204
+ | `context` / `ContextMiddleware` | Request-scoped key/value bag (auto-wired by `register`) |
205
+ | `user` / `current_user` / `guest` / `check` | Auth stubs reading context (filled by `nyxa-auth`) |
206
+ | `env` / `config` / `settings` | Process env + BaseSettings under `config/` (`model_dump` + typed `settings(stem)`) |
207
+ | `Route` / `Controller` | Routing DSL + controller base (constructor `Depends` OK) |
208
+ | `route(name, **params)` | Reverse URL helper from compiled named routes |
209
+ | `FormRequest` | Pydantic request base (`authorize()` → `ForbiddenError` when falsy) |
210
+ | `ServiceProvider` | App boot hooks (`register` / `boot`) discovered under `providers/` |
211
+ | `bind` / `singleton` / `instance` / `make` / `resolve` / `inject` / `has` | Lean service container (flushed on `register`) |
212
+ | `ForbiddenError` | HTTP 403 `AppError` subclass |
213
+ | `register(app)` | Default AppError handler, custom handlers, middleware, context, container flush, providers, `routes.py`, listeners |
214
+ | `dispatcher` / `EventDispatcher` | In-process events |
215
+
216
+ Everything else under `nyxa.*` (`templates`, `generate`, `make`, `config` internals, …) is **private** and may change without notice.
217
+
218
+ See also [`API.md`](API.md).
219
+
220
+ ---
221
+
222
+ ## Config (`nyxa.toml`, optional)
223
+
224
+ All keys optional. Defaults:
225
+
226
+ ```toml
227
+ app = "myapp" # required in practice — set by `init` / `new`
228
+ src = "src"
229
+
230
+ [dev]
231
+ module = "myapp.main:app" # default: "{app}.main:app"
232
+ host = "0.0.0.0"
233
+ port = 8000
234
+ reload = true
235
+
236
+ [paths]
237
+ errors = "http/exception/errors"
238
+ handlers = "http/exception/handlers"
239
+ requests = "http/requests"
240
+ responses = "http/responses"
241
+ middleware = "http/middleware"
242
+ controllers = "http/controllers"
243
+ dependencies = "http/dependencies"
244
+ services = "services"
245
+ schemas = "schemas"
246
+ enums = "domain/enums"
247
+ config = "config"
248
+ events = "events"
249
+ listeners = "listeners"
250
+ jobs = "jobs"
251
+ providers = "providers"
252
+ tests = "tests" # relative to project root
253
+
254
+ [middleware]
255
+ # global = ["timing"] # optional Kernel-style list (module stems); request-inbound order
256
+ # When unset, all http/middleware/*/Middleware exports are auto-discovered (alphabetical).
257
+
258
+ [providers]
259
+ # boot = ["app_service_provider"] # optional ordered list (module stems under paths.providers)
260
+ # When unset, all providers/*/Provider exports are auto-discovered (alphabetical).
261
+
262
+ [env]
263
+ file = ".env" # primary dotenv under project root
264
+ # environment = "local" # also load .env.{environment}; else NYXA_ENV / APP_ENV
265
+
266
+ [bases]
267
+ error = "nyxa.errors:AppError"
268
+ ```
269
+
270
+ Fallback: `[tool.nyxa] app = "..."` in `pyproject.toml`.
271
+
272
+ ### Middleware (two concepts)
273
+
274
+ | Mechanism | Role |
275
+ | --- | --- |
276
+ | `Route.middleware(...)` / `Route.group(..., middleware=[...])` | FastAPI **`Depends`** on routes (per-route / group) |
277
+ | Discovered `Middleware` export under `http/middleware/` | Global **Starlette** `BaseHTTPMiddleware` stack via `register` |
278
+
279
+ Use `[middleware] global = ["timing", ...]` for an explicit Kernel-style order (first entry is outermost / sees the request first). Omit `global` to auto-discover all modules alphabetically.
280
+
281
+ ### Service providers
282
+
283
+ App boot hooks under `providers/`. Each module exports `Provider` (a `ServiceProvider` subclass) with optional `register` / `boot` methods. `register(app)` calls **all** `register` hooks, then **all** `boot` hooks, before loading `routes.py`.
284
+
285
+ ```bash
286
+ uv run nyxa make:provider AppService
287
+ ```
288
+
289
+ ```toml
290
+ [providers]
291
+ boot = ["app_service_provider"] # optional; omit for alphabetical discovery
292
+ ```
293
+
294
+ ### Service container
295
+
296
+ Lean bind/resolve for app-scoped services. Bind in a provider; inject via FastAPI:
297
+
298
+ ```python
299
+ from nyxa import singleton, inject
300
+
301
+ # in ServiceProvider.register:
302
+ singleton(UserService)
303
+
304
+ # in controller __init__:
305
+ service: Annotated[UserService, Depends(inject(UserService))]
306
+ ```
307
+
308
+ Also: `bind`, `instance`, `make` / `resolve`, `has`. The global `container` is flushed at the start of each `register()`.
309
+
310
+ ### Environment (`.env`)
311
+
312
+ `register(app)` loads dotenv files into the process environment:
313
+
314
+ 1. Primary file from `[env] file` (default `.env`)
315
+ 2. Optional `.env.{environment}` when `[env] environment` is set, else `NYXA_ENV`, else `APP_ENV`
316
+
317
+ Among files, the environment-specific file overrides the primary for shared keys. Existing OS/CI variables are **never** overridden. Missing files are no-ops. `init` / `new` write `.env.example` as a template — copy to `.env` (and optionally `.env.local`, etc.) locally (gitignored).
318
+
319
+ ```toml
320
+ [env]
321
+ file = ".env"
322
+ environment = "local" # loads .env then .env.local
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Out of scope (for now)
328
+
329
+ - Database / migrations — **Phase 2** (`nyxa-db`) — Done
330
+ - Auth / policies — **Phase 3** (`nyxa-auth`) — **Done** (JWT, sessions, personal tokens, Google OAuth)
331
+ - Queues, cache, mail, storage — **Phase 4 Done** (`nyxa-queue`, `nyxa-cache`, `nyxa-mail`, `nyxa-storage`)
332
+ - Frontend scaffolding — never (API-only)
333
+
334
+ ---
335
+
336
+ ## Tests
337
+
338
+ ```bash
339
+ # from workspace root
340
+ uv sync --group dev
341
+ uv run pytest packages/nyxa/tests
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Publish (maintainers)
347
+
348
+ ### GitHub Release (recommended)
349
+
350
+ [`.github/workflows/publish-nyxa.yml`](../../.github/workflows/publish-nyxa.yml) publishes core `nyxa` and all seven `nyxa-*` packs from one Release tag, using PyPI Trusted Publishing (OIDC, no API token secret).
351
+
352
+ **One-time setup**
353
+
354
+ 1. In GitHub → **Settings → Environments**, create one environment per package: `pypi-nyxa`, `pypi-nyxa-db`, `pypi-nyxa-auth`, `pypi-nyxa-queue`, `pypi-nyxa-cache`, `pypi-nyxa-mail`, `pypi-nyxa-storage`, `pypi-nyxa-postgres`.
355
+ 2. On PyPI, add a Trusted Publisher for each package (GitHub tab):
356
+
357
+ | PyPI project name | Owner | Repository name | Workflow name | Environment name |
358
+ | --- | --- | --- | --- | --- |
359
+ | `nyxa` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa` |
360
+ | `nyxa-db` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa-db` |
361
+ | `nyxa-auth` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa-auth` |
362
+ | `nyxa-queue` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa-queue` |
363
+ | `nyxa-cache` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa-cache` |
364
+ | `nyxa-mail` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa-mail` |
365
+ | `nyxa-storage` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa-storage` |
366
+ | `nyxa-postgres` | `NyxaDev` | `Nyxa` | `publish-nyxa.yml` | `pypi-nyxa-postgres` |
367
+
368
+ For a project that doesn't exist yet, register it as a *pending* publisher at [pypi.org/manage/account/publishing](https://pypi.org/manage/account/publishing/). PyPI allows 3 pending publishers per account at a time, and each slot frees up once its project is created. Pending publishers must also be unique on owner/repo/workflow/environment, which is why every package has its own environment.
369
+
370
+ **Each release**
371
+
372
+ ```bash
373
+ gh release create v0.1.1 --target master --title "Nyxa v0.1.1" --notes "…"
374
+ gh run watch
375
+ ```
376
+
377
+ The tag (leading `v` stripped) becomes the version of every `packages/nyxa*/pyproject.toml`. The workflow builds all eight packages once, then runs one publish job per package in its own environment. A failed job can be re-run with `gh run rerun <run-id> --failed`; files already on PyPI are skipped.
378
+
379
+ ### Verify a build locally
380
+
381
+ From the workspace root, build exactly as CI does, then install the wheels into a scratch environment:
382
+
383
+ ```bash
384
+ for p in nyxa nyxa-db nyxa-auth nyxa-queue nyxa-cache nyxa-mail nyxa-storage nyxa-postgres; do
385
+ uv build --package "$p" --no-sources --out-dir /tmp/nyxa-dist
386
+ done
387
+ uvx twine check /tmp/nyxa-dist/*
388
+ uvx --from /tmp/nyxa-dist/nyxa-0.1.0-py3-none-any.whl nyxa new /tmp/demoapp --no-input
389
+ # resolve the scaffold against local wheels before they are on PyPI:
390
+ cd /tmp/demoapp && uv sync --find-links /tmp/nyxa-dist
391
+ ```
392
+
393
+ ---
394
+
395
+ ## Roadmap
396
+
397
+ Workspace plans: [`roadmap/README.md`](../../roadmap/README.md).