Skip to content

Commit fd2e782

Browse files
anthony-murphy-agentclaudeanthony-murphy
committed
refactor(sequence): make transient interval creation reusable without Client
Extract resolvePositionRef helper and change createPositionReferenceFromSegoff to accept ISharedSegmentSequence<any> instead of Client, enabling transient interval creation from code that only has a sequence reference. Refactor createSequenceInterval to use resolvePositionRef for non-op paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-Authored-By: anthony-murphy <anthony.murphy@microsoft.com>
1 parent ee728ab commit fd2e782

File tree

3 files changed

+94
-34
lines changed

3 files changed

+94
-34
lines changed

packages/dds/sequence/src/intervalCollection.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
createSequenceInterval,
6262
getSerializedProperties,
6363
} from "./intervals/index.js";
64+
import type { ISharedSegmentSequence } from "./sequence.js";
6465

6566
export type ISerializedIntervalCollectionV1 = ISerializedInterval[];
6667

@@ -1596,7 +1597,7 @@ export class IntervalCollection
15961597
if (needsStartUpdate) {
15971598
const props = interval.start.properties;
15981599
interval.start = createPositionReferenceFromSegoff({
1599-
client: this.client,
1600+
sequence: this.client as unknown as ISharedSegmentSequence<any>,
16001601
segoff: newStart,
16011602
refType: interval.start.refType,
16021603
op,
@@ -1615,7 +1616,7 @@ export class IntervalCollection
16151616
if (needsEndUpdate) {
16161617
const props = interval.end.properties;
16171618
interval.end = createPositionReferenceFromSegoff({
1618-
client: this.client,
1619+
sequence: this.client as unknown as ISharedSegmentSequence<any>,
16191620
segoff: newEnd,
16201621
refType: interval.end.refType,
16211622
op,

packages/dds/sequence/src/intervals/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ export {
2222
createSequenceInterval,
2323
createPositionReferenceFromSegoff,
2424
createTransientInterval,
25+
resolvePositionRef,
2526
getSerializedProperties,
2627
} from "./sequenceInterval.js";

packages/dds/sequence/src/intervals/sequenceInterval.ts

Lines changed: 90 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import {
3535
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
3636
import { v4 as uuid } from "uuid";
3737

38+
import type { ISharedSegmentSequence } from "../sequence.js";
39+
3840
import {
3941
ISerializableInterval,
4042
ISerializedInterval,
@@ -504,7 +506,7 @@ export class SequenceIntervalClass
504506
this.verifyNotDispose();
505507

506508
const startRef = createPositionReferenceFromSegoff({
507-
client: this.client,
509+
sequence: this.client as unknown as ISharedSegmentSequence<any>,
508510
segoff: rebased.start,
509511
refType: this.start.refType,
510512
slidingPreference: this.start.slidingPreference,
@@ -516,7 +518,7 @@ export class SequenceIntervalClass
516518
this.start = startRef;
517519

518520
const endRef = createPositionReferenceFromSegoff({
519-
client: this.client,
521+
sequence: this.client as unknown as ISharedSegmentSequence<any>,
520522
segoff: rebased.end,
521523
refType: this.end.refType,
522524
slidingPreference: this.end.slidingPreference,
@@ -636,7 +638,7 @@ export class SequenceIntervalClass
636638
}
637639

638640
export function createPositionReferenceFromSegoff({
639-
client,
641+
sequence,
640642
segoff,
641643
refType,
642644
op,
@@ -646,7 +648,7 @@ export function createPositionReferenceFromSegoff({
646648
canSlideToEndpoint,
647649
rollback,
648650
}: {
649-
client: Client;
651+
sequence: ISharedSegmentSequence<any>;
650652
segoff: { segment: ISegment; offset: number } | undefined | "start" | "end";
651653
refType: ReferenceType;
652654
op?: ISequencedDocumentMessage;
@@ -657,9 +659,9 @@ export function createPositionReferenceFromSegoff({
657659
rollback?: boolean;
658660
}): LocalReferencePosition {
659661
if (segoff === "start" || segoff === "end") {
660-
return client.createLocalReferencePosition(
661-
segoff,
662-
undefined,
662+
return sequence.createLocalReferencePosition(
663+
segoff as any,
664+
undefined as any,
663665
refType,
664666
undefined,
665667
slidingPreference,
@@ -668,7 +670,7 @@ export function createPositionReferenceFromSegoff({
668670
}
669671

670672
if (segoff?.segment) {
671-
const ref = client.createLocalReferencePosition(
673+
const ref = sequence.createLocalReferencePosition(
672674
segoff.segment,
673675
segoff.offset,
674676
refType,
@@ -697,6 +699,39 @@ export function createPositionReferenceFromSegoff({
697699
return createDetachedLocalReferencePosition(slidingPreference, refType);
698700
}
699701

702+
/**
703+
* Resolves a position (number or "start"/"end" sentinel) to a {@link LocalReferencePosition}
704+
* using an {@link ISharedSegmentSequence}. This is the simple (non-op) path for creating
705+
* position references, usable from code that doesn't have a `Client` reference.
706+
*/
707+
export function resolvePositionRef(
708+
sequence: ISharedSegmentSequence<any>,
709+
pos: number | "start" | "end",
710+
refType: ReferenceType,
711+
slidingPreference: SlidingPreference,
712+
canSlideToEndpoint?: boolean,
713+
): LocalReferencePosition {
714+
if (pos === "start" || pos === "end") {
715+
return createPositionReferenceFromSegoff({
716+
sequence,
717+
segoff: pos,
718+
refType,
719+
slidingPreference,
720+
canSlideToEndpoint,
721+
});
722+
}
723+
const { segment, offset } = sequence.getContainingSegment(pos);
724+
const segoff =
725+
segment !== undefined && offset !== undefined ? { segment, offset } : undefined;
726+
return createPositionReferenceFromSegoff({
727+
sequence,
728+
segoff,
729+
refType,
730+
slidingPreference,
731+
canSlideToEndpoint,
732+
});
733+
}
734+
700735
function createPositionReference({
701736
client,
702737
pos,
@@ -746,7 +781,7 @@ function createPositionReference({
746781
}
747782

748783
return createPositionReferenceFromSegoff({
749-
client,
784+
sequence: client as unknown as ISharedSegmentSequence<any>,
750785
segoff,
751786
refType,
752787
op,
@@ -761,14 +796,14 @@ function createPositionReference({
761796
export function createTransientInterval(
762797
start: SequencePlace | undefined,
763798
end: SequencePlace | undefined,
764-
client: Client,
799+
clientOrSequence: Client | ISharedSegmentSequence<any>,
765800
) {
766801
return createSequenceInterval(
767802
"transient",
768803
uuid(),
769804
start,
770805
end,
771-
client,
806+
clientOrSequence as unknown as Client,
772807
IntervalType.Transient,
773808
);
774809
}
@@ -824,34 +859,57 @@ export function createSequenceInterval(
824859
endSide,
825860
);
826861

827-
const startLref = createPositionReference({
828-
client,
829-
pos: startPos,
830-
refType: beginRefType,
831-
op,
832-
fromSnapshot,
833-
slidingPreference: startSlidingPreference,
834-
canSlideToEndpoint: canSlideToEndpoint && stickiness !== IntervalStickiness.NONE,
835-
rollback,
836-
});
837-
838862
const endSlidingPreference = endReferenceSlidingPreference(
839863
startPos,
840864
startSide,
841865
endPos,
842866
endSide,
843867
);
844868

845-
const endLref = createPositionReference({
846-
client,
847-
pos: endPos,
848-
refType: endRefType,
849-
op,
850-
fromSnapshot,
851-
slidingPreference: endSlidingPreference,
852-
canSlideToEndpoint: canSlideToEndpoint && stickiness !== IntervalStickiness.NONE,
853-
rollback,
854-
});
869+
const canSlide = canSlideToEndpoint && stickiness !== IntervalStickiness.NONE;
870+
871+
let startLref: LocalReferencePosition;
872+
let endLref: LocalReferencePosition;
873+
874+
if (op ?? fromSnapshot) {
875+
// Op-context path: needs Client-specific getContainingSegment with op context
876+
startLref = createPositionReference({
877+
client,
878+
pos: startPos,
879+
refType: beginRefType,
880+
op,
881+
fromSnapshot,
882+
slidingPreference: startSlidingPreference,
883+
canSlideToEndpoint: canSlide,
884+
rollback,
885+
});
886+
endLref = createPositionReference({
887+
client,
888+
pos: endPos,
889+
refType: endRefType,
890+
op,
891+
fromSnapshot,
892+
slidingPreference: endSlidingPreference,
893+
canSlideToEndpoint: canSlide,
894+
rollback,
895+
});
896+
} else {
897+
// Non-op path (transient or local creation): use resolvePositionRef
898+
startLref = resolvePositionRef(
899+
client as unknown as ISharedSegmentSequence<any>,
900+
startPos,
901+
beginRefType,
902+
startSlidingPreference,
903+
canSlide,
904+
);
905+
endLref = resolvePositionRef(
906+
client as unknown as ISharedSegmentSequence<any>,
907+
endPos,
908+
endRefType,
909+
endSlidingPreference,
910+
canSlide,
911+
);
912+
}
855913

856914
const rangeProp = {
857915
[reservedRangeLabelsKey]: [label],

0 commit comments

Comments
 (0)