or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdcontainer-lifecycle.mdindex.mdprotocol-quorum.mdutilities.md
tile.json

configuration.mddocs/

Configuration

The configuration system provides flexible options for customizing loader behavior and integrating with various Fluid Framework services.

Core Configuration Interfaces

ILoaderProps

The main configuration interface for creating a Loader instance.

interface ILoaderProps {
  readonly codeLoader: ICodeDetailsLoader;
  readonly configProvider?: IConfigProviderBase;
  readonly documentServiceFactory: IDocumentServiceFactory;
  readonly logger?: ITelemetryBaseLogger;
  readonly options?: ILoaderOptions;
  readonly protocolHandlerBuilder?: ProtocolHandlerBuilder;
  readonly scope?: FluidObject;
  readonly urlResolver: IUrlResolver;
}

Required Properties

  • codeLoader: Loads code packages for containers
  • documentServiceFactory: Creates document service instances for storage operations
  • urlResolver: Resolves Fluid URLs to storage locations

Optional Properties

  • configProvider: Provides runtime configuration values
  • logger: Telemetry logger for monitoring and debugging
  • options: Additional loader behavior options
  • protocolHandlerBuilder: Custom protocol handler factory
  • scope: Dependency injection scope for services

ILoaderServices

Interface representing the services available from a configured loader.

interface ILoaderServices {
  readonly codeLoader: ICodeDetailsLoader;
  readonly documentServiceFactory: IDocumentServiceFactory;
  readonly options: ILoaderOptions;
  readonly protocolHandlerBuilder?: ProtocolHandlerBuilder;
  readonly scope: FluidObject;
  readonly subLogger: ITelemetryLoggerExt;
  readonly urlResolver: IUrlResolver;
}

Configuration Examples

Basic Configuration

import { Loader } from "@fluidframework/container-loader";
import type { ILoaderProps } from "@fluidframework/container-loader";

const loaderProps: ILoaderProps = {
  codeLoader: myCodeLoader,
  documentServiceFactory: myDocumentServiceFactory,
  urlResolver: myUrlResolver
};

const loader = new Loader(loaderProps);

Advanced Configuration with All Options

import { Loader } from "@fluidframework/container-loader";
import type { 
  ILoaderProps, 
  ProtocolHandlerBuilder,
  ILoaderOptions 
} from "@fluidframework/container-loader";

const protocolHandlerBuilder: ProtocolHandlerBuilder = (
  attributes,
  snapshot,
  sendProposal
) => {
  return new CustomProtocolHandler(attributes, snapshot, sendProposal);
};

const loaderOptions: ILoaderOptions = {
  maxClientLeaveWaitTime: 30000,
  noopCountFrequency: 100,
  noopTimeFrequency: 30000,
  // Additional options...
};

const advancedLoaderProps: ILoaderProps = {
  // Required services
  codeLoader: myCodeLoader,
  documentServiceFactory: myDocumentServiceFactory,
  urlResolver: myUrlResolver,
  
  // Optional configuration
  configProvider: myConfigProvider,
  logger: myTelemetryLogger,
  options: loaderOptions,
  protocolHandlerBuilder: protocolHandlerBuilder,
  scope: myDependencyScope
};

const loader = new Loader(advancedLoaderProps);

// Access configured services
const services = loader.services;
console.log("Configured options:", services.options);
console.log("Sub-logger available:", !!services.subLogger);

Configuration for Different Environments

Development Configuration

import { ConsoleLogger } from "@fluidframework/telemetry-utils";

const devLoaderProps: ILoaderProps = {
  codeLoader: myCodeLoader,
  documentServiceFactory: localDevServiceFactory,
  urlResolver: localUrlResolver,
  logger: new ConsoleLogger(), // Verbose logging for debugging
  options: {
    // Development-friendly options
    maxClientLeaveWaitTime: 5000, // Shorter timeouts
    enableOfflineLoad: true
  }
};

Production Configuration

import { createChildLogger } from "@fluidframework/telemetry-utils";

const prodLoaderProps: ILoaderProps = {
  codeLoader: myCodeLoader,
  documentServiceFactory: productionServiceFactory,
  urlResolver: productionUrlResolver,
  logger: createChildLogger({ 
    namespace: "FluidLoader",
    properties: { environment: "production" }
  }),
  configProvider: productionConfigProvider,
  options: {
    // Production-optimized options
    maxClientLeaveWaitTime: 30000,
    enableOfflineLoad: false,
    // Performance and reliability settings
  }
};

Service Integration Patterns

Custom Document Service Factory

import type { IDocumentServiceFactory } from "@fluidframework/driver-definitions";

class CustomDocumentServiceFactory implements IDocumentServiceFactory {
  public readonly protocolName = "custom";
  
  async createDocumentService(resolvedUrl, logger, clientDetails) {
    // Custom implementation for your storage backend
    return new CustomDocumentService(resolvedUrl, logger, clientDetails);
  }
  
  async createContainer(createNewSummary, resolvedUrl, logger, clientDetails) {
    // Custom container creation logic
    return this.createDocumentService(resolvedUrl, logger, clientDetails);
  }
}

const loaderProps: ILoaderProps = {
  codeLoader: myCodeLoader,
  documentServiceFactory: new CustomDocumentServiceFactory(),
  urlResolver: myUrlResolver
};

Custom URL Resolver

import type { IUrlResolver } from "@fluidframework/driver-definitions";

class CustomUrlResolver implements IUrlResolver {
  async resolve(request) {
    // Custom URL resolution logic
    const resolvedUrl = {
      type: "fluid",
      id: extractIdFromUrl(request.url),
      url: transformToStorageUrl(request.url),
      tokens: {},
      endpoints: {
        deltaStorageUrl: "https://my-delta-service.com",
        storageUrl: "https://my-storage-service.com"
      }
    };
    
    return resolvedUrl;
  }
  
  async getAbsoluteUrl(resolvedUrl, relativeUrl) {
    // Handle relative URL resolution
    return `${resolvedUrl.url}/${relativeUrl}`;
  }
}

const loaderProps: ILoaderProps = {
  codeLoader: myCodeLoader,
  documentServiceFactory: myDocumentServiceFactory,
  urlResolver: new CustomUrlResolver()
};

Configuration Provider Integration

import type { IConfigProviderBase } from "@fluidframework/core-interfaces";

class AppConfigProvider implements IConfigProviderBase {
  getRawFeatureGates() {
    return {
      "Fluid.Container.enableOfflineLoad": true,
      "Fluid.Container.maxReconnectDelayMs": 30000
    };
  }
  
  getNumber(name: string): number | undefined {
    const value = this.getRawFeatureGates()[name];
    return typeof value === "number" ? value : undefined;
  }
  
  getBoolean(name: string): boolean | undefined {
    const value = this.getRawFeatureGates()[name];
    return typeof value === "boolean" ? value : undefined;
  }
  
  getString(name: string): string | undefined {
    const value = this.getRawFeatureGates()[name];
    return typeof value === "string" ? value : undefined;
  }
}

const loaderProps: ILoaderProps = {
  codeLoader: myCodeLoader,
  documentServiceFactory: myDocumentServiceFactory,
  urlResolver: myUrlResolver,
  configProvider: new AppConfigProvider()
};

Accessing Configuration at Runtime

// After creating a loader, access configured services
const loader = new Loader(loaderProps);
const services = loader.services;

// Access specific services
const documentService = await services.documentServiceFactory
  .createDocumentService(resolvedUrl, services.subLogger);

// Use sub-logger for consistent telemetry
services.subLogger.sendTelemetryEvent({
  eventName: "ContainerLoadAttempt",
  properties: { containerId: "my-container" }
});

// Access loader options
if (services.options.enableOfflineLoad) {
  console.log("Offline loading is enabled");
}

Configuration Best Practices

Error Handling

try {
  const loader = new Loader(loaderProps);
  const container = await loader.resolve(request);
} catch (error) {
  // Handle configuration or loading errors
  if (error.errorType === "fluidInvalidSchema") {
    console.error("Invalid configuration schema:", error);
  } else if (error.errorType === "cannotConnect") {
    console.error("Cannot connect to service:", error);
  }
  throw error;
}

Resource Cleanup

// Loaders and their services should be properly disposed
const loader = new Loader(loaderProps);

// When shutting down
if (loader.services.documentServiceFactory.dispose) {
  loader.services.documentServiceFactory.dispose();
}

// Clean up any custom resources
if (myCustomService.cleanup) {
  await myCustomService.cleanup();
}

Configuration Validation

function validateLoaderProps(props: ILoaderProps): void {
  if (!props.codeLoader) {
    throw new Error("codeLoader is required");
  }
  if (!props.documentServiceFactory) {
    throw new Error("documentServiceFactory is required");
  }
  if (!props.urlResolver) {
    throw new Error("urlResolver is required");
  }
  
  // Additional validation as needed
  if (props.options?.maxClientLeaveWaitTime && 
      props.options.maxClientLeaveWaitTime < 0) {
    throw new Error("maxClientLeaveWaitTime must be non-negative");
  }
}

// Use validation before creating loader
validateLoaderProps(loaderProps);
const loader = new Loader(loaderProps);