fix: resolve SH-116 governance and type errors

This commit is contained in:
Alexandre Brandizzi 2026-09-23 04:49:16 -03:00
parent d6252552ca
commit 6321f176aa
2 changed files with 52 additions and 25 deletions

View file

@ -214,6 +214,48 @@ async function sendChunk(
});
}
type ChunkRecoveryContext = {
args: ResumableUploadArgs;
session: MediaUploadSessionDto;
index: number;
chunkSize: number;
recoveryAttempts: Map<number, number>;
};
async function sendChunkWithRecovery({
args,
session,
index,
chunkSize,
recoveryAttempts,
}: ChunkRecoveryContext): Promise<{
session: MediaUploadSessionDto;
recovered: boolean;
}> {
try {
const ack = await sendChunk(args, session.uploadId, index, chunkSize);
const receivedChunks = Array.isArray(ack?.receivedChunks) ? ack.receivedChunks : [];
if (!receivedChunks.includes(index)) {
throw new Error("Chunk acknowledgement missing from server response.");
}
return { session: { ...session, receivedChunks }, recovered: false };
} catch (error) {
if (!isNetworkUploadError(error)) throw error;
const recovered = await recoverSession(args.endpoints, session.uploadId, args.signal);
if (recovered.receivedChunks.includes(index)) {
recoveryAttempts.delete(index);
return { session: recovered, recovered: true };
}
const attempts = (recoveryAttempts.get(index) ?? 0) + 1;
recoveryAttempts.set(index, attempts);
if (attempts >= MAX_CHUNK_RECOVERY_ATTEMPTS) throw error;
await new Promise((resolve) => setTimeout(resolve, CHUNK_RECOVERY_BACKOFF_MS * attempts));
assertNotAborted(args.signal);
return { session: recovered, recovered: true };
}
}
/**
* Upload one file through the resumable session API: create → chunks → commit → poll.
* One in-flight chunk request per file; durable progress counts only acknowledged parts.
@ -248,30 +290,15 @@ export async function uploadFileResumable(
if (missing.length === 0) break;
for (const index of missing) {
assertNotAborted(args.signal);
try {
const ack = await sendChunk(args, session.uploadId, index, chunkSize);
const acked = Array.isArray(ack?.receivedChunks) ? ack.receivedChunks : [];
if (!acked.includes(index)) {
// Treat a malformed ack as not-yet-acked so the loop cannot spin forever.
throw new Error("Chunk acknowledgement missing from server response.");
}
session = { ...session, receivedChunks: acked };
} catch (error) {
if (!isNetworkUploadError(error)) throw error;
session = await recoverSession(args.endpoints, session.uploadId, args.signal);
if (session.receivedChunks.includes(index)) {
recoveryAttempts.delete(index);
} else {
const attempts = (recoveryAttempts.get(index) ?? 0) + 1;
recoveryAttempts.set(index, attempts);
if (attempts >= MAX_CHUNK_RECOVERY_ATTEMPTS) throw error;
await new Promise((resolve) =>
setTimeout(resolve, CHUNK_RECOVERY_BACKOFF_MS * attempts),
);
assertNotAborted(args.signal);
}
break; // Re-derive missing indices from the recovered server state.
}
const result = await sendChunkWithRecovery({
args,
session,
index,
chunkSize,
recoveryAttempts,
});
session = result.session;
if (result.recovered) break;
reportProgress(args, session.receivedChunks, args.file.size, chunkSize);
}
}

View file

@ -129,7 +129,7 @@ function baseRow(type: WorkOrderTableRow["type"] = "Emergency"): WorkOrderTableR
};
}
function renderModal(rowType = "Emergency") {
function renderModal(rowType: WorkOrderTableRow["type"] = "Emergency") {
const client = new QueryClient({
defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
});