or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

events.mdindex.mdlegacy-factories.mdshared-directory.mdshared-map.md
tile.json

shared-directory.mddocs/

SharedDirectory Operations

SharedDirectory provides a hierarchical distributed data structure that combines key-value storage with directory-like organization. It enables structured data storage with subdirectories, supporting both Map-like operations and directory navigation.

Capabilities

Creating SharedDirectory

Creates a new SharedDirectory instance for hierarchical collaborative data storage.

/**
 * Creates a new SharedDirectory instance
 * @param runtime - Fluid data store runtime
 * @param id - Optional unique identifier for the directory
 * @returns SharedDirectory instance
 */
const SharedDirectory: ISharedObjectKind<ISharedDirectory>;

interface ISharedDirectory extends ISharedObject<ISharedDirectoryEvents & IDirectoryEvents>, Omit<IDirectory, "on" | "once" | "off"> {
  [Symbol.iterator](): IterableIterator<[string, any]>;
  readonly [Symbol.toStringTag]: string;
}

Usage Example:

import { SharedDirectory } from "@fluidframework/map";

// Create a new shared directory
const appData = SharedDirectory.create(runtime, "app-data");
const userWorkspace = SharedDirectory.create(runtime); // Auto-generated ID

Directory Path Information

Every directory has an absolute path that identifies its location in the hierarchy.

/**
 * The absolute path of the directory
 */
readonly absolutePath: string;

Usage Examples:

const rootDir = SharedDirectory.create(runtime, "root");
console.log(rootDir.absolutePath); // "/"

const subDir = rootDir.createSubDirectory("configs");
console.log(subDir.absolutePath); // "/configs"

const nestedDir = subDir.createSubDirectory("user-prefs");
console.log(nestedDir.absolutePath); // "/configs/user-prefs"

Key-Value Operations

SharedDirectory supports all standard Map operations for storing and retrieving key-value pairs within each directory level.

Getting Values

/**
 * Retrieves the value stored at the given key from the directory
 * @param key - Key to retrieve from
 * @returns The stored value, or undefined if the key is not set
 */
get<T = any>(key: string): T | undefined;

Setting Values

/**
 * Sets the value stored at key to the provided value
 * @param key - Key to set at
 * @param value - Value to set (must be JSON-serializable or a handle)
 * @returns The IDirectory itself for chaining
 */
set<T = unknown>(key: string, value: T): this;

Usage Examples:

// Set values in root directory
appData.set("app-name", "My Collaborative App");
appData.set("version", "1.0.0");
appData.set("config", { theme: "dark", debug: false });

// Get values from directory
const appName = appData.get<string>("app-name");
const config = appData.get<AppConfig>("config");

// Chaining operations
const userSettings = appData.createSubDirectory("user-settings");
userSettings
  .set("theme", "dark")
  .set("language", "en")
  .set("notifications", true);

Subdirectory Management

SharedDirectory provides methods for creating, accessing, and managing subdirectories.

Creating Subdirectories

/**
 * Creates an IDirectory child of this IDirectory, or retrieves the existing IDirectory child if one with the
 * same name already exists
 * @param subdirName - Name of the new child directory to create
 * @returns The IDirectory child that was created or retrieved
 */
createSubDirectory(subdirName: string): IDirectory;

Usage Examples:

// Create subdirectories
const userProfiles = appData.createSubDirectory("user-profiles");
const gameData = appData.createSubDirectory("game-data");

// Create nested structure
const playerStats = gameData.createSubDirectory("player-stats");
const achievements = gameData.createSubDirectory("achievements");

// Creating the same subdirectory twice returns the existing one
const sameUserProfiles = appData.createSubDirectory("user-profiles");
console.log(userProfiles === sameUserProfiles); // true

Getting Existing Subdirectories

/**
 * Gets an IDirectory child of this IDirectory, if it exists
 * @param subdirName - Name of the child directory to get
 * @returns The requested IDirectory, or undefined if it doesn't exist
 */
getSubDirectory(subdirName: string): IDirectory | undefined;

Usage Examples:

// Get existing subdirectory
const existingProfiles = appData.getSubDirectory("user-profiles");
if (existingProfiles) {
  // Work with the subdirectory
  existingProfiles.set("active-user", "alice");
}

// Check for non-existent subdirectory
const missingDir = appData.getSubDirectory("non-existent");
console.log(missingDir); // undefined

Checking Subdirectory Existence

/**
 * Checks whether this directory has a child directory with the given name
 * @param subdirName - Name of the child directory to check
 * @returns True if it exists, false otherwise
 */
hasSubDirectory(subdirName: string): boolean;

Usage Examples:

// Check before accessing
if (appData.hasSubDirectory("user-profiles")) {
  const profiles = appData.getSubDirectory("user-profiles");
  // Safe to use profiles
}

// Conditional creation
if (!appData.hasSubDirectory("temp-data")) {
  appData.createSubDirectory("temp-data");
}

Deleting Subdirectories

/**
 * Deletes an IDirectory child of this IDirectory, if it exists, along with all descendent keys and directories
 * @param subdirName - Name of the child directory to delete
 * @returns True if the IDirectory existed and was deleted, false if it did not exist
 */
deleteSubDirectory(subdirName: string): boolean;

Usage Examples:

// Delete a subdirectory and all its contents
const wasDeleted = appData.deleteSubDirectory("temp-data");
if (wasDeleted) {
  console.log("Temporary data directory was removed");
}

// Clean up old data
if (appData.hasSubDirectory("old-sessions")) {
  appData.deleteSubDirectory("old-sessions");
}

Counting Subdirectories

/**
 * Get the number of subdirectories within the directory
 * @returns The number of subdirectories within a directory
 */
countSubDirectory?(): number;

Usage Example:

const subdirCount = appData.countSubDirectory?.() ?? 0;
console.log(`Directory has ${subdirCount} subdirectories`);

Directory Navigation

SharedDirectory provides methods for navigating the directory hierarchy using relative paths.

Working Directory Navigation

/**
 * Get an IDirectory within the directory, in order to use relative paths from that location
 * @param relativePath - Path of the IDirectory to get, relative to this IDirectory
 * @returns The requested IDirectory, or undefined if the path doesn't exist
 */
getWorkingDirectory(relativePath: string): IDirectory | undefined;

Usage Examples:

// Navigate using relative paths
const userPrefs = appData.getWorkingDirectory("user-settings/preferences");
if (userPrefs) {
  userPrefs.set("auto-save", true);
}

// Navigate to nested directories
const playerData = appData.getWorkingDirectory("game-data/player-stats");
if (playerData) {
  playerData.set("level", 25);
  playerData.set("score", 150000);
}

// Path navigation with multiple levels
const configDir = appData.getWorkingDirectory("settings/ui/themes");
configDir?.set("current", "dark-mode");

// Invalid paths return undefined
const invalidPath = appData.getWorkingDirectory("non/existent/path");
console.log(invalidPath); // undefined

Directory Iteration

SharedDirectory provides methods for iterating over subdirectories and their contents.

Iterating Over Subdirectories

/**
 * Gets an iterator over the IDirectory children of this IDirectory
 * @returns The IDirectory iterator yielding [name, directory] pairs
 */
subdirectories(): IterableIterator<[string, IDirectory]>;

Usage Examples:

// Iterate over all subdirectories
for (const [name, subdir] of appData.subdirectories()) {
  console.log(`Subdirectory: ${name} at path: ${subdir.absolutePath}`);
  console.log(`Contains ${subdir.size} keys`);
}

// Process all subdirectories
const subdirs = Array.from(appData.subdirectories());
subdirs.forEach(([name, dir]) => {
  console.log(`Processing ${name}...`);
});

// Find specific subdirectory
const gameDir = Array.from(appData.subdirectories())
  .find(([name]) => name === "game-data")?.[1];

Standard Map Iteration

SharedDirectory supports all standard Map iteration methods for keys and values within the current directory level.

/**
 * Returns an iterator over the keys in the directory
 */
keys(): IterableIterator<string>;

/**
 * Returns an iterator over the values in the directory
 */
values(): IterableIterator<any>;

/**
 * Returns an iterator over key-value pairs in the directory
 */
entries(): IterableIterator<[string, any]>;

/**
 * Default iterator - same as entries()
 */
[Symbol.iterator](): IterableIterator<[string, any]>;

/**
 * Executes a provided function once for each key-value pair
 */
forEach(callbackfn: (value: any, key: string, map: Map<string, any>) => void, thisArg?: any): void;

Usage Examples:

// Iterate over keys and values in current directory
for (const key of appData.keys()) {
  console.log(`Key: ${key}`);
}

for (const value of appData.values()) {
  console.log(`Value:`, value);
}

// Iterate over entries
for (const [key, value] of appData) {
  console.log(`${key}: ${value}`);
}

// Use forEach
appData.forEach((value, key) => {
  console.log(`Processing ${key}: ${JSON.stringify(value)}`);
});

Map-Compatible Operations

SharedDirectory implements the complete Map interface for the current directory level.

/**
 * Returns true if the specified key exists in the directory
 */
has(key: string): boolean;

/**
 * Removes the specified key and its value from the directory
 */
delete(key: string): boolean;

/**
 * Removes all key-value pairs from the directory
 */
clear(): void;

/**
 * Returns the number of key-value pairs in the directory
 */
readonly size: number;

Usage Examples:

// Check key existence
if (appData.has("user-count")) {
  const count = appData.get("user-count");
}

// Delete specific keys
appData.delete("temporary-flag");

// Get directory size
console.log(`Directory contains ${appData.size} keys`);

// Clear all keys (but preserves subdirectories)
appData.clear();

Directory Events

SharedDirectory emits comprehensive events for both the root directory and individual subdirectories.

interface ISharedDirectoryEvents extends ISharedObjectEvents {
  /**
   * Emitted when a key is set or deleted anywhere in the directory hierarchy
   */
  (event: "valueChanged", listener: (changed: IDirectoryValueChanged, local: boolean, target: IEventThisPlaceHolder) => void): any;
  
  /**
   * Emitted when any directory in the hierarchy is cleared
   */
  (event: "clear", listener: (local: boolean, target: IEventThisPlaceHolder) => void): any;
  
  /**
   * Emitted when a subdirectory is created anywhere in the hierarchy
   */
  (event: "subDirectoryCreated", listener: (path: string, local: boolean, target: IEventThisPlaceHolder) => void): any;
  
  /**
   * Emitted when a subdirectory is deleted anywhere in the hierarchy
   */
  (event: "subDirectoryDeleted", listener: (path: string, local: boolean, target: IEventThisPlaceHolder) => void): any;
}

interface IDirectoryEvents extends IEvent {
  /**
   * Emitted when a key is set or deleted directly in this directory
   */
  (event: "containedValueChanged", listener: (changed: IValueChanged, local: boolean, target: IEventThisPlaceHolder) => void): any;
  
  /**
   * Emitted when a direct subdirectory is created
   */
  (event: "subDirectoryCreated", listener: (path: string, local: boolean, target: IEventThisPlaceHolder) => void): any;
  
  /**
   * Emitted when a direct subdirectory is deleted
   */
  (event: "subDirectoryDeleted", listener: (path: string, local: boolean, target: IEventThisPlaceHolder) => void): any;
  
  /**
   * Emitted when this directory is deleted
   */
  (event: "disposed", listener: (target: IEventThisPlaceHolder) => void): any;
  
  /**
   * Emitted when this previously deleted directory is restored
   */
  (event: "undisposed", listener: (target: IEventThisPlaceHolder) => void): any;
}

interface IDirectoryValueChanged extends IValueChanged {
  path: string;
}

Usage Examples:

// Listen for any value changes in the entire hierarchy
appData.on("valueChanged", (changed, local) => {
  console.log(`Value changed at path: ${changed.path}`);
  console.log(`Key: ${changed.key}, Previous: ${changed.previousValue}`);
  console.log(`Change was ${local ? "local" : "remote"}`);
});

// Listen for subdirectory creation
appData.on("subDirectoryCreated", (path, local) => {
  console.log(`New subdirectory created at: ${path}`);
});

// Listen for subdirectory deletion
appData.on("subDirectoryDeleted", (path, local) => {
  console.log(`Subdirectory deleted at: ${path}`);
});

// Listen for direct changes only (not in subdirectories)
const userSettings = appData.createSubDirectory("user-settings");
userSettings.on("containedValueChanged", (changed, local) => {
  console.log(`Direct change in user-settings: ${changed.key}`);
});

// Listen for directory disposal (when deleted)
userSettings.on("disposed", () => {
  console.log("User settings directory was deleted");
});

// Listen for directory restoration (rollback scenarios)
userSettings.on("undisposed", () => {
  console.log("User settings directory was restored");
});

Advanced Usage Patterns

Nested Directory Structure

// Create a complex nested structure
const appData = SharedDirectory.create(runtime, "app");

// User management
const users = appData.createSubDirectory("users");
const alice = users.createSubDirectory("alice");
alice.set("email", "alice@example.com");
alice.set("role", "admin");

const bob = users.createSubDirectory("bob");
bob.set("email", "bob@example.com");
bob.set("role", "user");

// Application settings
const settings = appData.createSubDirectory("settings");
const ui = settings.createSubDirectory("ui");
ui.set("theme", "dark");
ui.set("language", "en");

const api = settings.createSubDirectory("api");
api.set("endpoint", "https://api.example.com");
api.set("timeout", 5000);

// Navigate and modify
const aliceData = appData.getWorkingDirectory("users/alice");
aliceData?.set("last-login", new Date().toISOString());

const uiSettings = appData.getWorkingDirectory("settings/ui");
uiSettings?.set("sidebar-collapsed", false);

Bulk Operations

// Process all user directories
const usersDir = appData.getSubDirectory("users");
if (usersDir) {
  for (const [username, userDir] of usersDir.subdirectories()) {
    // Update all users
    userDir.set("last-updated", new Date().toISOString());
    
    // Process user-specific data
    const email = userDir.get("email");
    if (email && !email.includes("@")) {
      console.warn(`Invalid email for user: ${username}`);
    }
  }
}

Hierarchical Event Handling

// Set up comprehensive event monitoring
const appData = SharedDirectory.create(runtime, "app");

// Monitor all changes in the application
appData.on("valueChanged", (changed, local) => {
  // Log all value changes with their full path
  console.log(`[${local ? "LOCAL" : "REMOTE"}] ${changed.path}/${changed.key}: ${changed.previousValue} → ${appData.getWorkingDirectory(changed.path)?.get(changed.key)}`);
});

// Monitor structural changes
appData.on("subDirectoryCreated", (path, local) => {
  console.log(`[STRUCTURE] Directory created: ${path}`);
});

appData.on("subDirectoryDeleted", (path, local) => {
  console.log(`[STRUCTURE] Directory deleted: ${path}`);
});

// Set up specific monitoring for critical sections
const criticalData = appData.createSubDirectory("critical");
criticalData.on("containedValueChanged", (changed, local) => {
  // Alert on critical data changes
  console.warn(`CRITICAL DATA CHANGED: ${changed.key}`);
});

Value and Path Constraints

Value Constraints

Same as SharedMap - values must be JSON-serializable or Fluid Framework handles.

Path Constraints

  • Directory names must be valid strings
  • Paths use forward slashes / as separators
  • Absolute paths start with /
  • Relative paths are resolved from the current directory
  • Invalid path characters may cause navigation to fail

Conflict Resolution

SharedDirectory uses the same last-write-wins conflict resolution as SharedMap:

  • Directory structure changes (create/delete) use last-write-wins
  • Key-value operations within directories use last-write-wins
  • No automatic merging occurs for concurrent modifications