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

legacy-factories.mddocs/

Legacy Factory Classes

Legacy factory classes provide direct control over SharedMap and SharedDirectory creation and loading. These classes are primarily used for advanced integration scenarios and backward compatibility. For most applications, the modern SharedMap and SharedDirectory factory functions are recommended.

Capabilities

MapFactory

Factory class for creating and loading SharedMap instances with full control over the creation process.

/**
 * Factory class for ISharedMap instances
 * @deprecated Consider using SharedMap factory function instead
 */
class MapFactory implements IChannelFactory<ISharedMap> {
  /** Channel type identifier for SharedMap */
  static readonly Type: "https://graph.microsoft.com/types/map";
  
  /** Default channel attributes for SharedMap */
  static readonly Attributes: IChannelAttributes;
  
  /** Instance type property */
  get type(): string;
  
  /** Instance attributes property */
  get attributes(): IChannelAttributes;
  
  /**
   * Create a new SharedMap instance
   * @param runtime - Fluid data store runtime
   * @param id - Unique identifier for the map
   * @returns New ISharedMap instance
   */
  create(runtime: IFluidDataStoreRuntime, id: string): ISharedMap;
  
  /**
   * Load an existing SharedMap instance from storage
   * @param runtime - Fluid data store runtime
   * @param id - Unique identifier for the map
   * @param services - Channel services for loading
   * @param attributes - Channel attributes
   * @returns Promise resolving to loaded ISharedMap instance
   */
  load(
    runtime: IFluidDataStoreRuntime,
    id: string,
    services: IChannelServices,
    attributes: IChannelAttributes
  ): Promise<ISharedMap>;
}

/**
 * Channel attributes interface
 */
interface IChannelAttributes {
  type: string;
  snapshotFormatVersion: string;
  packageVersion: string;
}

/**
 * Channel factory interface
 */
interface IChannelFactory<T = IFluidLoadable> {
  readonly type: string;
  readonly attributes: IChannelAttributes;
  load(runtime: IFluidDataStoreRuntime, id: string, services: IChannelServices, channelAttributes: Readonly<IChannelAttributes>): Promise<T>;
  create(document: IFluidDataStoreRuntime, id: string): T;
}

Usage Examples:

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

// Access factory metadata
console.log("Map type:", MapFactory.Type);
console.log("Map attributes:", MapFactory.Attributes);

// Create a new map using the factory
const factory = new MapFactory();
const myMap = factory.create(runtime, "my-map-id");

// The factory approach gives you the same ISharedMap interface
myMap.set("key1", "value1");
myMap.set("key2", { nested: "object" });

console.log("Map type check:", factory.type === MapFactory.Type); // true

// Load an existing map from storage (advanced scenario)
const loadedMap = await factory.load(
  runtime,
  "existing-map-id",
  channelServices,
  MapFactory.Attributes
);

DirectoryFactory

Factory class for creating and loading SharedDirectory instances with full control over the creation process.

/**
 * Factory class for ISharedDirectory instances
 * @deprecated Consider using SharedDirectory factory function instead
 */
class DirectoryFactory implements IChannelFactory<ISharedDirectory> {
  /** Channel type identifier for SharedDirectory */
  static readonly Type: "https://graph.microsoft.com/types/directory";
  
  /** Default channel attributes for SharedDirectory */
  static readonly Attributes: IChannelAttributes;
  
  /** Instance type property */
  get type(): string;
  
  /** Instance attributes property */
  get attributes(): IChannelAttributes;
  
  /**
   * Create a new SharedDirectory instance
   * @param runtime - Fluid data store runtime
   * @param id - Unique identifier for the directory
   * @returns New ISharedDirectory instance
   */
  create(runtime: IFluidDataStoreRuntime, id: string): ISharedDirectory;
  
  /**
   * Load an existing SharedDirectory instance from storage
   * @param runtime - Fluid data store runtime
   * @param id - Unique identifier for the directory
   * @param services - Channel services for loading
   * @param attributes - Channel attributes
   * @returns Promise resolving to loaded ISharedDirectory instance
   */
  load(
    runtime: IFluidDataStoreRuntime,
    id: string,
    services: IChannelServices,
    attributes: IChannelAttributes
  ): Promise<ISharedDirectory>;
}

Usage Examples:

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

// Access factory metadata
console.log("Directory type:", DirectoryFactory.Type);
console.log("Directory attributes:", DirectoryFactory.Attributes);

// Create a new directory using the factory
const factory = new DirectoryFactory();
const myDirectory = factory.create(runtime, "my-directory-id");

// Use the directory normally
myDirectory.set("root-key", "root-value");
const subDir = myDirectory.createSubDirectory("subdirectory");
subDir.set("sub-key", "sub-value");

console.log("Directory type check:", factory.type === DirectoryFactory.Type); // true

// Load an existing directory from storage (advanced scenario)
const loadedDirectory = await factory.load(
  runtime,
  "existing-directory-id",
  channelServices,
  DirectoryFactory.Attributes
);

Factory Metadata

Both factory classes provide static metadata that identifies the channel type and format information.

// MapFactory metadata
MapFactory.Type === "https://graph.microsoft.com/types/map";
MapFactory.Attributes.type === "https://graph.microsoft.com/types/map";
MapFactory.Attributes.snapshotFormatVersion === "0.2";
MapFactory.Attributes.packageVersion; // Current package version

// DirectoryFactory metadata  
DirectoryFactory.Type === "https://graph.microsoft.com/types/directory";
DirectoryFactory.Attributes.type === "https://graph.microsoft.com/types/directory";
DirectoryFactory.Attributes.snapshotFormatVersion === "0.1";
DirectoryFactory.Attributes.packageVersion; // Current package version

Usage Examples:

// Compare factory types
const mapFactory = new MapFactory();
const dirFactory = new DirectoryFactory();

console.log("Factories have different types:", mapFactory.type !== dirFactory.type);

// Check version compatibility
function isCompatibleVersion(attributes: IChannelAttributes): boolean {
  const currentVersion = MapFactory.Attributes.packageVersion;
  return attributes.packageVersion === currentVersion;
}

// Use metadata for registration or configuration
const channelRegistry = new Map<string, IChannelFactory>();
channelRegistry.set(MapFactory.Type, new MapFactory());
channelRegistry.set(DirectoryFactory.Type, new DirectoryFactory());

function createChannelFromType(type: string, runtime: IFluidDataStoreRuntime, id: string) {
  const factory = channelRegistry.get(type);
  if (!factory) {
    throw new Error(`Unknown channel type: ${type}`);
  }
  return factory.create(runtime, id);
}

Advanced Usage Patterns

Custom Factory Registration

import { MapFactory, DirectoryFactory } from "@fluidframework/map";

// Register factories in a Fluid Framework application
class DataStoreFactory {
  private readonly channelFactories = new Map<string, IChannelFactory>();
  
  constructor() {
    // Register the map and directory factories
    this.channelFactories.set(MapFactory.Type, new MapFactory());
    this.channelFactories.set(DirectoryFactory.Type, new DirectoryFactory());
  }
  
  createChannel(type: string, runtime: IFluidDataStoreRuntime, id: string) {
    const factory = this.channelFactories.get(type);
    if (!factory) {
      throw new Error(`No factory registered for type: ${type}`);
    }
    return factory.create(runtime, id);
  }
  
  async loadChannel(
    type: string,
    runtime: IFluidDataStoreRuntime,
    id: string,
    services: IChannelServices
  ) {
    const factory = this.channelFactories.get(type);
    if (!factory) {
      throw new Error(`No factory registered for type: ${type}`);
    }
    return factory.load(runtime, id, services, factory.attributes);
  }
  
  getSupportedTypes(): string[] {
    return Array.from(this.channelFactories.keys());
  }
}

// Usage
const dataStoreFactory = new DataStoreFactory();
console.log("Supported types:", dataStoreFactory.getSupportedTypes());

// Create channels by type
const map = dataStoreFactory.createChannel(MapFactory.Type, runtime, "map1");
const directory = dataStoreFactory.createChannel(DirectoryFactory.Type, runtime, "dir1");

Factory-Based Loading with Error Handling

async function loadOrCreateChannel<T>(
  factory: IChannelFactory<T>,
  runtime: IFluidDataStoreRuntime,
  id: string,
  services?: IChannelServices
): Promise<T> {
  if (services) {
    try {
      // Try to load existing channel
      return await factory.load(runtime, id, services, factory.attributes);
    } catch (error) {
      console.warn(`Failed to load channel ${id}, creating new one:`, error);
    }
  }
  
  // Create new channel if loading failed or no services provided
  return factory.create(runtime, id);
}

// Usage examples
const mapFactory = new MapFactory();
const dirFactory = new DirectoryFactory();

// Load or create a map
const myMap = await loadOrCreateChannel(mapFactory, runtime, "persistent-map", services);

// Load or create a directory
const myDir = await loadOrCreateChannel(dirFactory, runtime, "persistent-dir", services);

Version Compatibility Checking

function checkCompatibility(
  factory: IChannelFactory,
  loadedAttributes: IChannelAttributes
): boolean {
  // Check type compatibility
  if (factory.attributes.type !== loadedAttributes.type) {
    console.error("Type mismatch:", factory.attributes.type, "vs", loadedAttributes.type);
    return false;
  }
  
  // Check version compatibility
  if (factory.attributes.snapshotFormatVersion !== loadedAttributes.snapshotFormatVersion) {
    console.warn("Snapshot format version mismatch:", 
      factory.attributes.snapshotFormatVersion, "vs", loadedAttributes.snapshotFormatVersion);
    // Might still be compatible, depending on your versioning strategy
  }
  
  // Check package version (example: allow loading from same major version)
  const factoryVersion = factory.attributes.packageVersion.split('.')[0];
  const loadedVersion = loadedAttributes.packageVersion.split('.')[0];
  
  if (factoryVersion !== loadedVersion) {
    console.warn("Major version mismatch:", factoryVersion, "vs", loadedVersion);
    return false;
  }
  
  return true;
}

// Usage in loading logic
async function safeLoadChannel<T>(
  factory: IChannelFactory<T>,
  runtime: IFluidDataStoreRuntime,
  id: string,
  services: IChannelServices,
  loadedAttributes: IChannelAttributes
): Promise<T | null> {
  if (!checkCompatibility(factory, loadedAttributes)) {
    console.error(`Cannot load channel ${id} due to compatibility issues`);
    return null;
  }
  
  try {
    return await factory.load(runtime, id, services, loadedAttributes);
  } catch (error) {
    console.error(`Failed to load channel ${id}:`, error);
    return null;
  }
}

Migration from Legacy Factories

If you're currently using legacy factory classes, here's how to migrate to the modern approach:

Before (Legacy Factory Approach)

import { MapFactory, DirectoryFactory } from "@fluidframework/map";

// Old approach
const mapFactory = new MapFactory();
const myMap = mapFactory.create(runtime, "my-map");

const dirFactory = new DirectoryFactory();
const myDir = dirFactory.create(runtime, "my-dir");

After (Modern Factory Functions)

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

// New approach
const myMap = SharedMap.create(runtime, "my-map");
const myDir = SharedDirectory.create(runtime, "my-dir");

Benefits of Modern Approach

  1. Simpler API: Direct function calls instead of factory instantiation
  2. Better TypeScript Support: Improved type inference and checking
  3. Reduced Boilerplate: Less code needed for common operations
  4. Future-Proof: Active development focuses on modern patterns

When to Use Legacy Factories

Legacy factories are still useful for:

  • Advanced Integration: When you need direct control over factory lifecycle
  • Custom Registration: When building factory registries or plugin systems
  • Backward Compatibility: When maintaining existing code that depends on factory patterns
  • Metadata Access: When you need to inspect factory metadata or attributes
  • Complex Loading Scenarios: When implementing custom loading logic with error handling

Type Definitions

/**
 * Core types used by factory classes
 */

// Runtime and service interfaces (re-exported from Fluid Framework)
interface IFluidDataStoreRuntime {
  // Runtime interface for creating and managing channels
  readonly id: string;
  readonly connected: boolean;
  // ... additional runtime properties and methods
}

interface IChannelServices {
  // Services provided to channels for persistence and communication
  readonly deltaConnection: IDeltaConnection;
  readonly objectStorage: IChannelStorageService;
  // ... additional service properties
}

interface IFluidLoadable {
  // Base interface for loadable Fluid objects
  readonly url: string;
  // ... additional loadable properties
}

// Legacy serialization interfaces
interface ISerializableValue {
  type: string;
  value: any;
}

interface ICreateInfo {
  /** Sequence number at which this subdirectory was created */
  csn: number;
  /** Client IDs of the clients which created this subdirectory */
  ccIds: string[];
}

interface IDirectoryDataObject {
  ci?: ICreateInfo;
  storage?: Record<string, ISerializableValue>;
  subdirectories?: Record<string, IDirectoryDataObject>;
}

interface IDirectoryNewStorageFormat {
  blobs: string[];
  content: IDirectoryDataObject;
}

Best Practices

Factory Lifecycle Management

  • Create factory instances once and reuse them
  • Don't create new factory instances for each channel creation
  • Cache factory instances in registries or dependency injection containers

Error Handling

  • Always handle loading failures gracefully
  • Implement fallback strategies for compatibility issues
  • Log factory operations for debugging and monitoring

Performance Considerations

  • Factory creation is lightweight, but channel creation/loading can be expensive
  • Consider lazy loading patterns for channels that aren't immediately needed
  • Cache loaded channels appropriately to avoid redundant operations

Legacy Code Migration

  • Migrate gradually from factory classes to modern factory functions
  • Maintain backward compatibility during transition periods
  • Test thoroughly when switching between factory patterns