Skip to content

Content Operations

Content operations are the core actions that can be performed on content within the system. This document explains the operation concepts, lifecycle, options, and patterns.

Core Operations

The content system provides five primary operations:

Read Operation

Retrieves content from storage:

typescript
const content = await contentStore.read('articles/welcome.md')

The read operation:

  • Locates content using the provided URI
  • Retrieves the content data from the storage adapter
  • Processes the content through the middleware pipeline
  • Returns a Content object with data, metadata, and contentType

Write Operation

Saves content to storage:

typescript
await contentStore.write('articles/welcome.md', {
  data: '# Welcome\n\nThis is a welcome article.',
  contentType: 'text/markdown',
  metadata: {
    title: 'Welcome',
    createdAt: new Date(),
    author: 'System',
  },
})

The write operation:

  • Validates the content object
  • Processes the content through the middleware pipeline
  • Converts the content to the appropriate storage format
  • Saves the content to the storage adapter
  • Emits change events

Delete Operation

Removes content from storage:

typescript
await contentStore.delete('articles/outdated.md')

The delete operation:

  • Verifies content exists (if required by options)
  • Processes the operation through the middleware pipeline
  • Removes the content from the storage adapter
  • Emits change events

List Operation

Lists available content matching a pattern:

typescript
const articleUris = await contentStore.list('articles/*.md')

The list operation:

  • Processes the operation through the middleware pipeline
  • Retrieves content URIs matching the pattern from the storage adapter
  • Returns an array of URIs

Exists Operation

Checks if content exists:

typescript
const exists = await contentStore.exists('articles/welcome.md')

The exists operation:

  • Processes the operation through the middleware pipeline
  • Checks if the content exists in the storage adapter
  • Returns a boolean indicating existence

Operation Lifecycle

Each operation follows a consistent lifecycle:

  1. Initialization: Create operation context with parameters
  2. Validation: Validate operation parameters
  3. Middleware Processing: Process through middleware pipeline
  4. Adapter Operation: Execute operation on the storage adapter
  5. Result Processing: Process operation results
  6. Event Emission: Emit events for the operation
  7. Return: Return operation results

This lifecycle ensures consistent behavior and extension points across operations.

Operation Context

Each operation creates a context object that flows through the system:

typescript
interface ContentContext {
  // Operation information
  operation: 'read' | 'write' | 'delete' | 'list' | 'exists'
  uri: string
  options?: OperationOptions

  // Content data (for write operations or after read)
  content?: Content

  // Results (for list operations)
  results?: string[]

  // Error information
  error?: Error

  // Middleware state
  state: Record<string, any>

  // Metadata
  startTime?: number
  endTime?: number
}

This context provides complete information about the operation and allows middleware to modify or enhance the operation.

Operation Options

Each operation accepts options to customize behavior:

ReadOptions

typescript
interface ReadOptions {
  // Format and transformation
  format?: string
  transformations?: ContentTransformation[]

  // Caching
  noCache?: boolean
  maxAge?: number

  // Error handling
  failIfNotFound?: boolean

  // Authentication
  authentication?: AuthenticationOptions

  // Performance
  timeout?: number
  signal?: AbortSignal
}

WriteOptions

typescript
interface WriteOptions {
  // Creation
  createDirectories?: boolean
  failIfExists?: boolean

  // Update
  overwrite?: boolean

  // Versioning
  createVersion?: boolean
  versionId?: string

  // Metadata
  updateMetadata?: boolean
  mergeMetadata?: boolean

  // Validation
  validate?: boolean
  schema?: ContentSchema

  // Authentication
  authentication?: AuthenticationOptions

  // Performance
  timeout?: number
  signal?: AbortSignal
}

DeleteOptions

typescript
interface DeleteOptions {
  // Delete behavior
  failIfNotFound?: boolean
  recursive?: boolean

  // Versioning
  softDelete?: boolean

  // Authentication
  authentication?: AuthenticationOptions

  // Performance
  timeout?: number
  signal?: AbortSignal
}

ListOptions

typescript
interface ListOptions {
  // Filtering
  recursive?: boolean
  depth?: number
  contentTypes?: string[]
  metadataFilter?: (metadata: ContentMetadata) => boolean

  // Sorting
  sortBy?: 'name' | 'createdAt' | 'updatedAt' | 'size'
  sortDirection?: 'asc' | 'desc'

  // Pagination
  limit?: number
  offset?: number

  // Performance
  timeout?: number
  signal?: AbortSignal
}

ExistsOptions

typescript
interface ExistsOptions {
  // Check behavior
  checkMetadata?: boolean

  // Authentication
  authentication?: AuthenticationOptions

  // Performance
  timeout?: number
  signal?: AbortSignal
}

Operation Patterns

Basic Operations

The simplest operation patterns:

typescript
// Basic read
const content = await store.read('articles/welcome.md')

// Basic write
await store.write('articles/welcome.md', {
  data: '# Welcome',
  contentType: 'text/markdown',
  metadata: { title: 'Welcome' },
})

// Basic delete
await store.delete('articles/outdated.md')

// Basic list
const uris = await store.list('articles/*.md')

// Basic exists
const exists = await store.exists('articles/welcome.md')

Conditional Operations

Operations with conditional behavior:

typescript
// Read with format transformation
const htmlContent = await store.read('articles/welcome.md', {
  format: 'html',
})

// Write only if not exists
try {
  await store.write('articles/welcome.md', newContent, {
    failIfExists: true,
  })
  console.log('Content created')
} catch (error) {
  console.log('Content already exists')
}

// Delete if exists
await store.delete('articles/maybe-exists.md', {
  failIfNotFound: false,
})

// List with filtering
const recentArticles = await store.list('articles/*.md', {
  metadataFilter: metadata => {
    const oneWeekAgo = new Date()
    oneWeekAgo.setDate(oneWeekAgo.getDate() - 7)
    return metadata.publishedAt > oneWeekAgo
  },
})

Bulk Operations

Operations on multiple content items:

typescript
// Batch read
const [article, sidebar, footer] = await Promise.all([
  store.read('articles/welcome.md'),
  store.read('components/sidebar.md'),
  store.read('components/footer.md'),
])

// Batch write
await Promise.all([
  store.write('articles/article1.md', article1),
  store.write('articles/article2.md', article2),
  store.write('articles/article3.md', article3),
])

// Recursive delete
await store.delete('articles', {
  recursive: true,
})

// List and process
const articleUris = await store.list('articles/*.md')
const articles = await Promise.all(articleUris.map(uri => store.read(uri)))

Transactional Operations

Atomic operations with rollback capability:

typescript
try {
  // Begin transaction
  await store.beginTransaction()

  // Multiple operations in transaction
  await store.write('articles/part1.md', part1Content)
  await store.write('articles/part2.md', part2Content)
  await store.write('index.md', indexContent)

  // Commit transaction
  await store.commitTransaction()
} catch (error) {
  // Rollback on error
  await store.rollbackTransaction()
  throw error
}

Operation Error Handling

Each operation can throw specific error types:

ContentNotFoundError

Thrown when content does not exist:

typescript
try {
  const content = await store.read('articles/missing.md')
} catch (error) {
  if (error instanceof ContentNotFoundError) {
    console.log(`Content not found: ${error.uri}`)
  }
}

ContentAccessError

Thrown when content cannot be accessed:

typescript
try {
  const content = await store.read('articles/restricted.md')
} catch (error) {
  if (error instanceof ContentAccessError) {
    console.log(`Access denied: ${error.message}`)
  }
}

ContentValidationError

Thrown when content fails validation:

typescript
try {
  await store.write('articles/invalid.md', invalidContent)
} catch (error) {
  if (error instanceof ContentValidationError) {
    console.log(`Validation failed: ${error.message}`)
    console.log('Validation errors:', error.validationErrors)
  }
}

ContentOperationError

Generic error for operation failures:

typescript
try {
  await store.write('articles/problem.md', content)
} catch (error) {
  if (error instanceof ContentOperationError) {
    console.log(`Operation failed: ${error.operation} on ${error.uri}`)
    console.log('Cause:', error.cause)
  }
}

Advanced Operation Concepts

Content Watching

Watch for changes to content:

typescript
// Watch for changes to all markdown files
const unsubscribe = store.watch('**/*.md', (uri, content, changeType) => {
  console.log(`Content ${changeType}: ${uri}`)

  if (content) {
    // Update application state
    updateState(uri, content)
  }
})

// Later, stop watching
unsubscribe()

The watch operation:

  • Sets up change observation for content matching the pattern
  • Calls the callback when content changes
  • Returns an unsubscribe function to stop watching

Content Transformation

Apply transformations during operations:

typescript
// Apply transformations during read
const content = await store.read('articles/welcome.md', {
  transformations: [extractTableOfContents, renderMarkdownToHtml, sanitizeHtml],
})

// Apply transformations during write
await store.write('articles/welcome.md', rawContent, {
  transformations: [validateFrontmatter, normalizeLinks, optimizeImages],
})

Content Versioning

Work with content versions:

typescript
// Write new version
await store.write('articles/welcome.md', updatedContent, {
  createVersion: true,
  versionId: 'v2',
})

// Read specific version
const v1Content = await store.read('articles/welcome.md', {
  version: 'v1',
})

// List versions
const versions = await store.listVersions('articles/welcome.md')

Content Metadata

Manipulate content metadata:

typescript
// Update only metadata
await store.updateMetadata('articles/welcome.md', {
  status: 'published',
  publishedAt: new Date(),
})

// Merge metadata
await store.write('articles/welcome.md', content, {
  mergeMetadata: true,
})

// Filter by metadata
const publishedArticles = await store.list('articles/*.md', {
  metadataFilter: metadata => metadata.status === 'published',
})

Operation Performance

Caching

Optimize read operations with caching:

typescript
// Cache read for 1 hour
const content = await store.read('articles/welcome.md', {
  maxAge: 3600000, // 1 hour in milliseconds
})

// Bypass cache
const freshContent = await store.read('articles/welcome.md', {
  noCache: true,
})

Partial Content

Work with partial content:

typescript
// Read only first 1000 characters
const preview = await store.read('articles/long-article.md', {
  range: { start: 0, end: 1000 },
})

// Read only specific sections
const sections = await store.readSections('articles/long-article.md', [
  'introduction',
  'conclusion',
])

Batch Processing

Process content in batches:

typescript
// Process content in batches
const allArticleUris = await store.list('articles/**/*.md')

// Process in batches of 10
for (let i = 0; i < allArticleUris.length; i += 10) {
  const batch = allArticleUris.slice(i, i + 10)
  await Promise.all(
    batch.map(async uri => {
      const content = await store.read(uri)
      // Process content
      const processed = processContent(content)
      await store.write(uri, processed)
    })
  )
}

Released under the MIT License.