haematite-cli 0.21.0

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
use super::record_client::Native;
use serde_json::{Value, json};

pub(super) fn sequence(
    native: &mut Native,
    person: &str,
    id: &str,
    version: &Value,
) -> anyhow::Result<u64> {
    let prefix = format!("{person}/{id}/");
    let mut after = Value::Null;
    let mut maximum = 0;
    loop {
        let answer=native.read("documents.list",&json!({"selection":{"branch":"main","version":version},"collection":"_assistant","prefix":prefix,"content":"none","after":after,"limit":native.page})).map_err(|error|anyhow::anyhow!("record_migration_refused: {}",error.code))?;
        for entry in answer["entries"]
            .as_array()
            .ok_or_else(|| anyhow::anyhow!("record_migration_page_invalid"))?
        {
            let key = entry["id"]
                .as_str()
                .and_then(|key| key.strip_prefix(&prefix))
                .ok_or_else(|| anyhow::anyhow!("record_migration_key_invalid"))?;
            if key == "meta" {
                continue;
            }
            anyhow::ensure!(
                key.len() == 20 && key.bytes().all(|byte| byte.is_ascii_digit()),
                "record_migration_sequence_invalid"
            );
            maximum = maximum.max(key.parse()?);
        }
        let next = answer
            .get("next_after")
            .ok_or_else(|| anyhow::anyhow!("record_migration_cursor_missing"))?;
        if next.is_null() {
            return Ok(maximum);
        }
        anyhow::ensure!(
            next.as_str().is_some() && next != &after,
            "record_migration_cursor_invalid"
        );
        after = next.clone();
    }
}