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
- 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)
- [1. Add the package to the project](#1-add-the-package-to-the-project)
- [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)
- [4. Configure the sync engine on the client](#4-configure-the-sync-engine-on-the-client)
- [5. Wire the sync call on the client](#5-wire-the-sync-call-on-the-client)
- [3. Configure the sync engine on the client](#3-configure-the-sync-engine-on-the-client)
- [4. Wire the sync call on the client](#4-wire-the-sync-call-on-the-client)
- [Usage](#usage)
- [Scopes and sharing](#scopes-and-sharing)
- [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
dependencies:
serverpod_offline_sync_client: 0.0.4
```
```0.0.5
```yaml
# your_project_server/pubspec.yaml
dependencies:
serverpod_offline_sync_server: 0.0.4
```
```0.0.5
> [!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
`generator.yaml` file and enable the experimental `database: sync` option:
@ -145,53 +144,25 @@ fields:
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`
objects are database instances that tracks operations for sync.
### 3. Configure the sync engine on the client
The generated client opens a CRDT-wrapped session for the same tables:
```dart
final pod = Serverpod(
args,
Protocol(),
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.
final session = await client.createSyncSession(
databasePath,
isDebugMode: kDebugMode,
persistentUserId: persistentUserId,
);
// Initialize the sync engine.
await session.db.initialize();
```
Then, store the `session` instance in the service locator.
> [!NOTE]
> 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
### 4. Wire the sync call on the client
Be sure to authenticate the client before calling the sync methods. Otherwise,
the sync operation will fail with a `Unauthorized` error.

View file

@ -11,7 +11,7 @@ dependencies:
benchmark_harness: ^2.4.0
intl: ^0.20.2
path: ^1.9.1
serverpod_database: 4.0.0-rc.1
serverpod_database: 4.0.0-rc.2
serverpod_offline_sync:
path: ../packages/serverpod_offline_sync
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
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
class: Person
table: person
database: all
database: sync
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
```
2. Configure the sync engine on the server.
2. Configure the sync engine on the client.
```dart
final pod = Serverpod(
args,
Protocol(),
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],
final session = await client.createSyncSession(
databasePath,
isDebugMode: kDebugMode,
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
// Push local changes and merge remote ones once (i.e. pull-to-refresh).
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_test_client/serverpod_offline_sync_test_client.dart';
import 'table_ops.dart' show demoSyncTables;
/// One local replica: a Serverpod client database wrapped with the
/// `serverpod_offline_sync` CRDT layer, plus the calls that synchronize it.
///
@ -61,7 +59,7 @@ class OfflineReplica {
);
final crdtSession = CrdtDatabaseSession.wraps(
rawSession,
syncTables: demoSyncTables,
syncTables: syncTables,
persistentUserId: persistentUserId,
);
await crdtSession.db.initialize();

View file

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

View file

@ -16,7 +16,7 @@ dependencies:
http: ^1.5.0
path: ^1.9.1
path_provider: ^2.1.5
serverpod_database: 4.0.0-rc.1
serverpod_database: 4.0.0-rc.2
serverpod_offline_sync_client:
path: ../../packages/serverpod_offline_sync_client
serverpod_offline_sync_test_client:

View file

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

View file

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

View file

@ -10,7 +10,6 @@
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
/// 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: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
// ignore_for_file: unnecessary_null_comparison
// ignore_for_file: dead_code, unnecessary_null_comparison
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00;

View file

@ -10,7 +10,6 @@
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
/// 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: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
// ignore_for_file: unnecessary_null_comparison
// ignore_for_file: dead_code, unnecessary_null_comparison
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00;

View file

@ -10,7 +10,6 @@
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
/// Materialized visibility state for a CRDT row.

View file

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

View file

@ -10,7 +10,6 @@
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00;
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: no_leading_underscores_for_library_prefixes
import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
as _icw2tu00;

View file

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

View file

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

View file

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

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