Add frontmatter, markdown, path, xml, rss, and web packages

Sync local packages into the registry repo and update index.json
and README.md to include all 9 packages.
This commit is contained in:
2026-02-24 21:04:20 -05:00
parent c5a2276f6e
commit cbb66fbb73
42 changed files with 3844 additions and 0 deletions

204
packages/web/README.md Normal file
View File

@@ -0,0 +1,204 @@
# Lux Web Framework
A full-stack web framework for building websites with Lux. Provides routing, layouts, a blog engine, form processing, SEO helpers, static file serving, and middleware — all built on Lux's effect system.
## Quick Start
```lux
import web
fn main(): Unit with {Console, HttpServer, File} = {
let app = web.app("My Site", "A Lux website", "./static", "./content")
let app = web.addRoute(app, web.get("/", homePage))
let app = web.addRoute(app, web.get("/about", aboutPage))
web.serve(app, 8080)
}
fn homePage(req: web.Request): web.Response =
web.htmlResponse(200,
web.layout(myLayout, "Home | My Site", "Welcome",
web.hero("Welcome", "Build something great", "/about", "Learn More")
)
)
```
## Features
| Feature | Description |
|---------|-------------|
| **Effects-based** | Every handler declares its side effects (`File`, `Sql`, `HttpServer`) |
| **Type-safe** | Elm-style Html builder catches broken markup at compile time |
| **Zero JS** | Server-rendered HTML. No client bundle. Pages work without JavaScript |
| **Blog engine** | Markdown + frontmatter blog with zero config |
| **SQLite built-in** | `Sql` effect is a language primitive. No ORM setup |
| **Single binary** | Compile to C via `lux compile` for single binary deployment |
## API Reference
### `web.app` — Application Builder
```lux
web.app(title, description, staticDir, contentDir): App
web.addRoute(app, route): App
web.addMiddleware(app, middleware): App
web.serve(app, port): Unit with {Console, HttpServer, File}
```
### Route Builders
```lux
web.get(pattern, handler): Route
web.post(pattern, handler): Route
web.put(pattern, handler): Route
web.delete(pattern, handler): Route
```
### `web.page` — Layout & Components
```lux
web.layout(config, title, description, body): String
web.hero(title, subtitle, ctaUrl, ctaText): String
web.miniHero(title, subtitle): String
web.card(icon, title, description): String
web.cardGrid(cards): String
web.statsBar(stats): String
web.emailSignup(heading, buttonText, action): String
web.ctaSection(heading, ctaUrl, ctaText): String
```
### `web.blog` — Blog Engine
```lux
web.loadPosts(contentDir, section): List<Post> with {File}
web.latestPosts(contentDir, section, count): List<Post> with {File}
web.loadPost(contentDir, section, slug): Option<Post> with {File}
web.blogIndexHtml(posts): String
web.blogPostHtml(post): String
web.rssFeed(title, url, description, posts): String
```
### `web.form` — Form Processing
```lux
web.parseBody(body): List<(String, String)>
web.getField(fields, name): Option<String>
web.requireField(fields, name): Result<String, String>
web.validateEmail(email): Result<String, String>
web.sanitize(input): String
```
### `web.db` — Database Helpers
```lux
web.saveSubscriber(dbPath, email): Result<Unit, String> with {Sql}
web.getSubscribers(dbPath): List<String> with {Sql}
```
### `web.seo` — SEO Helpers
```lux
web.metaTags(config): String
web.structuredData(name, description, url): String
web.sitemap(pages): String
web.robots(sitemapUrl): String
```
### `web.static` — Static File Serving
```lux
web.serveStatic(basePath, requestPath): Response with {File}
web.mimeType(path): String
web.cacheHeaders(maxAge): List<(String, String)>
```
### `web.middleware` — Composable Middleware
```lux
web.logging(): Middleware with {Console}
web.cors(origin): Middleware
web.securityHeaders(): Middleware
```
### Response Helpers
```lux
web.htmlResponse(status, body): Response
web.jsonResponse(status, body): Response
web.textResponse(status, body): Response
web.redirect(location): Response
web.redirectPermanent(location): Response
```
## Guide: Build a Marketing Site
1. Create project structure:
```
my-site/
├── lux.toml
├── main.lux
├── content/blog/
├── static/styles.css
```
2. Add dependency in `lux.toml`:
```toml
[dependencies]
web = { version = "0.1.0", path = "../lux/packages/web" }
```
3. Define your routes and pages in `main.lux`
4. Add blog posts as markdown files in `content/blog/`
5. Run: `lux main.lux`
## Guide: Add a Blog
```lux
// Load and display blog posts
let app = web.addRoute(app, web.get("/blog", fn(req: web.Request): web.Response with {File} => {
let posts = web.loadPosts("./content", "blog")
web.htmlResponse(200, web.layout(myLayout, "Blog", "Our blog",
web.miniHero("Blog", "Latest posts") + web.blogIndexHtml(posts)
))
}))
// Individual post pages
let app = web.addRoute(app, web.get("/blog/:slug", fn(req: web.Request): web.Response with {File} => {
let slug = web.getPathParam(req.path, "/blog/:slug", "slug")
match slug {
Some(s) => match web.loadPost("./content", "blog", s) {
Some(post) => web.htmlResponse(200, web.layout(myLayout, post.title, post.excerpt, web.blogPostHtml(post))),
None => web.htmlResponse(404, "Not found")
},
None => web.htmlResponse(404, "Not found")
}
}))
```
## Guide: Handle Form Submissions
```lux
let app = web.addRoute(app, web.post("/api/subscribe", fn(req: web.Request): web.Response with {Sql} => {
let fields = web.parseBody(req.body)
match web.getField(fields, "email") {
Some(email) => match web.validateEmail(email) {
Ok(validEmail) => {
web.saveSubscriber("data/subscribers.db", validEmail)
web.redirect("/thanks")
},
Err(msg) => web.htmlResponse(400, msg)
},
None => web.htmlResponse(400, "Email required")
}
}))
```
## Architecture
The framework is built on Lux's algebraic effect system:
- **`HttpServer` effect** — Accepts connections and sends responses
- **`File` effect** — Reads static files and markdown content
- **`Sql` effect** — SQLite database for form submissions
- **`Console` effect** — Request logging
Effects are declared in function signatures, making it clear what each handler does. For testing, swap effect handlers to mock the database, filesystem, or HTTP server.