/*! * Copyright (c) Microsoft Corporation and contributors. All rights reserved. * Licensed under the MIT License. */ import { TypedEventEmitter } from "@fluid-internal/client-utils"; import type { IEvent } from "@fluidframework/core-interfaces"; import type { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal"; import type { Client, LocalReferencePosition, PropertySet, SequencePlace } from "@fluidframework/merge-tree/internal"; import { Side } from "@fluidframework/merge-tree/internal"; import type { IIntervalCollectionTypeOperationValue, IntervalAddLocalMetadata, IntervalChangeLocalMetadata, SequenceOptions } from "./intervalCollectionMapInterfaces.js"; import { type IEndpointIndex, type IIdIntervalIndex, type ISequenceOverlappingIntervalsIndex, type SequenceIntervalIndex } from "./intervalIndex/index.js"; import type { CompressedSerializedInterval, ISerializedInterval, SequenceInterval, SequenceIntervalClass, SerializedIntervalDelta } from "./intervals/index.js"; import type { ISharedSegmentSequence } from "./sequence.js"; export type ISerializedIntervalCollectionV1 = ISerializedInterval[]; export interface ISerializedIntervalCollectionV2 { label: string; version: 2; intervals: CompressedSerializedInterval[]; } export declare function toSequencePlace(pos: number | "start" | "end", side: Side | undefined): SequencePlace; export declare function toOptionalSequencePlace(pos: number | "start" | "end" | undefined, side: Side | undefined): SequencePlace | undefined; export declare class LocalIntervalCollection { private readonly client; private readonly label; private readonly options; /** Callback invoked each time one of the endpoints of an interval slides. */ private readonly onPositionChange?; readonly overlappingIntervalsIndex: ISequenceOverlappingIntervalsIndex; readonly idIntervalIndex: IIdIntervalIndex; readonly endIntervalIndex: IEndpointIndex; private readonly indexes; constructor(sequence: ISharedSegmentSequence, client: Client, label: string, options: Partial, /** Callback invoked each time one of the endpoints of an interval slides. */ onPositionChange?: ((interval: SequenceIntervalClass, previousInterval: SequenceIntervalClass) => void) | undefined); /** * Validates that a serialized interval has the ID property. Creates an ID * if one does not already exist * * @param serializedInterval - The interval to be checked * @returns The interval's existing or newly created id */ private removeIntervalFromIndexes; appendIndex(index: SequenceIntervalIndex): void; removeIndex(index: SequenceIntervalIndex): boolean; removeExistingInterval(interval: SequenceIntervalClass): void; addInterval(id: string, start: SequencePlace, end: SequencePlace, props?: PropertySet, op?: ISequencedDocumentMessage): SequenceIntervalClass; private linkEndpointsToInterval; private addIntervalToIndexes; add(interval: SequenceIntervalClass): void; changeInterval(interval: SequenceIntervalClass, start: SequencePlace | undefined, end: SequencePlace | undefined, op?: ISequencedDocumentMessage, localSeq?: number): SequenceIntervalClass; serialize(version: "1" | "2"): ISerializedIntervalCollectionV1 | ISerializedIntervalCollectionV2; private addIntervalListeners; private removeIntervalListeners; } /** * @legacy @beta */ export type DeserializeCallback = (properties: PropertySet) => void; declare class IntervalCollectionIterator implements Iterator { private readonly results; private index; constructor(collection: IntervalCollection, iteratesForward?: boolean, start?: number, end?: number); next(): IteratorResult; } /** * Change events emitted by `IntervalCollection`s * @legacy @beta */ export interface ISequenceIntervalCollectionEvents extends IEvent { /** * This event is invoked whenever the endpoints of an interval may have changed. * This can happen on: * - local endpoint modification * - ack of a remote endpoint modification * - position change due to segment sliding (slides due to mergeTree segment deletion will always appear local) * The `interval` argument reflects the new values. * `previousInterval` contains transient `ReferencePosition`s at the same location as the interval's original * endpoints. These references should be used for position information only. * `local` reflects whether the change originated locally. * `op` is defined if and only if the server has acked this change. * `slide` is true if the change is due to sliding on removal of position */ (event: "changeInterval", listener: (interval: SequenceInterval, previousInterval: SequenceInterval, local: boolean, op: ISequencedDocumentMessage | undefined, slide: boolean) => void): void; /** * This event is invoked whenever an interval is added or removed from the collection. * `local` reflects whether the change originated locally. * `op` is defined if and only if the server has acked this change. */ (event: "addInterval" | "deleteInterval", listener: (interval: SequenceInterval, local: boolean, op: ISequencedDocumentMessage | undefined) => void): void; /** * This event is invoked whenever an interval's properties have changed. * `interval` reflects the state of the updated properties. * `propertyDeltas` is a map-like whose keys contain all values that were changed, and whose * values contain all previous values of the property set. * This object can be used directly in a call to `changeProperties` to revert the property change if desired. * `local` reflects whether the change originated locally. * `op` is defined if and only if the server has acked this change. */ (event: "propertyChanged", listener: (interval: SequenceInterval, propertyDeltas: PropertySet, local: boolean, op: ISequencedDocumentMessage | undefined) => void): void; /** * This event is invoked whenever an interval's endpoints or properties (or both) have changed. * `interval` reflects the state of the updated endpoints or properties. * `propertyDeltas` is a map-like whose keys contain all values that were changed, and whose * values contain all previous values of the property set. * This object can be used directly in a call to `changeProperties` to revert the property change if desired. * 'previousInterval' contains transient `ReferencePosition`s at the same location as the interval's original * endpoints. These references should be used for position information only. In the case of a property change * only, this argument should be undefined. * `local` reflects whether the change originated locally. * `slide` is true if the change is due to sliding on removal of position. */ (event: "changed", listener: (interval: SequenceInterval, propertyDeltas: PropertySet, previousInterval: SequenceInterval | undefined, local: boolean, slide: boolean) => void): void; } /** * Collection of intervals that supports addition, modification, removal, and efficient spatial querying. * Changes to this collection will be incur updates on collaborating clients (i.e. they are not local-only). * @legacy @beta */ export interface ISequenceIntervalCollection extends TypedEventEmitter { readonly attached: boolean; /** * Attaches an index to this collection. * All intervals which are part of this collection will be added to the index, and the index will automatically * be updated when this collection updates due to local or remote changes. * * @remarks After attaching an index to an interval collection, applications should typically store this * index somewhere in their in-memory data model for future reference and querying. */ attachIndex(index: SequenceIntervalIndex): void; /** * Detaches an index from this collection. * All intervals which are part of this collection will be removed from the index, and updates to this collection * due to local or remote changes will no longer incur updates to the index. * * @returns `false` if the target index cannot be found in the indexes, otherwise remove all intervals in the index and return `true`. */ detachIndex(index: SequenceIntervalIndex): boolean; /** * @returns the interval in this collection that has the provided `id`. * If no interval in the collection has this `id`, returns `undefined`. */ getIntervalById(id: string): SequenceInterval | undefined; /** * Creates a new interval and add it to the collection. * @param start - interval start position (inclusive) * @param end - interval end position (exclusive) * @param props - properties of the interval * @returns the created interval * @remarks See documentation on {@link SequenceInterval} for comments on * interval endpoint semantics: there are subtleties with how the current * half-open behavior is represented. * * Note that intervals may behave unexpectedly if the entire contents * of the string are deleted. In this case, it is possible for one endpoint * of the interval to become detached, while the other remains on the string. * * By adjusting the `side` and `pos` values of the `start` and `end` parameters, * it is possible to control whether the interval expands to include content * inserted at its start or end. * * See {@link @fluidframework/merge-tree#SequencePlace} for more details on the model. * * @example * * Given the string "ABCD": * *```typescript * // Refers to "BC". If any content is inserted before B or after C, this * // interval will include that content * // * // Picture: * // \{start\} - A[- B - C -]D - \{end\} * // \{start\} - A - B - C - D - \{end\} * collection.add(\{ pos: 0, side: Side.After \}, \{ pos: 3, side: Side.Before \}, IntervalType.SlideOnRemove); * // Equivalent to specifying the same positions and Side.Before. * // Refers to "ABC". Content inserted after C will be included in the * // interval, but content inserted before A will not. * // \{start\} -[A - B - C -]D - \{end\} * // \{start\} - A - B - C - D - \{end\} * collection.add(0, 3, IntervalType.SlideOnRemove); *``` * * In the case of the first example, if text is deleted, * * ```typescript * // Delete the character "B" * string.removeRange(1, 2); * ``` * * The start point of the interval will slide to the position immediately * before "C", and the same will be true. * * ``` * \{start\} - A[- C -]D - \{end\} * ``` * * In this case, text inserted immediately before "C" would be included in * the interval. * * ```typescript * string.insertText(1, "EFG"); * ``` * * With the string now being, * * ``` * \{start\} - A[- E - F - G - C -]D - \{end\} * ``` * * @privateRemarks TODO: ADO:5205 the above comment regarding behavior in * the case that the entire interval has been deleted should be resolved at * the same time as this ticket */ add({ start, end, props, }: { start: SequencePlace; end: SequencePlace; props?: PropertySet; }): SequenceInterval; /** * Removes an interval from the collection. * @param id - Id of the interval to remove * @returns the removed interval */ removeIntervalById(id: string): SequenceInterval | undefined; /** * Changes the endpoints, properties, or both of an existing interval. * @param id - Id of the Interval to change * @returns the interval that was changed, if it existed in the collection. * Pass the desired new start position, end position, and/or properties in an object. Start and end positions must be changed * simultaneously - they must either both be specified or both undefined. To only change the properties, leave both endpoints * undefined. To only change the endpoints, leave the properties undefined. */ change(id: string, { start, end, props }: { start?: SequencePlace; end?: SequencePlace; props?: PropertySet; }): SequenceInterval | undefined; /** * @deprecated This api is not meant or necessary for external consumption and will be removed in subsequent release */ attachDeserializer(onDeserialize: DeserializeCallback): void; /** * @returns an iterator over all intervals in this collection. */ [Symbol.iterator](): Iterator; /** * @returns a forward iterator over all intervals in this collection with start point equal to `startPosition`. */ CreateForwardIteratorWithStartPosition(startPosition: number): Iterator; /** * @returns a backward iterator over all intervals in this collection with start point equal to `startPosition`. */ CreateBackwardIteratorWithStartPosition(startPosition: number): Iterator; /** * @returns a forward iterator over all intervals in this collection with end point equal to `endPosition`. */ CreateForwardIteratorWithEndPosition(endPosition: number): Iterator; /** * @returns a backward iterator over all intervals in this collection with end point equal to `endPosition`. */ CreateBackwardIteratorWithEndPosition(endPosition: number): Iterator; /** * Gathers iteration results that optionally match a start/end criteria into the provided array. * @param results - Array to gather the results into. In lieu of a return value, this array will be populated with * intervals matching the query upon edit. * @param iteratesForward - whether or not iteration should be in the forward direction * @param start - If provided, only match intervals whose start point is equal to `start`. * @param end - If provided, only match intervals whose end point is equal to `end`. */ gatherIterationResults(results: SequenceInterval[], iteratesForward: boolean, start?: number, end?: number): void; /** * @deprecated Users must manually attach the corresponding interval index to utilize this functionality, for instance: * * ```typescript * const overlappingIntervalsIndex = createOverlappingIntervalsIndex(sharedString); * collection.attachIndex(overlappingIntervalsIndex) * const result = overlappingIntervalsIndex.findOverlappingIntervals(start, end); * ``` * * @returns an array of all intervals in this collection that overlap with the interval * `[startPosition, endPosition]`. */ findOverlappingIntervals(startPosition: number, endPosition: number): SequenceInterval[]; /** * Applies a function to each interval in this collection. */ map(fn: (interval: SequenceInterval) => void): void; /** * @deprecated due to the forthcoming change where the endpointIndex will no longer be * automatically added to the collection. Users are advised to independently attach the * index to the collection and utilize the API accordingly, for instance: * ```typescript * const endpointIndex = createEndpointIndex(sharedString); * collection.attachIndex(endpointIndex); * const result1 = endpointIndex.previousInterval(pos); * ``` * If an index is used repeatedly, applications should generally attach it once and store it in memory. */ previousInterval(pos: number): SequenceInterval | undefined; /** * @deprecated due to the forthcoming change where the endpointIndex will no longer be * automatically added to the collection. Users are advised to independently attach the * index to the collection and utilize the API accordingly, for instance: * ```typescript * const endpointIndex = createEndpointIndex(sharedString); * collection.attachIndex(endpointIndex); * const result2 = endpointIndex.nextInterval(pos); * ``` */ nextInterval(pos: number): SequenceInterval | undefined; } /** * {@inheritdoc IIntervalCollection} */ export declare class IntervalCollection extends TypedEventEmitter implements ISequenceIntervalCollection { private readonly options; private savedSerializedIntervals?; private localCollection; private onDeserialize; private client; private readonly pending; get attached(): boolean; private readonly submitDelta; constructor(submitDelta: (op: IIntervalCollectionTypeOperationValue, md: unknown) => void, serializedIntervals: ISerializedIntervalCollectionV1 | ISerializedIntervalCollectionV2, options?: Partial); /** * {@inheritdoc IIntervalCollection.attachIndex} */ attachIndex(index: SequenceIntervalIndex): void; /** * {@inheritdoc IIntervalCollection.detachIndex} */ detachIndex(index: SequenceIntervalIndex): boolean; rollback(op: IIntervalCollectionTypeOperationValue, maybeMetadata: unknown): void; process(op: IIntervalCollectionTypeOperationValue, local: boolean, message: ISequencedDocumentMessage, maybeMetadata: unknown): void; resubmitMessage(op: IIntervalCollectionTypeOperationValue, maybeMetadata: unknown, squash: boolean): void; applyStashedOp(op: IIntervalCollectionTypeOperationValue): void; private rebaseReferenceWithSegmentSlide; private computeRebasedPositions; attachGraph(sequence: ISharedSegmentSequence, client: Client, label: string): void; /** * Gets the next local sequence number, modifying this client's collab window in doing so. */ private getNextLocalSeq; private emitChange; /** * {@inheritdoc IIntervalCollection.getIntervalById} */ getIntervalById(id: string): SequenceIntervalClass | undefined; private assertStickinessEnabled; /** * {@inheritdoc IIntervalCollection.add} */ add({ id, start, end, props, }: { id?: string; start: SequencePlace; end: SequencePlace; props?: PropertySet; }): SequenceIntervalClass; private deleteExistingInterval; /** * {@inheritdoc IIntervalCollection.removeIntervalById} */ removeIntervalById(id: string): SequenceIntervalClass | undefined; /** * {@inheritdoc IIntervalCollection.change} */ change(id: string, { start, end, props, rollback, }: { start?: SequencePlace; end?: SequencePlace; props?: PropertySet; rollback?: boolean; }): SequenceIntervalClass | undefined; private get isCollaborating(); private hasPendingEndpointChanges; ackChange(serializedInterval: SerializedIntervalDelta, local: boolean, op: ISequencedDocumentMessage, localOpMetadata: IntervalChangeLocalMetadata | undefined): SequenceIntervalClass | undefined; /** * {@inheritdoc IIntervalCollection.attachDeserializer} */ attachDeserializer(onDeserialize: DeserializeCallback): void; /** * Returns new interval after rebasing. If undefined, the interval was * deleted as a result of rebasing. This can occur if the interval applies * to a range that no longer exists, and the interval was unable to slide. * */ rebaseLocalInterval(original: SerializedIntervalDelta, localOpMetadata: IntervalAddLocalMetadata | IntervalChangeLocalMetadata, squash: boolean): SerializedIntervalDelta | undefined; private getSlideToSegment; private ackInterval; ackAdd(serializedInterval: ISerializedInterval, local: boolean, op: ISequencedDocumentMessage, localOpMetadata: IntervalAddLocalMetadata | undefined): SequenceIntervalClass; ackDelete(serializedInterval: SerializedIntervalDelta, local: boolean, op: ISequencedDocumentMessage): void; serializeInternal(version: "1" | "2"): ISerializedIntervalCollectionV1 | ISerializedIntervalCollectionV2; /** * Creates an iterator over all intervals in this collection. */ [Symbol.iterator](): IntervalCollectionIterator; /** * {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition} */ CreateForwardIteratorWithStartPosition(startPosition: number): IntervalCollectionIterator; /** * {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition} */ CreateBackwardIteratorWithStartPosition(startPosition: number): IntervalCollectionIterator; /** * {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition} */ CreateForwardIteratorWithEndPosition(endPosition: number): IntervalCollectionIterator; /** * {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition} */ CreateBackwardIteratorWithEndPosition(endPosition: number): IntervalCollectionIterator; /** * {@inheritdoc IIntervalCollection.gatherIterationResults} */ gatherIterationResults(results: SequenceIntervalClass[], iteratesForward: boolean, start?: number, end?: number): void; /** * {@inheritdoc IIntervalCollection.findOverlappingIntervals} */ findOverlappingIntervals(startPosition: number, endPosition: number): SequenceInterval[]; /** * {@inheritdoc IIntervalCollection.map} */ map(fn: (interval: SequenceIntervalClass) => void): void; /** * {@inheritdoc IIntervalCollection.previousInterval} */ previousInterval(pos: number): SequenceInterval | undefined; /** * {@inheritdoc IIntervalCollection.nextInterval} */ nextInterval(pos: number): SequenceInterval | undefined; } /** * Information that identifies an interval within a `Sequence`. * @internal */ export interface IntervalLocator { /** * Label for the collection the interval is a part of */ label: string; /** * Interval within that collection */ interval: SequenceIntervalClass; } /** * Returns an object that can be used to find the interval a given LocalReferencePosition belongs to. * @returns undefined if the reference position is not the endpoint of any interval (e.g. it was created * on the merge tree directly by app code), otherwise an {@link IntervalLocator} for the interval this * endpoint is a part of. * @internal */ export declare function intervalLocatorFromEndpoint(potentialEndpoint: LocalReferencePosition): IntervalLocator | undefined; export {}; //# sourceMappingURL=intervalCollection.d.ts.map