From c399af2228491a0a5d2f4e78f0cb7b26adcd8671 Mon Sep 17 00:00:00 2001 From: BizWiz Date: Tue, 2 Jun 2026 13:05:45 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"feat:=20update=20project=20metadata,?= =?UTF-8?q?=20enhance=20documentation,=20and=20add=20new=20mid=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b4e9bc46706bf4b3c2f4c5faa8b836dc4989912d. --- docs/.vitepress/config.mjs | 12 +-- docs/docs/blog/changelog.md | 42 --------- docs/docs/guides/exception.md | 69 -------------- docs/docs/guides/middleware.md | 102 ++++++++++----------- docs/docs/guides/middleware/guard.md | 77 ---------------- docs/docs/guides/middleware/interceptor.md | 91 ------------------ docs/docs/guides/middleware/validator.md | 75 --------------- pesde.toml | 8 +- 8 files changed, 52 insertions(+), 424 deletions(-) delete mode 100644 docs/docs/guides/exception.md delete mode 100644 docs/docs/guides/middleware/guard.md delete mode 100644 docs/docs/guides/middleware/interceptor.md delete mode 100644 docs/docs/guides/middleware/validator.md diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index 3c3451b..1f2e9d2 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -31,17 +31,7 @@ export default defineConfig({ { text: 'Project Stucture', link: '/docs/guides/project-structure' }, { text: 'Routing', link: '/docs/guides/routing' }, { text: 'Request & Response', link: '/docs/guides/req-res' }, - { - text: 'Middleware', - link: '/docs/guides/middleware', - items: [ - { text: 'Guard', link: '/docs/guides/middleware/guard' }, - { text: 'Validator', link: '/docs/guides/middleware/validator' }, - { text: 'Interceptor', link: '/docs/guides/middleware/interceptor' }, - - ] - }, - { text: 'Exception', link: '/docs/guides/exception' }, + { text: 'Middlewares', link: '/docs/guides/middleware' } ] }, { diff --git a/docs/docs/blog/changelog.md b/docs/docs/blog/changelog.md index c76c2a3..d8a3794 100644 --- a/docs/docs/blog/changelog.md +++ b/docs/docs/blog/changelog.md @@ -1,47 +1,5 @@ # Changelog -## `v0.6.0` - June 2, 2026 - -***`Nova.chain()` has been removed from the public API. Route-specific middleware is now handled exclusively through Attributes (`--@Guard`, `--@Interceptor`, `--@Validator`).*** - -### Added - -- Exception handling utility with common HTTP error responses (`Nova.exception`) (#76) -- Request validation via `--@Validator` attribute with support for `body`, `param`, and `query` (#73) -- Interceptor support via `--@Interceptor` attribute using onion architecture (#72) -- Guard attribute now supports multiple rules — `--@Guard(Auth, Admin)` (#72) -- Initial `--@Guard` attribute support with comment-based syntax (#69) - -### Changed - -- Startup logger now displays registered routes with their applied Attributes (#75) -- Runtime request logs now include status code and applied Attributes, with color-coded output by status class (`2xx` green, `3xx` blue, `4xx`/`5xx` red) (#75) -- Core architecture restructured and modularized; adapters renamed (#71) -- `libs/` renamed to `adapters/` (#69) -- `Nova.chain()` is no longer exposed publicly — route-specific middleware must now be defined using Attributes (#69) - -### Fixed - -- Correct status property access in `devMiddleware` (#74) - ---- - -**Before** -```luau -Home.Get = Nova.chain({ AuthMiddleware, AdminMiddleware }, function() - return Nova.response.send("Hello, World") -end) -``` - -**After** -```luau ---@Guard(AuthRule, AdminRule) ---@Interceptor(TransformRule) -function Home.Get() - return Nova.response.send("Hello, World") -end -``` - ## `v0.5.7-test` - May 4, 2026 ### Changed diff --git a/docs/docs/guides/exception.md b/docs/docs/guides/exception.md deleted file mode 100644 index 7457148..0000000 --- a/docs/docs/guides/exception.md +++ /dev/null @@ -1,69 +0,0 @@ -# Exception - -Nova provides a built-in `Exception` module for throwing structured HTTP errors from anywhere in your application — route handlers, Guard rules, Interceptors, or Validators. Exceptions are caught by Nova's global error handler and converted into a proper HTTP response automatically. - -## Usage - -Luau does not have a `throw` keyword, but `error()` behaves similarly — it halts execution immediately and can be caught by `pcall`. Nova's global error handler uses `pcall` under the hood, so it detects when an Exception is thrown and responds with the appropriate status code and message. - -```luau -local Nova = require("@nova") -local Exception = Nova.exception - -local function Auth(req: Nova.Request) - error(Exception.Forbidden()) -end - -return Auth -``` - -This reads almost exactly like `throw new ForbiddenException()` in other languages. - -## Custom Messages - -Every built-in Exception accepts an optional message. If omitted, a default message is used. - -```luau --- Uses default message: "Forbidden" -error(Exception.Forbidden()) - --- Uses a custom message -error(Exception.Forbidden("You do not have access to this resource")) -``` - -## Built-in Exceptions - -### 4xx — Client Errors - -| Function | Status | Default Message | -|---|---|---| -| `Exception.BadRequest()` | `400` | Bad Request | -| `Exception.Unauthorized()` | `401` | Unauthorized | -| `Exception.Forbidden()` | `403` | Forbidden | -| `Exception.NotFound()` | `404` | Not Found | -| `Exception.Conflict()` | `409` | Conflict | -| `Exception.TooManyRequests()` | `429` | Too Many Requests | - -### 5xx — Server Errors - -| Function | Status | Default Message | -|---|---|---| -| `Exception.InternalServerError()` | `500` | Internal Server Error | -| `Exception.BadGateway()` | `502` | Bad Gateway | -| `Exception.ServiceUnavailable()` | `503` | Service Unavailable | - -## Custom Exceptions - -If you need a status code not covered by the built-in functions, use `Exception.HttpException()` directly — it is the base function all others are built on: - -```luau -error(Exception.HttpException(418, "I'm a teapot")) -``` - -The first argument is the status code and the second is the message. Both are required. - -## Important Notes - -- `error()` halts execution immediately. Nothing after it runs. -- Exceptions are only meaningful when thrown inside Nova's request pipeline. Throwing one outside of a request context will not produce an HTTP response. -- All built-in Exceptions accept an optional custom message. `HttpException` requires one. \ No newline at end of file diff --git a/docs/docs/guides/middleware.md b/docs/docs/guides/middleware.md index 072763a..d988295 100644 --- a/docs/docs/guides/middleware.md +++ b/docs/docs/guides/middleware.md @@ -7,52 +7,45 @@ Common use cases include: - **Logging:** Tracking request times and paths. - **Authentication:** Checking if a user is logged in. - **Validation:** Ensuring the request body contains the correct data. -- **Security:** Adding headers. +- **Security:** Adding headers or blocking malicious IPs. ## The Middleware Function A middleware in Nova is a simple function that receives two arguments: -- `req` — The Request object. -- `next` — A function that, when called, passes control to the next middleware in the stack. +- `req:` The Request object. +- `next:` A function that, when called, passes control to the next middleware in the stack. ### The Onion Pattern -Nova uses the **Onion Pattern**. This means that when you call `next()`, execution **dives** into the next middleware or the final handler. Once the handler finishes, execution "bubbles" back up, running the code after `next()`. +Nova uses the **Onion Pattern**. This means that when you call `next()`, the code execution **dives** into the next middleware or the final handler. Once the handler finishes, the execution "bubbles" back up, running the code after `next()`. ```luau local function myMiddleware(req, next) print("1. This runs BEFORE the route handler") - - next() - + + next() -- Move to the next middleware or handler + print("2. This runs AFTER the route handler is finished") end ``` -**Example Flow:** - -If you have Global Middleware `A`, Attribute `B`, and Handler `C`: - -```bash -A (before next) → B (before next) → C (Handler) → B (after next) → A (after next) -``` - ## Global Middleware -Global middleware runs on **every single request** made to your server. These are defined when you initialize your Nova application. +Global middlewares are executed for **every single request** made to your server. These are defined when you initialize your Nova application. -`Nova.new()` accepts an optional second argument: a table of middleware functions. +The `Nova.new()` constructor accepts an optional second argument: a table of middleware functions. ```luau local Nova = require("path/to/nova") local app = Nova.new(8080, { - myMiddleware1 = function(req, next) + function(req, next) print(`[{req.method}] {req.path}`) next() end, - myMiddleware1 = function(req, next) + function(req, next) + -- Another global middleware next() end }) @@ -62,58 +55,57 @@ app:listen(function() end) ``` -Global middlewares execute in the order they are defined. +## Route-Specific Middleware (`Nova.chain`) + +Sometimes you only want middleware to run on specific routes (e.g., protecting a `/dashboard` route). For this, Nova provides the `Nova.chain` utility. -## Route-Specific Middleware (Attributes) +`Nova.chain` takes two arguments: -For middleware that should only run on specific routes, Nova uses **Attributes** — a comment-based syntax inspired by how decorators work in other languages. +- A table of middlewares. +- The final route handler function. -Attributes are defined directly above a route handler function using the `--@` prefix: +Usage in `route.luau` ```luau +local Nova = require("path/to/nova") + local Home = {} ---@Guard(Auth) ---@Interceptor(Transform) -function Home.Get() - return Nova.response.send("Hello, World") +-- A simple auth middleware +local function checkAuth(req, next) + if req.headers["Authorization"] then + next() + else + -- If we don't call next(), the chain stops here + return Nova.response.json({ error = "Unauthorized" }, { status = 401 }) + end end -return Home -``` - -Nova ships with three built-in Attributes, each serving a distinct purpose: - -| Attribute | Purpose | -|---|---| -| [`--@Guard`](/middleware/guard) | Protect routes — authentication and authorization | -| [`--@Interceptor`](/middleware/interceptor) | Wrap the handler — logging, response transformation | -| [`--@Validator`](/middleware/validator) | Validate incoming data — body, params, and query | - -### Execution Order - -Attributes always execute in this order, regardless of how they are defined: +-- Using Nova.chain to protect this specific GET route +Home.Get = Nova.chain({ checkAuth }, function(req) + return Nova.response.json({ + message = "Welcome to the protected dashboard!" + }) +end) -```bash -Guard → Validator → Interceptor → Route Handler +return Home ``` -Nova enforces this order at startup. If your Attributes are defined out of order, Nova will throw an error with a clear message telling you what to fix. +## Execution Order -### Convention-Driven Resolution +It is important to understand the order in which Nova executes your code: -Each Attribute references a **Rule** by name. Nova resolves Rules by convention from your `src/` directory: +- **Global Middlewares:** Executed in the order they were defined in `Nova.new`. +- **Route Middlewares:** Executed in the order they appear in the `Nova.chain` table. +- **Route Handler:** The actual logic inside your `Home.Get` or similar function. +- **The "Bubble Up":** The code after `next()` in all middlewares runs in reverse order. -| Attribute | Rule Directory | -|---|---| -| `--@Guard(...)` | `src/guards/` | -| `--@Interceptor(...)` | `src/interceptors/` | -| `--@Validator(...)` | `src/validators/` | +**Example Flow:** -See the dedicated pages for each Attribute to learn how to define Rules. +If you have Global Middleware `A`, Route Middleware `B`, and Handler` C`: +`A (before next)` → `B (before next)` → `C (Handler)` → `B (after next)` → `A (after next)` ## Important Notes -- **Always call `next()`** in global middleware. If you do not, the request will never reach the handler unless you are intentionally returning an early response. -- **Global middleware runs first**, before any Attributes on the route. -- **Attributes are route-specific** — they have no effect on routes that do not declare them. \ No newline at end of file +- **Always call `next()`:** If you do not call `next()`, the request will "hang" and never reach the handler (unless you intentionally return a response early to stop the request). +- **Order Matters:** If Middleware A depends on data added to the request by Middleware B, Middleware B must come first in the table. \ No newline at end of file diff --git a/docs/docs/guides/middleware/guard.md b/docs/docs/guides/middleware/guard.md deleted file mode 100644 index a0447a6..0000000 --- a/docs/docs/guides/middleware/guard.md +++ /dev/null @@ -1,77 +0,0 @@ -# Guard - -A Guard protects a route by running an authorization check before the handler is reached. If the check passes, the request proceeds. If it fails, Nova halts the pipeline and responds immediately. - -Guards are the first Attribute to execute in the pipeline: - -```bash -Guard → Validator → Interceptor → Route Handler -``` - -## Defining a Guard Attribute - -Apply a Guard to a route handler using the `--@Guard` comment above the function: - -```luau -local Home = {} - ---@Guard(Auth) -function Home.Get() - return Nova.response.send("Hello, World") -end - -return Home -``` - -Multiple rules can be passed to a single Guard: - -```luau ---@Guard(Auth, Admin) -function Home.Get() - return Nova.response.send("Hello, World") -end -``` - -Rules are executed left to right. If any rule fails, the pipeline halts. - -## Defining a Guard Rule - -Guard Rules live in `src/guards/`. Each file exports a single function that receives the request and returns a `boolean`. - -```luau --- src/guards/Auth.luau -local Nova = require("@nova") - -local function Auth(req: Nova.Request) - return true -- allow the request -end - -return Auth -``` - -If the rule returns `false`, Nova responds with `401 Unauthorized` and the pipeline stops. If it returns `true`, the request moves to the next step. - -## Throwing Exceptions - -Returning `false` always produces a `401`. If you need a different status code, use `Nova.exception` with Luau's `error()` instead: - -```luau -local Nova = require("@nova") -local Exception = Nova.exception - -local function Auth(req: Nova.Request) - error(Exception.Forbidden()) -end - -return Auth -``` - -`error()` halts execution immediately and Nova's global error handler catches it, responding with the appropriate status code and message. - -You can also pass a custom message: - -```luau -error(Exception.Forbidden("You do not have access to this resource")) -``` - -See the [Exception](/docs/guides/exception) page for all available exceptions. \ No newline at end of file diff --git a/docs/docs/guides/middleware/interceptor.md b/docs/docs/guides/middleware/interceptor.md deleted file mode 100644 index e6ef359..0000000 --- a/docs/docs/guides/middleware/interceptor.md +++ /dev/null @@ -1,91 +0,0 @@ -# Interceptor - -An Interceptor wraps the route handler, giving you access to both the incoming request and the outgoing response. This makes it useful for logging, response transformation, timing, and similar cross-cutting concerns. - -Interceptors are the last Attribute to execute before the handler: - -```bash -Guard → Validator → Interceptor → Route Handler` -``` - -## Defining an Interceptor Attribute - -Apply an Interceptor to a route handler using the `--@Interceptor` comment above the function: - -```luau -local Home = {} - ---@Interceptor(Transform) -function Home.Get() - return Nova.response.send("Hello, World") -end - -return Home -``` - -Multiple rules can be passed: - -```luau ---@Interceptor(Logger, Transform) -function Home.Get() - return Nova.response.send("Hello, World") -end -``` - -## Defining an Interceptor Rule - -Interceptor Rules live in `src/interceptors/`. Each file exports a single function that receives `req` and `next`. - -```luau --- src/interceptors/Logger.luau -local Nova = require("@nova") - -local function Logger(req: Nova.Request, next: Nova.Next) - print(`[{req.method}] {req.path}`) - next() -end - -return Logger -``` - -## The Onion Pattern - -Interceptors follow the **Onion Pattern**. Calling `next()` dives into the remaining pipeline — the next Interceptor rule or the final route handler. Any code after `next()` runs once the handler has finished, on the way back out. - -```luau -local Nova = require("@nova") - -local function Logger(_req: Nova.Request, next: Nova.Next) - local start = os.clock() - - next() - - local elapsed = os.clock() - start - print(`Request took {elapsed * 1000} ms`) -end - -return Logger -``` - -## Intercepting the Response - -If you return a value from an Interceptor rule, it replaces whatever the handler returned. This is how you transform or override responses: - -```luau -local Nova = require("@nova") - -local function Transform(_req: Nova.Request, next: Nova.Next) - next() - return Nova.response.json({ message = "Intercepted" }) -end - -return Transform -``` - -Here, `next()` still runs the handler, but since `Transform` does not capture its return value, it replaces the response with its own. - -To respond, you can return a raw table with `body` and `config`, or use the `Nova.response` helpers: - -```luau -return Nova.response.json({ message = "Hello" }, { status = 200, headers = {} }) -``` \ No newline at end of file diff --git a/docs/docs/guides/middleware/validator.md b/docs/docs/guides/middleware/validator.md deleted file mode 100644 index b87ba95..0000000 --- a/docs/docs/guides/middleware/validator.md +++ /dev/null @@ -1,75 +0,0 @@ -# Validator - -A Validator checks the incoming request data before it reaches the route handler. If any field fails validation, Nova responds immediately with a list of all errors. If everything passes, the request proceeds. - -Validators run after Guards and before Interceptors: - -```bash -Guard → Validator → Interceptor → Route Handler -``` - -## Defining a Validator Attribute - -Apply a Validator to a route handler using the `--@Validator` comment above the function: - -```luau -local Home = {} - ---@Validator(CreateUser) -function Home.Post() - return Nova.response.json({ message = "User created" }) -end - -return Home -``` - -## Defining a Validator Rule - -Validator Rules live in `src/validators/`. Each file exports a table with up to three optional keys: `body`, `param`, and `query`. You only need to define the ones you want to validate. - -```luau --- src/validators/CreateUser.luau -return { - body = { - username = { type = "string", required = true, min = 3, max = 20 }, - email = { type = "string", required = true, pattern = "^[%w.]+@[%w]+%.[%a]+$" }, - age = { type = "number", required = true, min = 13 }, - }, - query = { - ref = { required = false }, - }, -} -``` - -## Validation Fields - -Each field key maps to a rule definition with the following options: - -| Field | Type | Description | -|---|---|---| -| `type` | `string?` | Expected type — `"string"`, `"number"`, etc. Not applicable to `param` and `query` since they are always strings | -| `required` | `boolean?` | Whether the field must be present | -| `min` | `number?` | Minimum length (strings) or minimum value (numbers) | -| `max` | `number?` | Maximum length (strings) or maximum value (numbers) | -| `pattern` | `string?` | Lua pattern the value must match — commonly used for email format validation | - -## Validation Targets - -A Validator Rule can validate three parts of the request: - -- **`body`** — The request body. Supports all fields including `type`. -- **`param`** — Route parameters (e.g. `/users/[id]`). `type` is omitted since params are always strings. -- **`query`** — Query string values (e.g. `?page=1`). Same as `param` — `type` is omitted. - -## Error Response - -Nova validates **all fields** before responding. If multiple fields fail, the response includes every error at once rather than stopping at the first: - -```json -{ - "errors": [ - "username is required", - "email does not match the expected pattern" - ] -} -``` \ No newline at end of file diff --git a/pesde.toml b/pesde.toml index dcff63a..a3a185a 100644 --- a/pesde.toml +++ b/pesde.toml @@ -1,9 +1,9 @@ -name = "hypernova_lab/nova_core" -version = "0.6.0" +name = "nova_guild/nova_core" +version = "0.5.7" description = "A filesystem-based web framework for Luau runtimes, with out-of-the-box support for Lute, Lune and Zune." -authors = ["Hypernova Lab (https://github.com/hypernova-lab)"] +authors = ["Nova Guild (https://github.com/nova-guild)"] license = "MIT" -repository = "https://github.com/hypernova-lab/nova" +repository = "https://github.com/BizWiz3/nova" private = false includes =[ "pesde.toml",