ssb-crut

    3.0.4 • Public • Published

    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(ssb, spec) // ssb = an ssb server
    
    crut.create(
      {
        title: 'Ahau launch party',
        attendees: { add: ['Mix'] },
    
        recps: ['%A9OUzXtv7BhaAfSMqBzOO6JC8kvwmZWGVxHDAlM+/so=.cloaked']
        // makes it a private record, can only be set on create
      },
      (err, gatheringId) => {
        //
      }
    )
    // later
    
    crut.update(
      gatheringId,
      {
        description: "Let's celebrate this new phase!",
        attendees: { add: ['Cherese'] }  // props
      },
      (err, updateId) => {
        crut.read(gatheringId, (err, gathering) => {
          //
        })
      }
    )

    Requirements

    Requires and ssb server with ssb-backlinks installed.

    Optionally, if you want to make private (encrypted) records, you will need to have a plugin installed which knows how to handle your recps

    • ssb-tribes (recommended) for records stored in a private group, or direct messages
    • ssb-private1 for records as direct messages

    API

    new CRUT(ssb, spec) => crut

    Takes ssb, a 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 messages
    • props Object
      • defines how the mutable parts of the record will behaved.
      • each property is expected to be an instance of a tangle strategy (e.g. @tangle/simple-set)
      • reserved props: ['type', 'recps', 'tangle', 'tombstone']

    Optional properties:

    • tangle String
      • where the tangle info will be stored content.tangles[tangle]
      • defaults to type.split('/')[0]
    • typePattern String
      • a regex pattern string which can be used to over-write the default. Useful if you've used regex characters that need escaping in you spec.type (e.g. *)
      • defaults to : "^" + spec.type + "$"
    • staticProps Object
      • a list of props which are not mutable, and will be baked into root message
      • format is in JSON Schema, for example:
      {
        source: { type: 'string', required: true },
        live: { type: 'boolean' }
      }
      
      • reserved staticProps: ['type', 'recps', 'tangle', 'tombstone']
    • isValidNextStep Function
      • a function run before writing and during reading to confirm the validity of message extending the tangle
      • signature: fn(context, msg) => Boolean where
        • context = { accT, graph } where accT is the accumulated transform for the position immediately before this message in the tangle, and graph is a @tangle/graph instance for the tangle so far.
        • msg the message being assessed
      • used by:
        • create to check the message about to be published is valid. In this case accT = I, the (empty) identity transform.
        • update to check the message about to be published is valid given the existing tangle state it would extend
        • read to determine which messages are valid to include in reducing
      • NOTE - in create you don't have access to context.graph, as there's no graph yet
    • hooks Object with properties:
      • isRoot Array a collection of validator functions which each root message must pass to proceed
      • isUpdate Array a collection of validator functions which each update message must pass to proceed
      • NOTE these validators are expected to have signature isValid (content) => true|Error
    • getTransformation Function
      • a function which is used to map a full message ({ key, value }) to a transformation
      • default: m => m.value.content
      • this is particularly useful for coercing legacy messages into a format your spec can process
      • WARNING: create a new object if you plan to mutate the shape of a message, otherwise ssb-crut will break

    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
      • recps Array (optional) a list of recipients who this record will be encrypted to. Once this is set on create, it cannot be updated
      • allowPublic Boolean (optional) for if you have ssb-guard-recps installed and want to explicitly allow a public message through

    Notes:

    • if cb is not passed, a Promise is returned instead.

    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,          // the key of the tangle root message
      type,
      ...staticProps,  // any staticProp values
      states: [
        {
          key: D,      // key of tangle tip message
          ...props     // reified state of props for this tangle tip
        },
        {
          key: B,      // key of tangle tip message
          ...props     // reified state of props for this tangle tip
        }
      }
    }

    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.
    • if cb is not passed, a Promise is returned instead.

    crut.update(id, props, cb)

    Updates record id. props Object can have properties

    • all/ some/ none of the props declared in spec.props
    • allowPublic Boolean (optional) for if you have ssb-guard-recps installed and want to explicitly allow a public message through

    The props provided are used to generate a transformation which is then checked with isValidUpdate (if provided), they are:

    Message contents are also checked against isUpdate before publishing. Calls back with the key of the update message published.

    Notes:

    • if cb is not passed, a Promise is returned instead.
    • 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, opts, cb)

    A convenience helper mainly here to put the T in CRUT.

    • opts Object with properties:
      • reason String (optional) give a reason for why you're tombstoning the record
      • undo Boolean (optional) set to true to remove the tombstone
      • allowPublic Boolean (optional) for if you have ssb-guard-recps installed and want to explicitly allow a public message through

    Calls back with the key of the update message which tombstoned.

    Notes:

    • if cb is not passed, a Promise is returned instead.

    TODO

    • 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

    Install

    npm i ssb-crut

    DownloadsWeekly Downloads

    102

    Version

    3.0.4

    License

    AGPL-3.0

    Unpacked Size

    69.3 kB

    Total Files

    20

    Last publish

    Collaborators

    • avatar
    • avatar
    • avatar
    • avatar
    • avatar