Collection of utility functions for Fluid Framework including async operations, data structures, performance monitoring, and cross-platform compatibility.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Efficient data structures including priority heaps and range tracking utilities designed for high-performance scenarios in Fluid Framework applications.
Ordered heap implementation with customizable comparison functions for priority queue operations.
/**
* Comparer interface for heap ordering
*/
interface IComparer<T> {
/** Minimum value for the type */
min: T;
/** Comparison function returning negative, zero, or positive values */
compare(a: T, b: T): number;
}
/**
* Built-in number comparer for numeric heaps
*/
const NumberComparer: IComparer<number>;
/**
* Heap node interface for direct heap manipulation
*/
interface IHeapNode<T> {
/** The stored value */
value: T;
/** Internal heap position (managed by heap) */
position: number;
}
/**
* Ordered heap data structure with customizable comparison
* Maintains heap property where parent <= children (min-heap by default)
*/
class Heap<T> {
/**
* Creates a new heap with required comparer
* @param comp - Comparer for heap ordering (required parameter)
*/
constructor(comp: IComparer<T>);
/**
* Gets and removes the minimum element from the heap
* @returns The minimum element value
*/
get(): T;
/**
* Gets the minimum element without removing it
* @returns The heap node containing the minimum element
*/
peek(): IHeapNode<T>;
/**
* Adds a new value to the heap
* @param value - Value to add
* @returns The heap node that was created
*/
add(value: T): IHeapNode<T>;
/**
* Updates the position of a node after its value has changed
* @param node - The heap node to update
*/
update(node: IHeapNode<T>): void;
/**
* Removes a specific node from the heap
* @param node - The heap node to remove
*/
remove(node: IHeapNode<T>): void;
/**
* Gets the number of elements in the heap
* @returns The number of elements in the heap
*/
count(): number;
}Usage Examples:
import { Heap, NumberComparer } from "@fluidframework/common-utils";
// Basic numeric heap (min-heap)
const numberHeap = new Heap<number>(NumberComparer);
numberHeap.add(5);
numberHeap.add(2);
numberHeap.add(8);
numberHeap.add(1);
console.log(numberHeap.peek().value); // 1 (minimum)
console.log(numberHeap.get()); // 1 (removes and returns minimum)
console.log(numberHeap.get()); // 2 (next minimum)
// Custom comparer for max-heap
const maxComparer = {
min: Number.NEGATIVE_INFINITY,
compare: (a: number, b: number) => b - a // Reverse comparison for max-heap
};
const maxHeap = new Heap<number>(maxComparer);
maxHeap.add(5);
maxHeap.add(2);
maxHeap.add(8);
console.log(maxHeap.peek().value); // 8 (maximum in max-heap)
// Object heap with custom comparison
interface Task {
id: string;
priority: number;
description: string;
}
const taskComparer: IComparer<Task> = {
min: { id: "", priority: Number.NEGATIVE_INFINITY, description: "" },
compare: (a, b) => a.priority - b.priority // Lower number = higher priority
};
const taskQueue = new Heap<Task>(taskComparer);
taskQueue.add({ id: "1", priority: 3, description: "Low priority task" });
taskQueue.add({ id: "2", priority: 1, description: "High priority task" });
taskQueue.add({ id: "3", priority: 2, description: "Medium priority task" });
// Process tasks in priority order
while (taskQueue.count() > 0) {
const nextTask = taskQueue.get();
console.log(`Processing: ${nextTask.description}`);
}
// Using heap nodes for updates
const urgentTask = { id: "4", priority: 5, description: "Initially low priority" };
const taskNode = taskQueue.add(urgentTask);
// Later, update priority
urgentTask.priority = 0; // Make it highest priority
taskQueue.update(taskNode); // Heap reorders accordinglyTracks 1:N relationships between two ranges, commonly used for mapping between different coordinate systems.
/**
* Range interface representing a span with primary and secondary coordinates
*/
interface IRange {
/** Primary range start position */
primary: number;
/** Secondary range start position */
secondary: number;
/** Length of the range */
length: number;
}
/**
* Serializable snapshot of range tracker state
*/
interface IRangeTrackerSnapshot {
/** Array of tracked ranges */
ranges: IRange[];
/** Last primary position */
lastPrimary: number;
/** Last secondary position */
lastSecondary: number;
}
/**
* Tracks 1:N relation between two ranges
* Used by Fluid Framework deli for branch mapping and coordinate transformation
*/
class RangeTracker {
/**
* Creates a new RangeTracker
*/
constructor();
/**
* Adds a new range mapping
* @param primaryStart - Start position in primary coordinate space
* @param primaryEnd - End position in primary coordinate space
* @param secondaryStart - Start position in secondary coordinate space
*/
add(primaryStart: number, primaryEnd: number, secondaryStart: number): void;
/**
* Gets range information for a primary position
* @param primaryPosition - Position in primary coordinate space
* @param primaryEnd - Optional end position for range queries
* @returns Range information or undefined if not found
*/
get(primaryPosition: number, primaryEnd?: number): IRange | undefined;
/**
* Updates the base positions for relative calculations
* @param primaryBase - New primary base position
* @param secondaryBase - New secondary base position
*/
updateBase(primaryBase: number, secondaryBase: number): void;
/**
* Serializes the current state for persistence
* @returns Serializable snapshot of current state
*/
serialize(): IRangeTrackerSnapshot;
/**
* Restores state from a serialized snapshot
* @param snapshot - Previously serialized snapshot
*/
deserialize(snapshot: IRangeTrackerSnapshot): void;
}Usage Examples:
import { RangeTracker } from "@fluidframework/common-utils";
// Document coordinate mapping
const coordMapper = new RangeTracker();
// Map document sections to display coordinates
// Document chars 0-100 map to display pixels 0-500
coordMapper.add(0, 100, 0);
// Document chars 100-200 map to display pixels 600-1100 (with gap)
coordMapper.add(100, 200, 600);
// Find display coordinates for document position
const range1 = coordMapper.get(50); // Document position 50
console.log(range1?.secondary); // Display coordinate for doc position 50
const range2 = coordMapper.get(150); // Document position 150
console.log(range2?.secondary); // Display coordinate for doc position 150
// Branch mapping in collaborative editing
class BranchMapper {
private tracker = new RangeTracker();
addEdit(localStart: number, localEnd: number, remoteStart: number): void {
this.tracker.add(localStart, localEnd, remoteStart);
}
mapToRemote(localPosition: number): number | undefined {
const range = this.tracker.get(localPosition);
if (!range) return undefined;
const offset = localPosition - range.primary;
return range.secondary + offset;
}
save(): string {
return JSON.stringify(this.tracker.serialize());
}
load(serialized: string): void {
const snapshot = JSON.parse(serialized);
this.tracker.deserialize(snapshot);
}
}
// Time series data mapping
const timeMapper = new RangeTracker();
// Map time ranges to data indices
// Hours 0-24 (day 1) map to data indices 0-1440 (minute-level data)
timeMapper.add(0, 24, 0);
// Hours 24-48 (day 2) map to data indices 1440-2880
timeMapper.add(24, 48, 1440);
// Find data index for specific time
const dataRange = timeMapper.get(36); // Hour 36 (day 2, hour 12)
if (dataRange) {
const hourOffset = 36 - dataRange.primary;
const dataIndex = dataRange.secondary + (hourOffset * 60); // Convert to minutes
console.log(`Hour 36 maps to data index: ${dataIndex}`);
}
// Viewport mapping with dynamic updates
class ViewportMapper {
private tracker = new RangeTracker();
addViewport(contentStart: number, contentEnd: number, viewStart: number): void {
this.tracker.add(contentStart, contentEnd, viewStart);
}
scrollTo(newContentBase: number, newViewBase: number): void {
this.tracker.updateBase(newContentBase, newViewBase);
}
getViewPosition(contentPosition: number): number | undefined {
const range = this.tracker.get(contentPosition);
if (!range) return undefined;
return range.secondary + (contentPosition - range.primary);
}
}import { Heap } from "@fluidframework/common-utils";
// Event scheduling system
interface ScheduledEvent {
id: string;
timestamp: number;
callback: () => void;
data?: any;
}
class EventScheduler {
private eventHeap = new Heap<ScheduledEvent>({
min: { id: "", timestamp: 0, callback: () => {} },
compare: (a, b) => a.timestamp - b.timestamp
});
schedule(event: ScheduledEvent): void {
this.eventHeap.add(event);
}
processNext(): ScheduledEvent | null {
if (this.eventHeap.isEmpty) return null;
const nextEvent = this.eventHeap.get();
if (nextEvent.timestamp <= Date.now()) {
nextEvent.callback();
return nextEvent;
}
// Put it back if not ready
this.eventHeap.add(nextEvent);
return null;
}
getNextTimestamp(): number | null {
if (this.eventHeap.isEmpty) return null;
return this.eventHeap.peek().timestamp;
}
}
// Usage
const scheduler = new EventScheduler();
scheduler.schedule({
id: "reminder",
timestamp: Date.now() + 5000, // 5 seconds from now
callback: () => console.log("Reminder fired!"),
data: { message: "Don't forget the meeting" }
});import { RangeTracker } from "@fluidframework/common-utils";
// Multi-layer coordinate system
class MultiLayerMapper {
private layers: Map<string, RangeTracker> = new Map();
addLayer(layerId: string): void {
this.layers.set(layerId, new RangeTracker());
}
mapRange(layerId: string, sourceStart: number, sourceEnd: number, targetStart: number): void {
const tracker = this.layers.get(layerId);
if (tracker) {
tracker.add(sourceStart, sourceEnd, targetStart);
}
}
// Map position through multiple layers
mapThroughLayers(position: number, layerOrder: string[]): number {
let currentPosition = position;
for (const layerId of layerOrder) {
const tracker = this.layers.get(layerId);
if (tracker) {
const range = tracker.get(currentPosition);
if (range) {
const offset = currentPosition - range.primary;
currentPosition = range.secondary + offset;
}
}
}
return currentPosition;
}
}These data structures provide efficient solutions for priority-based processing and coordinate mapping scenarios common in Fluid Framework applications.