The configuration system provides flexible options for customizing loader behavior and integrating with various Fluid Framework services.
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;
}codeLoader: Loads code packages for containersdocumentServiceFactory: Creates document service instances for storage operationsurlResolver: Resolves Fluid URLs to storage locationsconfigProvider: Provides runtime configuration valueslogger: Telemetry logger for monitoring and debuggingoptions: Additional loader behavior optionsprotocolHandlerBuilder: Custom protocol handler factoryscope: Dependency injection scope for servicesInterface 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;
}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);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);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
}
};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
}
};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
};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()
};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()
};// 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");
}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;
}// 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();
}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);