ssb-crut
Easily mint CRUD (Create, Read, Update, Delete) methods for scuttlebutt records!
Note there is no Delete, so instead we have T for Tombstone (CRUT!)
Example usage
const Crut = require('ssb-crut')
const Overwrite = require('@tangle/overwrite')
const SimpleSet = require('@tangle/simple-set')
const spec = {
type: 'gathering',
props: {
title: Overwrite(),
description: Overwrite(),
attendees: SimpleSet(),
},
}
const crut = new Crut(server, spec)
crut.create(
{
title: 'Ahau launch party',
attendees: { add: ['Mix'] }
recps: ['%group'] },
},
(err, gatheringId) => {
}
)
crut.update(
gatheringId
{
description: "Let's celebrate this new phase!",
attendees: { add: ['Cherese'] }
},
(err, updateId) => {
}
)
API
new CRUT(server, spec) => crut
Takes an scuttelbutt server instance and a spec
and returns an crut instance
with methods for mutable records.
A spec
is an Object with properties:
type
String - directly related to the type
field that will occur on messagesprops
Object
- defines how the mutable parts of the record will behaved.
- each property is expected to story an instangle of a tangle strategy (e.g.
@tangle/simple-set
)
Optional properties:
crut.create(allProps, cb)
Makes a new record, and calls back with the id
of that record.
allProps
Object
- none/ some/ all of the properties declared in
spec.props
- none/ some/ all of the properties declared in
spec.staticProps
Message content is checked with isRoot
before publishing.
crut.read(id, cb)
Takes a record id and calls back with a Record.
A tangle here is a collection of messages linked in a directed acyclic graph.
Each of thee messages contains an "operational transform" which is an
instuction about how to update the record state.
Transformations are concatenated (added up) while traversing the graph.
For a tangle made up of messages linked like this:
A << root
/ \
B C << concurrent updates
|
D << an update which is also a tip
Then the reduced Record would look like:
{
key: A,
type,
...staticProps,
states: [
{
key: D,
...props
},
{
key: B,
...props
}
}
}
There will be 1 or more "states" depending on whether the tangle is a in a
branched / forked state at the moment.
The state of the props returned are "riefied" (meaning has been made real),
because often the transformation format is optimised for mathematical properties,
but not very human friendly.
NOTES:
states
is sorted "most recent" to "least recent" by the tip messages's declared timestamp.
crut.update(id, props, cb)
Updates record id
. The props
provided are used to generate a transformation which is then
checked with isValidUpdate
(if provided).
Message contents are also checked against isUpdate
before publishing.
Calls back with the key of the update message published.
NOTES:
- by default, updates are accepted from everyone
- to change this, specifiy behaviour in
isValidUpdate
e.g.
spec.isValidUpdate = (context, msg) => {
const { accT, graph } = context
if (!graph) return true
// crut.read has graph, but crut.update doesn't yet
// this means updates from others can be published but will be ignored
return graph.rootNodes.some(root => {
return root.value.author === msg.value.author
})
}
crut.tombstone(id, reason, cb)
:warning: TODO
A convenience helper mainly here to put the T in CRUT.
Requires props
strategy with{ tombstone: require('tangle/overwrite)() }
Calls back with the key of the update message which tombstoned.
Notes
-
crut.update
does not currently publish merges
- currently extends the tip with most recent activity
- want to change this in the future but @tangle/reduce will needs more work
-
need to add support for ssb-recps-guard
-
want to remove isRoot
+ isUpdate
- auto-generate them from
type
, tangle
, staticProps
, props
- would need strategies to provide schema
- could still optionally provide these methods
-
need restrictions
tangle
!=== 'group'props
excludes: [type, recps, tangle, ...staticProps]staticProps
excludes: [tombstone, tangle, ...props]- where should
type
+ recps
be ???
-
isValidUpdate
currently only receives ({ context })
(no graph)
- adding the graph is possible but awkward, I think we should only add this if absolutely needed
- if @tangle/reduce is modified to be able to "update" as new messages come in, that could be useful for this case too (as that will likely have a copy of the graph