Skip to content

Key Concepts

This document provides an overview of the key concepts in ReX’s content system. Understanding these concepts is essential for working effectively with the system.

Core Concepts

Content Model

The foundation of ReX is the unified Content model, which has three main components:

typescript
interface Content<T = string> {
  // The actual content data (generic to support different representations)
  data: T

  // MIME type of the content
  contentType: string

  // Metadata about the content
  metadata: ContentMetadata
}

interface ContentMetadata {
  // Standard metadata fields
  title?: string
  description?: string
  createdAt?: Date
  updatedAt?: Date

  // Custom metadata (extensible)
  [key: string]: any
}

The Content<T> interface uses a generic type parameter to support different content representations:

  • Content<string>: Text-based content (default)
  • Content<Buffer>: Binary content
  • Content<object>: Structured data
  • Content<T>: Custom data types

URI System

All content is identified by a URI (Uniform Resource Identifier), not a path or ID. URIs provide a consistent addressing system across different storage backends:

scheme:[//authority]/path[?query][#fragment]

Examples:

  • content:blog/posts/hello-world.md: Content in the default store
  • file:/content/blog/posts/hello-world.md: File system content
  • memory:temporary/draft.md: In-memory content
  • http://api.example.com/content/post-123: Remote content

Content Operations

The system provides five core content operations:

  1. Read: Retrieve content by URI
  2. Write: Create or update content at a URI
  3. Delete: Remove content from a URI
  4. List: Find content URIs matching a pattern
  5. Exists: Check if content exists at a URI

In addition, content changes can be observed:

  1. Observe: Register an observer for content changes

Architectural Concepts

Compositional Architecture

ReX uses a compositional architecture based on function composition rather than inheritance. This approach:

  • Builds complex behavior from simple, focused functions
  • Promotes reuse through composition
  • Creates a flexible, adaptable system

Example:

typescript
// Create base store
const baseStore = createContentStore({ adapter })

// Enhance with middleware
const enhancedStore = pipe(
  baseStore,
  withValidation(schema),
  withCaching({ maxAge: 3600 }),
  withLogging({ level: 'info' })
)

Layer Boundaries

The system is organized into distinct layers with clear responsibilities:

  1. Integration Layer: React hooks, components, API surface
  2. Content Facade Layer: ContentStore, Registry interfaces
  3. Enhancement Layer: Middleware, composition functions
  4. Storage Adapter Layer: Environment-specific storage implementations
  5. Foundation Layer: Types, errors, utilities

Each layer has well-defined boundaries and interfaces, enhancing modularity and maintainability.

Middleware Pattern

The middleware pattern enables extensibility through function composition:

typescript
type Middleware<T = ContentContext> = (
  context: T,
  next: () => Promise<T>
) => Promise<T>

Middleware functions:

  • Receive a context object with operation details
  • Can modify the context before passing to the next middleware
  • Can process the result after the next middleware completes
  • Can short-circuit the chain (not calling next)

Common middleware includes:

  • Validation: Validate content against schemas
  • Caching: Cache content for performance
  • Logging: Log operations for debugging
  • Transformation: Transform content during operations

Environment Adaptability

The system adapts to different runtime environments:

  • Node.js: Full filesystem access, server operations
  • Browser: IndexedDB/localStorage, fetch API
  • Service Worker: Offline capabilities, background sync

Environment detection allows the system to use the appropriate implementations automatically.

Storage Adapters

Storage adapters abstract the details of specific storage backends:

  • FileSystemAdapter: Node.js filesystem storage
  • MemoryAdapter: In-memory storage (all environments)
  • IndexedDBAdapter: Browser persistent storage
  • HttpAdapter: Remote content via fetch API
  • ServiceWorkerAdapter: Offline-capable storage

Each adapter implements the common ContentAdapter interface but may provide additional capabilities.

Event System

Content Change Notification

The system uses an observer pattern for content change notifications:

typescript
// Register observer
const unsubscribe = store.observe((uri, content, changeType) => {
  // Handle change notification
  console.log(`${changeType} event for ${uri}`)
}, options)

// Later, unsubscribe to prevent memory leaks
unsubscribe()

Change types include:

  • created: Content was created
  • updated: Content was updated
  • deleted: Content was deleted
  • moved: Content was moved (URI changed)

Observer Options

Observation can be filtered by pattern and change type:

typescript
store.observe(observer, {
  // Only observe content matching this pattern
  pattern: 'blog/**/*.md',

  // Only observe these change types
  types: ['created', 'updated'],
})

Error Handling

The system uses specialized error classes for different scenarios:

  • ContentError: Base error class for all content errors
  • ContentNotFoundError: Content doesn’t exist
  • ContentAccessError: Permission issues accessing content
  • ContentFormatError: Invalid content format
  • ContentValidationError: Content failed validation

Errors include context for troubleshooting:

typescript
try {
  await store.read('non-existent.md')
} catch (error) {
  if (error instanceof ContentNotFoundError) {
    console.log(`Content not found at URI: ${error.uri}`)
  }
}

Environment Support

Isomorphic Design

The system is designed to work consistently across environments:

  • Same API interface in all environments
  • Environment-specific implementations are abstracted
  • Capability detection for feature availability

Capability Detection

Rather than environment detection, the system uses capability detection:

typescript
import { hasFileSystemAccess, hasIndexedDB } from '@ReX/core/utils/env'

// Choose adapter based on capabilities
const adapter = hasFileSystemAccess()
  ? createFileSystemAdapter({ baseDir: './content' })
  : hasIndexedDB()
    ? createIndexedDBAdapter({ dbName: 'content' })
    : createMemoryAdapter()

This approach focuses on what the environment can do rather than what it is.

Next Steps

After understanding these key concepts:

  1. Explore the Architecture Overview for system design details
  2. Read the Getting Started guide for practical examples
  3. Review specific API details in the Reference documentation
  4. Check the Glossary for standardized terminology

Released under the MIT License.