chore(release): bump workspace to v1.1.2 + CHANGELOG
Workspace package version 1.1.1 -> 1.1.2; dashboard 0.7.0 -> 0.8.0 (workspace alignment, no docs-specific UI yet); SDK_VERSION 1.2 -> 1.3 for the docs:: surface + ctx.event.docs additions. CHANGELOG entry documents the docs store, the query DSL subset, the docs:* trigger kind, the prev_data change-data-capture surface, and the new AppDocsRead/AppDocsWrite capabilities. Includes a downgrade caveat (v1.1.2 -> v1.1.1 with queued docs outbox rows would fail TriggerEvent deserialization) and known-limitations notes for the text-lex comparison gotcha and the concurrent-update prev_data race. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
87
CHANGELOG.md
87
CHANGELOG.md
@@ -1,5 +1,92 @@
|
|||||||
# PiCloud Changelog
|
# 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<dyn DocsService>` 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)
|
## v1.1.1 — Storage & Events (unreleased)
|
||||||
|
|
||||||
The triggers framework — KV store + universal outbox + dispatcher +
|
The triggers framework — KV store + universal outbox + dispatcher +
|
||||||
|
|||||||
18
Cargo.lock
generated
18
Cargo.lock
generated
@@ -1505,7 +1505,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud"
|
name = "picloud"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1531,7 +1531,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-cli"
|
name = "picloud-cli"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
@@ -1552,7 +1552,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-executor"
|
name = "picloud-executor"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"picloud-executor-core",
|
"picloud-executor-core",
|
||||||
@@ -1564,7 +1564,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-executor-core"
|
name = "picloud-executor-core"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -1585,7 +1585,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-manager"
|
name = "picloud-manager"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"picloud-manager-core",
|
"picloud-manager-core",
|
||||||
@@ -1597,7 +1597,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-manager-core"
|
name = "picloud-manager-core"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1622,7 +1622,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-orchestrator"
|
name = "picloud-orchestrator"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"picloud-orchestrator-core",
|
"picloud-orchestrator-core",
|
||||||
@@ -1634,7 +1634,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-orchestrator-core"
|
name = "picloud-orchestrator-core"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -1653,7 +1653,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "picloud-shared"
|
name = "picloud-shared"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.92"
|
rust-version = "1.92"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "picloud-dashboard",
|
"name": "picloud-dashboard",
|
||||||
"version": "0.7.0",
|
"version": "0.8.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user