mirror of
https://github.com/narrrl/proton-sdk-rs.git
synced 2026-11-03 16:41:26 +01:00
11 KiB
11 KiB
Upstream sync
Tracks reconciliation of this pure-Rust port against the canonical C# SDK
(ProtonDriveApps/sdk, subtree client/cs/sdk/src). The sdk/ checkout is
gitignored, so this file is the only durable record of what we last reviewed.
- Upstream: https://github.com/ProtonDriveApps/sdk
- Reconciled subtree:
client/cs/src(wasclient/cs/sdk/srcbefore the mid-2026 projects reorg; the script watches both) - Pinned:
df6b6bfde620c12868cd5a444d027f8d01974b75 - Date: 2026-08-10
Note
: upstream force-pushes — the SHAs in rows before 2026-07-14 no longer exist in its history. Triage by commit subject/date when a pin goes stale.
Workflow
./scripts/upstream-sync.sh— fetches upstream, lists cs-relevant commits since the pinned SHA, drops noise (chore/docs/test/ci/build), prints diffs.- Triage each surviving commit: behavioral change to port, or structural/cosmetic.
- Port the behavioral diffs into
crates/. - Bump Pinned (and Date) above to the new upstream HEAD; commit.
Log
| date | from → to | ported | notes |
|---|---|---|---|
| 2026-06-25 | initial pin fb4173ab |
— | baseline; delta reviewed, 0 behavioral changes outstanding |
| 2026-06-26 | fb4173ab → 36430318 |
36430318 |
ported: enumeration returns NodeUids (enumerate_folder_children_node_uids / enumerate_trash_node_uids), caller materializes via enumerate_nodes. noise dropped: BOM/deps/kt-enum-order/cs-account-refactor |
| 2026-07-10 | 36430318 → f2496161 |
9a1c39b3 |
ported: validation check in Thumbnail::new returning Result instead of panicking. noise dropped: projects reorg / C# Account client move to incubating. deferred: device support (32a8eed0). |
| 2026-07-14 | f2496161 → 2219a42f |
d15fa9b9, bead8f65, f6b12911, c0940f52, b88b8041, 2eb69c46 |
upstream force-pushed + moved the cs subtree, so the old pin was dangling and the script silently reported "up to date" (both fixed). ported: devices (devices.rs + enumerate_devices / create_device / rename_device / delete_device; volume-free DeviceUid), node sharing flags (Node::is_shared / is_shared_publicly from the link Sharing block), revision state (NodeKind::File::active_revision_state), shared-with-me (enumerate_shared_with_me_node_uids, GET v2/sharedwithme), leave shared node (leave_shared_node, DELETE v2/shares/{sid}/members/{mid}). no-op: 15fa845e (non-JSON error bodies — http.rs already falls back to a status-only error when the envelope won't parse). diverged deliberately: 9a7f1d02 + b4dadf02 removed the C# entity cache and its tags; ours stays. cosmetic, not ported: 30ab32c1 / 388d3900 (C# field renames), 1a781ade (buffer lengths). |
| 2026-07-24 | 1b6ff835 → e0ad37e3 |
d2ffde20, 724b6771 |
both features were already ported from the TypeScript SDK, so this closed the remaining parity deltas. ported: shared-by-me on the photos volume (SharingOperations.EnumerateSharedNodeUidsAsync now takes a volume resolver — page_shared_by_me(volume_id) + ProtonPhotosClient::enumerate_shared_node_uids, empty when the account has no photos volume) and SharedByMeLinkDto.ContextShareID (kept Option — recent upstream addition, older backends omit it); per-node move outcomes (move_nodes now returns Vec<(NodeUid, Result<()>)> in input order, mirroring the breaking MoveNodesAsync result map; per-link envelope codes via the new aggregate_outcomes, which check_aggregate now sits on). cosmetic, not ported: AnchorID retyped string → LinkId (ours is String). diverged deliberately: we keep the batch endpoint PUT volumes/{vid}/links/move-multiple — upstream still loops per node behind a // FIXME: … use the batch move endpoint; and the drive-side listing keeps the _by_me suffix (upstream renamed both to EnumerateSharedNodeUids) because proton-drive-linux already consumes that name. |
| 2026-07-22 | 2219a42f → 1b6ff835 |
ded1b716, 071f976f |
ported: atomic small uploads for buffered new files and revisions (DriveSmallFileUpload is an explicit opt-in because this crate has no remote feature-flag provider), and photo duplicate detection using name/content HMAC matching. already covered/no-op: ee0b3fba (Rust rename accepts Option<&str>), 193c26f1 (Rust in-memory cache already has bounded LRU eviction), 29d359e0 (digest representation refactor retains hex JSON), 5c370289 (C# disposal lifetime only), 95752055 (Photos cache construction only). deliberately not ported: 2bf49aae / 2590d227 cache storage-format changes; Rust exposes a distinct string repository contract and ships no persistent backend to migrate/reset. |
| 2026-07-31 | e0ad37e3 → 941aafa5 |
42526703, f2fa871b, 814ae781, c528a376 |
ported: node-name validation (validate_node_name: empty or >255 chars rejected before create/rename; counted in chars — C# counts UTF-16 units, and neither is the server's own accounting); album read ops (AlbumDto + enumerate_album_node_uids / enumerate_album, GET photos/volumes/{vid}/albums and …/albums/{lid}/children?Sort=Captured&Desc=1, both paged on AnchorID; LinkDetailsDto::folder_properties reads an album's hash key out of the Album block, which also makes parent_hash_key work for album children); photo + album node metadata (Node::photo / Node::album — capture time, content hash, main/related photos, tags, album inclusions; photo count, cover photo, last activity — added as optional Node fields rather than NodeKind variants so persisted node blobs keep deserializing, mirroring how membership landed); photo tag updates (update_photos, POST/DELETE photos/volumes/{vid}/links/{lid}/tags via the new ApiHttpClient::delete_with_body, POST …/favorite for photos on our own photos volume, one outcome per input update like move_nodes); shared-with-me photos/albums (ShareTargetType::is_photos_item over v2/sharedwithme + the dedicated GET photos/albums/shared-with-me, whose rows carry their own VolumeID). deferred: 03b1cb7f favoriting shared photos — needs PhotosCrypto + PhotoTransferPayloadBuilder (name/passphrase session keys rewrapped to the timeline root, related-photo fan-out, batches of 20); our rewrap_message_to / recover_message_session_key make it feasible, but until it lands update_photos fails that one update explicitly instead of silently no-oping. not ported: 4c1dd783 (IsSharedPublicly → IsSharedByUrl rename — proton-drive-linux consumes is_shared_publicly), 1477ea45 (C# user-facing error strings; our error text is our own), the second half of 42526703 (server-supplied names validated into a per-node InvalidNameError — we have no node error list to put it in). no-op: 82d605b7 (C# FifoFlexibleSemaphore cancel race — tokio's Semaphore is cancel-safe and drops queued waiters), 8bdd9420 (throw on resume-after-dispose; Rust ownership makes it unreachable), ca11470f + c947fce7 (CExports/interop only). Album writes (create, add/remove photos) remain unported — upstream has no C# public API for them either. Live validation pending: every photos endpoint here needs a photos-volume account. |
| 2026-08-01 | — (no upstream delta) | — | not an upstream port. Extended the TypeScript-sourced public-link consume side with a streaming surface that has no TS or C# counterpart and is ours: open_revision / open_revision_at / download_range / download_thumbnail / enumerate_thumbnails / refresh_session / with_max_inflight_blocks on ProtonDrivePublicLinkClient. Enabling change: RevisionReader was welded to ProtonDriveClient, so it now takes a RevisionTransport (new transport.rs — block permits + a route-prefixed ApiHttpClient + a BlockSession for renewal); fetch_revision_blocks and the block-size ranking moved there out of client.rs. Also ApiHttpClient::without_token_refresh (proton-sdk), because the anonymous session's refresh token is empty and the built-in 401 refresh masked the real 401. Perf on the visitor path: LRU node-key cache + iterative ancestor walk behind SingleFlight (was re-deriving the whole chain, at an S2K each, per child), enumerate_nodes chunked at 150 and fanned out (it did not chunk at all), download_file_to paged (a single un-paged GET truncated anything past 50 blocks) and concurrent. Live-verified against the test account, including the one uncertain fact the design rested on: the drive/unauth/ revision response does carry XAttr, so a visitor resolves true per-block sizes and can seek. |
| 2026-08-10 | 941aafa5 → df6b6bfd |
f493c5ee, 62895702 |
ported: per-node trash/restore/delete outcomes, streamed. Upstream turned TrashNodesAsync / RestoreNodesAsync / DeleteNodesAsync from ValueTask<IReadOnlyDictionary<NodeUid, Result<Exception>>> into IAsyncEnumerable<NodeActionResult>, and UpdatePhotosAsync into IAsyncEnumerable<PhotoUpdateResult>. Rust equivalent: trash_nodes / restore_nodes / delete_nodes now return Vec<(NodeUid, Result<()>)> (breaking — they returned Result<()> and failed the whole call through check_aggregate, which is now gone), plus *_streaming siblings returning impl Stream<Item = Result<(NodeUid, Result<()>)>> that yield each batch's outcomes as the batch lands; update_photos_streaming does the same for photo tag updates. Kept the (NodeUid, Result<()>) tuple used by move_nodes rather than adding NodeActionResult / PhotoUpdateResult record types — C# needs a named type for its iterators, Rust does not. Outcome mapping mirrors upstream exactly: only the links the aggregate envelope reports on produce an outcome, and the stream's outer Err (a request that never produced an envelope) ends it. diverged deliberately: upstream's rewrite also dropped its per-volume Task.WhenAll, so both sides are now sequential over volume groups — ours already was. not ported: 9dd63423 (explicit stream disposal through interop — CExports/proto only; Rust ownership drops readers/writers at scope end). |
| 2026-08-10 | — (no upstream delta) | — | not an upstream port. ProtonDriveClient::with_entity_repository — the chainable form of with_entity_cache, added because that one is a constructor and so could not be combined with with_key_salts; a client that resumes a persisted session needs the salts and wants a persistent entity cache, and upstream C# has no equivalent tension (its cache is set on the client options object). Additive only: with_entity_cache and the in-memory default are unchanged. Consumed by proton-drive-linux, which now backs the entity cache with an encrypted SQLite store. |