import type { DeletePlanResponse, ProductEndpoint, Root, SessionResponse } from '../generated/contract.ts'; import { unfinishedDeleteItems, type PendingDelete } from '../mutations.ts'; import { requireValue } from '../transport.ts'; import type { RecursiveDeletePersistence } from './recursive-delete-persistence.ts'; export type RecursiveDeleteResult = { deleted: number; pages: number; }; export class RecursiveDeletePageLimitError extends Error { readonly pages: number; constructor(pages: number) { super(`Recursive delete stopped after ${pages} pages; run it again to continue.`); this.name = 'RecursiveDeletePageLimitError'; this.pages = pages; } } export function savedRecursiveDelete( persistence: RecursiveDeletePersistence ): PendingDelete | null { return persistence.read(); } export async function runRecursiveDelete( client: ProductEndpoint, persistence: RecursiveDeletePersistence, session: SessionResponse, root: Root, prefix: string, nextOperationId: () => string, pageLimit = 100 ): Promise { let deleted = 0; let completedPages = 0; let continuation: string | null = null; for (let page = 0; page < pageLimit; page += 1) { let items = persistence.load(root, prefix); let nextContinuation: string | null = null; if (items.length === 0) { const plan: DeletePlanResponse = requireValue( await client.deletePlan({ root, path: prefix, ...(continuation === null ? {} : { continuation }) }) ); if (plan.paths.length === 0) { if (plan.continuation !== null) { continuation = plan.continuation; continue; } persistence.clear(); return { deleted, pages: completedPages }; } nextContinuation = plan.continuation; items = plan.paths.map((path) => ({ path, operation_id: nextOperationId() })); persistence.save(root, prefix, items); } const response = requireValue( await client.deleteBatch({ body: { root, csrf: session.csrf, items } }) ); const unfinished = unfinishedDeleteItems(response.results, nextOperationId); deleted += response.results.length - unfinished.length; if (unfinished.length > 0) { persistence.save(root, prefix, unfinished); throw new Error( `${unfinished.length} objects remain. Pending operations keep their IDs; terminal failures restart with new IDs.` ); } persistence.clear(); completedPages += 1; continuation = nextContinuation; } throw new RecursiveDeletePageLimitError(pageLimit); }