feat: soft-delete mappings to .trash/ - #141
Conversation
Trashing a mapping now moves it to a .trash/ prefix on the remote rather than deleting it, so a mapping is never permanently lost; permanent deletes (Empty Trash / 30-day cleanup) only clear local state, leaving the .trash/ copy recoverable. - trashRemote / restoreRemote move files to/from .trash/ via copy-then-delete (the GitHub contents API has no rename). - Sync is centralized in moveToFolder so both the button and drag-and-drop paths are covered uniformly. - contentsUrl encodes path segments individually so .trash/ stays a real subdir rather than a percent-escaped filename. - moveRemote retries as an update on conflict to tolerate GitHub eventual consistency when re-trashing a just-restored file. Known follow-ups: folder trash does not recurse into contained files; same-name files can collide on the flat remote; trash should move to a server-side primitive once the Worker backend lands.
|
Update (approach changed): Following the 6/18 meeting decision that the GitHub remote should never be permanently deleted, this PR no longer deletes the remote file. It now soft-deletes by moving the mapping to a The original description below describes the earlier delete-outright approach, which has been reversed. What's in the latest commits:
Verified end-to-end: save → trash → |
|
Added 409-retry in moveRemote for GitHub's eventual consistency on rapid consecutive trash moves, where a write can hit a stale sha. This mitigates but doesn't fully eliminate the race. The root cause is concurrent fire-and-forget moves from moveToFolder; a full fix (serializing moves) is tracked as follow-up. |
Deleting a mapping from Trash now also removes it from the user's
cress-mappingsrepo when logged in — previously it only deleted the local PouchDB copy, leaving the GitHub file orphaned (found 2026-06-18). Part of #139.Changes
deleteRemoteOnly(remote-only delete). It fetches the blob sha from the backend on cache miss, so cross-session deletes work even when the file wasn't opened this session.deleteFileEntrynow callsdeleteRemoteOnly, then removes the local doc by id. All three permanent-delete paths (Delete, Empty Trash, 30-day cleanup) route through it. Move-to-Trash / Put Back deliberately untouched — files stay recoverable while in Trash.moveRemotenow retries on 409 with backoff (re-probes the dest sha) to handle GitHub's eventual consistency on rapid consecutive trash moves. Mitigates but doesn't fully eliminate the concurrent-move race; full fix (serializing moves) tracked as follow-up.Testing (manual, PAT in localStorage)
Notes
feat/github-storage; merge after feat: save and load mapping files to/from GitHub #140.