CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fluidframework--driver-utils

Collection of utility functions for Fluid drivers

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

tree-blob-utilities.mddocs/

Tree and Blob Utilities

Data structure utilities for converting between different tree formats and handling blob operations. Essential for working with Fluid Framework's tree-based data structures.

Capabilities

Tree Entry Classes

Classes for creating different types of tree entries that implement the ITreeEntry interface.

/**
 * Tree entry for blob nodes (files)
 */
class BlobTreeEntry implements ITreeEntry {
  /**
   * @param path - Path of the blob within the tree
   * @param contents - String content of the blob
   * @param encoding - Text encoding, defaults to "utf-8"
   */
  constructor(path: string, contents: string, encoding?: "utf-8" | "base64");

  readonly mode: FileMode.File;
  readonly type: TreeEntry.Blob;
  readonly path: string;
  readonly value: IBlob;
}

/**
 * Tree entry for directory nodes
 */
class TreeTreeEntry implements ITreeEntry {
  /**
   * @param path - Path of the directory within the tree
   * @param value - ITree representing the directory structure
   */
  constructor(path: string, value: ITree);

  readonly mode: FileMode.Directory;
  readonly type: TreeEntry.Tree;
  readonly path: string;
  readonly value: ITree;
}

/**
 * Tree entry for external blob attachments
 */
class AttachmentTreeEntry implements ITreeEntry {
  /**
   * @param path - Path of the attachment within the tree
   * @param id - External blob ID reference
   */
  constructor(path: string, id: string);

  readonly mode: FileMode.File;
  readonly type: TreeEntry.Attachment;
  readonly path: string;
  readonly value: IAttachment;
}

Usage Examples:

import { 
  BlobTreeEntry, 
  TreeTreeEntry, 
  AttachmentTreeEntry 
} from "@fluidframework/driver-utils";

// Create blob entry for a text file
const textBlob = new BlobTreeEntry(
  "documents/readme.txt",
  "Welcome to our application!",
  "utf-8"
);

// Create blob entry for binary data
const binaryBlob = new BlobTreeEntry(
  "images/logo.png",
  base64ImageData,
  "base64"
);

// Create directory entry
const directoryEntry = new TreeTreeEntry(
  "config",
  {
    id: "config-tree-id",
    entries: [
      /* tree entries for config files */
    ]
  }
);

// Create attachment entry referencing external blob
const attachmentEntry = new AttachmentTreeEntry(
  "attachments/large-file.zip",
  "external-blob-id-123"
);

Tree Conversion Functions

Functions for converting between different tree and snapshot formats used throughout the Fluid Framework.

/**
 * Converts ISummaryTree format to ITree format
 * Handles combined app+protocol summary layouts
 * @param summaryTree - Summary tree to convert
 * @returns Converted ITree structure
 */
function convertSummaryTreeToSnapshotITree(summaryTree: ISummaryTree): ITree;

/**
 * Builds hierarchical snapshot tree from flat tree entries
 * @param entries - Array of tree entries to organize into hierarchy
 * @param blobMap - Map to populate with blob UUIDs and content
 * @returns Hierarchical snapshot tree structure
 */
function buildSnapshotTree(
  entries: ITreeEntry[], 
  blobMap: Map<string, ArrayBufferLike>
): ISnapshotTree;

Usage Examples:

import { 
  convertSummaryTreeToSnapshotITree,
  buildSnapshotTree,
  BlobTreeEntry
} from "@fluidframework/driver-utils";

// Convert summary tree to snapshot tree
const summaryTree: ISummaryTree = {
  type: SummaryType.Tree,
  tree: {
    ".app": {
      type: SummaryType.Tree,
      tree: {
        "data.json": {
          type: SummaryType.Blob,
          content: '{"key": "value"}'
        }
      }
    }
  }
};

const snapshotTree = convertSummaryTreeToSnapshotITree(summaryTree);

// Build snapshot tree from entries
const entries = [
  new BlobTreeEntry("config.json", '{"setting": true}'),
  new BlobTreeEntry("data/items.json", '["item1", "item2"]'),
  new BlobTreeEntry("readme.md", "# Project Documentation")
];

const blobMap = new Map<string, ArrayBufferLike>();
const snapshot = buildSnapshotTree(entries, blobMap);

// blobMap now contains blob content keyed by UUID
console.log(`Created ${blobMap.size} blobs`);

Storage Utilities

Utility functions for working with snapshots and extracting tree structures.

/**
 * Extracts ISnapshotTree from either ISnapshot or ISnapshotTree input
 * @param tree - Input that may be either format
 * @returns The snapshot tree portion
 */
function getSnapshotTree(tree: ISnapshotTree | ISnapshot): ISnapshotTree;

/**
 * Type guard to check if object is ISnapshot
 * @param obj - Object to check
 * @returns True if object is ISnapshot (checks for snapshotFormatV: 1)
 */
function isInstanceOfISnapshot(
  obj: ISnapshotTree | ISnapshot | undefined
): obj is ISnapshot;

Usage Examples:

import { 
  getSnapshotTree, 
  isInstanceOfISnapshot 
} from "@fluidframework/driver-utils";

// Handle both snapshot formats
function processSnapshot(data: ISnapshotTree | ISnapshot) {
  const tree = getSnapshotTree(data);
  
  // Now work with the tree structure
  processTreeNodes(tree);
}

// Type checking
function analyzeSnapshotData(data: ISnapshotTree | ISnapshot | undefined) {
  if (!data) {
    console.log("No snapshot data");
    return;
  }

  if (isInstanceOfISnapshot(data)) {
    console.log("Processing full snapshot with blobs");
    console.log(`Snapshot has ${Object.keys(data.blobs).length} blobs`);
    const tree = data.snapshotTree;
    // Process tree...
  } else {
    console.log("Processing tree-only snapshot");
    // data is ISnapshotTree
    // Process tree directly...
  }
}

Advanced Usage Patterns

Building Complex Tree Structures

import { 
  BlobTreeEntry, 
  TreeTreeEntry, 
  buildSnapshotTree 
} from "@fluidframework/driver-utils";

class DocumentTreeBuilder {
  private entries: ITreeEntry[] = [];

  addDocument(path: string, content: any): this {
    const jsonContent = JSON.stringify(content, null, 2);
    this.entries.push(new BlobTreeEntry(path, jsonContent));
    return this;
  }

  addBinaryFile(path: string, data: ArrayBuffer): this {
    const base64Data = this.arrayBufferToBase64(data);
    this.entries.push(new BlobTreeEntry(path, base64Data, "base64"));
    return this;
  }

  addDirectory(path: string, subtree: ITree): this {
    this.entries.push(new TreeTreeEntry(path, subtree));
    return this;
  }

  build(): { tree: ISnapshotTree; blobs: Map<string, ArrayBufferLike> } {
    const blobMap = new Map<string, ArrayBufferLike>();
    const tree = buildSnapshotTree(this.entries, blobMap);
    return { tree, blobs: blobMap };
  }

  private arrayBufferToBase64(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    const binary = Array.from(bytes, byte => String.fromCharCode(byte)).join('');
    return btoa(binary);
  }
}

// Usage
const builder = new DocumentTreeBuilder();
const result = builder
  .addDocument("manifest.json", { version: "1.0", name: "MyApp" })
  .addDocument("config/settings.json", { debug: true })
  .addBinaryFile("assets/icon.png", iconBuffer)
  .build();

console.log(`Built tree with ${result.blobs.size} blobs`);

Tree Transformation Pipeline

import { 
  convertSummaryTreeToSnapshotITree,
  getSnapshotTree,
  BlobTreeEntry
} from "@fluidframework/driver-utils";

class TreeTransformer {
  static transformSummaryToSnapshot(summaryTree: ISummaryTree): ITree {
    return convertSummaryTreeToSnapshotITree(summaryTree);
  }

  static extractTextBlobs(tree: ISnapshotTree): Map<string, string> {
    const textBlobs = new Map<string, string>();
    
    this.walkTree(tree, "", (path, blobId) => {
      // This would need access to blob storage to read content
      // Simplified for example
      if (path.endsWith('.json') || path.endsWith('.txt') || path.endsWith('.md')) {
        textBlobs.set(path, blobId);
      }
    });
    
    return textBlobs;
  }

  private static walkTree(
    tree: ISnapshotTree, 
    currentPath: string,
    onBlob: (path: string, blobId: string) => void
  ): void {
    // Process blobs at current level
    for (const [name, blobId] of Object.entries(tree.blobs)) {
      const fullPath = currentPath ? `${currentPath}/${name}` : name;
      onBlob(fullPath, blobId);
    }

    // Recursively process subtrees
    for (const [name, subtree] of Object.entries(tree.trees)) {
      const fullPath = currentPath ? `${currentPath}/${name}` : name;
      this.walkTree(subtree, fullPath, onBlob);
    }
  }

  static createTreeFromPaths(
    pathToBlobMap: Map<string, string>
  ): ITreeEntry[] {
    const entries: ITreeEntry[] = [];
    
    for (const [path, content] of pathToBlobMap) {
      entries.push(new BlobTreeEntry(path, content));
    }
    
    return entries;
  }
}

// Usage
const transformer = TreeTransformer;

// Transform summary to snapshot format
const snapshotTree = transformer.transformSummaryToSnapshot(summaryData);

// Extract text files from a tree
const textFiles = transformer.extractTextBlobs(snapshotTree);

// Create tree entries from paths
const newEntries = transformer.createTreeFromPaths(
  new Map([
    ["docs/readme.md", "# Welcome"],
    ["config.json", '{"version": 1}']
  ])
);

Snapshot Analysis Utility

import { 
  getSnapshotTree, 
  isInstanceOfISnapshot 
} from "@fluidframework/driver-utils";

class SnapshotAnalyzer {
  static analyze(snapshot: ISnapshotTree | ISnapshot): SnapshotStats {
    const tree = getSnapshotTree(snapshot);
    const stats: SnapshotStats = {
      treeNodeCount: 0,
      blobCount: 0,
      totalBlobSize: 0,
      maxDepth: 0,
      fileTypes: new Map()
    };

    // If it's a full snapshot, we can get blob sizes
    let blobSizes: Map<string, number> | undefined;
    if (isInstanceOfISnapshot(snapshot)) {
      blobSizes = new Map();
      for (const [id, blob] of Object.entries(snapshot.blobs)) {
        const size = blob.byteLength || blob.size || 0;
        blobSizes.set(id, size);
      }
    }

    this.analyzeTree(tree, stats, 0, blobSizes);
    return stats;
  }

  private static analyzeTree(
    tree: ISnapshotTree,
    stats: SnapshotStats,
    depth: number,
    blobSizes?: Map<string, number>
  ): void {
    stats.treeNodeCount++;
    stats.maxDepth = Math.max(stats.maxDepth, depth);

    // Analyze blobs
    for (const [name, blobId] of Object.entries(tree.blobs)) {
      stats.blobCount++;
      
      const extension = name.split('.').pop()?.toLowerCase() || 'unknown';
      stats.fileTypes.set(extension, (stats.fileTypes.get(extension) || 0) + 1);
      
      if (blobSizes) {
        const size = blobSizes.get(blobId) || 0;
        stats.totalBlobSize += size;
      }
    }

    // Recursively analyze subtrees
    for (const subtree of Object.values(tree.trees)) {
      this.analyzeTree(subtree, stats, depth + 1, blobSizes);
    }
  }
}

interface SnapshotStats {
  treeNodeCount: number;
  blobCount: number;
  totalBlobSize: number;
  maxDepth: number;
  fileTypes: Map<string, number>;
}

// Usage
const stats = SnapshotAnalyzer.analyze(snapshotData);
console.log(`Tree has ${stats.treeNodeCount} nodes and ${stats.blobCount} blobs`);
console.log(`Max depth: ${stats.maxDepth}, Total blob size: ${stats.totalBlobSize} bytes`);

docs

data-processing-summary.md

index.md

network-utilities.md

protocol-message-utilities.md

request-management.md

retry-rate-limiting.md

storage-services.md

tree-blob-utilities.md

url-compression.md

tile.json