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',
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()