Appearance
Editing
Two ways to update documents: save() for full content replacement, and edit() for targeted changes with MongoDB-style operators.
save() — full replacement
Pass the complete document content. The store computes the minimal diff automatically:
typescript
const [errors, newHash] = await store.save(uid, hash, {
title: 'Updated Title',
url: 'https://example.com',
content: 'New content here',
});Good for form editors where you have the full state.
edit() — targeted changes
Apply specific changes using update operators:
typescript
const [errors, newHash] = await store.edit(uid, { hash }, {
$set: { title: 'New Title' }
});Operators
$set — set field values
typescript
await store.edit(uid, { hash }, {
$set: { title: 'New Title', status: 'archived' }
});$unset — remove fields
typescript
await store.edit(uid, { hash }, {
$unset: { description: '' }
});$inc — increment numbers
typescript
await store.edit(uid, { hash }, {
$inc: { views: 1 }
});$push — add to array
typescript
await store.edit(uid, { hash }, {
$push: { tags: 'new-tag' }
});$pull — remove from array
typescript
// Remove by value
await store.edit(uid, { hash }, {
$pull: { tags: 'old-tag' }
});
// Remove by object match
await store.edit(uid, { hash }, {
$pull: { members: { userId: someUid } }
});
// Dot-path matching for nested fields
await store.edit(uid, { hash }, {
$pull: { photos: { 'file.hash': fileHash } }
});$pullAll — remove all matching values
typescript
await store.edit(uid, { hash }, {
$pullAll: { tags: ['tag1', 'tag2'] }
});$addToSet — add unique to array
typescript
await store.edit(uid, { hash }, {
$addToSet: { tags: 'unique-tag' }
});
// Add multiple
await store.edit(uid, { hash }, {
$addToSet: { tags: { $each: ['tag1', 'tag2'] } }
});$delete — soft delete
typescript
await store.edit(uid, { hash }, {
$delete: true
});This creates an edit that syncs to peers — they'll delete their copy too.
Positional $ — update matched array element
The filter in edit() can match an array element, and $ in the update path refers to that element:
typescript
// Update a specific member's role
await store.edit(uid, { hash, 'members.userId': bobUid }, {
$set: { 'members.$.role': 'admin' }
});
// Update a nested field on a matched element
await store.edit(uid, { hash, 'file.id': 'b' }, {
$set: { 'file.$.name': 'Bee' }
});Works with $set, $unset, and $pull on nested arrays:
typescript
// Remove a value from a sub-array on a matched element
await store.edit(uid, { hash, 'members.id': 'b' }, {
$pull: { 'members.$.tags': 'old-tag' }
});Operator ordering
$pull runs before $push in the same operation. This lets you do remove-then-add:
typescript
await store.edit(uid, { hash }, {
$pull: { assignee: oldUser },
$push: { assignee: newUser }
});Version history
Every edit is stored. You can get the full chain:
typescript
const { original, versions, edits } = await store.versions(hash);
// original: the first version
// versions: computed state after each edit
// edits: raw edit documents with operators and timestampsWhen to use which
| Scenario | Method | Why |
|---|---|---|
| Form submission | save() | You have the full state, let the store diff it |
| Toggle a flag | edit() | Targeted $set, no need to fetch first |
| Add to a list | edit() | $push without reading the current list |
| Remove from a list | edit() | $pull by value or object match |
| Delete a document | edit() or delete() | Both create a synced $delete edit |