Metadata-Version: 2.4
Name: dbbasic-web
Version: 0.1.2
Summary: Unixy micro-framework: file routing, SSE/WS, jobs, bus, flat files.
Author-email: DBBasic Team <hello@dbbasic.com>
License: MIT
Project-URL: Homepage, https://dbbasic.com
Project-URL: Documentation, https://github.com/dbbasic/dbbasic-web
Project-URL: Repository, https://github.com/dbbasic/dbbasic-web
Project-URL: Issues, https://github.com/dbbasic/dbbasic-web/issues
Keywords: web,framework,cgi,unix,tsv,async,asgi
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: AsyncIO
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: uvicorn[standard]>=0.30.0
Requires-Dist: jinja2>=3.1.4
Requires-Dist: watchfiles>=0.21.0
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: itsdangerous>=2.2.0
Requires-Dist: websockets>=12.0
Requires-Dist: dbbasic-tsv
Requires-Dist: dbbasic-queue
Requires-Dist: dbbasic-pipe
Requires-Dist: dbbasic-sessions
Dynamic: license-file

# dbbasic-web

A Unix-philosophy micro-framework that restores the simplicity of CGI while delivering modern performance (~4000 req/s vs 100 req/s for traditional CGI).

## Philosophy

Modern web frameworks moved away from Unix principles and lost key capabilities:
- ❌ Message passing → bolted-on queue systems
- ❌ Cron jobs → separate background job libraries
- ❌ Filesystem routing → giant route tables
- ❌ Flat files → everything forced into SQL
- ❌ Streams → poor SSE/WebSocket support

**dbbasic-web restores these capabilities** using:
- ✅ Filesystem routing (like CGI, but async)
- ✅ TSV-based storage, queues, and streams (no Redis/SQL required)
- ✅ First-class WebSockets and Server-Sent Events
- ✅ Background jobs with dbbasic-queue
- ✅ Message bus with dbbasic-pipe
- ✅ Flat files alongside databases

## Features

### 1. Filesystem Routing
No route tables. No decorators. Just files:

```
api/hello.py        → handles /hello
api/user.py         → handles /user, /user/123, /user/123/posts
templates/about.html → renders /about
public/css/app.css  → serves /css/app.css
```

### 2. Hierarchical API Handlers
Each handler can manage its own sub-routes:

```python
# api/user.py
def handle(request):
    path_parts = request['path_parts']  # ['user', '123', 'edit']

    if len(path_parts) == 1:
        return list_users()
    elif len(path_parts) == 2:
        return get_user(path_parts[1])
    elif len(path_parts) == 3:
        return edit_user(path_parts[1], path_parts[2])
```

### 3. TSV-Based Storage (No SQL Required)
```python
from dbbasic_web.storage import write_text, read_text

# Flat files with automatic directory creation
write_text("notes/2024-01-15.txt", "Meeting notes...")
content = read_text("notes/2024-01-15.txt")
```

### 4. Background Jobs (No Celery/Redis)
```python
from dbbasic_web.jobs import enqueue

# Jobs stored in TSV files
enqueue("write_flatfile", relpath="exports/data.csv", content=csv_data)
```

Run worker:
```bash
python manage.py worker
```

### 5. Message Bus (No Kafka/Redis)
```python
from dbbasic_web.bus import EventBus

bus = EventBus()
await bus.publish("notifications", {"user_id": 123, "event": "login"})

async for message in bus.consume("notifications", group="processors", consumer="worker-1"):
    print(message['data'])
```

### 6. WebSockets & Server-Sent Events
Built-in, no configuration needed:

**WebSocket:**
```javascript
const ws = new WebSocket('ws://localhost:8000/ws/room-name');
ws.send(JSON.stringify({message: 'Hello!'}));
```

**SSE:**
```javascript
const events = new EventSource('/sse/counter');
events.addEventListener('tick', (e) => console.log(e.data));
```

## Quick Start

### Installation

```bash
pip install dbbasic-web
```

Or install from source:
```bash
git clone https://github.com/askrobots/dbbasic-web.git
cd dbbasic-web
pip install -e .
```

### Run the Server

```bash
python manage.py serve
```

Visit: http://localhost:8000

### Project Structure

```
dbbasic-web/
├── dbbasic_web/
│   ├── api/              # API handlers (filesystem routing)
│   │   ├── hello.py      # /hello
│   │   └── user.py       # /user, /user/*, /user/*/posts
│   ├── templates/        # Jinja2 templates
│   │   ├── base.html
│   │   └── index.html
│   ├── public/           # Static files
│   │   ├── css/app.css
│   │   └── js/app.js
│   ├── asgi.py           # ASGI application
│   ├── router.py         # Filesystem router
│   ├── jobs.py           # Background jobs
│   ├── bus.py            # Message bus
│   ├── storage.py        # Flat-file storage
│   ├── websocket.py      # WebSocket hub
│   └── sse.py            # Server-Sent Events
├── _data/                # Auto-created data directory
│   ├── jobs.tsv          # Job queue
│   └── streams/          # Message bus streams
├── manage.py             # CLI
└── pyproject.toml
```

## Creating API Endpoints

### Simple Endpoint

```python
# api/status.py
import json
from dbbasic_web.responses import json as json_response

def handle(request):
    return json_response(json.dumps({"status": "ok"}))
```

Access: `GET /status`

### REST Resource with Sub-Routes

```python
# api/posts.py
def handle(request):
    parts = request['path_parts']
    method = request['method']

    # /posts
    if len(parts) == 1:
        if method == 'GET':
            return list_posts()
        elif method == 'POST':
            return create_post(request)

    # /posts/123
    elif len(parts) == 2:
        post_id = parts[1]
        if method == 'GET':
            return get_post(post_id)
        elif method == 'PUT':
            return update_post(post_id, request)

    # /posts/123/comments
    elif len(parts) == 3 and parts[2] == 'comments':
        return get_comments(parts[1])
```

## Performance

| Implementation | Requests/sec |
|---------------|--------------|
| Traditional CGI | ~100 |
| dbbasic-web | ~4000 |

Achieved by combining:
- Async I/O (ASGI/uvicorn)
- Filesystem routing (no regex matching)
- Direct module imports (no middleware chains)
- TSV files instead of database round-trips

## Dependencies

Minimal and purposeful:
- `uvicorn` - ASGI server
- `jinja2` - Templates
- `dbbasic-tsv` - TSV database
- `dbbasic-queue` - Job queue
- `dbbasic-pipe` - Message streams
- `dbbasic-sessions` - Authentication
- `websockets` - WebSocket support

**No Redis. No Celery. No PostgreSQL required.**

## Commands

```bash
# Run development server
python manage.py serve

# Run background job worker
python manage.py worker

# Interactive shell
python manage.py shell
```

## Philosophy in Action

This framework proves that:
1. **Simplicity scales** - Filesystem routing is faster than route tables
2. **Flat files work** - TSV beats Redis for many use cases
3. **Unix was right** - Pipes, files, and processes are enough
4. **Less is more** - 8000 lines total vs 200k+ for Django

## License

MIT

## Contributing

Built for clarity and hackability. Every module is under 500 lines. Read the source.
