Local-first
NOTE
This is functionality is limited to applications using the Solid Protocol.
In some situations, it is desirable to keep track of changes made to a model over time. That is the cornerstone of making local-first applications.
History tracking is disabled by default, but you can enable it in the model declaration:
class Person extends SolidModel {
static history = true;
}
Once this is enabled, any changes that are made to the model will create new operations using the built-in operations
relationship:
const alice = await Person.create({ name: 'Alice' });
await alice.update({ name: 'Alice Doe' });
console.log(alice.operations.length);
// console output: 2
The code above will generate the following operations:
- Set
name
to "Alice" - Set
name
to "Alice Doe"
This is a trivial example, but this allows implementing a crude CRDT mechanism that uses the Solid POD as mediator. This can be demonstrated by creating two instances of a model, updating them separately, and synchronizing them afterwards using the synchronize
method:
// Create two divergent models.
const original = await Person.create({ name: 'Peter Parker', alias: 'Spider-Man' });
const divergent = original.clone({ clean: true });
// Update them independently.
await original.update({ alias: 'The Amazing Spider-Man' });
await divergent.update({ name: 'Miles Morales' });
// Synchronize updates.
await SolidModel.synchronize(original, divergent);
// Both are now "Miles Morales - The Amazing Spider-Man". 🤔
console.log(original.getAttributes());
console.log(divergent.getAttributes());
Deletions
In a pure local-first approach, you wouldn't be able to delete documents, given that CRDT operations are supposed to be immutable. However, this library allows to delete documents. In order to make sure that these are not re-created on sync, by default deleted documents will leave a crdt:Tombstone
behind:
await alice.delete(); // This will delete all data and operations from alice, and leave a crdt:Tombstone with the deletion date.
This behaviour can be disabled by setting the tombstone
in the model declaration, but it's not recommended:
class Person extends SolidModel {
static history = true;
static tombstone = false;
}
Finally, it is also possible to soft-delete documents. This won't delete any of the data, but it will mark a resource as deleted:
await alice.softDelete();
alice.isSoftDeleted(); // true
If you want to make sure that all models are soft-deleted, you can use the useSoftDeletes
static method:
Person.useSoftDeletes(true); // or false to disable
await alice.delete(); // The resource will be soft-deleted.
await alice.forceDelete(); // Use to make sure that the resource is deleted either way.
Learn more
If you want to learn more about CRDTs in Solid, check out the following presentation: