CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fluidframework--map

Distributed data structures for collaborative applications providing SharedMap and SharedDirectory implementations with real-time synchronization and last-write-wins conflict resolution

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

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

docs

events.md

index.md

legacy-factories.md

shared-directory.md

shared-map.md

tile.json