Worker message containing entries to sync.
Set tracking active task IDs for cancellation.
Promise that posts result and progress messages.
export async function handleBatchSync(
message: BatchSyncMessage,
activeTasks: Set<string>,
): Promise<void> {
const { taskId, entries } = message.payload;
try {
activeTasks.add(taskId);
console.info(
`[Worker] 📦 Starting batch sync pre-processing for ${entries.length} entries`,
);
const startTime = performance.now();
if (!activeTasks.has(taskId)) {
console.warn(
`[Worker] ⚠️ Batch sync task ${taskId} was cancelled during organizing`,
);
globalThis.postMessage({
type: "BATCH_SYNC_CANCELLED",
payload: {
taskId,
phase: "organizing",
},
});
return;
}
const progressMsg1: BatchSyncProgressMessage = {
type: "BATCH_SYNC_PROGRESS",
payload: {
taskId,
phase: "organizing",
processed: 0,
total: entries.length,
},
};
globalThis.postMessage(progressMsg1);
const entriesByMediaId = organizeEntriesByMediaIdForWorker(entries);
const mediaIds = Object.keys(entriesByMediaId)
.map(Number)
.sort((a, b) => a - b);
if (!activeTasks.has(taskId)) {
console.warn(
`[Worker] ⚠️ Batch sync task ${taskId} was cancelled during building`,
);
globalThis.postMessage({
type: "BATCH_SYNC_CANCELLED",
payload: {
taskId,
phase: "building",
},
});
return;
}
const progressMsg2: BatchSyncProgressMessage = {
type: "BATCH_SYNC_PROGRESS",
payload: {
taskId,
phase: "building",
processed: 0,
total: mediaIds.length,
},
};
globalThis.postMessage(progressMsg2);
const operations: PreparedSyncOperation[] = [];
const failedEntries: Array<{ mediaId: number; error: string }> = [];
let totalApiCallsEstimate = 0;
for (let i = 0; i < mediaIds.length; i++) {
if (!activeTasks.has(taskId)) {
console.warn(
`[Worker] ⚠️ Batch sync task ${taskId} was cancelled during building`,
);
globalThis.postMessage({
type: "BATCH_SYNC_CANCELLED",
payload: {
taskId,
phase: "building",
},
});
return;
}
const mediaId = mediaIds[i];
const mediaEntries = entriesByMediaId[mediaId];
try {
const steps = mediaEntries
.map((e) => e.syncMetadata?.step || 1)
.filter((step, idx, arr) => arr.indexOf(step) === idx)
.sort((a, b) => a - b);
const variables = mediaEntries.map((entry) =>
buildGraphQLVariablesForEntry(entry, entry.syncMetadata?.step || 1),
);
const operation: PreparedSyncOperation = {
mediaId,
entries: mediaEntries,
steps,
variables,
estimatedApiCalls: steps.length,
};
operations.push(operation);
totalApiCallsEstimate += steps.length;
if ((i + 1) % 10 === 0) {
const progressMsg: BatchSyncProgressMessage = {
type: "BATCH_SYNC_PROGRESS",
payload: {
taskId,
phase: "building",
processed: i + 1,
total: mediaIds.length,
currentMediaId: mediaId,
},
};
globalThis.postMessage(progressMsg);
}
} catch (error) {
failedEntries.push({
mediaId,
error: error instanceof Error ? error.message : String(error),
});
}
}
if (!activeTasks.has(taskId)) {
console.warn(
`[Worker] ⚠️ Batch sync task ${taskId} was cancelled before completion`,
);
globalThis.postMessage({
type: "BATCH_SYNC_CANCELLED",
payload: {
taskId,
phase: "completion",
},
});
return;
}
const progressMsg3: BatchSyncProgressMessage = {
type: "BATCH_SYNC_PROGRESS",
payload: {
taskId,
phase: "ready",
processed: mediaIds.length,
total: mediaIds.length,
},
};
globalThis.postMessage(progressMsg3);
const totalTime = performance.now() - startTime;
const resultMsg: BatchSyncResultMessage = {
type: "BATCH_SYNC_RESULT",
payload: {
taskId,
operations,
totalApiCallsEstimate,
failedEntries,
},
};
console.info(
`[Worker] ✅ Batch sync pre-processing completed: ${operations.length} operations, ${totalApiCallsEstimate} estimated API calls, ${failedEntries.length} failures (${totalTime.toFixed(2)}ms)`,
);
globalThis.postMessage(resultMsg);
} catch (error) {
console.error(`[Worker] ❌ Error in batch sync task ${taskId}:`, error);
globalThis.postMessage({
type: "ERROR",
payload: {
taskId,
error: getErrorDetails(error),
},
});
} finally {
activeTasks.delete(taskId);
}
}
Prepares batch sync operations in a worker (grouping, steps, variables).