Implement `/foo/1/edit` route/view.
Allow setting HTTP verb by passing e.g. `/_PATCH` as the last segment of
a URL - this allows browsers to submit a `POST` request with a
pseudo-HTTP verb encoded in the URL which Jetzig can translate, i.e.
allowing forms to submit a `PATCH`.
Add to middleware in app's `src/main.zig`:
```zig
pub const jetzig_options = struct {
pub const middleware: []const type = &.{
jetzig.middleware.AntiCsrfMiddleware,
};
};
```
CSRF token available in Zmpl templates:
```
{{context.authenticityToken()}}
```
or render a hidden form element:
```
{{context.authenticityFormElement()}}
```
The following HTML requests are rejected (403 Forbidden) if the
submitted query param does not match the value stored in the encrypted
session (added automatically when the token is generated for a template
value):
* POST
* PUT
* PATCH
* DELETE
JSON requests are not impacted - users should either disable JSON
endpoints or implement a different authentication method to protect
them.
Move all route types into a single union and remove leftover junk.
Deprecate view functions that receive a `*jetzig.Data` argument (we will
stay backward-compatible until we have a valid reason to drop support
for legacy view functions - the extra code overhead is pretty minimal).
Add `jetzig test` command which runs build step `jetzig:test`.
Add `jetzig.testing` namespace which provides test helpers and a test
app.
Add tests to view generator (i.e. include tests for generated routes).
Create mailers with `jetzig generate mailer <name>`. Mailers define
default values for email fields (e.g. subject, from address, etc.).
Mailers use Zmpl for rendering text/HTML parts.
Send an email from a request with `request.mail()`. Call
`deliver(.background, .{})` on the return value to use the built-in
mail job to send the email asynchronously.
Improve query/HTTP request body param parsing - unescape `+` and `%XX`
characters.
Use in-memory KV store (JetKV) for simple job queue.
Build script generates an array of Zig modules in `src/app/jobs/` and
stores their name + run function (`run(allocator, params, logger)`).
View functions schedule jobs with arbitrary params.
Thread pool spawns a (configurable) number of workers and pops jobs from
the queue, then invokes the appropriate run function.