Metadata-Version: 2.4
Name: starddb
Version: 1.0.2
Summary: A lightweight JSON document database with field-level operation queuing and concurrency support
Author: 
License-Expression: MIT
Project-URL: Homepage, https://github.com/lucidityai/stardb
Project-URL: Repository, https://github.com/lucidityai/stardb
Keywords: json,database,document-db,queue,concurrency,lightweight,embedded
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: filelock>=3.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"

# StarDDB — Python

> "Shoot for the moon. Even if you miss, you'll land among the stars." - Norman Vincent Peale

StarDDB is a lightweight, single-file JSON database for Python with field-level operation queuing, lazy loading, and automatic background persistence.

## Features

- **Lazy loading** — field values are read from disk on first access via `mmap`; untouched fields never enter memory
- **Cache eviction** — fields not accessed within a configurable threshold are dropped from memory and reloaded on demand
- **Field-level queue** — arithmetic operations are serialized per-field, preventing race conditions on shared values
- **Thread-safe** — `RLock` per field, `Lock` on the database for save/reload
- **Auto-persistence** — background thread saves every N seconds; only dirty fields are re-serialized
- **Atomic writes** — saves go to a `.tmp` file then `os.replace()` into place
- **Nested documents** — arbitrary depth with a configurable recursion limit

## Installation

```bash
pip install starddb
```

Or from source:

```bash
pip install filelock
```

## Quick Start

### Field-only usage

```python
from stardb import StarDDBField

field = StarDDBField(0)
field.update("set", 1)
field.update("mult", 5)
field.update("div", 0.5)
field.flush()
print(field.value)  # 10.0
```

### Load an existing file (lazy mode)

```python
from stardb import StarDDB

# Values are loaded from disk only when accessed
db = StarDDB("data.json", save_time=5)
hook = db.db()

hook["health"].update("sub", 30)
hook["mana"].update("mult", 2)

db.close()  # flushes, saves, stops background thread
```

### Create a new database from a dict

```python
db = StarDDB("data.json", save_time=5, database_hook={
    "player": {"health": 100, "mana": 50},
    "world": {"level": 1}
})
hook = db.db()
hook["player"]["health"].update("sub", 10)
db.close()
```

### Cache eviction

```python
# Fields unused for 30 seconds are evicted from memory
db = StarDDB("data.json", save_time=5, cache_threshold=30)
```

## API

### `StarDDBField(value=None, max_queue_size=10000, offsets=None, loader=None)`

| Parameter | Description |
|-----------|-------------|
| `value` | Initial value (any JSON-serializable type) |
| `max_queue_size` | Maximum queued operations before raising `RuntimeError` (default: 10000) |

**Properties:**
- `value` — get or set the field's current value (triggers lazy load on first get)

**Methods:**
- `update(method, value)` — queue an operation (see Operations table below)
- `flush()` — block until all queued operations are processed
- `drop_if_unused(threshold_seconds)` — evict value from memory if clean and not recently accessed

### `StarDDB(database, save_time, database_hook=None, safe_root=None, cache_threshold=60)`

| Parameter | Description |
|-----------|-------------|
| `database` | Path to the JSON database file |
| `save_time` | Seconds between automatic background saves |
| `database_hook` | Initial data dict; if omitted, file must exist and is loaded lazily |
| `safe_root` | If set, restricts the database path to this directory (prevents path traversal) |
| `cache_threshold` | Seconds since last access before a clean field is evicted from memory (default: 60) |

**Methods:**

| Method | Description |
|--------|-------------|
| `db()` | Return the database hook (nested dict of `StarDDBField` instances) |
| `flush()` | Block until all pending field operations are complete |
| `save()` | Write the database to disk immediately |
| `close()` | Flush, save, and stop the background thread |
| `add_field(key_path, value)` | Add a new field at runtime (see below) |

### Operations

| Op | Description |
|----|-------------|
| `set` | Set value directly |
| `add` | Add to current value |
| `sub` | Subtract from current value |
| `mult` | Multiply current value |
| `div` | Divide current value (zero is rejected) |
| `push` | Append to a list field |

## `add_field` examples

`key_path` is a dot-separated string or a list of keys. Dict values are automatically crawled into `StarDDBField` instances.

```python
# Add a primitive
db.add_field("players.new_guy", 0)

# Add a list field
db.add_field("players.new_guy.inventory", [])

# Append to it
hook["players"]["new_guy"]["inventory"].update("push", "sword")

# Add a nested dict (auto-crawled)
db.add_field("players.new_guy", {"coins": 0, "level": 1})
hook["players"]["new_guy"]["coins"].update("add", 5)

# Use a list to avoid dot ambiguity in key names
db.add_field(["some.weird.key", "nested"], 42)
```

## Testing

```bash
cd src_python/tests
python test_crawl.py
python test_lazy.py
python test_errors.py
python single_field.py
python multi_field.py
```
