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.
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 IDEvery 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"SharedDirectory supports all standard Map operations for storing and retrieving key-value pairs within each directory level.
/**
* 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;/**
* 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);SharedDirectory provides methods for creating, accessing, and managing 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/**
* 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/**
* 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");
}/**
* 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");
}/**
* 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`);SharedDirectory provides methods for navigating the directory hierarchy using relative paths.
/**
* 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); // undefinedSharedDirectory provides methods for iterating over subdirectories and their contents.
/**
* 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];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)}`);
});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();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");
});// 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);// 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}`);
}
}
}// 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}`);
});Same as SharedMap - values must be JSON-serializable or Fluid Framework handles.
/ as separators/SharedDirectory uses the same last-write-wins conflict resolution as SharedMap: