Metadata-Version: 2.4
Name: workpeg
Version: 0.22.0
Summary: Workpeg function runtime and SDK
Author-email: Workpeg <support@workpeg.com>
License: MIT
Project-URL: Homepage, https://developers.workpeg.com
Project-URL: Repository, https://gitlab.com/workpeg/workpeg-sdk
Keywords: workpeg,serverless,functions,runtime
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31.0
Requires-Dist: rjsmin>=1.2.0
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
Dynamic: license-file

# Workpeg SDK

Python SDK and CLI for building, packaging, publishing, and distributing Workpeg Pegs.

Workpeg is a platform for developing portable applications called **Pegs**. Pegs can be built, packaged, distributed through the PegStore, and integrated into the broader Workpeg ecosystem.

The SDK covers:

- **Functions** — serverless backend logic, Docker packaging, registry publishing
- **CDN** — static asset uploads for Peg branding and distribution
- **UI Compiler** — compile Python UI apps directly to browser JavaScript

---

## Installation

```bash
pip install workpeg
```

Install locally from source:

```bash
pip install -e .
```

Verify:

```bash
workpeg --version
```

---

## CLI

Workpeg uses a namespace-oriented CLI.

```
workpeg function ...
workpeg cdn ...
workpeg ui ...
```

| Namespace  | Purpose                                           |
| ---------- | ------------------------------------------------- |
| `function` | Create, build, run, and publish Workpeg Functions |
| `cdn`      | Upload public Peg assets to the CDN               |
| `ui`       | Scaffold, compile, and serve Python Peg UI apps   |

```bash
workpeg --help
workpeg function --help
workpeg cdn --help
workpeg ui --help
```

---

## Functions

Functions are the backend logic of a Peg. They are packaged as Docker images and published to the Workpeg Registry.

**Create a project:**

```bash
workpeg function new hello
```

```
hello/
├── app/
│   ├── __init__.py
│   └── main.py
├── Dockerfile
├── requirements.txt
└── README.md
```

**Write a function:**

```python
def main(context, payload):
    return {
        "message": "Hello from Workpeg",
        "payload": payload,
    }
```

**Run locally (no Docker):**

```bash
echo '{"context": {}, "payload": {"name": "world"}}' \
  | workpeg function runtime
```

**Run with Docker:**

```bash
workpeg function build
workpeg function run --with docker
```

**Publish:**

```bash
export WORKPEG_PK=<your-token>
workpeg function submit hello:1.0.0
```

Full reference: [docs/functions.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/functions.md)

---

## CDN

Upload static assets — logos, screenshots, banners — to the Peg CDN.

```bash
export WORKPEG_PK=<your-token>

# Single file
workpeg cdn submit logo.svg

# Directory
workpeg cdn submit ./assets

# With explicit CDN path
workpeg cdn submit ./assets --path brand

# Replace existing
workpeg cdn submit ./assets --overwrite
```

Full reference: [docs/cdn.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/cdn.md)

---

## UI Compiler

Write Peg UI apps in Python. The SDK compiles them to vanilla JavaScript — no React, no Vue, no Python in the browser.

```
Python App (single-page or multi-route Router)
    ↓  AST Parser
Workpeg IR (AppIR | RouterIR)
    ↓  JS Generator
app.js  +  workpeg-runtime.js  +  index.html
```

**Scaffold, compile, and run in one command:**

```bash
workpeg ui new counter
```

```
Creating counter...

  counter/main.py
  counter/dist/

  Serving  →  http://localhost:8080
  Watching →  main.py
  Stop     →  Ctrl+C
```

This creates a ready-to-run Counter app, compiles the Python to JavaScript, and opens a local dev server with **live reload**. Open `http://localhost:8080` in any browser. Edit `counter/main.py` and save — the browser reloads automatically.

**Project structure:**

```
counter/
├── main.py              ← Router entry point
├── pages/
│   ├── __init__.py
│   ├── home.py          ← HomePage
│   └── counter.py       ← CounterPage
└── dist/
    ├── index.html
    ├── app.js
    └── workpeg-runtime.js
```

**`main.py`** — Router with routes declared:

```python
from workpeg.ui import Router, Route

from pages.home import HomePage
from pages.counter import CounterPage

class Counter(Router):
    routes = [
        Route("/", HomePage),
        Route("/counter", CounterPage),
    ]
```

**`pages/counter.py`** — counter logic:

```python
from workpeg.ui import App, State, Column, Text, Button, Link

class CounterPage(App):
    count = State(0)

    def increment(self):
        self.count += 1

    def build(self):
        return Column(
            Text(f"Count: {self.count}"),
            Button("Increment", on_click=self.increment),
            Link("← Home", href="/"),
        )
```

**Options for `new`:**

```bash
# Custom port
workpeg ui new counter --port 3000

# Scaffold and compile only, no server
workpeg ui new counter --no-serve

# Overwrite an existing project
workpeg ui new counter --force
```

**Multi-page apps with routing:**

```python
from workpeg.ui import App, Router, Route, Column, Text, Link

class Home(App):
    def build(self):
        return Column(Text("Home"), Link("About", href="/about"))

class About(App):
    def build(self):
        return Column(Text("About"), Link("Home", href="/"))

class MyApp(Router):
    routes = [
        Route("/", Home),
        Route("/about", About),
    ]
```

The compiler generates a History-API-based SPA. Clicking `Link` widgets with internal `href` values updates the URL and swaps the view — no full-page reload. The back and forward buttons work correctly.

**Lists work too.** State fields can hold lists, and Python list operations compile to idiomatic JavaScript — literals, slicing, comprehensions, `map`/`filter`/`reduce`, `sorted`/`len`/`in`, mutation (`append`/`pop`/`sort`/…), and `for` loops. Mutating a state list re-renders automatically:

```python
class Todos(App):
    items = State(["Buy milk", "Walk dog"])

    def add(self):
        self.items.append(f"Task {len(self.items) + 1}")   # → push, re-renders

    def build(self):
        return Column(
            Text(f"{len(self.items)} tasks"),
            Text(f"sorted: {sorted(self.items)}"),
            Text(f"with a: {[t for t in self.items if 'a' in t]}"),
            Button("Add", on_click=self.add),
        )
```

See the [full Python→JavaScript mapping](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/core-concepts.md#lists-and-sequences) in the Core concepts docs.

**Serve an existing project (with live reload):**

```bash
# Pass the project directory — main.py is found automatically
workpeg ui serve counter/

# Pin a port
workpeg ui serve counter/ --port 3000

# Explicit file form (same result)
workpeg ui serve counter/main.py

# Serve a pre-built dist directory directly (no recompile)
workpeg ui serve counter/dist/
```

**Compile only, no server:**

```bash
# Pass the project directory — output goes to counter/dist/ automatically
workpeg ui build counter/

# Custom output directory
workpeg ui build counter/ --out counter/build

# Explicit file form
workpeg ui build counter/main.py
```

Full reference: [docs/getting-started.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/getting-started.md) and the rest of the **Peg UI** docs (Core concepts, Widgets, Routing, Data & realtime, Styling, Examples, Reference).

---

## Schema-based UI

For cases where you want to define UI as a data structure (for use with the Workpeg platform renderer rather than the JS compiler), the SDK also ships a schema API:

```python
from workpeg.ui import PegApp, Page, Column, Text, Button
from workpeg.ui import State, Binding, IncrementStateAction

state = State({"counter": 0})

app = PegApp(
    title="My Peg",
    state=state,
    child=Page(
        title="Dashboard",
        child=Column(
            children=[
                Text(Binding("counter")),
                Button("Increment", action=IncrementStateAction("counter")),
            ]
        ),
    ),
)

print(app.to_schema())
```

Full reference: [docs/schema-ui.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/schema-ui.md)

---

## Documentation

| Document | Contents |
| --- | --- |
| [docs/functions.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/functions.md) | Function contract, context, runtime, Docker, publishing |
| [docs/cdn.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/cdn.md) | CDN uploads, paths, authentication, asset structure |
| [docs/getting-started.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/getting-started.md) | Peg UI: how it works, quick start, CLI, live reload |
| [docs/core-concepts.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/core-concepts.md) | `App`, `State`, `build()`, supported Python, lists, reactivity |
| [docs/widgets.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/widgets.md) | Widget gallery + full per-widget reference |
| [docs/routing.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/routing.md) · [data-realtime.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/data-realtime.md) · [state-components.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/state-components.md) · [styling.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/styling.md) | Routing, data/realtime, global state, theming |
| [docs/reference.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/reference.md) · [examples.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/examples.md) | Internals (IR, JS, runtime, errors) + complete examples |
| [docs/schema-ui.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/schema-ui.md) | Schema-based UI, PegApp, widgets, bindings, actions |
| [docs/architecture.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/docs/architecture.md) | Platform overview, pipeline diagrams, design decisions |

---

## Authentication

Both `function submit` and `cdn submit` require a Workpeg token:

```bash
export WORKPEG_PK=<your-token>
```

Optional API override:

```bash
export WORKPEG_API_BASE=https://repo.workpeg.com
```

---

## Configuration

Optional `workpeg.json` in your project root:

```json
{
  "function": {
    "entrypoint": "app.main:main"
  },
  "runtime": {
    "default": "docker",
    "docker": {
      "port": 8000
    }
  },
  "build": {
    "image": "workpeg-fn-hello"
  },
  "cdn": {
    "assets_path": "assets"
  }
}
```

---

## Roadmap

| Feature | Status |
| --- | --- |
| Function runtime (local) | Available |
| Function runtime (Docker) | Available |
| Function publish | Available |
| CDN asset uploads | Available |
| UI Compiler (Python → JS) | Available |
| `workpeg ui new` (scaffold + serve + live reload) | Available |
| Schema-based UI | Available |
| Firecracker runtime | Planned |
| Peg packaging workflows | Planned |
| PegStore publishing | Planned |
| Additional widgets | Planned |
| UI widget library | Planned |
| Multi-page routing (History API SPA) | Available |
| Method expressions (arithmetic, comparisons, ternary, `min`/`max`/`abs`) | Available |
| Control flow — `if` / `elif` / `else`, `for` loops | Available |
| Conditional rendering in `build()` (`if`/`else` → different widget trees) | Available |
| Reusable `Component` classes — props, optional own `State` (independent per instance), per-instance handlers, callback props | Available |
| Layout widgets — `Column`/`Row`, `Center`/`Align` (single-child), `GridView` (multi-child), `ListView`; custom single-child component slots | Available |
| Async data loading — `async`/`await`, `http.get`/`post`/`put`/`patch`/`delete`, `on_mount`, `try`/`except` | Available |
| `http.*` blocked from Workpeg domains (compile + runtime); reach the platform via the namespaced `workpeg.*` client | Available |
| `workpeg.get`/`post`/`put`/`patch`/`delete` — Workpeg-domains-only client; resolves paths against `apiBase` | Available |
| `workpeg.*` sends a `Namespace` whoami header (explicit > serving host > baked `peg-namespace` meta) | Available |
| Method local variables (`step = 5; self.count += step`) | Available |
| Console logging (`print()` → `console.log()`) | Available |
| Dev error overlay (compile + runtime errors in the browser) | Available |
| Fake cloud (`--fake-cloud`: local `https://dev.<app>.workpeg.com`, macOS/Linux/Windows) | Available |
| Widget categories — layout / display / interactive (Flutter-style) | Available |
| `ListView` with lazy, virtualized `ListView.builder` | Available |
| `Center` layout widget | Available |
| `Image` widget (object-fit, rounding, lazy loading, reactive src) | Available |
| `TextInput` widget (two-way binding, `on_change`/`on_submit`, focus-preserving) | Available |
| List support — literals, slicing, comprehensions, `map`/`filter`/`reduce`, mutation, `for` loops | Available |
| Reactive state arrays (in-place `append`/`sort`/… re-render) | Available |

---

## Contributing

External contributions follow GitLab Flow — fork, feature branch, merge
request against `main` at <https://gitlab.com/workpeg/workpeg-sdk>.
See [CONTRIBUTING.md](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/CONTRIBUTING.md) for the workflow, commit message
format, and development setup.

---

## License

MIT — see [LICENSE](https://gitlab.com/workpeg/workpeg-sdk/-/blob/main/LICENSE).
