Skip to main content

max / goingson

sync: adopt synckit typed-ID newtypes at the SDK boundary synckit-client now exposes DeviceId/UserId/AppId. Convert at the call boundary (DeviceId::new / .as_uuid) so GO keeps threading bare UUIDs internally; no behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-22 22:16 UTC
Commit: 66fb0edc5c26c7f7f7abe6cc91fdb6c23515af39
Parent: 9f5795a
5 files changed, +13 insertions, -7 deletions
@@ -221,6 +221,8 @@ pub async fn sync_complete_auth(
221 221 .authenticate_with_code(&input.code, &flow.code_verifier, flow.port, "__internal__")
222 222 .await
223 223 .map_api_err("Token exchange failed", ApiError::external_service)?;
224 + // GO stores and surfaces these as bare UUIDs; unwrap the SDK newtypes here.
225 + let (user_id, app_id) = (user_id.as_uuid(), app_id.as_uuid());
224 226
225 227 let session_info = client
226 228 .session_info()
@@ -378,7 +378,11 @@ fn load_sync_client(data_dir: &std::path::Path) -> Option<SyncKitClient> {
378 378 match crate::oauth::CredentialStore::get_sync_token() {
379 379 Some(creds) => {
380 380 info!("Restoring sync session from keychain (user={})", creds.user_id);
381 - client.restore_session(&creds.token, creds.user_id, creds.app_id);
381 + client.restore_session(
382 + &creds.token,
383 + synckit_client::UserId::new(creds.user_id),
384 + synckit_client::AppId::new(creds.app_id),
385 + );
382 386 if client.is_token_expired() {
383 387 warn!("Stored sync token is expired, clearing session");
384 388 client.clear_session();
@@ -4,7 +4,7 @@ use chrono::Utc;
4 4 use goingson_core::CoreError;
5 5 use sqlx::{SqliteConnection, SqlitePool};
6 6 use synckit_client::{
7 - ChangeEntry, ChangeOp, Hlc, Resolution, SyncKitClient,
7 + ChangeEntry, ChangeOp, DeviceId, Hlc, Resolution, SyncKitClient,
8 8 detect_conflicts, resolve_lww,
9 9 };
10 10 use tracing::{debug, warn};
@@ -31,7 +31,7 @@ pub async fn pull_changes(
31 31
32 32 loop {
33 33 let (changes, new_cursor, has_more) = client
34 - .pull_rich(device_id, cursor)
34 + .pull_rich(DeviceId::new(device_id), cursor)
35 35 .await
36 36 .map_err(|e| CoreError::sync(format!("pull failed: {}", e)))?;
37 37
@@ -47,7 +47,7 @@ pub async fn pull_changes(
47 47 }
48 48
49 49 // Detect conflicts between remote changes and local pending
50 - let (clean, conflicts) = detect_conflicts(changes, &local_pending, device_id);
50 + let (clean, conflicts) = detect_conflicts(changes, &local_pending, DeviceId::new(device_id));
51 51
52 52 // Apply non-conflicting changes directly
53 53 let clean_count = clean.len() as i64;
@@ -3,7 +3,7 @@
3 3 use chrono::Utc;
4 4 use goingson_core::CoreError;
5 5 use sqlx::SqlitePool;
6 - use synckit_client::{ChangeEntry, ChangeOp, Hlc, SyncKitClient};
6 + use synckit_client::{ChangeEntry, ChangeOp, DeviceId, Hlc, SyncKitClient};
7 7 use tracing::{debug, warn};
8 8 use uuid::Uuid;
9 9
@@ -82,7 +82,7 @@ pub async fn push_changes(
82 82 let is_last_batch = row_count < PUSH_BATCH_LIMIT;
83 83
84 84 client
85 - .push(device_id, changes)
85 + .push(DeviceId::new(device_id), changes)
86 86 .await
87 87 .map_err(|e| CoreError::sync(format!("push failed: {}", e)))?;
88 88
@@ -70,5 +70,5 @@ pub async fn ensure_device_registered(
70 70 set_sync_state(pool, "device_id", &device.id.to_string()).await?;
71 71 info!("Registered device: {} ({})", device.device_name, device.id);
72 72
73 - Ok(device.id)
73 + Ok(device.id.as_uuid())
74 74 }