feat(ext/sw): trash operations — listTrashed, restoreItem, purgeItem, purgeAllTrash

listTrashed filters manifest for trashed_at != null, sorted newest-first.
restoreItem clears trashed_at. purgeItem deletes item + attachments.
purgeAllTrash also scans for orphan blobs in attachments/ directory.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-26 15:57:08 -04:00
parent 0003c3e658
commit d2cb6d8461
3 changed files with 175 additions and 5 deletions

View File

@@ -315,11 +315,38 @@ export async function handle(
return { ok: true };
}
// Handlers for these cases are added in Tasks 45.
case 'list_trashed':
case 'restore_item':
case 'purge_item':
case 'purge_all_trash':
case 'list_trashed': {
if (!state.manifest) return { ok: false, error: 'vault_locked' };
const items = vault.listTrashed(state.manifest);
return { ok: true, data: { items } };
}
case 'restore_item': {
const handle = session.getCurrent();
if (!handle || !state.gitHost || !state.manifest) return { ok: false, error: 'vault_locked' };
await vault.restoreItem(state.gitHost, handle, state.manifest, msg.id);
return { ok: true };
}
case 'purge_item': {
const handle = session.getCurrent();
if (!handle || !state.gitHost || !state.manifest) return { ok: false, error: 'vault_locked' };
await vault.purgeItem(state.gitHost, msg.id, state.manifest);
await vault.encryptAndWriteManifest(
state.gitHost, handle, state.manifest,
`manifest: purge ${state.manifest.items[msg.id]?.title ?? msg.id}`,
);
return { ok: true };
}
case 'purge_all_trash': {
const handle = session.getCurrent();
if (!handle || !state.gitHost || !state.manifest) return { ok: false, error: 'vault_locked' };
const result = await vault.purgeAllTrash(state.gitHost, handle, state.manifest);
return { ok: true, data: result };
}
// Handler for this case is added in Task 5.
case 'get_field_history':
return { ok: false, error: 'not_implemented' };
}