@sanity/diff
Version:
Generates diffs between documents and primitive types
357 lines • 11.5 kB
TypeScript
/**
* Options available for doing diffs. Currently no options are defined.
*
* @public
*/
type DiffOptions = Record<string, never>;
/**
* The recognized diff value types
*
* @public
*/
type ValueType = 'array' | 'boolean' | 'null' | 'number' | 'object' | 'string' | 'undefined';
/**
* An "input" holds the _type_ of the value, the actual value, an optional annotation,
* along with potential helper methods and properties, which vary dependending on the type
*
* @public
*/
type Input<T> = NumberInput<T> | BooleanInput<T> | StringInput<T> | NullInput<T> | ObjectInput<T> | ArrayInput<T>;
/**
* Shared properties for all input types
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface BaseInput<A> {
annotation: A;
}
/**
* Input type for strings, which supports slicing parts of the string while maintaining the
* annotation of the parent.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface StringInput<A> extends BaseInput<A> {
type: 'string';
value: string;
sliceAnnotation(start: number, end: number): {
text: string;
annotation: A;
}[];
}
/**
* Input type for numbers.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface NumberInput<A> extends BaseInput<A> {
type: 'number';
value: number;
}
/**
* Input type for booleans.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface BooleanInput<A> extends BaseInput<A> {
type: 'boolean';
value: boolean;
}
/**
* Input type for `null` values.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface NullInput<A> extends BaseInput<A> {
type: 'null';
value: null;
}
/**
* Input type for object values. Caches the available keys, and allows retrieval of properties,
* while automatically wrapping the retrieved property in a typed input container.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface ObjectInput<A> extends BaseInput<A> {
type: 'object';
/**
* The actual object value
*/
value: Record<string, unknown>;
/**
* The keys of the object
*/
keys: string[];
/**
* Retrieve the property with the given `key`, automatically wrapping it in an input container.
*
* @param key - The key of the property you want to retrieve.
* @returns Typed input container, or `undefined` if the property does not exist
*/
get(key: string): Input<A> | undefined;
}
/**
* Input type for array values. Helper functions are available for getting the item and/or
* annotation at a given index.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface ArrayInput<A> extends BaseInput<A> {
type: 'array';
/**
* The actual array value
*/
value: unknown[];
/**
* The length of the array
*/
length: number;
/**
* Retrieve the value at the given `index`, automatically wrapping it in an input container.
*
* @param index - The index of the item to retrieve
* @returns Typed input container, or `undefined` if the item does not exist
*/
at(index: number): Input<A>;
/**
* Retrieve the _annotation_ for an item at the given index
*
* @param index - The index of the item to fetch the annotation for
* @returns The annotation at the given index, or `undefined` if the item does not exist
*/
annotationAt(index: number): A;
}
/**
* Diff for something that has been added - eg a property in an object,
* an item in an array, a segment of a string or similar.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @typeParam V - Value. The type of the destination (eg `after` or `to`) value.
* @public
*/
interface AddedDiff<A, V> {
action: 'added';
isChanged: true;
fromValue: null | undefined;
toValue: V;
annotation: A;
}
/**
* Diff for something that has been removed - eg a property in an object,
* an item in an array, a segment of a string or similar.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @typeParam V - Value. The type of the source (eg `before` or `from`) value.
* @public
*/
interface RemovedDiff<A, V> {
action: 'removed';
isChanged: true;
fromValue: V;
toValue: null | undefined;
annotation: A;
}
/**
* Diff for something that has changed - eg it was not added or removed, but the
* value has changed "in place". Note that {@link TypeChangeDiff} is used for values that change
* their _type_, thus the `V` type parameter represents both the source and the destination value
* in this type.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @typeParam V - Value. The type of the value.
* @public
*/
interface ChangedDiff<A, V> {
action: 'changed';
isChanged: true;
fromValue: V;
toValue: V;
annotation: A;
}
/**
* Diff (or lack thereof, in this case) for a value that has _not_ changed.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @typeParam V - Value. The type of the destination (eg `after`) value.
* @public
*/
interface UnchangedDiff<A, V> {
action: 'unchanged';
isChanged: false;
fromValue: V;
toValue: V;
}
/**
* Diff with all the possible diff types.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @typeParam V - Value. Type of the value repesented in the diff.
* @public
*/
type FullDiff<A, V> = AddedDiff<A, V> | RemovedDiff<A, V> | ChangedDiff<A, V> | UnchangedDiff<A, V>;
/**
* Diff of a string. Holds an additional array of string _segments_,
* indicating which portions of the string is changed/unchanged.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
type StringDiff<A> = FullDiff<A, string> & {
type: 'string';
segments: StringDiffSegment<A>[];
};
/**
* Diff of a number.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
type NumberDiff<A> = FullDiff<A, number> & {
type: 'number';
};
/**
* Diff of a boolean.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
type BooleanDiff<A> = FullDiff<A, boolean> & {
type: 'boolean';
};
/**
* Diff for a value that has changed from one type to another.
* For example, an object property going from `null` to `string`.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface TypeChangeDiff<A> {
type: 'typeChange';
action: 'changed';
isChanged: true;
fromType: string;
fromValue: unknown;
fromDiff: Diff<A> & {
action: 'removed';
};
toType: string;
toValue: unknown;
toDiff: Diff<A> & {
action: 'added';
};
annotation: A;
}
/**
* Diff for an object value.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @typeParam T - Value type.
* @public
*/
type ObjectDiff<A, T extends object = Record<string, any>> = FullDiff<A, T> & {
type: 'object';
fields: Record<keyof T, Diff<A>>;
};
/**
* Diff for an array value.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @typeParam V - Item value type.
* @public
*/
type ArrayDiff<A, V = unknown> = FullDiff<A, V[]> & {
type: 'array';
items: ItemDiff<A>[];
};
/**
* Diff for a `null` value.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
type NullDiff<A> = FullDiff<A, null> & {
type: 'null';
};
/**
* Diff for any value type.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
type Diff<A> = NullDiff<A> | StringDiff<A> | NumberDiff<A> | BooleanDiff<A> | ObjectDiff<A> | ArrayDiff<A> | TypeChangeDiff<A>;
/**
* Diff of a string segment (eg a portion/slice), and whether or not it was changed or unchanged.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
type StringDiffSegment<A> = StringSegmentChanged<A> | StringSegmentUnchanged;
/**
* Diff of a string segment that has changed.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface StringSegmentChanged<A> {
type: 'stringSegment';
action: 'added' | 'removed';
text: string;
annotation: A;
}
/**
* Diff of a string segment that is unchanged.
*
* @public
*/
interface StringSegmentUnchanged {
type: 'stringSegment';
action: 'unchanged';
text: string;
}
/**
* Diff of an item in an array, representing whether or not it has moved within the array,
* and if so, which index it was moved from/to.
*
* If not moved, `fromIndex` and `toIndex` will have the same value.
* If the item was added, `fromIndex` will be `undefined`.
* If the item was removed, `toIndex` will be `undefined`.
*
* @typeParam A - Annotation. Timestamps, author and similar info is attached by Sanity, but anything is allowed.
* @public
*/
interface ItemDiff<A> {
fromIndex: number | undefined;
toIndex: number | undefined;
hasMoved: boolean;
diff: Diff<A>;
annotation: A;
}
/**
* Takes a `from` and `to` input and calulates a diff between the two
*
* @param fromInput - The source (`from`) input - use {@link wrap | the wrap() method} to generate an "input"
* @param toInput - The destination (`to`) input - use {@link wrap | the wrap() method} to generate an "input"
* @param options - Options for the diffing process - currently no options are defined
* @returns A diff object representing the change
* @public
*/
declare function diffInput<A>(fromInput: Input<A>, toInput: Input<A>, options?: DiffOptions): Diff<A>;
/**
* Takes an input (any JSON-serializable value) and an annotation, and generates an input
* object for it, to be used with {@link diffInput | the diffInput() method} and others.
*
* @param input - The value to wrap in an input object
* @param annotation - Annotation attached to the input - will be bound to generated diffs
* @returns A input object
* @throws if `input` is not a JSON-serializable type
* @public
*/
declare function wrap<A>(input: unknown, annotation: A): Input<A>;
export { AddedDiff, ArrayDiff, ArrayInput, BaseInput, BooleanDiff, BooleanInput, ChangedDiff, Diff, DiffOptions, FullDiff, Input, ItemDiff, NullDiff, NullInput, NumberDiff, NumberInput, ObjectDiff, ObjectInput, RemovedDiff, StringDiff, StringDiffSegment, StringInput, StringSegmentChanged, StringSegmentUnchanged, TypeChangeDiff, UnchangedDiff, ValueType, diffInput, wrap };