Skip to content

Content Store API

The Content Store provides the main high-level API for working with content in the ReX system.

Overview

The Content Store serves as the primary entry point for content operations. It provides a consistent interface for reading, writing, and manipulating content, regardless of the underlying storage mechanism.

API Reference

ContentStore Interface

typescript
/**
 * Main interface for content operations
 */
interface ContentStore {
  /**
   * Read content from a URI
   * @param uri The content URI
   * @param options Read options
   * @returns Promise resolving to the content
   */
  read<T = string>(
    uri: string,
    options?: ContentReadOptions
  ): Promise<Content<T>>

  /**
   * Write content to a URI
   * @param uri The content URI
   * @param content The content to write
   * @param options Write options
   */
  write<T = string>(
    uri: string,
    content: Content<T>,
    options?: ContentWriteOptions
  ): Promise<void>

  /**
   * Delete content at a URI
   * @param uri The content URI
   * @param options Delete options
   */
  delete(uri: string, options?: ContentDeleteOptions): Promise<void>

  /**
   * List content matching a pattern
   * @param pattern Optional glob pattern to match
   * @param options List options
   * @returns Promise resolving to an array of matching URIs
   */
  list(pattern?: string, options?: ContentListOptions): Promise<string[]>

  /**
   * Check if content exists at a URI
   * @param uri The content URI
   * @param options Exists options
   * @returns Promise resolving to true if content exists
   */
  exists(uri: string, options?: ContentExistsOptions): Promise<boolean>

  /**
   * Register a change observer
   * @param observer The observer function to call when content changes
   * @param options Options for filtering observations
   * @returns Unsubscribe function
   */
  observe(
    observer: ContentChangeObserver,
    options?: ObserveOptions
  ): Unsubscribe

  /**
   * Register a change observer (legacy, prefer observe)
   * @param observer The change observer function
   * @returns Unsubscribe function
   * @deprecated Use observe() instead
   */
  onChange(observer: ContentChangeObserver): Unsubscribe

  /**
   * Get the underlying adapter
   */
  getAdapter(): ContentAdapter

  /**
   * Clean up resources
   */
  dispose(): Promise<void>
}

Creating a Content Store

typescript
/**
 * Options for creating a content store
 */
interface ContentStoreOptions {
  /**
   * Adapter to use for storage
   */
  adapter?: ContentAdapter

  /**
   * Factory function for creating an adapter
   */
  adapterFactory?: ContentAdapterFactory

  /**
   * Root URI prefix
   */
  root?: string

  /**
   * Whether to automatically create directories
   */
  createDirectories?: boolean

  /**
   * Default read options
   */
  defaultReadOptions?: ContentReadOptions

  /**
   * Default write options
   */
  defaultWriteOptions?: ContentWriteOptions

  /**
   * Default list options
   */
  defaultListOptions?: ContentListOptions

  /**
   * Initial content to populate the store with
   */
  initialContent?: ContentCollection
}

/**
 * Create a new content store
 * @param options Store options
 * @returns A new content store
 */
function createContentStore(options?: ContentStoreOptions): ContentStore {
  // Implementation details...
}

Environment-Specific Stores

typescript
/**
 * Create a content store optimized for the current environment
 * @param options Store options
 * @returns A new environment-specific content store
 */
function createEnvironmentStore(options?: ContentStoreOptions): ContentStore {
  // Implementation details...
}

/**
 * Create a memory-backed content store
 * @param options Store options
 * @returns A new memory content store
 */
function createMemoryStore(options?: ContentStoreOptions): ContentStore {
  // Implementation details...
}

/**
 * Create a filesystem-backed content store (Node.js only)
 * @param options Store options
 * @returns A new filesystem content store
 */
function createFileSystemStore(options?: ContentStoreOptions): ContentStore {
  // Implementation details...
}

/**
 * Create an IndexedDB-backed content store (Browser only)
 * @param options Store options
 * @returns A new IndexedDB content store
 */
function createIndexedDBStore(options?: ContentStoreOptions): ContentStore {
  // Implementation details...
}

Usage

Basic Usage

typescript
import { createContentStore } from '@lib/content'

// Create a default store
const store = createContentStore()

// Read content
const content = await store.read('posts/welcome.md')
console.log(content.data) // Markdown text
console.log(content.metadata.title) // "Welcome"

// Write content
await store.write('posts/new-post.md', {
  data: '# New Post\n\nThis is a new post.',
  contentType: 'text/markdown',
  metadata: {
    title: 'New Post',
    createdAt: new Date(),
  },
})

// Delete content
await store.delete('posts/old-post.md')

// List content
const posts = await store.list('posts/*.md')
console.log(posts) // ['posts/welcome.md', 'posts/new-post.md']

// Check if content exists
const exists = await store.exists('posts/welcome.md')
console.log(exists) // true

// Observe changes
const unsubscribe = store.observe(
  (uri, content, changeType) => {
    console.log(`${changeType} event for ${uri}`)
  },
  { pattern: 'posts/*.md' } // Optional filter by pattern
)

// Cleanup
unsubscribe() // Remove the observer
await store.dispose() // Release resources

Custom Adapter

typescript
import { createContentStore } from '@lib/content'
import { createFileSystemAdapter } from '@lib/content/adapters/node'

// Create a store with a specific adapter
const store = createContentStore({
  adapter: createFileSystemAdapter({
    root: '/path/to/content',
  }),
})

// Use the store
const content = await store.read('config.json')

Environment Detection

typescript
import { createEnvironmentStore } from '@lib/content'

// Create a store based on the current environment
const store = createEnvironmentStore({
  // These options work in any environment
  root: 'content',
  createDirectories: true,
  defaultReadOptions: {
    includeReferences: true,
  },
})

// In Node.js, this will use the filesystem adapter
// In a browser, this will use IndexedDB or memory adapter
const content = await store.read('welcome.md')

// Observe content changes across environments
const unsubscribe = store.observe(
  (uri, content, changeType) => {
    console.log(`Content ${changeType}: ${uri}`)
    updateUI(content)
  },
  {
    pattern: '**/*.md',
    types: [ContentChangeType.CREATED, ContentChangeType.UPDATED],
  }
)

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

Multiple Stores

typescript
import { createContentStore, createMemoryStore } from '@lib/content'

// Create stores for different purposes
const persistentStore = createContentStore()
const temporaryStore = createMemoryStore()

// Read from one store, modify, and save to another
const content = await persistentStore.read('template.md')

const modifiedContent = {
  ...content,
  data: content.data.replace('{{name}}', 'John'),
  metadata: {
    ...content.metadata,
    temporary: true,
  },
}

await temporaryStore.write('rendered.md', modifiedContent)

Store with Initial Content

typescript
import { createContentStore } from '@lib/content'

// Create a store with initial content
const store = createContentStore({
  initialContent: {
    'welcome.md': {
      data: '# Welcome\n\nWelcome to the content system.',
      contentType: 'text/markdown',
      metadata: {
        title: 'Welcome',
        createdAt: new Date(),
      },
    },
    'config.json': {
      data: '{"theme":"light","language":"en"}',
      contentType: 'application/json',
      metadata: {
        title: 'Configuration',
      },
    },
  },
})

// Content is already available
const content = await store.read('welcome.md')
console.log(content.metadata.title) // "Welcome"

Advanced Usage

Content Transformation

typescript
import { createContentStore } from '@lib/content'

const store = createContentStore()

// Read Markdown and transform to HTML
const content = await store.read('post.md', {
  transform: 'auto',
  targetContentType: 'text/html',
})

console.log(content.contentType) // "text/html"
console.log(content.data) // HTML content

Content References

typescript
import { createContentStore } from '@lib/content'

const store = createContentStore()

// Write content with references
await store.write('blog/post.md', {
  data: '# Post with Image\n\n![Image](../images/photo.jpg)',
  contentType: 'text/markdown',
  metadata: {
    title: 'Post with Image',
    references: [{ uri: 'images/photo.jpg', type: 'embed' }],
  },
})

// Read content with references
const content = await store.read('blog/post.md', {
  includeReferences: true,
})

// Access references
const references = content.metadata.references || []
for (const ref of references) {
  console.log(`Referenced ${ref.type}: ${ref.uri}`)
}

Batch Operations

typescript
import { createContentStore } from '@lib/content'

const store = createContentStore()

// Batch write multiple content items
async function importBatch(items) {
  for (const [uri, content] of Object.entries(items)) {
    await store.write(uri, content)
  }
}

// Batch read multiple content items
async function exportBatch(uris) {
  const result = {}
  for (const uri of uris) {
    try {
      result[uri] = await store.read(uri)
    } catch (error) {
      console.error(`Failed to read ${uri}: ${error.message}`)
    }
  }
  return result
}

Store Composition

typescript
import { createContentStore } from '@lib/content'
import { pipe, withLogging, withValidation } from '@lib/content/middleware'

// Create a store with enhanced behavior through composition
const store = pipe(
  createContentStore(),
  withLogging({ level: 'info' }),
  withValidation({
    schemas: {
      'posts/*.md': markdownSchema,
      'config.json': configSchema,
    },
  })
)

// Use the enhanced store
await store.write('posts/example.md', content) // Validates against markdownSchema

Error Handling

The Content Store API uses structured error types:

typescript
import { createContentStore } from '@lib/content'
import {
  ContentNotFoundError,
  ContentAccessError,
  ContentFormatError,
  ContentValidationError,
} from '@lib/content/errors'

const store = createContentStore()

try {
  await store.read('missing.md')
} catch (error) {
  if (error instanceof ContentNotFoundError) {
    console.error(`Not found: ${error.path}`)
  } else if (error instanceof ContentAccessError) {
    console.error(`Access denied: ${error.path}`)
  } else if (error instanceof ContentFormatError) {
    console.error(`Invalid format: ${error.message}`)
  } else if (error instanceof ContentValidationError) {
    console.error(`Validation failed: ${error.validationErrors.join(', ')}`)
  } else {
    console.error(`Unexpected error: ${error.message}`)
  }
}

Cleanup

Always dispose stores when they’re no longer needed:

typescript
import { createContentStore } from '@lib/content'

const store = createContentStore()

// Use the store...

// Clean up
await store.dispose()

Released under the MIT License.