diff --git a/CHANGELOG.md b/CHANGELOG.md index 21d96bf..ead643f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,92 @@ # PiCloud Changelog +## v1.1.2 — Documents (unreleased) + +`docs::*` SDK — schemaless JSONB document storage with a first-cut +query DSL — plus `docs:*` triggers as the second concrete kind on the +v1.1.1 triggers framework. Sets the precedent for the v1.2 query DSL +expansion and `dead_letters::list`. + +### Added + +- **Docs store** — `docs` table keyed `(app_id, collection, id)` with + JSONB values and a GIN-on-`jsonb_path_ops` index. Rhai SDK exposes + the handle pattern: + `docs::collection(name).{create,get,find,find_one,update,delete,list}`. + Cursor-style pagination on `list`. Cross-app isolation enforced via + `cx.app_id` (never script-passed). Document envelope shape returned + by reads: `#{ id, data: #{...}, created_at, updated_at }` — explicit + metadata + user-data separation (sets precedent for v1.2 + `dead_letters::list`). +- **Query DSL (v1.1.2 subset)** — implicit equality at top level + (`#{ tier: "gold" }`), operator-object form + (`#{ created_at: #{ "$gt": "..." } }`), dotted field paths up to 5 + levels (`"user.email"`), and operators `$eq`/`$ne`/`$gt`/`$gte`/ + `$lt`/`$lte`/`$in`. Filter modifiers `$sort` (single field) and + `$limit`. Unsupported operators (`$or`, `$regex`, etc.) reject with + a clear v1.2-pointer error. +- **Docs triggers (`docs:*`)** — `docs_trigger_details` table mirrors + `kv_trigger_details`. Admin endpoint + `POST /api/v1/admin/apps/{id}/triggers/docs` accepts the same DTO + shape as the KV endpoint with `ops` of `DocsEventOp` (create / + update / delete). Dispatcher routes `OutboxSourceKind::Docs` through + the same generic path as KV + dead-letter. +- **`ctx.event.docs.prev_data`** — change-data-capture surface for + docs trigger handlers. `prev_data` carries the document state prior + to the mutation (`None` for create), letting handlers see what + changed. The repo reads the old row in the same SQL statement as + the write so the trigger event has the prior value. +- **`Capability::AppDocsRead(AppId)`** + `AppDocsWrite(AppId)` — + granted to Viewer / Editor respectively in the per-app role table. + Same trust shape as KV's `AppKvRead` / `AppKvWrite`. + +### Changed + +- **Workspace version**: `1.1.1` → `1.1.2`. +- **Rhai SDK version**: `1.2` → `1.3` (additive — every v1.2 script + still runs unchanged; new surfaces: `docs::collection(name).{...}`, + `ctx.event.docs` for triggered handlers). +- **Dashboard version**: `0.7.0` → `0.8.0`. Workspace alignment; no + docs-specific UI in v1.1.2 (the dashboard's Rhai-mode hints don't + list KV completions either — focused UX pass is a separate task). +- **`Services` bundle** — grows a `docs: Arc` field. + Constructor signature becomes + `Services::new(kv, docs, dead_letters, events)`. +- **Scope mapping**: API keys with `script:read` scope can call + `docs::find` / `get` / `list`; `script:write` can call + `docs::create` / `update` / `delete`. Same trust shape as KV — + honors the seven-scope commitment from v1.1.0. + +### Migrations + +- `0013_docs.sql` — `docs` table + per-`(app_id, collection)` index + + GIN-on-`jsonb_path_ops` index. +- `0014_docs_triggers.sql` — extends `triggers.kind` and + `outbox.source_kind` CHECK constraints to include `'docs'`; adds + `docs_trigger_details` table. + +### Downgrade caveats + +Rolling a deployment back from v1.1.2 → v1.1.1 with `docs`-source +outbox rows still queued will cause the v1.1.1 dispatcher to fail +deserialising `TriggerEvent::Docs` (`#[serde(tag = "source")]` +rejects unknown variants). Drain or delete +`outbox WHERE source_kind = 'docs'` before downgrading. Trunk-only +deployments don't hit this. + +### Known limitations + +- Text-lex comparison for `$gt` / `$gte` / `$lt` / `$lte` is + incorrect for unpadded numbers crossing digit-count boundaries + (`'10' < '9'` is TRUE under any text collation). Workaround: + zero-pad numeric strings. v1.2's advanced query expansion adds + numeric-aware operators. +- Concurrent `update()`s on the same doc may both emit the + pre-update `prev_data` (last-writer-wins). Inherited from KV's + `set` pattern; documented for forensic-trace use cases. +- v1.1.2 has no partial-update DSL — scripts that want partial + update do `get + modify + update`. Planned for v1.2. + ## v1.1.1 — Storage & Events (unreleased) The triggers framework — KV store + universal outbox + dispatcher + diff --git a/Cargo.lock b/Cargo.lock index 657ec08..07f8248 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1505,7 +1505,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "picloud" -version = "1.1.1" +version = "1.1.2" dependencies = [ "anyhow", "async-trait", @@ -1531,7 +1531,7 @@ dependencies = [ [[package]] name = "picloud-cli" -version = "1.1.1" +version = "1.1.2" dependencies = [ "anyhow", "assert_cmd", @@ -1552,7 +1552,7 @@ dependencies = [ [[package]] name = "picloud-executor" -version = "1.1.1" +version = "1.1.2" dependencies = [ "anyhow", "picloud-executor-core", @@ -1564,7 +1564,7 @@ dependencies = [ [[package]] name = "picloud-executor-core" -version = "1.1.1" +version = "1.1.2" dependencies = [ "async-trait", "base64", @@ -1585,7 +1585,7 @@ dependencies = [ [[package]] name = "picloud-manager" -version = "1.1.1" +version = "1.1.2" dependencies = [ "anyhow", "picloud-manager-core", @@ -1597,7 +1597,7 @@ dependencies = [ [[package]] name = "picloud-manager-core" -version = "1.1.1" +version = "1.1.2" dependencies = [ "argon2", "async-trait", @@ -1622,7 +1622,7 @@ dependencies = [ [[package]] name = "picloud-orchestrator" -version = "1.1.1" +version = "1.1.2" dependencies = [ "anyhow", "picloud-orchestrator-core", @@ -1634,7 +1634,7 @@ dependencies = [ [[package]] name = "picloud-orchestrator-core" -version = "1.1.1" +version = "1.1.2" dependencies = [ "async-trait", "axum", @@ -1653,7 +1653,7 @@ dependencies = [ [[package]] name = "picloud-shared" -version = "1.1.1" +version = "1.1.2" dependencies = [ "async-trait", "chrono", diff --git a/Cargo.toml b/Cargo.toml index b23884c..8063bfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ ] [workspace.package] -version = "1.1.1" +version = "1.1.2" edition = "2021" rust-version = "1.92" license = "MIT OR Apache-2.0" diff --git a/dashboard/package.json b/dashboard/package.json index 8c578e6..45d4ea2 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -1,6 +1,6 @@ { "name": "picloud-dashboard", - "version": "0.7.0", + "version": "0.8.0", "private": true, "type": "module", "scripts": {