Skip to main content

CDN Plugin

Overview

The CDN plugin provides a centralized service for managing content delivery network operations within the Token Ring ecosystem. It offers a unified abstraction layer for CDN providers, enabling consistent file upload, download, delete, and existence check operations across different CDN implementations.

This package is designed as a Token Ring plugin that integrates with the application framework using the service registry pattern, allowing agents and other services to perform CDN operations through a consistent interface.

Key Features

  • Multi-Provider Support: Register and manage multiple CDN providers with a unified interface
  • Unified API: Consistent interface across different CDN implementations (AWS S3, Cloudflare R2, custom providers)
  • Core Operations: Upload, download, delete, and check file existence
  • Type-Safe Configuration: Zod-based schema validation for configuration
  • Service Registry: Built on Token Ring's KeyedRegistry for provider management
  • Plugin Integration: Seamless integration with Token Ring applications via service registry
  • Comprehensive Error Handling: Clear error messages for common scenarios
  • Flexible Configuration: Customizable options for different CDN providers
  • Default Implementations: Built-in HTTP-based download and exists operations

Core Components

CDNService

The main service class that manages CDN operations and provider registration. Implements the TokenRingService interface and is registered with the Token Ring application when the plugin is installed.

import CDNService from "@tokenring-ai/cdn/CDNService.ts";

const cdnService = new CDNService();

Methods

registerProvider

Register a CDN provider with a unique name.

cdnService.registerProvider('s3', new S3CDNProvider());

Parameters:

  • name (string): Unique identifier for the provider
  • provider (CDNProvider): Provider implementation instance
getCDNByName

Retrieves a registered CDN provider by name.

const provider = cdnService.getCDNByName('s3');

Parameters:

  • cdnName (string): Name of the registered CDN provider

Returns: The CDNProvider instance

Throws: Error if provider is not found

upload

Uploads data to a specific CDN provider.

const result = await cdnService.upload('s3', fileBuffer, {
filename: 'example.txt',
contentType: 'text/plain',
metadata: { author: 'test' }
});

Parameters:

  • cdnName (string): Name of the registered CDN provider
  • data (string | Buffer): File content as string or Buffer
  • options (UploadOptions): Optional upload parameters

Returns: Promise resolving to UploadResult

Throws: Error if no CDN provider is set or provider not found

delete

Deletes a file from a specific CDN provider.

const result = await cdnService.delete('s3', 'https://example.com/file.txt');

Parameters:

  • cdnName (string): Name of the registered CDN provider
  • url (string): URL of the file to delete

Returns: Promise resolving to DeleteResult

Throws: Error if CDN not found or delete method not supported

download

Downloads a file from a specific CDN provider.

const data = await cdnService.download('s3', 'https://example.com/file.txt');

Parameters:

  • cdnName (string): Name of the registered CDN provider
  • url (string): URL of the file to download

Returns: Promise resolving to Buffer containing file data

Throws: Error if CDN not found or download fails

exists

Checks if a file exists in a specific CDN provider.

const exists = await cdnService.exists('s3', 'https://example.com/file.txt');

Parameters:

  • cdnName (string): Name of the registered CDN provider
  • url (string): URL of the file to check

Returns: Promise resolving to boolean indicating file existence

Note: Returns false if CDN not found (does not throw)

CDNProvider

Abstract base class for implementing CDN providers. Subclass this to create custom provider implementations.

import CDNProvider from "@tokenring-ai/cdn/CDNProvider.ts";

class MyCDNProvider extends CDNProvider {
// Implementation here
}

Required Methods

upload

Implement upload logic for your CDN provider.

async upload(data: Buffer, options?: UploadOptions): Promise<UploadResult> {
// Implement your upload logic
const url = await this.uploadToCustomCDN(data, options);
return { url };
}

Parameters:

  • data (Buffer): File content as Buffer
  • options (UploadOptions): Optional upload parameters

Returns: Promise resolving to UploadResult with url, optional id, and metadata

Throws: Must be implemented by subclass

Optional Methods

delete

Delete a file from the CDN. Override this method if your provider supports deletion.

async delete(url: string): Promise<DeleteResult> {
const success = await this.deleteFromCustomCDN(url);
return { success };
}

Parameters:

  • url (string): URL of the file to delete

Returns: Promise resolving to DeleteResult

Default behavior: Throws "Method 'delete' must be implemented by subclasses"

download

Download a file from the CDN. Default implementation uses HTTP GET via fetch.

async download(url: string): Promise<Buffer> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to download file: ${response.statusText}`);
}
return Buffer.from(await response.arrayBuffer());
}

Parameters:

  • url (string): URL of the file to download

Returns: Promise resolving to Buffer containing file data

Throws: "Failed to download file" on HTTP errors

exists

Check if a file exists in the CDN. Default implementation uses HTTP HEAD via fetch.

async exists(url: string): Promise<boolean> {
try {
const response = await fetch(url, { method: 'HEAD' });
return response.ok;
} catch (error) {
return false;
}
}

Parameters:

  • url (string): URL of the file to check

Returns: Promise resolving to boolean

Note: Returns false on network errors

Type Definitions

UploadOptions

export interface UploadOptions {
filename?: string;
contentType?: string;
metadata?: Record<string, string>;
}

UploadResult

export interface UploadResult {
url: string;
id?: string;
metadata?: Record<string, any>;
}

DeleteResult

export interface DeleteResult {
success: boolean;
message?: string;
}

CDNConfigSchema

Zod schema for validating CDN configuration:

export const CDNConfigSchema = z.object({
providers: z.record(z.string(), z.any())
}).optional();

Configuration

Configure the CDN service through the Token Ring application configuration:

// In your app configuration
const config = {
cdn: {
providers: {
// Your CDN provider configurations here
}
}
};

Configuration Structure:

  • cdn.providers: Record of provider names to provider configurations
  • Each provider can define its own configuration schema

Example provider configuration:

{
cdn: {
providers: {
s3: {
bucket: 'my-bucket',
region: 'us-east-1',
// Provider-specific settings
},
cloudflare: {
accountId: 'my-account',
zoneId: 'my-zone',
// Provider-specific settings
}
}
}
}

Note: The plugin checks if config.cdn exists. If present, the CDNService is registered with the Token Ring application. The actual provider implementations are registered programmatically using registerProvider().

Plugin Integration

As a Token Ring plugin, the CDN service automatically:

  • Checks for config.cdn configuration in the app settings
  • Registers the CDNService with the Token Ring application when the plugin is installed
  • Makes the service available through the Token Ring application registry
  • Uses KeyedRegistry internally for provider management
import plugin from "@tokenring-ai/cdn/plugin.ts";

app.use(plugin, {
cdn: {
providers: {
// Provider configurations (optional)
}
}
});

// Access the CDN service from the app
const cdnService = app.getService('CDNService');

Usage Examples

Basic Integration

import TokenRingApp from "@tokenring-ai/app";
import CDNPlugin from "@tokenring-ai/cdn";

const app = new TokenRingApp();
app.use(CDNPlugin);
app.start();

Working with Multiple Providers

// Register multiple providers
cdnService.registerProvider('s3', new S3CDNProvider());
cdnService.registerProvider('cloudflare', new CloudflareCDNProvider());

// Upload to specific provider
const s3Result = await cdnService.upload('s3', fileBuffer);
const cloudflareResult = await cdnService.upload('cloudflare', fileBuffer);

// Download from specific provider
const s3Data = await cdnService.download('s3', s3Result.url);
const cloudflareData = await cdnService.download('cloudflare', cloudflareResult.url);

// Check if file exists
const s3Exists = await cdnService.exists('s3', s3Result.url);

Custom CDN Provider

import CDNProvider from "@tokenring-ai/cdn";
import type { UploadOptions, UploadResult, DeleteResult } from "@tokenring-ai/cdn/types";

class MyCustomCDNProvider extends CDNProvider {
async upload(data: Buffer, options?: UploadOptions): Promise<UploadResult> {
// Implement your upload logic
const url = await this.uploadToCustomCDN(data, options);
return {
url,
id: options?.filename,
metadata: options?.metadata
};
}

async delete?(url: string): Promise<DeleteResult> {
// Implement your delete logic
const success = await this.deleteFromCustomCDN(url);
return {
success,
message: success ? 'File deleted successfully' : 'Failed to delete file'
};
}
}

// Register the provider
cdnService.registerProvider('custom', new MyCustomCDNProvider());

Using Default Provider Implementations

CDNProvider provides default implementations for download and exists using fetch:

class HTTPCDNProvider extends CDNProvider {
async upload(data: Buffer, options?: UploadOptions): Promise<UploadResult> {
// Implement only upload - download and exists use defaults
const url = `https://my-cdn.com/${options?.filename || 'default.txt'}`;
return { url };
}
// download() uses default fetch implementation
// exists() uses default HEAD implementation
}

Error Handling

The CDN service provides clear error handling for common scenarios:

ScenarioError Message
Provider Not FoundCDN [name] not found. Please register it first with registerCDN(cdnName, cdnProvider).
No Active CDNNo active CDN set. Please set an active CDN before [operation].
Method Not ImplementedMethod '[method]' must be implemented by subclasses
Download FailuresFailed to download file: [statusText]
Configuration ErrorsValidation errors for invalid provider configurations
Delete Not SupportedActive CDN does not support deletion

Integration

Agent System

The CDNService is registered with the Token Ring agent system, allowing agents to perform CDN operations through the service registry. Agents can access the CDN service via app.getService('CDNService').

Utility Package

Uses @tokenring-ai/utility for the KeyedRegistry class which provides the provider management functionality.

Chat System

Integrates with the chat system to provide CDN operations as part of user interactions. Chat commands can access the CDN service through the agent system.

Monitoring and Debugging

  • Error Logging: All CDN operations log errors with relevant context
  • Provider Registry: Track registered providers and their status
  • Operation Tracking: Monitor upload, download, delete, and exists operations

Development

Testing

Run tests with:

bun run test
bun run test:coverage

Build

TypeScript type checking:

bun run build

Package Structure

pkg/cdn/
├── index.ts # Main exports and Zod schema
├── types.ts # TypeScript type definitions
├── CDNService.ts # Main CDN service implementation
├── CDNProvider.ts # Abstract CDN provider base class
├── plugin.ts # Token Ring plugin integration
├── package.json
├── LICENSE
└── test/ # Test files

License

MIT License - see LICENSE for details.