@sanity/mutate
Version:
Experimental toolkit for working with Sanity mutations in JavaScript & TypeScript
1,100 lines (931 loc) • 27.9 kB
text/typescript
import {Observable} from 'rxjs'
import {RawPatch} from 'mendoza'
import {ReconnectEvent} from '@sanity/client'
import {SanityClient} from '@sanity/client'
import {WelcomeEvent} from '@sanity/client'
export declare interface AccessibleDocumentResult {
id: string
document: SanityDocumentBase
accessible: true
}
export declare type AnyArray<T = any> = T[] | readonly T[]
export declare type AnyEmptyArray = [] | readonly []
export declare type AnyOp = SetOp<unknown> | SetIfMissingOp<unknown> | UnsetOp
export declare type ArrayOp =
| InsertOp<AnyArray, RelativePosition, Index | KeyedPathElement>
| UpsertOp<AnyArray, RelativePosition, Index | KeyedPathElement>
| ReplaceOp<AnyArray, Index | KeyedPathElement>
| TruncateOp
| RemoveOp<Index | KeyedPathElement>
export declare type AssignOp<T extends object = object> = {
type: 'assign'
value: T
}
export declare type ByIndex<P extends number, T extends AnyArray> = T[P]
export declare type Concat<
R extends Result<any, any>,
Arr extends any[],
> = R[1] extends any[] ? Ok<[...R[1], ...Arr]> : R
export declare type ConcatInner<
R extends Result<any, any>,
R2 extends Result<any, any>,
> = R2[1] extends any[] ? Concat<R, R2[1]> : R2
export declare type Conflict = {
path: Path
error: Error
base: SanityDocumentBase | undefined
local: SanityDocumentBase | undefined
}
/**
* Creates a function that can be used to listen for events that happens in a single document
* Features
* - builtin retrying and connection recovery (track disconnected state by listening for `reconnect` events)
* - builtin mutation event ordering (they might arrive out of order), lost events detection (/listen endpoint doesn't guarantee delivery) and recovery
* - discards already-applied mutation events received while fetching the initial document snapshot
* @param options
*/
export declare function createDocumentEventListener(options: {
loadDocument: DocumentLoader
listenerEvents: Observable<
WelcomeEvent | ListenerMutationEvent | ReconnectEvent
>
}): <Doc extends SanityDocumentBase>(
documentId: string,
) => Observable<ListenerEvent>
/**
* Creates a "dataloader" style document loader that fetches from the /doc endpoint
* @param {FetchDocuments} fetchDocuments - The client instance used for fetching documents.
* @param options
*/
export declare function createDocumentLoader(
fetchDocuments: FetchDocuments,
options?: {
durationSelector?: () => Observable<unknown>
tag?: string
},
): (key: string) => Observable<DocumentResult>
export declare function createDocumentLoaderFromClient(
client: SanityClient,
options?: {
durationSelector?: () => Observable<unknown>
tag?: string
},
): (key: string) => Observable<DocumentResult>
/**
* Creates a function that can be used to listen for document updates
* Emits the latest snapshot of the document along with the latest event
* @param options
*/
export declare function createDocumentUpdateListener(options: {
listenDocumentEvents: (documentId: string) => Observable<ListenerEvent>
}): <Doc extends SanityDocumentBase>(
documentId: string,
) => Observable<DocumentUpdate<Doc>>
export declare function createIdSetListener(
listen: IdSetListenFn,
fetch: FetchDocumentIdsFn,
): (
queryFilter: string,
params: QueryParams,
options?: {
tag?: string
},
) => Observable<DocumentIdSetEvent>
export declare function createIdSetListenerFromClient(
client: SanityClient,
): void
export declare type CreateIfNotExistsMutation<Doc extends SanityDocumentBase> =
{
type: 'createIfNotExists'
document: Doc
}
export declare function createMockBackendAPI(): MockBackendAPI
export declare type CreateMutation<
Doc extends Optional<SanityDocumentBase, '_id'>,
> = {
type: 'create'
document: Doc
}
/**
* Creates a local dataset that allows subscribing to documents by id and submitting mutations to be optimistically applied
* @param backend
*/
export declare function createOptimisticStore(
backend: OptimisticStoreBackend,
): OptimisticStore
export declare function createOptimisticStoreClientBackend(
client: SanityClient,
): OptimisticStoreBackend
export declare function createOptimisticStoreMockBackend(
backendAPI: MockBackendAPI,
): OptimisticStoreBackend
export declare type CreateOrReplaceMutation<Doc extends SanityDocumentBase> = {
type: 'createOrReplace'
document: Doc
}
/**
* @param listenDocumentUpdates – a function that takes a document id and returns an observable of document snapshots
* @param options
*/
export declare function createReadOnlyStore(
listenDocumentUpdates: DocumentUpdateListener<SanityDocumentBase>,
options?: {
shutdownDelay?: number
},
): ReadOnlyDocumentStore
/**
* Creates a (low level) shared listener that will emit 'welcome' for all new subscribers immediately, and thereafter emit every listener event, including welcome, mutation, and reconnects
* Useful for cases where you need control of how the listen request is set up
*/
export declare function createSharedListener(
listen: SharedListenerListenFn,
options?: ListenerOptions,
): Observable<WelcomeEvent | ListenerMutationEvent | ReconnectEvent>
/**
* Creates a (low level) shared listener that will emit 'welcome' for all new subscribers immediately, and thereafter emit every listener event, including welcome, mutation, and reconnects
* Requires a Sanity client instance
*/
export declare function createSharedListenerFromClient(
client: SanityClient,
options?: ListenerOptions,
): Observable<WelcomeEvent | ListenerMutationEvent | ReconnectEvent>
export declare type DecOp<Amount extends number> = {
type: 'dec'
amount: Amount
}
export declare type DeleteMutation = {
type: 'delete'
id: string
}
export declare type DiffMatchPatchOp = {
type: 'diffMatchPatch'
value: string
}
export declare type Digit =
| '0'
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
export declare interface DocEndpointResponse {
documents: SanityDocumentBase[]
omitted: OmittedDocument[]
}
export declare type DocumentIdSetEvent =
| {
type: 'sync'
documentIds: string[]
}
| {
type: 'reconnect'
}
| {
type: 'op'
op: 'add' | 'remove'
documentId: string
}
export declare type DocumentIdSetState = {
status: 'connecting' | 'reconnecting' | 'connected'
event: DocumentIdSetEvent | InitialEvent
snapshot: string[]
}
export declare type DocumentLoader = (
documentIds: string,
) => Observable<DocumentResult>
export declare type DocumentMap<Doc extends SanityDocumentBase> = {
get(id: string): Doc | undefined
set(id: string, doc: Doc | undefined): void
delete(id: string): void
}
export declare interface DocumentMutationUpdate<
Doc extends SanityDocumentBase,
> {
documentId: string
snapshot: Doc | undefined
event: ListenerMutationEvent
}
export declare interface DocumentReconnectUpdate<
Doc extends SanityDocumentBase,
> {
documentId: string
snapshot: Doc | undefined
event: ListenerReconnectEvent
}
export declare type DocumentResult =
| AccessibleDocumentResult
| InaccessibleDocumentResult
export declare interface DocumentSyncUpdate<Doc extends SanityDocumentBase> {
documentId: string
snapshot: Doc | undefined
event: ListenerSyncEvent<Doc>
}
export declare type DocumentUpdate<Doc extends SanityDocumentBase> =
| DocumentSyncUpdate<Doc>
| DocumentMutationUpdate<Doc>
| DocumentReconnectUpdate<any>
export declare type DocumentUpdateListener<Doc extends SanityDocumentBase> = (
id: string,
) => Observable<DocumentUpdate<Doc>>
export declare type ElementType<T extends AnyArray> =
T extends AnyArray<infer E> ? E : unknown
export declare type Err<E> = Result<E, null>
export declare type FetchDocumentIdsFn = (
query: string,
params?: QueryParams,
options?: {
tag?: string
},
) => Observable<string[]>
export declare type FetchDocuments = (
ids: string[],
) => Observable<DocEndpointResponse>
export declare type FindBy<P, T extends AnyArray> = T extends AnyEmptyArray
? undefined
: T[0] extends P
? T[0]
: T extends [any, ...infer Tail] | readonly [any, ...infer Tail]
? FindBy<P, Tail>
: ElementType<T>
export declare type FindInArray<
P extends KeyedPathElement | number,
T extends AnyArray,
> = P extends KeyedPathElement
? FindBy<P, T>
: P extends number
? ByIndex<P, T>
: never
export declare type Get<
P extends number | KeyedPathElement | Readonly<KeyedPathElement> | string,
T,
> = T extends AnyArray
? P extends KeyedPathElement | Readonly<KeyedPathElement> | number
? FindInArray<P, T>
: undefined
: P extends keyof T
? T[P]
: never
export declare type GetAtPath<
P extends readonly PathElement[],
T,
> = P extends []
? T
: P extends [infer Head, ...infer Tail]
? Head extends PathElement
? Tail extends PathElement[]
? GetAtPath<Tail, Get<Head, T>>
: undefined
: undefined
: undefined
export declare function getAtPath<const Head extends PathElement, const T>(
path: [head: Head],
value: T,
): Get<Head, T>
export declare function getAtPath<
const Head extends PathElement,
const Tail extends PathElement[],
T,
>(path: [head: Head, ...tail: Tail], value: T): GetAtPath<[Head, ...Tail], T>
export declare function getAtPath<T>(path: [], value: T): T
export declare function getAtPath(path: Path, value: unknown): unknown
export declare type IdSetListenFn = (
query: string,
params?: QueryParams,
options?: {
visibility: 'transaction'
events: ['welcome', 'mutation', 'reconnect']
includeResult: false
includeMutations: false
tag?: string
},
) => Observable<ListenerEndpointEvent>
export declare interface InaccessibleDocumentResult {
accessible: false
id: string
reason: InaccessibleReason
}
export declare type InaccessibleReason = 'existence' | 'permission'
export declare type IncOp<Amount extends number> = {
type: 'inc'
amount: Amount
}
export declare type Index = number
export declare type InitialEvent = {
type: 'connect'
}
export declare type Insert = {
before?: string
after?: string
replace?: string
items: any[]
}
export declare type InsertMethod = 'sorted' | 'prepend' | 'append'
export declare type InsertOp<
Items extends AnyArray,
Pos extends RelativePosition,
ReferenceItem extends Index | KeyedPathElement,
> = {
type: 'insert'
referenceItem: ReferenceItem
position: Pos
items: Items
}
export declare function isArrayElement(
element: PathElement,
): element is KeyedPathElement | number
export declare function isElementEqual(
segmentA: PathElement,
segmentB: PathElement,
): boolean
export declare function isEqual(path: Path, otherPath: Path): boolean
export declare function isIndexElement(segment: PathElement): segment is number
export declare function isKeyedElement(
element: PathElement,
): element is KeyedPathElement
export declare function isKeyElement(
segment: PathElement,
): segment is KeyedPathElement
export declare function isPropertyElement(
element: PathElement,
): element is string
export declare type KeyedPathElement = {
_key: string
}
export declare type ListenerChannelErrorEvent = {
type: 'channelError'
message: string
}
export declare type ListenerDisconnectEvent = {
type: 'disconnect'
reason: string
}
export declare type ListenerEndpointEvent =
| ListenerWelcomeEvent
| ListenerMutationEvent
| ListenerReconnectEvent
| ListenerChannelErrorEvent
| ListenerDisconnectEvent
export declare type ListenerEvent<
Doc extends SanityDocumentBase = SanityDocumentBase,
> = ListenerSyncEvent<Doc> | ListenerMutationEvent | ListenerReconnectEvent
export declare interface ListenerMutationEvent {
type: 'mutation'
documentId: string
transactionId: string
resultRev?: string
previousRev?: string
effects?: {
apply: RawPatch
}
mutations: SanityMutation[]
transition: 'update' | 'appear' | 'disappear'
}
export declare interface ListenerOptions {
/**
* Provide a custom filter to the listener. By default, this listener will include all events
* Note: make sure the filter includes events from documents you will subscribe to.
*/
filter?: string
/**
* Whether to include system documents or not
* This will be ignored if a custom filter is provided
*/
includeSystemDocuments?: boolean
/**
* How long after the last subscriber is unsubscribed to keep the connection open
*/
shutdownDelay?: number
/**
* Include mutations in listener events
*/
includeMutations?: boolean
/**
* Request tag
*/
tag?: string
}
export declare interface ListenerReconnectEvent {
type: 'reconnect'
}
export declare interface ListenerSyncEvent<
Doc extends SanityDocumentBase = SanityDocumentBase,
> {
type: 'sync'
document: Doc | undefined
}
export declare type ListenerWelcomeEvent = {
type: 'welcome'
listenerName: string
}
export declare type MapTuple<T, U> = {
[K in keyof T]: U
}
export declare type Merge<R extends Result<any, any>, E> = R[0] extends null
? Ok<R[1] & E>
: R
export declare type MergeInner<
R extends Result<any, any>,
R2 extends Result<any, any>,
> = R2[0] extends null ? Merge<R, R2[1]> : R
/**
* This is the interface that a mock backend instance needs to implement
*/
export declare interface MockBackendAPI {
listen(query: string): Observable<ListenerEndpointEvent>
getDocuments(ids: string[]): Observable<DocEndpointResponse>
submit(transaction: Transaction): Observable<SubmitResult>
}
export declare type Mutation<Doc extends SanityDocumentBase = any> =
| CreateMutation<Doc>
| CreateIfNotExistsMutation<Doc>
| CreateOrReplaceMutation<Doc>
| DeleteMutation
| PatchMutation
/**
* A mutation group represents an incoming, locally added group of mutations
* They can either be transactional or non-transactional
* - Transactional means that they must be submitted as a separate transaction (with an optional id) and no other mutations can be mixed with it
* – Non-transactional means that they can be combined with other mutations
*/
export declare type MutationGroup =
| NonTransactionalMutationGroup
| TransactionalMutationGroup
export declare interface MutationResult {}
export declare type NodePatch<
P extends Path = Path,
O extends Operation = Operation,
> = {
path: P
op: O
}
export declare type NodePatchList =
| [NodePatch, ...NodePatch[]]
| NodePatch[]
| readonly NodePatch[]
| readonly [NodePatch, ...NodePatch[]]
export declare interface NonTransactionalMutationGroup {
transaction: false
mutations: Mutation[]
}
export declare function normalize(path: string | Readonly<Path>): Readonly<Path>
export declare type NumberOp = IncOp<number> | DecOp<number>
export declare type ObjectOp = AssignOp | UnassignOp
export declare type Ok<V> = Result<null, V>
export declare interface OmittedDocument {
id: string
reason: 'existence' | 'permission'
}
export declare type OnlyDigits<S> = S extends `${infer Head}${infer Tail}`
? Head extends Digit
? Tail extends ''
? true
: OnlyDigits<Tail> extends true
? true
: false
: false
: false
export declare type Operation = PrimitiveOp | ArrayOp | ObjectOp
export declare interface OptimisticDocumentEvent {
type: 'optimistic'
id: string
before: SanityDocumentBase | undefined
after: SanityDocumentBase | undefined
mutations: Mutation[]
stagedChanges: Mutation[]
}
export declare interface OptimisticStore {
meta: {
/**
* A stream of events for anything that happens in the store
*/
events: Observable<OptimisticDocumentEvent | RemoteDocumentEvent>
/**
* A stream of current staged changes
*/
stage: Observable<MutationGroup[]>
/**
* A stream of current conflicts. TODO: Needs more work
*/
conflicts: Observable<Conflict[]>
}
/**
* Applies the given mutations. Mutations are not guaranteed to be submitted in the same transaction
* Can this mutate both local and remote documents at the same time
*/
mutate(mutation: Mutation[]): MutationResult
/**
* Makes sure the given mutations are posted in a single transaction
*/
transaction(
transaction:
| {
id?: string
mutations: Mutation[]
}
| Mutation[],
): MutationResult
/**
* Checkout a document for editing. This is required to be able to see optimistic changes
*/
listen(id: string): Observable<SanityDocumentBase | undefined>
/**
* Listen for events for a given document id
*/
listenEvents(
id: string,
): Observable<RemoteDocumentEvent | OptimisticDocumentEvent>
/**
* Optimize list of pending mutations
*/
optimize(): void
/**
* Submit pending mutations
*/
submit(): Promise<SubmitResult[]>
}
export declare interface OptimisticStoreBackend {
/**
* Sets up a subscription to a document
* The first event should either be a sync event or an error event.
* After that, it should emit mutation events, error events or sync events
* @param id
*/
listen: (id: string) => Observable<ListenerEvent>
submit: (mutationGroups: Transaction) => Observable<SubmitResult>
}
export declare type Optional<T, K extends keyof T> = Omit<T, K> &
Partial<Pick<T, K>>
export declare function parse<const T extends string>(path: T): StringToPath<T>
export declare type ParseAllProps<Props extends string[]> = Props extends [
`${infer Head}`,
...infer Tail,
]
? Tail extends string[]
? ConcatInner<ParseProperty<Trim<Head>>, ParseAllProps<Tail>>
: ParseProperty<Trim<Head>>
: Ok<[]>
export declare type ParseError<T extends string = 'unknown'> = T & {
error: true
}
export declare type ParseExpressions<S extends string> =
S extends `[${infer Expr}]${infer Remainder}`
? Trim<Remainder> extends ''
? ToArray<ParseInnerExpression<Trim<Expr>>>
: ConcatInner<
ToArray<ParseInnerExpression<Trim<Expr>>>,
ParseExpressions<Remainder>
>
: Err<ParseError<`Cannot parse object from "${S}"`>>
export declare type ParseInnerExpression<S extends string> = S extends ''
? Err<ParseError<'Saw an empty expression'>>
: Try<ParseNumber<S>, ParseObject<S>>
export declare type ParseKVPair<S extends string> =
Split<S, '=='> extends [`${infer LHS}`, `${infer RHS}`]
? ParseValue<Trim<RHS>> extends infer Res
? Res extends [null, infer Value]
? Ok<{
[P in Trim<LHS>]: Value
}>
: Err<
ParseError<`Can't parse right hand side as a value in "${S}" (Invalid value ${RHS})`>
>
: never
: Err<ParseError<`Can't parse key value pair from ${S}`>>
export declare type ParseNumber<S extends string> =
S extends `${infer Head}${infer Tail}`
? Head extends '-'
? OnlyDigits<Tail> extends true
? Ok<ToNumber<S>>
: Err<ParseError<`Invalid integer value "${S}"`>>
: OnlyDigits<S> extends true
? Ok<ToNumber<S>>
: Err<ParseError<`Invalid integer value "${S}"`>>
: Err<ParseError<`Invalid integer value "${S}"`>>
export declare type ParseObject<S extends string> =
S extends `${infer Pair},${infer Remainder}`
? Trim<Remainder> extends ''
? Ok<Record<never, never>>
: MergeInner<ParseKVPair<Pair>, ParseObject<Remainder>>
: ParseKVPair<S>
export declare type ParseProperty<S extends string> =
Trim<S> extends ''
? Err<ParseError<'Empty property'>>
: Split<Trim<S>, '[', true> extends [`${infer Prop}`, `${infer Expression}`]
? Trim<Prop> extends ''
? ParseExpressions<Trim<Expression>>
: ConcatInner<Ok<[Trim<Prop>]>, ParseExpressions<Trim<Expression>>>
: Ok<[Trim<S>]>
export declare type ParseValue<S extends string> = string extends S
? Err<ParseError<'ParseValue got generic string type'>>
: S extends 'null'
? Ok<null>
: S extends 'true'
? Ok<true>
: S extends 'false'
? Ok<false>
: S extends `"${infer Value}"`
? Ok<Value>
: Try<
ParseNumber<S>,
Err<
ParseError<`ParseValue failed. Can't parse "${S}" as a value.`>
>
>
export declare type PatchMutation<
Patches extends NodePatchList = NodePatchList,
> = {
type: 'patch'
id: string
patches: Patches
options?: PatchOptions
}
export declare type PatchOptions = {
ifRevision?: string
}
export declare type Path = PathElement[] | readonly PathElement[]
export declare type PathElement = PropertyName | Index | KeyedPathElement
export declare type PrimitiveOp = AnyOp | StringOp | NumberOp
export declare type PropertyName = string
export declare type QueryParams = Record<
string,
string | number | boolean | (string | number | boolean)[]
>
export declare interface ReadOnlyDocumentStore {
listenDocument: <Doc extends SanityDocumentBase>(
id: string,
) => Observable<DocumentUpdate<Doc>>
listenDocuments: <
Doc extends SanityDocumentBase,
const IdTuple extends string[],
>(
id: IdTuple,
) => Observable<MapTuple<IdTuple, DocumentUpdate<Doc>>>
}
export declare type RelativePosition = 'before' | 'after'
export declare type RemoteDocumentEvent = RemoteSyncEvent | RemoteMutationEvent
export declare interface RemoteMutationEvent {
type: 'mutation'
id: string
before: {
local: SanityDocumentBase | undefined
remote: SanityDocumentBase | undefined
}
after: {
local: SanityDocumentBase | undefined
remote: SanityDocumentBase | undefined
}
effects?: {
apply: RawPatch
}
previousRev?: string
resultRev?: string
mutations: Mutation[]
rebasedStage: MutationGroup[]
}
export declare interface RemoteSyncEvent {
type: 'sync'
id: string
before: {
local: SanityDocumentBase | undefined
remote: SanityDocumentBase | undefined
}
after: {
local: SanityDocumentBase | undefined
remote: SanityDocumentBase | undefined
}
rebasedStage: MutationGroup[]
}
export declare type RemoveOp<ReferenceItem extends Index | KeyedPathElement> = {
type: 'remove'
referenceItem: ReferenceItem
}
export declare type ReplaceOp<
Items extends AnyArray,
ReferenceItem extends Index | KeyedPathElement,
> = {
type: 'replace'
referenceItem: ReferenceItem
items: Items
}
/**
* These are fixed, and it's up to the implementation of the listen function to turn them into request parameters
*/
export declare interface RequestOptions {
events: ['welcome', 'mutation', 'reconnect']
includeResult: false
includePreviousRevision: false
visibility: 'transaction'
effectFormat: 'mendoza'
includeMutations?: boolean
tag?: string
}
export declare type Result<E, V> = [E, V]
export declare type SafePath<S extends string> = StripError<StringToPath<S>>
export declare type SanityCreateIfNotExistsMutation<
Doc extends SanityDocumentBase,
> = {
createIfNotExists: Doc
}
export declare type SanityCreateMutation<Doc extends SanityDocumentBase> = {
create: Doc
}
export declare type SanityCreateOrReplaceMutation<
Doc extends SanityDocumentBase,
> = {
createOrReplace: Doc
}
export declare type SanityDecPatch = {
id: string
dec: {
[path: string]: number
}
}
export declare type SanityDeleteMutation = {
delete: {
id: string
}
}
export declare type SanityDiffMatchPatch = {
id: string
diffMatchPatch: {
[path: string]: string
}
}
export declare interface SanityDocumentBase {
_id?: string
_type: string
_createdAt?: string
_updatedAt?: string
_rev?: string
}
export declare type SanityIncPatch = {
id: string
inc: {
[path: string]: number
}
}
export declare type SanityInsertPatch = {
id: string
insert: Insert
}
export declare type SanityMutation<
Doc extends SanityDocumentBase = SanityDocumentBase,
> =
| SanityCreateMutation<Doc>
| SanityCreateIfNotExistsMutation<Doc>
| SanityCreateOrReplaceMutation<Doc>
| SanityDeleteMutation
| SanityPatchMutation
export declare type SanityPatch =
| SanitySetPatch
| SanityUnsetPatch
| SanityInsertPatch
| SanitySetIfMissingPatch
| SanityDiffMatchPatch
| SanityIncPatch
| SanityDecPatch
export declare type SanityPatchMutation = {
patch:
| SanitySetPatch
| SanitySetIfMissingPatch
| SanityDiffMatchPatch
| SanityInsertPatch
| SanityUnsetPatch
}
export declare type SanitySetIfMissingPatch = {
id: string
setIfMissing: {
[path: string]: any
}
}
export declare type SanitySetPatch = {
id: string
set: {
[path: string]: any
}
}
export declare type SanityUnsetPatch = {
id: string
unset: string[]
}
export declare type SetIfMissingOp<T> = {
type: 'setIfMissing'
value: T
}
export declare type SetOp<T> = {
type: 'set'
value: T
}
export declare type SharedListenerListenFn = (
query: string,
queryParams: QueryParams,
options: RequestOptions,
) => Observable<ListenerEndpointEvent>
export declare type Split<
S extends string,
Char extends string,
IncludeSeparator extends boolean = false,
> = S extends `${infer First}${Char}${infer Remainder}`
? [First, `${IncludeSeparator extends true ? Char : ''}${Remainder}`]
: [S]
export declare type SplitAll<
S extends string,
Char extends string,
> = S extends `${infer First}${Char}${infer Remainder}`
? [First, ...SplitAll<Remainder, Char>]
: [S]
export declare function startsWith(parentPath: Path, path: Path): boolean
export declare function stringify(pathArray: Path): string
export declare type StringOp = DiffMatchPatchOp
export declare type StringToPath<S extends string> = Unwrap<
ParseAllProps<SplitAll<Trim<S>, '.'>>
>
export declare type StripError<
S extends StringToPath<string> | ParseError<string>,
> = S extends ParseError<string> ? never : S
export declare interface SubmitResult {}
export declare type ToArray<R extends Result<any, any>> = R extends [
infer E,
infer V,
]
? E extends null
? V extends any[]
? R
: Ok<[R[1]]>
: R
: R
export declare type ToNumber<T extends string> =
T extends `${infer N extends number}` ? N : never
/** Converts a stream of id set listener events into a state containing the list of document ids */
export declare function toState(options?: {
insert?: InsertMethod
}): (input$: Observable<DocumentIdSetEvent>) => Observable<DocumentIdSetState>
export declare interface Transaction {
id?: string
mutations: Mutation[]
}
export declare interface TransactionalMutationGroup {
transaction: true
id?: string
mutations: Mutation[]
}
export declare type Trim<
S extends string,
Char extends string = ' ',
> = TrimRight<TrimLeft<S, Char>, Char>
export declare type TrimLeft<
Str extends string,
Char extends string = ' ',
> = string extends Str
? Str
: Str extends `${Char}${infer Trimmed}`
? TrimLeft<Trimmed, Char>
: Str
export declare type TrimRight<
Str extends string,
Char extends string = ' ',
> = string extends Str
? Str
: Str extends `${infer Trimmed}${Char}`
? TrimRight<Trimmed, Char>
: Str
export declare type TruncateOp = {
type: 'truncate'
startIndex: number
endIndex?: number
}
export declare type Try<R extends Result<any, any>, Handled> = R[1] extends null
? Handled
: R
export declare type UnassignOp<
K extends readonly string[] = readonly string[],
> = {
type: 'unassign'
keys: K
}
export declare type UnsetOp = {
type: 'unset'
}
export declare type Unwrap<R extends Result<any, any>> = R extends [
infer E,
infer V,
]
? E extends null
? V
: E
: never
export declare type UpsertOp<
Items extends AnyArray,
Pos extends RelativePosition,
ReferenceItem extends Index | KeyedPathElement,
> = {
type: 'upsert'
items: Items
referenceItem: ReferenceItem
position: Pos
}
export {}