sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
88 lines (76 loc) • 2.68 kB
text/typescript
import {findIndex} from 'lodash'
import {type FIXME} from '../../FIXME'
import {applyPatch} from './applyPatch'
import {arrayInsert} from './arrayInsert'
const hasOwn = (obj: Record<string, unknown>, property: string) =>
Object.prototype.hasOwnProperty.call(obj, property)
function move(arr: unknown[], from: number, to: number) {
const nextValue = arr.slice()
const val = nextValue[from]
nextValue.splice(from, 1)
nextValue.splice(to, 0, val)
return nextValue
}
function findTargetIndex(array: unknown[], pathSegment: FIXME) {
if (typeof pathSegment === 'number') {
return pathSegment
}
const index = findIndex(array, pathSegment)
return index === -1 ? false : index
}
export function _arrayApply(value: FIXME, patch: FIXME) {
const nextValue = value.slice() // make a copy for internal mutation
if (patch.path.length === 0) {
// its directed to me
if (patch.type === 'setIfMissing') {
if (!Array.isArray(patch.value)) {
// eslint-disable-line max-depth
throw new Error('Cannot set value of an array to a non-array')
}
return value === undefined ? patch.value : value
} else if (patch.type === 'set') {
if (!Array.isArray(patch.value)) {
// eslint-disable-line max-depth
throw new Error('Cannot set value of an array to a non-array')
}
return patch.value
} else if (patch.type === 'unset') {
return undefined
} else if (patch.type === 'move') {
if (!patch.value || !hasOwn(patch.value, 'from') || !hasOwn(patch.value, 'to')) {
// eslint-disable-line max-depth
throw new Error(
`Invalid value of 'move' patch. Expected a value with "from" and "to" indexes, instead got: ${JSON.stringify(
patch.value,
)}`,
)
}
return move(nextValue, patch.value.from, patch.value.to)
}
throw new Error(`Invalid array operation: ${patch.type}`)
}
const [head, ...tail] = patch.path
const index = findTargetIndex(value, head)
// If the given selector could not be found, return as-is
if (index === false) {
return nextValue
}
if (tail.length === 0) {
if (patch.type === 'insert') {
const {position, items} = patch
return arrayInsert(value, position, index, items)
} else if (patch.type === 'unset') {
if (typeof index !== 'number') {
throw new Error(`Expected array index to be a number, instead got "${index}"`)
}
nextValue.splice(index, 1)
return nextValue
}
}
// The patch is not directed to me
nextValue[index] = applyPatch(nextValue[index], {
...patch,
path: tail,
})
return nextValue
}