executePaginatedSync method

Future<void> executePaginatedSync({
  1. required Future<SyncBatchResult> fetchBatch(
    1. SyncCheckpoint? checkpoint
    ),
  2. required Future<ApplyResult> applyBatch(
    1. List<SyncPayload<ServerItem>>
    ),
  3. required String featureKey,
  4. void onBatchComplete(
    1. SyncCheckpoint? checkpoint
    )?,
  5. bool isCancelled()?,
  6. SyncCheckpoint? initialCheckpoint,
})

Execute paginated sync with checkpoint-based pagination (for background sync).

Uses checkpoint (cursor/nextId) returned by the server to determine continuation. If the server returns no checkpoint (null) the pagination is considered complete. Empty items with no checkpoint also terminates.

fetchBatch - Function to fetch a batch of items given a checkpoint applyBatch - Function to apply a batch of items locally featureKey - Feature identifier for error reporting onBatchComplete - Optional callback after each batch is processed isCancelled - Function to check if operation should be cancelled initialCheckpoint - Starting checkpoint for pagination

Implementation

Future<void> executePaginatedSync({
  required Future<SyncBatchResult> Function(SyncCheckpoint? checkpoint)
      fetchBatch,
  required Future<ApplyResult> Function(List<SyncPayload<ServerItem>>)
      applyBatch,
  required String featureKey,
  void Function(SyncCheckpoint? checkpoint)? onBatchComplete,
  bool Function()? isCancelled,
  SyncCheckpoint? initialCheckpoint,
}) async {
  SyncCheckpoint? checkpoint = initialCheckpoint;

  while (true) {
    // Check cancellation
    if (isCancelled?.call() ?? false) {
      throw OperationCancelledException('Sync cancelled during pagination');
    }

    // Fetch batch with retry
    final batchResult = await retryPolicy.attempt(
      () async {
        final result = await fetchBatch(checkpoint);
        if (!result.success) {
          throw SyncFailedException(
              'Failed fetch batch: ${result.errorMessage ?? "unknown"}');
        }
        return result;
      },
    );

    if (batchResult == null) {
      throw MaxRetriesExceededException(
        'Failed to fetch batch for feature: $featureKey',
      );
    }

    if (isCancelled?.call() ?? false) {
      throw OperationCancelledException('Sync cancelled after fetch');
    }

    if (batchResult.items.isEmpty) {
      // No more items to process, end pagination
      break;
    }

    // Apply batch if not empty
    final applyResult = await applyBatch(batchResult.items);
    if (!applyResult.success) {
      throw SyncFailedException(
        'Failed to apply batch for feature: $featureKey. '
        'Errors: ${applyResult.errors.map((e) => e.message).join(', ')}',
      );
    }

    // Update checkpoint after successful apply
    if (batchResult.checkpoint != null) {
      checkpoint = batchResult.checkpoint;
      onBatchComplete?.call(checkpoint);
    }
  }
}