Immutable Dart collections via the builder pattern.

Overview

Built Collections for Dart

Build Status

Introduction

Built Collections are immutable collections using the builder pattern.

Each of the core SDK collections is split in two: a mutable builder class and an immutable "built" class. Builders are for computation, "built" classes are for safely sharing with no need to copy defensively.

Immutable collections work particularly well with immutable values. See built_value.

You can read more about built_collection on medium.

Design

Built Collections:

  • are immutable, if the elements/keys/values used are immutable;
  • are comparable;
  • are hashable;
  • use copy-on-write to avoid copying unnecessarily.

See below for details on each of these points.

Recommended Style

A project can benefit greatly from using Built Collections throughout. Methods that will not mutate a collection can accept the "built" version, making it clear that no mutation will happen and completely avoiding the need for defensive copying.

For code that is public to other projects or teams not using Built Collections, prefer to accept Iterable where possible. That way your code is compatible with SDK collections, Built Collections and any other collection implementation that builds on Iterable.

It's okay to accept List, Set or Map if needed. Built Collections provide efficient conversion to their SDK counterparts via BuiltList.toList, BuiltListMultimap.toMap, BuiltSet.toSet, BuiltMap.toMap and BuiltSetMultimap.toMap.

Built Collections are Immutable

Built Collections do not offer any methods that modify the collection. In order to make changes, first call toBuilder to get a mutable builder.

In particular, Built Collections do not implement or extend their mutable counterparts. BuiltList implements Iterable, but not List. BuiltSet implements Iterable, but not Set. BuiltMap, BuiltListMultimap and BuiltSetMultimap share no interface with the SDK collections.

Built Collections can contain mutable elements. However, this use is not recommended, as mutations to the elements will break comparison and hashing.

Built Collections are Comparable

Core SDK collections do not offer equality checks by default.

Built Collections do a deep comparison against other Built Collections of the same type, only. Hashing is used to make repeated comparisons fast.

Built Collections are Hashable

Core SDK collections do not compute a deep hashCode.

Built Collections do compute, and cache, a deep hashCode. That means they can be stored inside collections that need hashing, such as hash sets and hash maps. They also use the cached hash code to speed up repeated comparisons.

Built Collections Avoid Copying Unnecessarily

Built Collections and their builder and helper types collaborate to avoid copying unless it's necessary.

In particular, BuiltList.toList, BuiltListMultimap.toMap, BuiltSet.toSet, BuiltMap.toMap and BuiltSetMultimap.toMap do not make a copy, but return a copy-on-write wrapper. So, Built Collections can be efficiently and easily used with code that needs core SDK collections but does not mutate them.

When you want to provide a collection that explicitly throws when a mutation is attempted, use BuiltList.asList, BuiltListMultimap.asMap, BuiltSet.asSet, BuiltSetMultimap.asMap and BuiltMap.asMap.

Features and bugs

Please file feature requests and bugs at the issue tracker.

Comments
  • Set/Map/Multimap: Custom equality?

    Set/Map/Multimap: Custom equality?

    Any concern about support package:collection's Equality class, optionally?

    I have some use cases where I'd like to use BuiltCollection(s), but the keys I don't have control over and don't really want to write wrapper objects just to change the identity.

    If you're OK, I can send PRs. :)

    opened by matanlurey 19
  • feature request: expose interfaces that other classes can implement

    feature request: expose interfaces that other classes can implement

    I have written a class Superset that implements all methods of BuiltSet. I would like to actually write implements BuiltSet (for the static analysis hints during development), but that would break this line in your code.

    Related(?) issue: https://github.com/dart-lang/site-www/issues/382

    Btw: I can contribute a pull request for this (and the other issues I opened recently). I guess you assign yourself to an issue if you started working on it?

    opened by pschiffmann 13
  • Add option to use HashSet and HashMap instead of default Set (LinkedHashSet) and Map (LinkedHashMap)

    Add option to use HashSet and HashMap instead of default Set (LinkedHashSet) and Map (LinkedHashMap)

    Currently BuiltSet and BuiltMap use Dart's default Set and Map collections, that are not efficient in inserting or deletion large amounts of data. I would like to have ability to specify underlying Set and Map collection implementation, at least as a bool flag to use HashSet and HashMap. Another option would be to use HashSet and HashMap as default backend for those collections. What do you think about it?

    opened by ranquild 11
  • ListBuilder.replace(Iterable iterable) accepts dynamic Iterable

    ListBuilder.replace(Iterable iterable) accepts dynamic Iterable

    Is this intentional to pass Iterable<dynamic> iterable or it could be reused generic E here like?

    void replace(Iterable<E> iterable) {...}
    
    question 
    opened by audkar 10
  • Concurrent modification during iteration: _LinkedHashMap len:0

    Concurrent modification during iteration: _LinkedHashMap len:0

    • Dart SDK Version (dart --version) Flutter 1.5.4-hotfix.2 • channel stable • https://github.com/flutter/flutter.git Framework • revision 7a4c33425d (9 weeks ago) • 2019-04-29 11:05:24 -0700 Engine • revision 52c7a1e849 Tools • Dart 2.3.0 (build 2.3.0-dev.0.5 a1668566e5) (I'm using flutter to developer a mobile app)

    First, I'm not sure if this is a issue of built_collection, or dart SDK, or my code. But I feel like it's more likely a issue of built_collection so I want to report it here first.

    So here is what happened: I'm randomly seeing this error in my code, both in JIT & Compiled AOT app.

    [VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: Exception: Concurrent modification during iteration: _LinkedHashMap len:0.
    #0      _CompactIterator.moveNext (dart:collection-patch/compact_hash.dart:443:7)
    #1      Iterable.firstWhere (dart:core/iterable.dart)
    #2      DiscussionParser._isMutedDiscussionItem (package:munin/providers/bangumi/discussion/parser/DiscussionParser.dart:157:53)
    #3      DiscussionParser.processDiscussionItems (package:munin/providers/bangumi/discussion/parser/DiscussionParser.dart:173:12)
    #4      processDiscussion (package:munin/providers/bangumi/discussion/parser/isoldate.dart:15:8)
    #5      _IsolateConfiguration.apply (package:flutter/src/foundation/isolates.dart:110:16)
    #6      _spawn.<anonymous closure> (package:flutter/src/foundation/isolates.dart:117:45)
    <asynchronous suspension>
    #7      Timeline.timeSync (dart:developer/timeline.dart:161:22)
    #8      _spawn (package:flutter/src/foundation/isolates.dart:115:18)
    <asynchronous suspension>
    #9      _startIsolate.<anonymo<…>
    flutter: Another exception was thrown: Exception: Concurrent modification during iteration: _LinkedHashMap len:0.
    

    here is the corresponding code and here is the corresponding built_value class

    I basically pass a nested built_value class into a new isolate, and check a value in BuiltMapA.BuiltMapB.values.firstWhere, more specifically, I believe while I saw this error BuiltMapB is an empty BuiltMap.

    I think it's more likely due to an issue in built_value because

    1. I don't think I'm modifying this BuiltMap in my code during iteration, and it happened randomly (1 time out of 100?), but once it started happenning I can steadily reproduce it until I restart the app.
    2. It always happens in the same line, same column of my code, which is related to built_value: BuiltMapA.BuiltMapB.values.firstWhere

    Any thoughts on what might be the root cause?

    opened by edwardez 10
  • Serializers spread across projects?

    Serializers spread across projects?

    Hey @davidmorgan, could you possibly give me a little guidance with my serializer problem: My main serializers.dart file is in a package called core (dart native), my flutter app imports all the classes & serializers from the core project.

    I've just made a BlobSerializer so I can store Uint8List data in Firestore, however this requires me to import the package cloud_firestore_platform_interface so I can access the Blob interface, this package requires the flutter SDK so cannot imported by my core project.

    I've created the new serializer in my app project, but I'm unsure how to load it. Can my app can add to the existing serializers?

    One solution could be to move all the serializers to the app project out of core?

    question 
    opened by jimmyff 8
  • Cannot rebuild a BuiltSet entry in BuiltMap

    Cannot rebuild a BuiltSet entry in BuiltMap

    It appears that if you try to rebuild a BuiltSet which is an entry of BuiltMap, it does not rebuild as expected. The changes are not applied.

    main.dart

    import 'package:built_collection/built_collection.dart';
    import 'package:built_value_map_entry_rebuilder/models.dart';
    import 'package:flutter_test/flutter_test.dart';
    
    void main() {
      test('BuiltMap entry update', () {
        final joe = Person((b) => b..name = 'Joe');
        final luke = Person((b) => b..name = 'Luke');
    
        final org = Organization((b) => b
          ..name = 'Foolandia'
          ..people = MapBuilder({
            'mobile': BuiltSet<Person>({joe, luke}),
          }));
    
        final orgBuilder = org.toBuilder();
    
        /// Initial works
        expect(orgBuilder.people['mobile'], equals(BuiltSet<Person>({joe, luke})));
    
        /// Weird rebuild works
        orgBuilder.people['mobile'] = (orgBuilder.people['mobile']!.toBuilder()
              ..removeWhere((e) => e.name == 'Joe'))
            .build();
        expect(orgBuilder.people['mobile'], equals(BuiltSet<Person>({luke})));
    
        /// Rebuild fails, expected to work
        orgBuilder.people['mobile']!
            .rebuild((b) => b..removeWhere((e) => e.name == 'Luke'));
    
        expect(orgBuilder.people['mobile'], equals(BuiltSet<Person>({})));
      });
    }
    

    models.dart

    import 'package:built_value/built_value.dart';
    import 'package:built_value/serializer.dart';
    import 'package:built_collection/built_collection.dart';
    
    part 'models.g.dart';
    
    abstract class Person implements Built<Person, PersonBuilder> {
      static Serializer<Person> get serializer => _$personSerializer;
    
      Person._();
      factory Person([void Function(PersonBuilder) updates]) = _$Person;
    
      String get name;
    }
    
    abstract class Organization
        implements Built<Organization, OrganizationBuilder> {
      static Serializer<Organization> get serializer => _$organizationSerializer;
    
      Organization._();
      factory Organization([void Function(OrganizationBuilder) updates]) =
          _$Organization;
    
      String get name;
    
      BuiltMap<String, BuiltSet<Person>> get people;
    }
    

    built_value_test.dart

    import 'package:built_collection/built_collection.dart';
    import 'package:built_value_map_entry_rebuilder/models.dart';
    import 'package:flutter_test/flutter_test.dart';
    
    void main() {
      test('BuiltMap entry update', () {
        final joe = Person((b) => b..name = 'Joe');
        final luke = Person((b) => b..name = 'Luke');
    
        final org = Organization((b) => b
          ..name = 'Foolandia'
          ..people = MapBuilder({
            'mobile': BuiltSet<Person>({joe, luke}),
          }));
    
        final orgBuilder = org.toBuilder();
    
        /// Initial works
        expect(orgBuilder.people['mobile'], equals(BuiltSet<Person>({joe, luke})));
    
        /// Weird rebuild works
        orgBuilder.people['mobile'] = (orgBuilder.people['mobile']!.toBuilder()
              ..removeWhere((e) => e.name == 'Joe'))
            .build();
        expect(orgBuilder.people['mobile'], equals(BuiltSet<Person>({luke})));
    
        /// Rebuild fails, expected to work
        orgBuilder.people['mobile']!
            .rebuild((b) => b..removeWhere((e) => e.name == 'Luke'));
    
        expect(orgBuilder.people['mobile'], equals(BuiltSet<Person>({})));
      });
    }
    

    Log

    Expected: _BuiltSet<Person>:[]
      Actual: _BuiltSet<Person>:[
                _$Person:Person {  
                name=Luke,  
              }
              ]
       Which: at location [0] is _BuiltSet<Person>:[
                _$Person:Person {  
                name=Luke,  
              }
              ] which longer than expected
    
    package:test_api                                   expect
    expect
    package:flutter_test/src/widget_tester.dart:441
    main.<fn>
    test/built_value_test.dart:38
    2
    
    ✖ BuiltMap entry update
    Exited (1)
    
    question 
    opened by lukepighetti 7
  • BuiltMap<String, dynamic> not supported?

    BuiltMap not supported?

    So getting a Map from Firestore is basically always a Map<String, dynamic>. I was hoping that BuiltMap also supported mixed maps, as I'd opt to use BuiltMaps instead of Map, internally. But it doesn't look like it?

    p2 / feature requiest 
    opened by larssn 7
  • Add asCollection() methods to BuiltCollection classes.

    Add asCollection() methods to BuiltCollection classes.

    Closes https://github.com/google/built_collection.dart/issues/77

    1.2.0

    • Add asList, asMap, and asSet to the built collection classes.

    This change is Reviewable

    opened by matanlurey 7
  • Consider

    Consider "asList", "asMap", and "asSet"

    I like this library, but one thing stopping me from using it is that I don't want to require other users to use built_collection, so I tend to only use it as an implementation detail and pass a toList/toMap/toSet as part of my API.

    I would use asList/Map/Set, where they could be, for example:

    List<T> asList() => new List<T>.unmodifiable(_backingList);
    
    opened by matanlurey 7
  • Add const BuiltList<T>.empty()

    Add const BuiltList.empty()

    This would allow us to use it as a default for a named parameter.

    Related to https://github.com/google/built_collection.dart/issues/164 which is blocked by missing language features.

    p2 / feature requiest 
    opened by cbenhagen 6
  • Shouldn't BuiltMap<String, BuiltSet<int>> work out of the box?

    Shouldn't BuiltMap> work out of the box?

    So this throws a DeserializationError.

    @nullable
    BuiltMap<String, BuiltSet<int>> get entries;
    

    Using this raw data: [wv8P7YM21ryHwcvtCoGR, [688, 689, 690, 691, 692, 693, 694, 695, 696, 697]]

    Thought it was supported. Also tried making a custom builder factory for it, like the error message suggests - same error.

    Any hints?

    EDIT: This is auto-generated in serializers.g.dart:

    ..addBuilderFactory(
              const FullType(BuiltMap, const [
                const FullType(String),
                const FullType(BuiltSet, const [const FullType(int)])
              ]),
              () => new MapBuilder<String, BuiltSet<int>>())
    

    Looks correct? Doesn't work

    The exception message:

    ══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞═════════════════════════════════════════════════════════
    I/flutter (17084): The following DeserializationError was thrown:
    I/flutter (17084): 'Turnover' failed due to: Deserializing '[wv8P7YM21ryHwcvtCoGR, [688, 689, 690, 691, 692, 693, 694,
    I/flutter (17084): 695, 696, 697]]' to 'BuiltMap<String, BuiltSet<int>>' failed due to: Deserializing '[688, 689, 690,
    I/flutter (17084): 691, 692, 693, 694, 695, 696, 697]' to 'BuiltSet<int>' failed due to: Bad state: No builder factory
    I/flutter (17084): for BuiltSet<int>. Fix by adding one, see SerializersBuilder.addBuilderFactory.
    
    question 
    opened by larssn 9
  • [Feature Request/ Question] Native Support for json_serializable / json_annotation

    [Feature Request/ Question] Native Support for json_serializable / json_annotation

    It would be great when the package natively works together with the

    • https://pub.dev/packages/json_serializable and
    • https://pub.dev/packages/json_annotation

    packages for model and entity classes. Currently I have to write some boilerplate code to make it work.

    Or is this already working and I just missed it?

    opened by MSCL 3
  • Docs for `asList` misleading

    Docs for `asList` misleading

    It says "This differs from toList where mutations are explicitly disallowed." ... whic needs 'where' replacing with 'in that' to be correct, or even better a total rewrite to be clear.

    Same for other similar methods.

    p3 / bug 
    opened by davidmorgan 0
  • MapBuilder dont fail in compilation time if generics does not match on constructor

    MapBuilder dont fail in compilation time if generics does not match on constructor

    Currently you will have a runtime exception if you try to build a map with the wrong types.

    This will allow the developer to know at compile time if what he is intending to do will work or not, avoiding bugs.

    opened by danielgomezrico 2
  • Why is the return operator[] on BuiltSetMultimap nullable?

    Why is the return operator[] on BuiltSetMultimap nullable?

    It seems it can never return null. I imagine the API was based on SetMultiMap from package:collection before, but it seems that the return type of that one is not nullable.

    opened by Pacane 0
Releases(v5.1.1)
  • v5.1.1(Sep 3, 2021)

  • v5.1.0(Jun 10, 2021)

    5.1.0

    • Allow collections with nullable types, for example BuiltList<T?>.
    • Allow key/value types to be dynamic. This can be useful occasionally, and with Dart 2 and null safety it's much harder to use dynamic by accident.
    Source code(tar.gz)
    Source code(zip)
  • v5.0.0-nullsafety.0(Nov 17, 2020)

    • Migrate to NNBD.
    • Deps on package:collection and package:quiver have been removed.
    • Multimap builders no longer have addAll methods that accept multimaps from quiver. But, the constructors still work with quiver multimaps.
    Source code(tar.gz)
    Source code(zip)
  • v4.3.2(Jan 1, 2020)

  • v4.3.1(Dec 31, 2019)

  • v4.3.0(Nov 15, 2019)

    • Add extensions methods: List.build, Set.build, Map.build, Iterable.toBuiltList and Iterable.toBuiltSet. Requires SDK 2.6.0.
    • Cleanup: remove unnecessary new and const.
    Source code(tar.gz)
    Source code(zip)
  • v4.2.2(May 12, 2019)

  • v4.2.1(May 6, 2019)

  • v4.2.0(Apr 2, 2019)

    • Add benchmark for VM and dart2js.
    • Stop doing explicit type checks on elements; in Dart 2 these are implied.
    • Modify methods on ListBuilder and SetBuilder that take Iterable elements so they only iterate over the iterable once. Improves performance when the iterable are slow/lazy.
    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Dec 3, 2018)

    • Add return val to SetBuilder.add() to match Set.add().
    • Expand ListMultimapBuilder: implement [] and add return values to remove and removeAll.
    Source code(tar.gz)
    Source code(zip)
  • v4.0.0(Aug 20, 2018)

    • Many changes following Dart 2 improvements to collections:
      • Implement Iterable methods: cast, followedBy and whereType.
      • Remove obsolete Iterable method: retype.
      • Support orElse in singleWhere.
      • Add BuiltList methods: +, indexWhere, lastIndexWhere.
      • Add ListBuilder setters and getters: first, last.
      • Add BuiltMap methods: entries, map.
      • Add MapBuilder methods: addEntries, updateValue, updateAllValues.
      • Implement Dart 2 methods in internal collections used by toList, toMap and toSet.
    • Add from and of constructors to BuiltList, BuiltMap and BuiltSet. The from constructors, like the current constructors, take collections of any type and check each element. The of constructors, like the SDK of constructors, take a collection of the correct type. This means they can be used for type inference, allowing you to omit the explicit type.
    • Make remove, removeAt and removeLast methods on builders return values like their SDK collection equivalents.
    Source code(tar.gz)
    Source code(zip)
  • v3.2.0(Jul 31, 2018)

  • v3.1.0(Mar 13, 2018)

    BuiltList and BuiltSet now implement the new Dart 2 Iterable method followedBy.

    Note that other new methods remain unimplemented and will throw. These are methods with different behaviour in Dart 1 and Dart 2. They will be implemented later.

    Source code(tar.gz)
    Source code(zip)
  • v3.0.5(Feb 18, 2018)

  • v3.0.2(Jan 23, 2018)

  • v3.0.0(Jan 8, 2018)

    Add new methods that will appear in Dart 2. They are currently not implemented.

    This is a breaking change for libraries that implement their own built collections, hence the major version increase. Most users will not need to make any changes.

    Source code(tar.gz)
    Source code(zip)
  • v2.1.3(Jan 8, 2018)

    Changes for Dart 2 will be re-released as v3.0.0.

    They don't make any differences for most users, but for libraries that implement their own built collections, the changes were breaking. So they need a major version increase.

    Source code(tar.gz)
    Source code(zip)
  • v2.1.2(Dec 21, 2017)

  • v2.1.1(Dec 21, 2017)

  • v2.1.0(Dec 4, 2017)

    BuiltSet and BuiltMap now allow you to specify the underlying collection type. For example, you can construct a BuiltSet using a SplayTreeSet. This results in a set that is always in sorted order instead of preserving insertion order. Another useful possibility is to use a HashSet, which leads to a random order but improves performance over the default.

    See SetBuilder.withBase and MapBuilder.withBase.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Nov 20, 2017)

    Split collection classes into abstract interfaces and concrete, private, implementations. This allows new implementations of the interfaces. Note that this change is invisible unless you rely on the exact runtimeType of the collections.

    Source code(tar.gz)
    Source code(zip)
  • v.1.6.2(Nov 16, 2017)

  • v1.6.0(Oct 17, 2017)

    This allows the types of functions passed in to be inferred. Without this, the analyzer will start to complain unless you type them explicitly.

    Source code(tar.gz)
    Source code(zip)
Owner
Google
Google ❤️ Open Source
Google
Petrus Nguyễn Thái Học 193 Dec 29, 2022
Form builder image picker - Form builder image picker for flutter

form_builder_image_picker Field for picking image(s) from Gallery or Camera for

Ferri Sutanto 0 Jan 28, 2022
Responsive-Ui-builder - The responsive ui builder package contains widgets that helps you to create your UI responsive

Responsive Ui Builder Getting Started The responsive ui builder package contains

null 0 Feb 1, 2022
Immutable value types, enum classes, and serialization.

Built Values for Dart Introduction Built Value provides: Immutable value types; EnumClass, classes that behave like enums; JSON serialization. Immutab

Google 816 Dec 23, 2022
Github Trending app built with Flutter+Redux+Built(Immutable Data)

Github Trending app built with Flutter+Redux+Built(Immutable Data)

huangyanxiong 9 May 13, 2020
Functional extensions to Dart collections.

collection_ext A set of extension methods for Dart collections, designed for the purpose of making it easier to write concise, functional-programming-

Yingxin Wu 7 Nov 21, 2022
A collections of packages providing additional functionality for working with bloc

Bloc Extensions A collections of packages providing additional functionality for working with bloc. Index Packages Bloc Hooks Action Blocs Contributin

Aljoscha Grebe 2 Apr 19, 2022
Dart wrapper via `dart:js` for webusb

Dart wrapper via dart:js for https://wicg.github.io/webusb/ Features canUseUsb g

Woodemi Co., Ltd 1 Jan 25, 2022
A Dart Build Plugin that uploads debug symbols for Android, iOS/macOS and source maps for Web to Sentry via sentry-cli

Sentry Dart Plugin A Dart Build Plugin that uploads debug symbols for Android, iOS/macOS and source maps for Web to Sentry via sentry-cli. For doing i

Sentry 36 Jan 4, 2023
Package your Flutter app into OS-specific bundles (.dmg, .exe, etc.) via Dart or the command line.

flutter_distributor Package your Flutter app into OS-specific bundles (.dmg, .exe, etc.) via Dart or the command line. The flutter_distributor source

LeanFlutter 416 Dec 24, 2022
A builder for extracting a package version into code

Include the version of your package in our source code. Add build_version to pubspec.yaml. Also make sure there is a version field. name: my_pkg versi

Kevin Moore 39 Dec 7, 2022
Jannis 0 Jan 29, 2022
An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable.

An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable. For example the RX stream is a BehaviorSubject or a ReplaySubject.

Jon Samwell 8 Jan 22, 2022
Debounce builder, debounce timer

Debounce builder, debounce timer Features debounce builder - Widget provides debounce function from DebounceTimer instance debounce timer

Pokhodyun Alexander 2 Dec 22, 2022
Mempelajari Listview.builder

tugas_flutter_2 Sebuah tugas flutter yang dimana mempelajari banyak hal yaitu Listview.builder online API TabView Personal Deskripsi Project ini hasil

Jarganaya 3 Sep 9, 2022
An interpolated progress builder for video_player controllers

Have you been here before: you created a video player using video_player, but the progress bar updates way to infrequently and makes your UX look chop

Tim 8 Aug 2, 2022
QR.Flutter is a Flutter library for simple and fast QR code rendering via a Widget or custom painter.

QR.Flutter is a Flutter library for simple and fast QR code rendering via a Widget or custom painter. Need help? Please do not submit an issue for a "

Yakka 614 Jan 8, 2023
💳 A Flutter package for making payments via credo central. Provides support for both Android and iOS

?? Credo Package for Flutter TODO: Put a short description of the package here that helps potential users know whether this package might be useful fo

Samuel Abada 0 Dec 26, 2021
Sangre - Sangre streams your backend queries in realtime to your clients minimizing the load via diffs

Sangre Sangre streams your backend queries in realtime to your clients minimizin

P.O.M 5 Nov 27, 2022