chore: Prepare the release of 0.0.4 with serverpod-4.0.0-rc.1 support (#113)

This commit is contained in:
Marcelo Mendonça Soares 2026-08-31 21:15:35 -03:00 • committed by GitHub
commit fbad5c61fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
140 changed files with 22326 additions and 805 deletions

View file

@ -5,6 +5,11 @@ runs:
using: composite using: composite
steps: steps:
- uses: dart-lang/setup-dart@v1 - uses: dart-lang/setup-dart@v1
with:
sdk: 3.12.2
# Composite actions set GITHUB_ACTION_PATH to this folder, so the
# default matcher looks for dart-analyzer.json here and fails setup.
problem-matcher: false
- shell: bash - shell: bash
run: dart pub global activate melos run: dart pub global activate melos

View file

@ -1,9 +1,13 @@
## 0.0.4
- chore: Updated Serverpod to `4.0.0-rc.1`.
## 0.0.3 ## 0.0.3
- fix: Requires non-nullable foreign keys to be `deferred`. - fix: Requires non-nullable foreign keys to be `deferred`.
- fix: Rejects `onDelete=Restrict` on synced tables in favor of `onDelete=NoAction`. - fix: Rejects `onDelete=Restrict` on synced tables in favor of `onDelete=NoAction`.
- fix: Throws proper `DatabaseException` instead of bare `Exception` on the database. - fix: Throws proper `DatabaseException` instead of bare `Exception` on the database.
- chore: Updated Serverpod to `4.0.0-beta.4`. - chore: Updated Serverpod to `4.0.0-rc.1`.
## 0.0.2 ## 0.0.2

View file

@ -101,48 +101,47 @@ Add the packages to the server and client `pubspec.yaml` files.
```yaml ```yaml
# your_project_client/pubspec.yaml # your_project_client/pubspec.yaml
dependencies: dependencies:
serverpod_offline_sync_client: 0.0.3 serverpod_offline_sync_client: 0.0.4
``` ```
```yaml ```yaml
# your_project_server/pubspec.yaml # your_project_server/pubspec.yaml
dependencies: dependencies:
serverpod_offline_sync_server: 0.0.3 serverpod_offline_sync_server: 0.0.4
``` ```
> [!NOTE] > [!NOTE]
> Version `0.0.3` requires Serverpod `4.0.0-beta.4`. > Version `0.0.4` requires Serverpod `4.0.0-rc.1`.
After adding the dependencies, list the `serverpod_offline_sync` module on the After adding the dependencies, list the `serverpod_offline_sync` module on the
`generator.yaml` file: `generator.yaml` file and enable the experimental `database: sync` option:
```yaml ```yaml
modules: modules:
serverpod_offline_sync: serverpod_offline_sync:
nickname: offline_sync nickname: offline_sync
experimental_features:
databaseSync: true
``` ```
The next call to `serverpod generate` and `serverpod create-migration` will The next call to `serverpod generate` and `serverpod create-migration` will
include the `offline_sync` module in the project. Note that the client tables include the `offline_sync` module in the project. Note that the client tables
will only be present on the client's migration if at least one model on the will only be present on the client's migration if at least one model on the
project is defined with `database: all` or `database: client`. project is defined with `database: sync`, `database: all`, or `database: client`.
### 2. Prepare the synced tables ### 2. Prepare the synced tables
Each model you want to sync must be defined with `database: all` (so it exists Each model you want to sync must be defined with `database: sync`. That generates
on both the client and the server), have a UUID primary key, and declare a the table on both the client and the server, uses a UUID primary key, and
`scopeId` field. You mostly never set `scopeId` nor see its value on reads; it injects the `scopeId` ownership field. You mostly never set `scopeId` nor see
is maintained by the CRDT sync layer. its value on reads; it is maintained by the CRDT sync layer.
```yaml ```yaml
class: Person class: Person
table: person table: person
database: all database: sync
fields: fields:
id: UuidValue?, defaultPersist=random_v7
### Owner scope of this row. Maintained by the CRDT sync layer.
### The user never sets this field nor sees its value on reads.
scopeId: int?, relation(parent=crdt_scopes, onDelete=Cascade)
name: String name: String
``` ```
@ -263,13 +262,15 @@ sync session for changes to take effect.
### Data modeling limitations ### Data modeling limitations
Because of the nature of merge conflicts, the package imposes some data-modeling Because of the nature of merge conflicts, the package imposes some data-modeling
limitations, enforced at runtime during initialization. Most are fundamental to limitations. With `database: sync`, Serverpod reports most of them at generate
the design and can never be lifted. time. The rest are enforced at runtime during initialization. Most are
fundamental to the design and can never be lifted.
- Every synced table must: - Every synced table must be defined with `database: sync`.
- Exist on both the client and the server with `database: all`. - Will exist on both the client and the server, like `database: all`.
- Have a UUID primary key. - The type of the `id` will be `UuidValue` with `defaultPersist=random_v7`.
- Declare the field `scopeId: int?, relation(parent=crdt_scopes, onDelete=Cascade)`. - A `scopeId` field will be added as a cascade relation to `crdt_scopes`.
- Both `id` and `scopeId` can be manually declared for more control.
- Unique indexes must include `scopeId` together with the target columns. - Unique indexes must include `scopeId` together with the target columns.
- Global unique indexes are unsupported, except for FK-only indexes. - Global unique indexes are unsupported, except for FK-only indexes.
- Unique indexes are only supported with at least one - Unique indexes are only supported with at least one

View file

@ -3,7 +3,7 @@ description: Local CRDT vs baseline performance impact measurement.
publish_to: none publish_to: none
environment: environment:
sdk: '^3.10.3' sdk: '^3.12.2'
resolution: workspace resolution: workspace
@ -11,7 +11,7 @@ dependencies:
benchmark_harness: ^2.4.0 benchmark_harness: ^2.4.0
intl: ^0.20.2 intl: ^0.20.2
path: ^1.9.1 path: ^1.9.1
serverpod_database: 4.0.0-beta.4 serverpod_database: 4.0.0-rc.1
serverpod_offline_sync: serverpod_offline_sync:
path: ../packages/serverpod_offline_sync path: ../packages/serverpod_offline_sync
serverpod_offline_sync_server: serverpod_offline_sync_server:

View file

@ -5,7 +5,7 @@ publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ^3.10.3 sdk: '^3.12.2'
dependencies: dependencies:
flutter: flutter:
@ -16,7 +16,7 @@ dependencies:
http: ^1.5.0 http: ^1.5.0
path: ^1.9.1 path: ^1.9.1
path_provider: ^2.1.5 path_provider: ^2.1.5
serverpod_database: 4.0.0-beta.4 serverpod_database: 4.0.0-rc.1
serverpod_offline_sync_client: serverpod_offline_sync_client:
path: ../../packages/serverpod_offline_sync_client path: ../../packages/serverpod_offline_sync_client
serverpod_offline_sync_test_client: serverpod_offline_sync_test_client:

View file

@ -17,8 +17,8 @@ typedef CrdtSyncTransport =
class CrdtSyncSession { class CrdtSyncSession {
CrdtSyncSession._({ CrdtSyncSession._({
required this.done, required this.done,
required Future<void> Function() cancel, required this._cancel,
}) : _cancel = cancel; });
/// Completes when the sync session ends normally or after [cancel]. /// Completes when the sync session ends normally or after [cancel].
final Future<void> done; final Future<void> done;

View file

@ -55,18 +55,16 @@ bool _grantsEqual(List<CrdtScopeGrant>? a, List<CrdtScopeGrant> b) {
/// control flow); this owns the scopes. It needs only a [DatabaseSession] and /// control flow); this owns the scopes. It needs only a [DatabaseSession] and
/// the membership helpers, so it carries no protocol or transport concerns. /// the membership helpers, so it carries no protocol or transport concerns.
class CrdtScopeSyncSession { class CrdtScopeSyncSession {
/// Creates a scope session for [userId] acting in [mode] against [_session]. /// Creates a scope session for [_userId] acting in [_mode] against [_session].
/// ///
/// [peerNodeId] is the remote peer's CRDT node, learned from its connect /// [_peerNodeId] is the remote peer's CRDT node, learned from its connect
/// handshake before this session is constructed. /// handshake before this session is constructed.
CrdtScopeSyncSession( CrdtScopeSyncSession(
this._session, { this._session, {
required UuidValue userId, required this._userId,
required CrdtSyncPeerMode mode, required this._mode,
required UuidValue peerNodeId, required this._peerNodeId,
}) : _userId = userId, });
_mode = mode,
_peerNodeId = peerNodeId;
final DatabaseSession _session; final DatabaseSession _session;
final UuidValue _userId; final UuidValue _userId;

View file

@ -46,7 +46,7 @@ class CrdtSync {
int syncBatchSize = defaultSyncBatchSize, int syncBatchSize = defaultSyncBatchSize,
/// Delay between continuous sync rounds. /// Delay between continuous sync rounds.
Duration continuousSyncInterval = defaultContinuousSyncInterval, this._continuousSyncInterval = defaultContinuousSyncInterval,
}) : _syncTables = syncTables, }) : _syncTables = syncTables,
_serializationManager = serializationManager, _serializationManager = serializationManager,
_databaseContext = _databaseContext =
@ -55,8 +55,7 @@ class CrdtSync {
syncTables: syncTables, syncTables: syncTables,
serializationManager: serializationManager, serializationManager: serializationManager,
), ),
_syncBatchSize = syncBatchSize, _syncBatchSize = syncBatchSize {
_continuousSyncInterval = continuousSyncInterval {
if (syncBatchSize < 1) { if (syncBatchSize < 1) {
throw ArgumentError.value(syncBatchSize, 'syncBatchSize', 'Must be >= 1'); throw ArgumentError.value(syncBatchSize, 'syncBatchSize', 'Must be >= 1');
} }

View file

@ -67,14 +67,11 @@ class CrdtDatabase implements Database {
CrdtDatabase._( CrdtDatabase._(
this._delegate, this._delegate,
this._context, { this._context, {
required List<Table> syncTables, required this._syncTables,
required int syncBatchSize, required this._syncBatchSize,
required Duration continuousSyncInterval, required this._continuousSyncInterval,
required UuidValue? persistentUserId, required UuidValue? persistentUserId,
}) : _syncTables = syncTables, }) : _recorder = CrdtMutationRecorder(
_syncBatchSize = syncBatchSize,
_continuousSyncInterval = continuousSyncInterval,
_recorder = CrdtMutationRecorder(
_delegate, _delegate,
context: _context, context: _context,
persistentUserId: persistentUserId, persistentUserId: persistentUserId,

View file

@ -56,10 +56,9 @@ class CrdtForeignKeyProjector {
/// Creates a projector over the given recorder context. /// Creates a projector over the given recorder context.
CrdtForeignKeyProjector( CrdtForeignKeyProjector(
this._context, { this._context, {
required CrdtForeignKeyGraph foreignKeys, required this._foreignKeys,
required CrdtUniqueConflictResolver uniqueResolver, required this._uniqueResolver,
}) : _foreignKeys = foreignKeys, });
_uniqueResolver = uniqueResolver;
final CrdtRecorderContext _context; final CrdtRecorderContext _context;
final CrdtForeignKeyGraph _foreignKeys; final CrdtForeignKeyGraph _foreignKeys;

View file

@ -8,9 +8,9 @@
// ignore_for_file: type_literal_in_constant_pattern // ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters // ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: dead_code, unnecessary_null_comparison // ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
// ignore_for_file: unnecessary_null_comparison
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd; import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart' import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00; as _icw2tu00;

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss; import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
/// Why a synced user tombstone changed row visibility. /// Why a synced user tombstone changed row visibility.
@ -18,8 +19,7 @@ enum CrdtDataDeletedReason implements _iss.SerializableModel {
userReinsert, userReinsert,
userDelete, userDelete,
userCascadeDelete, userCascadeDelete,
uniqueLoser, uniqueLoser;
;
static CrdtDataDeletedReason fromJson(int index) { static CrdtDataDeletedReason fromJson(int index) {
switch (index) { switch (index) {

View file

@ -8,9 +8,9 @@
// ignore_for_file: type_literal_in_constant_pattern // ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters // ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: dead_code, unnecessary_null_comparison // ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
// ignore_for_file: unnecessary_null_comparison
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd; import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart' import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00; as _icw2tu00;

View file

@ -8,9 +8,9 @@
// ignore_for_file: type_literal_in_constant_pattern // ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters // ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: dead_code, unnecessary_null_comparison // ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
// ignore_for_file: unnecessary_null_comparison
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd; import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart' import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00; as _icw2tu00;

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss; import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
/// Why a foreign key projection override is active. /// Why a foreign key projection override is active.
@ -26,8 +27,7 @@ enum CrdtForeignKeyOverrideReason implements _iss.SerializableModel {
/// cannot repair the value (restrict, no action, cascade, or the column is /// cannot repair the value (restrict, no action, cascade, or the column is
/// non-nullable), so the row is hidden by projection while the attempt is /// non-nullable), so the row is hidden by projection while the attempt is
/// preserved. /// preserved.
missingParent, missingParent;
;
static CrdtForeignKeyOverrideReason fromJson(int index) { static CrdtForeignKeyOverrideReason fromJson(int index) {
switch (index) { switch (index) {

View file

@ -8,9 +8,9 @@
// ignore_for_file: type_literal_in_constant_pattern // ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters // ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: dead_code, unnecessary_null_comparison // ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
// ignore_for_file: unnecessary_null_comparison
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd; import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart' import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00; as _icw2tu00;

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss; import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
/// Materialized visibility state for a CRDT row. /// Materialized visibility state for a CRDT row.
@ -22,8 +23,7 @@ enum CrdtDataRowVisibility implements _iss.SerializableModel {
foreignKeyRestrictRestore, foreignKeyRestrictRestore,
userDelete, userDelete,
userCascadeDelete, userCascadeDelete,
foreignKeyCascade, foreignKeyCascade;
;
static CrdtDataRowVisibility fromJson(int index) { static CrdtDataRowVisibility fromJson(int index) {
switch (index) { switch (index) {

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss; import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
/// Base class for HLC timestamps. /// Base class for HLC timestamps.

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart' import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00; as _icw2tu00;
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss; import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member // ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd; import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart' import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00; as _icw2tu00;

Some files were not shown because too many files have changed in this diff Show more