Development utilities for URL resolution and optional compression support for storage operations. Includes compression configuration and factory adapters.
⚠️ WARNING: InsecureUrlResolver is intended for development and testing scenarios only. Do not use in production environments.
/**
* Simple URL resolver for development and testing scenarios
* WARNING: Not for production use - provides minimal security
*/
class InsecureUrlResolver implements IUrlResolver {
/**
* @param hostUrl - Base host URL for the service
* @param ordererUrl - URL for the ordering service
* @param storageUrl - URL for the storage service
* @param deltaStreamUrl - URL for delta stream connections
* @param tenantId - Tenant identifier
* @param bearer - Authentication bearer token
* @param isForNodeTest - Whether this is for Node.js testing (optional)
*/
constructor(
hostUrl: string,
ordererUrl: string,
storageUrl: string,
deltaStreamUrl: string,
tenantId: string,
bearer: string,
isForNodeTest?: boolean
);
/**
* Resolve a request to a resolved URL
* @param request - Request to resolve
* @returns Promise resolving to resolved URL structure
*/
resolve(request: IRequest): Promise<IResolvedUrl>;
/**
* Get absolute URL from resolved URL and relative path
* @param resolvedUrl - Previously resolved URL
* @param relativeUrl - Relative URL to append
* @returns Promise resolving to absolute URL string
*/
getAbsoluteUrl(resolvedUrl: IResolvedUrl, relativeUrl: string): Promise<string>;
/**
* Create request for creating a new document
* @param fileName - Optional file name for the new document
* @returns Request object for document creation
*/
createCreateNewRequest(fileName?: string): IRequest;
}Usage Example (Development Only):
import { InsecureUrlResolver } from "@fluidframework/driver-utils";
// ⚠️ DEVELOPMENT/TESTING ONLY
const resolver = new InsecureUrlResolver(
"http://localhost:7070", // host
"http://localhost:7070", // orderer
"http://localhost:7070", // storage
"ws://localhost:7070", // delta stream
"test-tenant", // tenant ID
"test-token", // bearer token
true // for Node.js testing
);
// Resolve a document URL
const resolvedUrl = await resolver.resolve({
url: "fluid://localhost/test-container",
headers: {}
});
// Create new document request
const createRequest = resolver.createCreateNewRequest("my-document");
const newDocUrl = await resolver.resolve(createRequest);Configuration and utilities for enabling compression in storage operations.
/**
* Available compression algorithms
*/
enum SummaryCompressionAlgorithm {
None = 0,
LZ4 = 1
}
/**
* Configuration for storage compression
*/
interface ICompressionStorageConfig {
/** Compression algorithm to use */
algorithm: SummaryCompressionAlgorithm;
/** Minimum size in bytes before compression is applied */
minSizeToCompress: number;
}
/**
* Default compression configuration with LZ4 algorithm
*/
const DefaultCompressionStorageConfig: ICompressionStorageConfig;
/**
* Blob name for compression metadata headers
*/
const blobHeadersBlobName: string;
/**
* Applies compression to document service factory based on configuration
* @param documentServiceFactory - Factory to wrap with compression
* @param config - Compression configuration or boolean (true = default config)
* @returns Wrapped factory with compression support
*/
function applyStorageCompression(
documentServiceFactory: IDocumentServiceFactory,
config?: ICompressionStorageConfig | boolean
): IDocumentServiceFactory;Usage Examples:
import {
applyStorageCompression,
SummaryCompressionAlgorithm,
ICompressionStorageConfig,
blobHeadersBlobName
} from "@fluidframework/driver-utils";
// Apply default compression
const compressedFactory = applyStorageCompression(
originalFactory,
true // Use default settings
);
// Apply custom compression configuration
const customConfig: ICompressionStorageConfig = {
algorithm: SummaryCompressionAlgorithm.LZ4,
minSizeToCompress: 1024 // Only compress files > 1KB
};
const customCompressedFactory = applyStorageCompression(
originalFactory,
customConfig
);
// Disable compression
const uncompressedFactory = applyStorageCompression(
originalFactory,
false
);
// Check for compression headers blob
if (snapshotTree.blobs[blobHeadersBlobName]) {
console.log("Snapshot contains compression metadata");
}import { InsecureUrlResolver } from "@fluidframework/driver-utils";
class DevEnvironmentSetup {
static createDevResolver(
port: number = 7070,
tenantId: string = "dev-tenant"
): InsecureUrlResolver {
const baseUrl = `http://localhost:${port}`;
const wsUrl = `ws://localhost:${port}`;
return new InsecureUrlResolver(
baseUrl, // host
baseUrl, // orderer
baseUrl, // storage
wsUrl, // delta stream
tenantId, // tenant
"dev-token", // bearer token
process.env.NODE_ENV === 'test'
);
}
static async createDevDocument(
resolver: InsecureUrlResolver,
containerName: string
): Promise<IResolvedUrl> {
const createRequest = resolver.createCreateNewRequest(containerName);
return await resolver.resolve(createRequest);
}
static async resolveDevDocument(
resolver: InsecureUrlResolver,
documentId: string
): Promise<IResolvedUrl> {
return await resolver.resolve({
url: `fluid://localhost/${documentId}`,
headers: {}
});
}
}
// Usage in development
const devResolver = DevEnvironmentSetup.createDevResolver(7070);
const newDoc = await DevEnvironmentSetup.createDevDocument(devResolver, "test-doc");
const existingDoc = await DevEnvironmentSetup.resolveDevDocument(devResolver, "existing-doc-id");import {
applyStorageCompression,
SummaryCompressionAlgorithm,
ICompressionStorageConfig
} from "@fluidframework/driver-utils";
class CompressionStrategyManager {
static readonly STRATEGIES = {
// No compression - fastest but largest
none: {
algorithm: SummaryCompressionAlgorithm.None,
minSizeToCompress: Number.MAX_SAFE_INTEGER
},
// Light compression - good for text-heavy documents
light: {
algorithm: SummaryCompressionAlgorithm.LZ4,
minSizeToCompress: 512 // 512 bytes
},
// Standard compression - balanced performance/size
standard: {
algorithm: SummaryCompressionAlgorithm.LZ4,
minSizeToCompress: 1024 // 1KB
},
// Aggressive compression - best size but slower
aggressive: {
algorithm: SummaryCompressionAlgorithm.LZ4,
minSizeToCompress: 256 // 256 bytes
}
} as const;
static applyStrategy(
factory: IDocumentServiceFactory,
strategyName: keyof typeof CompressionStrategyManager.STRATEGIES
): IDocumentServiceFactory {
const config = this.STRATEGIES[strategyName];
return applyStorageCompression(factory, config);
}
static createAdaptiveFactory(
factory: IDocumentServiceFactory,
networkSpeed: 'fast' | 'medium' | 'slow' = 'medium'
): IDocumentServiceFactory {
const strategyMap = {
fast: 'light', // Prioritize speed
medium: 'standard', // Balanced
slow: 'aggressive' // Prioritize bandwidth savings
} as const;
return this.applyStrategy(factory, strategyMap[networkSpeed]);
}
static measureCompressionEffectiveness(
originalSize: number,
compressedSize: number
): CompressionMetrics {
const compressionRatio = originalSize / compressedSize;
const spaceSavings = ((originalSize - compressedSize) / originalSize) * 100;
return {
originalSize,
compressedSize,
compressionRatio,
spaceSavingsPercent: spaceSavings,
worthwhileCompression: spaceSavings > 20 // >20% savings
};
}
}
interface CompressionMetrics {
originalSize: number;
compressedSize: number;
compressionRatio: number;
spaceSavingsPercent: number;
worthwhileCompression: boolean;
}
// Usage
const factory = CompressionStrategyManager.applyStrategy(
baseFactory,
'standard'
);
const adaptiveFactory = CompressionStrategyManager.createAdaptiveFactory(
baseFactory,
'slow' // Optimize for slow networks
);
const metrics = CompressionStrategyManager.measureCompressionEffectiveness(
10240, // 10KB original
3072 // 3KB compressed
);
console.log(`Compression saved ${metrics.spaceSavingsPercent.toFixed(1)}%`);import { InsecureUrlResolver } from "@fluidframework/driver-utils";
class MultiEnvironmentUrlResolver {
private resolvers = new Map<string, InsecureUrlResolver>();
private currentEnvironment: string = 'development';
constructor() {
this.setupEnvironments();
}
private setupEnvironments(): void {
// Development environment
this.resolvers.set('development', new InsecureUrlResolver(
'http://localhost:7070',
'http://localhost:7070',
'http://localhost:7070',
'ws://localhost:7070',
'dev-tenant',
'dev-token',
false
));
// Testing environment
this.resolvers.set('testing', new InsecureUrlResolver(
'http://localhost:8080',
'http://localhost:8080',
'http://localhost:8080',
'ws://localhost:8080',
'test-tenant',
'test-token',
true
));
// Local integration environment
this.resolvers.set('integration', new InsecureUrlResolver(
'http://integration.local:9090',
'http://integration.local:9090',
'http://integration.local:9090',
'ws://integration.local:9090',
'integration-tenant',
'integration-token',
false
));
}
setEnvironment(env: string): void {
if (!this.resolvers.has(env)) {
throw new Error(`Unknown environment: ${env}`);
}
this.currentEnvironment = env;
}
getCurrentResolver(): InsecureUrlResolver {
const resolver = this.resolvers.get(this.currentEnvironment);
if (!resolver) {
throw new Error(`No resolver configured for environment: ${this.currentEnvironment}`);
}
return resolver;
}
async resolve(request: IRequest): Promise<IResolvedUrl> {
const resolver = this.getCurrentResolver();
return await resolver.resolve(request);
}
async createNewDocument(fileName?: string): Promise<IResolvedUrl> {
const resolver = this.getCurrentResolver();
const createRequest = resolver.createCreateNewRequest(fileName);
return await resolver.resolve(createRequest);
}
addCustomEnvironment(
name: string,
hostUrl: string,
tenantId: string,
bearerToken: string
): void {
const resolver = new InsecureUrlResolver(
hostUrl, hostUrl, hostUrl,
hostUrl.replace('http', 'ws'),
tenantId, bearerToken, false
);
this.resolvers.set(name, resolver);
}
get availableEnvironments(): string[] {
return Array.from(this.resolvers.keys());
}
get currentEnvironmentName(): string {
return this.currentEnvironment;
}
}
// Usage
const multiResolver = new MultiEnvironmentUrlResolver();
// Switch environments
multiResolver.setEnvironment('testing');
const testDoc = await multiResolver.createNewDocument('test-document');
multiResolver.setEnvironment('development');
const devDoc = await multiResolver.resolve({
url: 'fluid://localhost/my-document',
headers: {}
});
// Add custom environment
multiResolver.addCustomEnvironment(
'staging',
'https://staging.example.com',
'staging-tenant',
'staging-auth-token'
);
multiResolver.setEnvironment('staging');
console.log(`Available environments: ${multiResolver.availableEnvironments.join(', ')}`);import { applyStorageCompression } from "@fluidframework/driver-utils";
class CompressionPerformanceMonitor {
private metrics = new Map<string, CompressionMetric[]>();
wrapFactoryWithMonitoring(
factory: IDocumentServiceFactory,
config: ICompressionStorageConfig,
label: string = 'default'
): IDocumentServiceFactory {
const compressedFactory = applyStorageCompression(factory, config);
// Wrap with monitoring (simplified implementation)
return {
...compressedFactory,
createDocumentService: async (...args) => {
const service = await compressedFactory.createDocumentService(...args);
return this.wrapServiceWithMonitoring(service, label);
}
};
}
private wrapServiceWithMonitoring(
service: IDocumentService,
label: string
): IDocumentService {
return {
...service,
connectToStorage: async () => {
const storage = await service.connectToStorage();
return this.wrapStorageWithMonitoring(storage, label);
}
};
}
private wrapStorageWithMonitoring(
storage: IDocumentStorageService,
label: string
): IDocumentStorageService {
return {
...storage,
createBlob: async (file: ArrayBufferLike) => {
const start = performance.now();
const originalSize = file.byteLength;
const result = await storage.createBlob(file);
const duration = performance.now() - start;
this.recordMetric(label, {
operation: 'createBlob',
originalSize,
compressedSize: file.byteLength, // Simplified
duration,
timestamp: Date.now()
});
return result;
}
};
}
private recordMetric(label: string, metric: CompressionMetric): void {
if (!this.metrics.has(label)) {
this.metrics.set(label, []);
}
const metrics = this.metrics.get(label)!;
metrics.push(metric);
// Keep only last 1000 metrics
if (metrics.length > 1000) {
metrics.shift();
}
}
getMetrics(label: string): CompressionSummary | null {
const metrics = this.metrics.get(label);
if (!metrics || metrics.length === 0) {
return null;
}
const totalOriginal = metrics.reduce((sum, m) => sum + m.originalSize, 0);
const totalCompressed = metrics.reduce((sum, m) => sum + m.compressedSize, 0);
const totalDuration = metrics.reduce((sum, m) => sum + m.duration, 0);
return {
label,
operationCount: metrics.length,
totalOriginalBytes: totalOriginal,
totalCompressedBytes: totalCompressed,
totalDurationMs: totalDuration,
averageDurationMs: totalDuration / metrics.length,
compressionRatio: totalOriginal / totalCompressed,
spaceSavingsPercent: ((totalOriginal - totalCompressed) / totalOriginal) * 100
};
}
getAllMetrics(): Map<string, CompressionSummary> {
const summaries = new Map<string, CompressionSummary>();
for (const label of this.metrics.keys()) {
const summary = this.getMetrics(label);
if (summary) {
summaries.set(label, summary);
}
}
return summaries;
}
clearMetrics(label?: string): void {
if (label) {
this.metrics.delete(label);
} else {
this.metrics.clear();
}
}
}
interface CompressionMetric {
operation: string;
originalSize: number;
compressedSize: number;
duration: number;
timestamp: number;
}
interface CompressionSummary {
label: string;
operationCount: number;
totalOriginalBytes: number;
totalCompressedBytes: number;
totalDurationMs: number;
averageDurationMs: number;
compressionRatio: number;
spaceSavingsPercent: number;
}