2023-05-10 05:40:31 +00:00
// @ts-check
2023-08-23 04:16:36 +00:00
const assert = require ( "assert" ) . strict
const mixin = require ( "mixin-deep" )
2023-05-10 05:40:31 +00:00
/** Mutates the input. */
function kstateStripConditionals ( kstate ) {
for ( const [ k , content ] of Object . entries ( kstate ) ) {
// conditional for whether a key is even part of the kstate (doing this declaratively on json is hard, so represent it as a property instead.)
if ( "$if" in content ) {
if ( content . $if ) delete content . $if
else delete kstate [ k ]
}
}
return kstate
}
function kstateToState ( kstate ) {
const events = [ ]
kstateStripConditionals ( kstate )
for ( const [ k , content ] of Object . entries ( kstate ) ) {
const [ type , state _key ] = k . split ( "/" )
assert . ok ( typeof type === "string" )
assert . ok ( typeof state _key === "string" )
events . push ( { type , state _key , content } )
}
return events
}
/ * *
* @ param { import ( "../types" ) . Event . BaseStateEvent [ ] } events
* @ returns { any }
* /
function stateToKState ( events ) {
const kstate = { }
for ( const event of events ) {
kstate [ event . type + "/" + event . state _key ] = event . content
}
return kstate
}
function diffKState ( actual , target ) {
const diff = { }
// go through each key that it should have
for ( const key of Object . keys ( target ) ) {
2023-08-23 00:39:37 +00:00
if ( ! key . includes ( "/" ) ) throw new Error ( ` target kstate's key " ${ key } " does not contain a slash separator; if a blank state_key was intended, add a trailing slash to the kstate key. ` )
2023-08-23 04:16:36 +00:00
if ( key === "m.room.power_levels/" ) {
// Special handling for power levels, we want to deep merge the actual and target into the final state.
2023-08-23 05:08:20 +00:00
if ( ! ( key in actual ) ) throw new Error ( ` want to apply a power levels diff, but original power level data is missing \n started with: ${ JSON . stringify ( actual ) } \n want to apply: ${ JSON . stringify ( target ) } ` )
2023-08-23 04:16:36 +00:00
const temp = mixin ( { } , actual [ key ] , target [ key ] )
try {
assert . deepEqual ( actual [ key ] , temp )
} catch ( e ) {
// they differ. use the newly prepared object as the diff.
diff [ key ] = temp
}
} else if ( key in actual ) {
2023-05-10 05:40:31 +00:00
// diff
try {
assert . deepEqual ( actual [ key ] , target [ key ] )
} catch ( e ) {
2023-08-23 04:16:36 +00:00
// they differ. use the target as the diff.
2023-05-10 05:40:31 +00:00
diff [ key ] = target [ key ]
}
2023-08-23 04:16:36 +00:00
2023-05-10 05:40:31 +00:00
} else {
// not present, needs to be added
diff [ key ] = target [ key ]
}
2023-08-23 04:16:36 +00:00
2023-05-10 05:40:31 +00:00
// keys that are missing in "actual" will not be deleted on "target" (no action)
}
return diff
}
module . exports . kstateStripConditionals = kstateStripConditionals
module . exports . kstateToState = kstateToState
module . exports . stateToKState = stateToKState
module . exports . diffKState = diffKState