@livestore-filesync/expo
v0.6.1
Published
Expo filesystem and image processing adapters for livestore-filesync
Downloads
824
Maintainers
Readme
@livestore-filesync/expo
Expo/React Native adapters for livestore-filesync. Provides filesystem and image processing implementations for mobile apps.
Features
- Effect Platform FileSystem - Full
@effect/platformFileSystem implementation usingexpo-file-system - Image Processor - URI-based image processor using
expo-image-manipulatorfor thumbnails and resizing - ExpoFile Wrapper - File-like wrapper that bridges URI-based files with the Web File API
Installation
pnpm add @livestore-filesync/expo @livestore-filesync/corePeer Dependencies
This package requires the following Expo packages to be installed in your app:
npx expo install expo-file-system expo-image-manipulatorUsage
FileSystem Adapter
The Expo FileSystem adapter implements the @effect/platform FileSystem interface, allowing livestore-filesync to work seamlessly on mobile:
import { initFileSync } from '@livestore-filesync/core'
import { layer as expoFileSystemLayer } from '@livestore-filesync/expo'
// Initialize with Expo filesystem (uses Documents directory by default)
const dispose = initFileSync(store, {
fileSystem: expoFileSystemLayer(),
remote: { signerBaseUrl: 'https://api.example.com' }
})Custom Base Directory
By default, files are stored in Paths.document (persistent storage). You can customize this:
import { layer as expoFileSystemLayer } from '@livestore-filesync/expo'
import { Paths } from 'expo-file-system'
// Use cache directory instead (can be cleared by OS)
const dispose = initFileSync(store, {
fileSystem: expoFileSystemLayer({ baseDirectory: Paths.cache }),
remote: { signerBaseUrl: 'https://api.example.com' }
})Image Processor
The Expo image processor uses expo-image-manipulator for native image processing:
import { createExpoImageProcessor } from '@livestore-filesync/expo'
const processor = createExpoImageProcessor()
await processor.init()
// Process a single image
const result = await processor.process('file:///path/to/image.jpg', {
maxDimension: 1500,
format: 'jpeg',
quality: 85
})
console.log(result.uri) // file:///path/to/processed.jpg
console.log(result.width) // 1500
console.log(result.height) // 1000
console.log(result.mimeType) // image/jpeg
// Process multiple sizes at once
const thumbnails = await processor.processMultiple(
'file:///path/to/image.jpg',
{ small: 200, medium: 800, large: 1500 },
{ format: 'jpeg', quality: 80 }
)Format Support
| Format | iOS | Android | |--------|-----|---------| | JPEG | Yes | Yes | | PNG | Yes | Yes | | WebP | No* | Yes |
*WebP encoding is only supported on Android. On iOS, requesting WebP will automatically fall back to JPEG.
ExpoFile Wrapper
The ExpoFile class provides a Web File-like interface for URI-based files, enabling compatibility with FilePreprocessor and other APIs that expect the standard File API:
import { ExpoFile } from '@livestore-filesync/expo'
// Create from a file URI
const file = ExpoFile.fromUri('file:///path/to/image.jpg', {
type: 'image/jpeg' // Optional, inferred from extension if not provided
})
// Use with any API expecting a File
const buffer = await file.arrayBuffer()
const text = await file.text()
const bytes = await file.bytes()
// Create from bytes (writes to cache directory)
const newFile = await ExpoFile.fromBytes(
uint8Array,
'output.jpg',
'image/jpeg'
)
console.log(newFile.uri) // file:///path/to/cache/output.jpgFile Properties
file.uri // The file:// URI
file.name // Filename
file.type // MIME type
file.size // File size in bytes (0 if unknown, use getSize() for accurate size)
file.lastModified // Last modified timestampAPI Reference
FileSystem Layer
// Default layer (uses Paths.document)
export const layerDefault: Layer.Layer<FileSystem>
// Configurable layer
export function layer(options?: ExpoFileSystemOptions): Layer.Layer<FileSystem>
interface ExpoFileSystemOptions {
baseDirectory?: string // Base directory for all operations
}Image Processor
export function createExpoImageProcessor(
options?: ExpoImageProcessorOptions
): UriImageProcessor
interface ExpoImageProcessorOptions {
defaultQuality?: number // Default quality 0-100 (default: 90)
}
interface UriImageProcessor {
readonly type: 'uri'
readonly capabilities: ImageProcessorCapabilities
init(): Promise<void>
isInitialized(): boolean
process(inputUri: string, options: ProcessImageOptions): Promise<ProcessedImageUri>
processMultiple(
inputUri: string,
sizes: Record<string, number>,
options: Omit<ProcessImageOptions, 'maxDimension'>
): Promise<Record<string, ProcessedImageUri>>
}
interface ProcessImageOptions {
maxDimension: number
format: 'jpeg' | 'webp' | 'png'
quality?: number // 0-100
}
interface ProcessedImageUri {
uri: string
width: number
height: number
mimeType: string
}ExpoFile
export class ExpoFile implements Blob {
readonly uri: string
readonly name: string
readonly type: string
readonly size: number
readonly lastModified: number
constructor(uri: string, name: string, type: string, options?: {
lastModified?: number
size?: number
})
// Async methods
arrayBuffer(): Promise<ArrayBuffer>
bytes(): Promise<Uint8Array>
text(): Promise<string>
getSize(): Promise<number> // Accurate size (reads from filesystem if needed)
stream(): ReadableStream<Uint8Array>
// Static factories
static fromUri(uri: string, options?: {
name?: string
type?: string
lastModified?: number
size?: number
}): ExpoFile
static fromBytes(
bytes: Uint8Array,
name: string,
type: string,
options?: { cacheDir?: string }
): Promise<ExpoFile>
}Supported FileSystem Operations
| Operation | Supported | Notes | |-----------|-----------|-------| | readFile / writeFile | Yes | | | readFileString / writeFileString | Yes | | | exists | Yes | | | makeDirectory | Yes | Recursive by default | | readDirectory | Yes | Supports recursive option | | remove | Yes | Supports recursive and force options | | copy / copyFile | Yes | | | rename | Yes | Uses move internally | | stat | Yes | Returns size, type, mtime, birthtime | | makeTempFile / makeTempDirectory | Yes | Uses cache directory | | chmod / chown | No-op | Not supported on mobile | | link / symlink / readLink | Error | Not supported on mobile | | watch | Error | Not supported by expo-file-system | | open (file handles) | Error | Use readFile/writeFile instead |
Limitations
- No file watching -
expo-file-systemdoesn't support filesystem watching - No symlinks/hardlinks - Not available on mobile platforms
- No permissions - chmod/chown are no-ops
- WebP on iOS - WebP encoding falls back to JPEG on iOS
- File handles - The
open()method is not implemented; usereadFile/writeFileinstead
License
MIT
