batch method

Run multiple writes as a single atomic transaction.

All operations commit together or none are applied — the server runs them in one database transaction and rolls back entirely on any failure. Operations apply in order and may span multiple collections.

Online-only by design (like upsert and deleteWhere): atomicity needs the server's authoritative view, so a batch is never queued offline — it throws on network failure. A server-side rejection throws a KoolbaseDataException whose message identifies which operation failed; nothing was persisted.

Returns one KoolbaseBatchResult per operation, in order.

Example:

final results = await Koolbase.db.batch([
  KoolbaseBatchOp.insert('orders', {'total': 50}),
  KoolbaseBatchOp.update(inventoryId, {'stock': 9}),
  KoolbaseBatchOp.upsert('counters', match: {'name': 'orders'}, data: {'value': 1}),
  KoolbaseBatchOp.delete(cartItemId),
]);

Implementation

Future<List<KoolbaseBatchResult>> batch(
    List<KoolbaseBatchOp> operations) async {
  if (operations.isEmpty) {
    throw ArgumentError('batch requires at least one operation');
  }

  final res = await http
      .post(
        Uri.parse('$baseUrl/v1/sdk/db/batch'),
        headers: await _headers(),
        body: jsonEncode({
          'operations': operations.map((o) => o.toJson()).toList(),
        }),
      )
      .timeout(const Duration(seconds: 15));

  if (res.statusCode != 200) {
    throw await koolbaseDataErrorNotifying(res,
        onSessionExpired: _onSessionExpired, fallbackMessage: 'Batch failed');
  }

  final body = jsonDecode(res.body) as Map<String, dynamic>;
  final results = (body['results'] as List<dynamic>? ?? [])
      .map((r) => KoolbaseBatchResult.fromJson(r as Map<String, dynamic>))
      .toList();

  // Keep the local cache consistent with what committed: save each written
  // record and invalidate its collection so the next query is fresh.
  for (final r in results) {
    final rec = r.record;
    final col = rec?.collection;
    if (rec != null && col != null) {
      await _cacheStore?.saveRecord(rec.id, col, rec.data, _userId,
          revision: rec.revision);
      await _cacheStore?.invalidateCollection(col);
      unawaited(refreshCollectionStreams(col));
    }
  }

  return results;
}