@tangle/strategy
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 identitytransformation,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
toA
to produce a new transformationC
 NOTE order matters
 takes two transformations and concatenates (applies)

mapToOutput(T) => output
Function takes a transformation
T
and maps it into a "real" state which can be used in e.g. human interfaces
 takes a transformation

mapFromInput(input, currentTips) => T
Function (optional) Takes a current transformation state,
currentTips
, which is an array ofT
along with a human friendly (descriptive)input
and returns a transformationT
which satisfies the change requested ininput
.  you can define a function which maps human
input
form to a transformationT
 useful if the raw transformation form is hard for people to write
 default:
t => t
 Takes a current transformation state,
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/simpleset')()
})
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 }
// }
strategy.mapToOutput(T3)
// => {
// title: 'Brunch at Mixs',
// attendees: ['ben', 'mix']
// }
API
Strategy.isValid(strategy) => Boolean
A helper for checking whether a strategy is valid (has everything required). This is a nonexhaustive 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/simpleset)
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 ssbcrut)
strategy.fields => Array
A getter which returns an array of the fields in the mutation strategy
strategy.isConflict(graph, nodeIds) => Boolean
where:

graph
is a@tangle/graph
instance 
nodeIds
is an Array of nodeIds you're wanting to check for conflict
strategy.isValidMerge(graph, mergeNode) => Boolean
where:

graph
is a@tangle/graph
instance 
mergeNode
is the proposed mergenode  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
elementconcat(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 freeform 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"
BONUS:
 if your strategy is commutative, merging gets REALLY easy
concat(a, b) === concat(b, a)