4.1.2 • Public • Published


A strategy is a way of describing "transformations" and how to apply them to one another

A valid strategy is an Object which provides the following:

  • identity() => I Function - an getter which return an element which represents the identity-transformation, I.
  • schema Object - a JSON schema which can be used to validate a transformation
  • concat(A, B) => C Function
    • takes two transformations and concatenates (applies) B to A to produce a new transformation C
    • NOTE order matters
  • mapToOutput(T) => output Function
    • takes a transformation T and maps it into a "real" state which can be used in e.g. human interfaces
  • mapFromInput(input, currentTips) => T Function (optional)
    • Takes a current transformation state, currentTips, which is an array of T along with a human friendly (descriptive) input and returns a transformation T which satisfies the change requested in input.
    • you can define a function which maps human input form to a transformation T
    • useful if the raw transformation form is hard for people to write
    • default: t => t

This module allows you compose larger strategies for transformations made up of transformations

Example Usage

const Strategy = require('@tangle/strategy')

const strategy = new Strategy({
  title: require('@tangle/overwrite')(),
  attendees: require('@tangle/simple-set')()

const T1 = {
  title: { set: 'brunch' },
  attendees: { mix: 1, alanna: 1 }

const T2 = {
  title: { set: 'Brunch at Mixs' },
  attendees: { alanna: -1, ben: 1 }

const T3 = strategy.concat(T1, T2)
// => {
//  title: { set: 'Brunch at Mixs' },
//  attendees: { mix: 1, ben: 1 }
// }

// => {
//  title: 'Brunch at Mixs',
//  attendees: ['ben', 'mix']
// }


Strategy.isValid(strategy) => Boolean

A helper for checking whether a strategy is valid (has everything required). This is a non-exhaustive test.

If the last result was false you can find a detailed Error under Strategy.isValid.error

See "Strategy requirements" below

Strategy.isValidComposition(composition) => Boolean

Checks a composition of strategies is valid (including whether the constituent stragies for each property are valid)

new Strategy(composition) => strategy

Takes an composition which is an Object where the keys are mutable fields, and the values are strategies for that field (e.g. see @tangle/simple-set)

Returns a new strategy - an instance with the following methods:

strategy.isValid(T) => Boolean

Check if a transformation is valid. If the last result was false you can find a detailed Error under strategy.isValid.error

strategy.concat(A, B) => C

Combine to transformations to produce a new transformation. NOTE order matters (unless your strategy is commutative!)

strategy.identity() => I

Returns the "identity transformation", I, for out strategy. This is the transformation with the property:

strategy.concat(T, I) = T
strategy.concat(I, T) = T

(Equivalent to multiplying by 1 for numbers)

strategy.mapToOutput(T) => t

Takes a transformation T and converts it into a nice human readable "state" t. Typically transformations look a particular way so that they have nice mathematical properties which makes combining them easy. Unfortunately these are not that easy for humans to read.

strategy.mapFromInput(change, currentTips) => newT

A helper method which transforms "human" changes into a new transformation, newT, based on a current transformation state currentTips.

strategy.mapToPure(messyT) => T

Takes an Object t, which may have supurflous or missing transformation fields and creates from it a clean + explicit transformation, T. Replaces any missing fields with the Identity for that field

strategy.schema => Schema

A getter which returns a JSON schema on which isValid was built. Useful for building higher order schema (e.g. see ssb-crut)

strategy.fields => Array

A getter which returns an array of the fields in the mutation strategy

strategy.isConflict(graph, nodeIds) => Boolean


  • graph is a @tangle/graph instance
  • nodeIds is an Array of nodeIds you're wanting to check for conflict

strategy.isValidMerge(graph, mergeNode) => Boolean


  • graph is a @tangle/graph instance
  • mergeNode is the proposed merge-node
  • If isValidMerge is false, you will can find more info under:
    • isValidMerge.error - a summary of the fields with errors
    • isValidMerge.errors - an Array of more detailed errors for each field
    • isValidMerge.fields - an Array field names which had conflicts

strategy.merge(graph, mergeNode) => T

similar to isValidMerge, but return a transformation, T If it cannot, an error is thrown!

Strategy Requirements

Rules for how a strategy must behave:

  • have a unique identity element
    concat(identity(), a) === a
    concat(a, identity()) === a
  • be associative
    concat(concat(a, b), c) === concat(a, concat(b, c))

An identity element is important because we need to be able to clearly communicate "I don't want to perform a change"

Associativity is important because in scuttlebutt we may have many contributions from different people across time, and being able to group chunks of transformations in a free-form way greatly increases our flexibility. (e.g. it allows us to summarise a bunch of transformations and save that. this is something useful for writing merge messages)

p.s. a Set which has an associative concat and an identity element is called a "monoid"


  • if your strategy is commutative, merging gets REALLY easy
    concat(a, b) === concat(b, a)



Package Sidebar


npm i @tangle/strategy

Weekly Downloads






Unpacked Size

32.6 kB

Total Files


Last publish


  • mixmix
  • chereseeriepa
  • cpilbrow