chore: Update Serverpod and prepare the release (#117)

This commit is contained in:
Marcelo Mendonça Soares 2026-09-04 22:10:53 -03:00 • committed by GitHub
commit 2a6d66c79e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
103 changed files with 358 additions and 323 deletions

View file

@ -1,3 +1,12 @@
## 0.0.5
- fix: BREAKING. Rebuilds foreign key and unique projection from authored facts.
- perf: Increases unique-conflict merge throughput by ~40%.
- perf: Increases foreign-key chain insert merge throughput by ~3.3×.
- perf: Increases foreign-key chain delete merge throughput by ~44%.
- perf: Reduces storage used by CRDT metadata on relations.
- chore: Updated Serverpod to `4.0.0-rc.2`.
## 0.0.4 ## 0.0.4
- chore: Updated Serverpod to `4.0.0-rc.1`. - chore: Updated Serverpod to `4.0.0-rc.1`.

View file

@ -17,9 +17,8 @@ out-of-the-box with Serverpod's existing APIs.
- [Getting started](#getting-started) - [Getting started](#getting-started)
- [1. Add the package to the project](#1-add-the-package-to-the-project) - [1. Add the package to the project](#1-add-the-package-to-the-project)
- [2. Prepare the synced tables](#2-prepare-the-synced-tables) - [2. Prepare the synced tables](#2-prepare-the-synced-tables)
- [3. Configure the sync engine on the server](#3-configure-the-sync-engine-on-the-server) - [3. Configure the sync engine on the client](#3-configure-the-sync-engine-on-the-client)
- [4. Configure the sync engine on the client](#4-configure-the-sync-engine-on-the-client) - [4. Wire the sync call on the client](#4-wire-the-sync-call-on-the-client)
- [5. Wire the sync call on the client](#5-wire-the-sync-call-on-the-client)
- [Usage](#usage) - [Usage](#usage)
- [Scopes and sharing](#scopes-and-sharing) - [Scopes and sharing](#scopes-and-sharing)
- [Data modeling limitations](#data-modeling-limitations) - [Data modeling limitations](#data-modeling-limitations)
@ -102,17 +101,17 @@ Add the packages to the server and client `pubspec.yaml` files.
# your_project_client/pubspec.yaml # your_project_client/pubspec.yaml
dependencies: dependencies:
serverpod_offline_sync_client: 0.0.4 serverpod_offline_sync_client: 0.0.4
``` ```0.0.5
```yaml ```yaml
# your_project_server/pubspec.yaml # your_project_server/pubspec.yaml
dependencies: dependencies:
serverpod_offline_sync_server: 0.0.4 serverpod_offline_sync_server: 0.0.4
``` ```0.0.5
> [!NOTE] > [!NOTE]
> Version `0.0.4` requires Serverpod `4.0.0-rc.1`. > Version `0.0.4` requires Serverpod `4.0.0-rc.2`.
0.0.5
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 and enable the experimental `database: sync` option: `generator.yaml` file and enable the experimental `database: sync` option:
@ -145,53 +144,25 @@ fields:
name: String name: String
``` ```
### 3. Configure the sync engine on the server The `serverpod generate` command will emit the `syncTables` list on the server
and the client. It will also wire the sync engine on the generated `Serverpod`
class, so no extra configuration is needed on the server.
On the server, the `databaseInterceptor` is what ensures that all `session.db` ### 3. Configure the sync engine on the client
objects are database instances that tracks operations for sync.
The generated client opens a CRDT-wrapped session for the same tables:
```dart ```dart
final pod = Serverpod( final session = await client.createSyncSession(
args, databasePath,
Protocol(), isDebugMode: kDebugMode,
Endpoints(),
// Add the database interceptor.
databaseInterceptor: crdtDatabaseInterceptor,
);
// Initialize the sync engine before pod.start() is called.
pod.initializeCrdtSync(
syncTables: [Person.t, Book.t, Author.t],
);
```
Don't forget to also configure the authentication following the regular
Serverpod auth setup. Authentication is required because the sync needs a
logged in user to synchronize data.
### 4. Configure the sync engine on the client
On the client, the regular `DatabaseSession` must be replaced by a wrapped
version that includes the sync engine.
```dart
final session = CrdtDatabaseSession.wraps(
await client.createSession(databasePath, isDebugMode: kDebugMode),
syncTables: [Person.t, Book.t, Author.t], // Must be the same as on the server.
persistentUserId: persistentUserId, persistentUserId: persistentUserId,
); );
// Initialize the sync engine.
await session.db.initialize();
``` ```
Then, store the `session` instance in the service locator. Then, store the `session` instance in the service locator.
> [!NOTE] ### 4. Wire the sync call on the client
> The list of synced tables must be the same on the server and the client.
> Otherwise, all sync attempts will fail with a schema mismatch error.
### 5. Wire the sync call on the client
Be sure to authenticate the client before calling the sync methods. Otherwise, Be sure to authenticate the client before calling the sync methods. Otherwise,
the sync operation will fail with a `Unauthorized` error. the sync operation will fail with a `Unauthorized` error.

View file

@ -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-rc.1 serverpod_database: 4.0.0-rc.2
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

@ -15,51 +15,27 @@ The work on this package was inspired by the excellent work on [Synql](https://g
The main selling point of the package is that there is almost nothing to learn. The app is built The main selling point of the package is that there is almost nothing to learn. The app is built
as a normal offline Serverpod app (generated models, ordinary CRUD) and the plugin handles replication automatically. as a normal offline Serverpod app (generated models, ordinary CRUD) and the plugin handles replication automatically.
The four steps below contain all required changes on a project to use the package: The three steps below contain all required changes on a project to use the package:
1. Prepare the synced tables by adding the `scopeId` field to the models. 1. Prepare the synced tables by using `database: sync` keyword on the models.
```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
``` ```
2. Configure the sync engine on the server. 2. Configure the sync engine on the client.
```dart ```dart
final pod = Serverpod( final session = await client.createSyncSession(
args, databasePath,
Protocol(), isDebugMode: kDebugMode,
Endpoints(),
// Add the database interceptor.
databaseInterceptor: crdtDatabaseInterceptor,
);
// Initialize the sync engine before pod.start() is called.
pod.initializeCrdtSync(
syncTables: [Person.t, Book.t, Author.t],
);
```
3. Configure the sync engine on the client.
```dart
final session = CrdtDatabaseSession.wraps(
await client.createSession(databasePath, isDebugMode: kDebugMode),
syncTables: [Person.t, Book.t, Author.t],
persistentUserId: persistentUserId, persistentUserId: persistentUserId,
); );
// Initialize the sync engine.
// Then store the `session` instance in the service locator.
await session.db.initialize();
``` ```
4. Wire the sync call on the client. 3. Wire the sync call on the client.
```dart ```dart
// Push local changes and merge remote ones once (i.e. pull-to-refresh). // Push local changes and merge remote ones once (i.e. pull-to-refresh).
await client.crdt.syncOnce(session); await client.crdt.syncOnce(session);

View file

@ -7,8 +7,6 @@ import 'package:serverpod_database/serverpod_database.dart'
import 'package:serverpod_offline_sync_client/serverpod_offline_sync_client.dart'; import 'package:serverpod_offline_sync_client/serverpod_offline_sync_client.dart';
import 'package:serverpod_offline_sync_test_client/serverpod_offline_sync_test_client.dart'; import 'package:serverpod_offline_sync_test_client/serverpod_offline_sync_test_client.dart';
import 'table_ops.dart' show demoSyncTables;
/// One local replica: a Serverpod client database wrapped with the /// One local replica: a Serverpod client database wrapped with the
/// `serverpod_offline_sync` CRDT layer, plus the calls that synchronize it. /// `serverpod_offline_sync` CRDT layer, plus the calls that synchronize it.
/// ///
@ -61,7 +59,7 @@ class OfflineReplica {
); );
final crdtSession = CrdtDatabaseSession.wraps( final crdtSession = CrdtDatabaseSession.wraps(
rawSession, rawSession,
syncTables: demoSyncTables, syncTables: syncTables,
persistentUserId: persistentUserId, persistentUserId: persistentUserId,
); );
await crdtSession.db.initialize(); await crdtSession.db.initialize();

View file

@ -261,8 +261,8 @@ final Map<String, TableOps> demoTableOps = {
ops.name: ops, ops.name: ops,
}; };
/// Tables synchronized by the demo, derived from [demoTableOps]. Mirrors the /// Tables shown in the demo UI, derived from [demoTableOps]. The CRDT engine
/// server `demoSyncTables`. /// uses the generated [syncTables] list so it matches the server.
final List<Table> demoSyncTables = [ final List<Table> demoSyncTables = [
for (final ops in demoTableOps.values) ops.table, for (final ops in demoTableOps.values) ops.table,
]; ];

View file

@ -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-rc.1 serverpod_database: 4.0.0-rc.2
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

@ -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, no_leading_underscores_for_library_prefixes // ignore_for_file: dead_code, unnecessary_null_comparison
// 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, no_leading_underscores_for_library_prefixes // ignore_for_file: dead_code, unnecessary_null_comparison
// 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,7 +10,6 @@
// 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.

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, no_leading_underscores_for_library_prefixes // ignore_for_file: dead_code, unnecessary_null_comparison
// 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,7 +10,6 @@
// 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 domain column currently differs from its authored value. /// Why a domain column currently differs from its authored value.

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, no_leading_underscores_for_library_prefixes // ignore_for_file: dead_code, unnecessary_null_comparison
// 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,7 +10,6 @@
// 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.

View file

@ -10,7 +10,6 @@
// 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,7 +10,6 @@
// 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,7 +10,6 @@
// 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;

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, no_leading_underscores_for_library_prefixes // ignore_for_file: dead_code, unnecessary_null_comparison
// 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, no_leading_underscores_for_library_prefixes // ignore_for_file: dead_code, unnecessary_null_comparison
// 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, no_leading_underscores_for_library_prefixes // ignore_for_file: dead_code, unnecessary_null_comparison
// 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;

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