Metadata-Version: 2.5
Name: dryv-author
Version: 0.0.1
Summary: Lightweight typed Python authoring SDK for Dryv
License: Apache-2.0
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# dryv-author

`dryv-author` is the Python Authoring SDK installed in an authored project. It mirrors the TypeScript author with Pythonic names, and produces a typed `AuthorState` that `dryv-compiler` lowers to canonical `dryv.ir/v1alpha1`.

The package does not depend on the Dryv Engine and never serializes Runtime IR, selects packs, renders templates, chooses output paths, or writes files.

## Example

```python
from dryv_author import body, dryv, field, http, operation, path, prop, respond, schema, storage

users = dryv.group("users")
accounts = users.feature("Accounts")                 # features are registration scopes

ids = accounts.properties({"Id": prop.string.uuid(), "Name": prop.string()})
models = accounts.schemas({
    "User": schema({
        "id": field(ids.Id).system(),
        "name": field(ids.Name).query(filter="*", sort=True),
    }).facets(storage=storage("users").primary_key("id")),
})

create = operation().role("create").inputs(models.User).outputs(models.User)
create.facets(
    http=http("POST", "/users").input(create.input(0), body()).output(create.output(0), respond(201)),
)
accounts.operations({"CreateUser": create})

users.schemas({"AuditEntry": schema({"id": field(ids.Id)})})   # group-owned: no feature

app = dryv.app("accounts").groups(users)
```

The entrypoint configured in `dryv.yaml` must expose exactly one module-level `dryv.app(...)`. It may import ordinary modules; every registration records the caller's file and line.

## Ownership

Registering through `group.feature("Name")` makes the group the declaration's `group` and the feature its `feature`. Registering directly on the group leaves it group-owned. A declaration can belong to one group and at most one feature ([decision 0011](../../../../.docs/reference/decisions/0011-declaration-owned-feature-membership.md)).

## API map (TypeScript → Python)

| TypeScript | Python |
| --- | --- |
| `property.string.uuid()` | `prop.string.uuid()` |
| `property.number.integer({ range: { min: 0 } })` | `prop.number.integer(range={"min": 0})` |
| `field(x).createOnly()` | `field(x).create_only()` |
| `.query({ filter: ["notEqual"] })` | `.query(filter=["not-equal"])` (`not_equal` also accepted) |
| `expression.and(...)`, `.or(...)`, `.not(...)` | `expression.all_of(...)`, `.any_of(...)`, `.not_(...)` |
| `operation().async()` | `operation().async_()` |
| `operationStep`, `waitFor`, `outputFrom`, `inputFrom` | `operation_step`, `wait_for`, `output_from`, `input_from` |
| `group.policies(ref1, ref2)` | same: a mapping registers, refs set scope policies |

## Development

```bash
uv sync
uv run ruff format . && uv run ruff check --fix . && uv run mypy && uv run pytest
```
