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:
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:
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:
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:
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:
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:
- Initialization: Create operation context with parameters
- Validation: Validate operation parameters
- Middleware Processing: Process through middleware pipeline
- Adapter Operation: Execute operation on the storage adapter
- Result Processing: Process operation results
- Event Emission: Emit events for the operation
- 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:
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
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
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
interface DeleteOptions {
// Delete behavior
failIfNotFound?: boolean
recursive?: boolean
// Versioning
softDelete?: boolean
// Authentication
authentication?: AuthenticationOptions
// Performance
timeout?: number
signal?: AbortSignal
}
ListOptions
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
interface ExistsOptions {
// Check behavior
checkMetadata?: boolean
// Authentication
authentication?: AuthenticationOptions
// Performance
timeout?: number
signal?: AbortSignal
}
Operation Patterns
Basic Operations
The simplest operation patterns:
// 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:
// 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:
// 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:
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:
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:
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:
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:
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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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)
})
)
}