@workspace-fs/core
v1.0.8
Published
Multi-project workspace manager for Firesystem with support for multiple sources
Maintainers
Readme
@firesystem/workspace
Revolutionary Multi-Project Workspace Manager - The first file system workspace that enables multiple projects running simultaneously in parallel, breaking the traditional "one project = one filesystem" paradigm. Unlike IDEs that switch between projects, Firesystem Workspace keeps ALL projects active and accessible at once.
🚀 Why This Changes Everything
Traditional development environments work with one active project at a time:
// ❌ Traditional approach (VSCode, etc.)
await closeProject("frontend"); // Lost from memory
await openProject("backend"); // Only this is accessible
await closeProject("backend"); // Lost from memory
await openProject("database"); // Only this is accessibleFiresystem Workspace revolutionizes this:
// ✅ Revolutionary approach - ALL PROJECTS ACTIVE SIMULTANEOUSLY
const frontend = workspace.getProject("frontend"); // Always accessible
const backend = workspace.getProject("backend"); // Always accessible
const database = workspace.getProject("database"); // Always accessible
// Cross-project operations without switching context
const config = await backend.fs.readFile("/config/database.json");
await database.fs.writeFile("/import/backend-config.json", config);
await frontend.fs.writeFile("/src/api-config.json", config);
// One project focused for UI, but ALL remain active
workspace.setActiveProject("frontend"); // Just changes UI focus🌟 Revolutionary Features
🔥 Multi-Project Parallelism
- Multiple file systems active simultaneously - not just "open tabs"
- Cross-project operations - copy/sync data between different storage types
- Zero context switching - all projects remain in memory and accessible
- Focus vs Active - UI shows one project, but all remain operational
🧩 Provider-Agnostic Architecture
- Workspace knows NOTHING about specific implementations
- Completely extensible - anyone can create providers for any storage
- Zero dependencies - workspace has no file system implementations
- Capability-aware - each provider declares what it can/cannot do
⚡ Intelligent Resource Management
- Hot enable/disable - pause projects for performance without losing config
- Automatic cleanup - orphaned references removed on startup
- Memory optimization - smart resource management for hundreds of projects
- Persistent state - everything survives restarts
🗑️ Advanced Project Lifecycle
- Complete deletion support - remove projects with optional data cleanup
- Provider-aware deletion - knows which providers store local vs remote data
- Confirmation events - intercept and cancel deletions programmatically
- Storage estimation - see how much space will be freed before deleting
📦 Installation
npm install @firesystem/workspace @firesystem/core
# or
yarn add @firesystem/workspace @firesystem/core
# or
pnpm add @firesystem/workspace @firesystem/core🚀 Quick Start
Basic Multi-Project Setup
import { WorkspaceFileSystem } from "@firesystem/workspace";
import { memoryProvider } from "@firesystem/memory/provider";
import { indexedDBProvider } from "@firesystem/indexeddb/provider";
// Create workspace and register providers
const workspace = new WorkspaceFileSystem();
workspace.registerProvider(memoryProvider);
workspace.registerProvider(indexedDBProvider);
// Initialize (restores previous state automatically)
await workspace.initialize();
// Load multiple projects simultaneously
const devProject = await workspace.loadProject({
id: "dev-env",
name: "Development Environment",
source: {
type: "memory",
config: {
initialData: {
"/src/app.ts": { content: "console.log('dev');" },
"/config/dev.json": { content: JSON.stringify({ env: "development" }) }
}
}
}
});
const prodBackup = await workspace.loadProject({
id: "prod-backup",
name: "Production Backup",
source: {
type: "indexeddb",
config: { dbName: "production-backup" }
}
});
// ALL PROJECTS ARE NOW ACTIVE AND ACCESSIBLE!
console.log("Active projects:", workspace.getProjects().length); // 2
// Cross-project operations (the revolutionary part!)
const prodConfig = await prodBackup.fs.readFile("/config/production.json");
await devProject.fs.writeFile("/config/prod-copy.json", prodConfig.content);
// Work with the focused project transparently
workspace.setActiveProject("dev-env");
await workspace.writeFile("/test.js", "// This goes to dev-env");
// But other projects remain accessible
await prodBackup.fs.writeFile("/logs/import.log", "Config copied to dev");🎯 Core Concepts
1. Multi-Project Parallelism
Unlike traditional workspaces that handle one project at a time:
// Traditional: Only one project accessible
const currentProject = getCurrentProject(); // Only this exists
// Firesystem: All projects accessible simultaneously
const projects = workspace.getProjects(); // All active projects
const dev = workspace.getProject("dev");
const staging = workspace.getProject("staging");
const prod = workspace.getProject("prod");
// Work with all at once
const devConfig = await dev.fs.readFile("/config.json");
const stagingData = await staging.fs.glob("**/*.log");
await prod.fs.writeFile("/backup/dev-config.json", devConfig.content);2. Focus vs Active States
- Active Projects: Loaded in memory, consuming resources, ready for operations
- Focused Project: The project shown in UI (File Explorer), receives direct operations
- Disabled Projects: Configuration preserved but not loaded, zero resource usage
// Load multiple projects - all become active
await workspace.loadProject({ id: "app", source: { type: "memory" } });
await workspace.loadProject({ id: "docs", source: { type: "indexeddb" } });
await workspace.loadProject({ id: "assets", source: { type: "s3" } });
// Set focus for UI - others remain active
await workspace.setActiveProject("app"); // File Explorer shows "app"
// Direct access to any project
const docs = workspace.getProject("docs");
const assets = workspace.getProject("assets");
// Proxy operations go to focused project
await workspace.writeFile("/test.txt", "content"); // Goes to "app"
// Direct operations go to specific project
await docs.fs.writeFile("/readme.md", "# Documentation");
await assets.fs.writeFile("/logo.png", imageData);3. Performance Optimization
// Disable projects for performance (keeps config)
await workspace.disableProject("large-dataset"); // Frees memory, keeps config
// Re-enable when needed
await workspace.enableProject("large-dataset"); // Reconstructs from config
// Batch operations
await workspace.disableProjects(["old-1", "old-2", "old-3"]);
await workspace.enableProjects(["needed-1", "needed-2"]);
// Auto-optimization settings
const workspace = new WorkspaceFileSystem({
settings: {
maxActiveProjects: 5, // Auto-disable least recently used
autoDisableAfter: 30 * 60 * 1000, // 30 minutes of inactivity
keepFocusedActive: true // Never auto-disable focused project
}
});4. Project Deletion
Delete projects with full control over data cleanup:
// Delete project configuration only (preserves data)
await workspace.deleteProject("temp-project");
// Delete project AND its data
await workspace.deleteProject("old-project", {
deleteData: true
});
// With confirmation event
workspace.events.on("project:delete-confirm", (event) => {
if (!confirm(`Delete ${event.project.name}?`)) {
event.cancelled = true; // Cancel deletion
}
});
// Skip confirmation for automation
await workspace.deleteProject("temp-project", {
skipConfirmation: true,
deleteData: true
});🔌 Provider System
The workspace is completely agnostic about file systems. Providers encapsulate all implementation knowledge:
Available Providers
| Provider | Package | Type | Description |
|----------|---------|------|-------------|
| Memory | @firesystem/memory/provider | "memory" | In-memory, fast iteration |
| IndexedDB | @firesystem/indexeddb/provider | "indexeddb" | Browser persistent storage |
| S3* | @firesystem/s3/provider | "s3" | AWS S3 cloud storage |
| GitHub* | @firesystem/github/provider | "github" | GitHub repository |
| API* | @firesystem/api/provider | "api" | REST API backend |
*Coming soon
Registering Providers
import { WorkspaceFileSystem } from "@firesystem/workspace";
import { memoryProvider } from "@firesystem/memory/provider";
import { indexedDBProvider } from "@firesystem/indexeddb/provider";
const workspace = new WorkspaceFileSystem();
// Register only what you need
workspace.registerProvider(memoryProvider);
workspace.registerProvider(indexedDBProvider);
// Provider capabilities are automatically detected
const memoryCapabilities = workspace.getProvider("memory")?.getCapabilities();
console.log(memoryCapabilities.supportsWatch); // true
console.log(memoryCapabilities.readonly); // falseCreating Custom Providers
import type { SourceProvider } from "@firesystem/workspace";
import { withEvents } from "@firesystem/core";
class DropboxProvider implements SourceProvider {
readonly scheme = "dropbox";
readonly displayName = "Dropbox Storage";
async createFileSystem(config: DropboxConfig) {
const baseFs = new DropboxFileSystem(config);
return withEvents(baseFs); // Add reactive events
}
getCapabilities() {
return {
readonly: false,
caseSensitive: false,
atomicRename: true,
supportsWatch: false, // Dropbox limitation
supportsMetadata: true,
maxFileSize: 150 * 1024 * 1024 // 150MB
};
}
async validateConfiguration(config: any) {
const errors = [];
if (!config.accessToken) errors.push("Access token required");
return { valid: errors.length === 0, errors };
}
// Optional: Data management methods
deleteProjectData?(config: DropboxConfig): Promise<void> {
// Remove Dropbox app folder if needed
}
hasLocalData(): boolean {
return false; // Dropbox is cloud storage
}
async estimateDataSize(config: DropboxConfig): Promise<number | null> {
// Could query Dropbox API for folder size
return null;
}
}
// Register and use
workspace.registerProvider(new DropboxProvider());
await workspace.loadProject({
id: "my-dropbox",
name: "Dropbox Files",
source: {
type: "dropbox",
config: { accessToken: "...", path: "/projects" }
}
});🔄 Cross-Project Operations
The revolutionary feature that enables true multi-project workflows:
Copy Files Between Projects
// Load different storage types
const memory = await workspace.loadProject({
id: "temp",
source: { type: "memory", config: {} }
});
const persistent = await workspace.loadProject({
id: "storage",
source: { type: "indexeddb", config: { dbName: "app-data" } }
});
// Copy files between different storage types
await workspace.copyFiles(
"temp", // Source project
"/work/*.json", // Source pattern
"storage", // Target project
"/backup/" // Target directory
);Sync Projects
// Sync entire project from S3 to local IndexedDB
await workspace.syncProjects("s3-backup", "local-cache", {
direction: "source-to-target",
deleteOrphaned: false,
skipExisting: true
});Compare Projects
const diff = await workspace.compareProjects("dev", "staging");
console.log(diff.added); // Files in staging not in dev
console.log(diff.removed); // Files in dev not in staging
console.log(diff.modified); // Files that differReal-Time Data Pipeline
// Set up multi-stage data pipeline
const rawData = await workspace.loadProject({
id: "s3-raw",
source: { type: "s3", config: { bucket: "raw-data" } }
});
const processing = await workspace.loadProject({
id: "memory-work",
source: { type: "memory", config: {} }
});
const results = await workspace.loadProject({
id: "indexeddb-cache",
source: { type: "indexeddb", config: { dbName: "results" } }
});
// Process: S3 → Memory → IndexedDB
const files = await rawData.fs.glob("batch-*.json");
for (const file of files) {
// 1. Read from S3
const data = await rawData.fs.readFile(file);
// 2. Process in memory (fast)
const processed = await processData(JSON.parse(data.content));
await processing.fs.writeFile(`/work${file}`, JSON.stringify(processed));
// 3. Save results to IndexedDB (persistent)
await results.fs.writeFile(`/final${file}`, JSON.stringify(processed));
}📡 Event System
Unified events across all projects with rich context:
// Project lifecycle events
workspace.events.on("project:loaded", ({ project }) => {
console.log(`✅ ${project.name} loaded (${project.source.type})`);
});
workspace.events.on("project:activated", ({ projectId, previousId }) => {
console.log(`🔄 Switched from ${previousId} to ${projectId}`);
});
// File operations with project context
workspace.events.on("project:file:written", ({ projectId, path, size }) => {
console.log(`📝 ${path} written in ${projectId} (${size} bytes)`);
});
// Cross-project operations
workspace.events.on("workspace:sync:completed", ({ sourceId, targetId, stats }) => {
console.log(`🔄 Synced ${stats.files} files from ${sourceId} to ${targetId}`);
});
// Performance events with enhanced data
workspace.events.on("project:disabled", ({ projectId, hasLocalData, reason }) => {
console.log(`💤 ${projectId} disabled`);
console.log(` Has local data: ${hasLocalData}`);
console.log(` Reason: ${reason}`);
});
workspace.events.on("project:enabled", ({ projectId }) => {
console.log(`⚡ ${projectId} re-enabled`);
});
// Deletion events
workspace.events.on("project:deleted", ({ projectId, deletedData }) => {
console.log(`🗑️ ${projectId} deleted${deletedData ? ' with data' : ''}`);
});💾 Persistent State & Auto-Recovery
Automatic State Management
const workspace = new WorkspaceFileSystem();
await workspace.initialize(); // Automatically restores:
// - All previously loaded projects
// - Active project selection
// - Project configurations
// - Settings and preferences
// State is automatically saved when:
// - Projects are loaded/unloaded
// - Active project changes
// - Settings are modified
// - Projects are enabled/disabledSmart Cleanup (v1.0.3+)
The workspace automatically handles corrupted state:
// Before v1.0.3: Could crash with "NotFoundError"
// v1.0.3+: Automatically cleans up orphaned references
await workspace.initialize(); // Always succeeds
// Console output:
// ⚠️ Removing orphaned activeProjectId: deleted-project-123
// ⚠️ Removing orphaned recentProjectId: missing-project-456
// ℹ️ Workspace state cleaned: removed orphaned project referencesProject Discovery
// Discover existing IndexedDB projects
const discovered = await workspace.discoverIndexedDBProjects();
console.log("Found projects:", discovered.map(p => p.name));
// Load discovered projects
for (const project of discovered) {
await workspace.loadProject(project);
}
// Get all disabled projects (async now)
const disabledProjects = await workspace.getDisabledProjects();
console.log(`${disabledProjects.length} projects are disabled`);🔐 Security & Credentials
Environment-Based Credentials
# S3 credentials
FIRESYSTEM_S3_ACCESS_KEY_ID=your_access_key
FIRESYSTEM_S3_SECRET_ACCESS_KEY=your_secret_key
FIRESYSTEM_S3_REGION=us-east-1
# GitHub token
FIRESYSTEM_GITHUB_TOKEN=ghp_xxxxxxxxxxxx
# API credentials
FIRESYSTEM_API_API_KEY=your_api_keyInteractive Credential Management
// Credentials requested when needed
const s3Project = await workspace.loadProject({
id: "cloud-files",
source: {
type: "s3",
config: { bucket: "my-bucket" }
// No credentials in config - resolved from environment
}
});
// Custom credential providers
workspace.registerCredentialProvider("s3", new InteractiveCredentialProvider({
prompt: async (message, secure) => {
return await showCredentialDialog(message, secure);
}
}));Export Security
// Exports are automatically sanitized
const exportData = await workspace.exportWorkspace({
includeFiles: true,
includeCredentials: false // Never exports credentials
});
// Safe for sharing/version control
await workspace.exportToGitHubGist({
token: "ghp_...",
description: "Team Workspace Configuration",
public: false
});⚛️ Framework Integration
React + Zustand
import { create } from "zustand";
import { WorkspaceFileSystem } from "@firesystem/workspace";
interface WorkspaceStore {
workspace: WorkspaceFileSystem | null;
projects: Project[];
activeProject: Project | null;
initWorkspace: () => Promise<void>;
createProject: (name: string, type: string) => Promise<void>;
switchProject: (projectId: string) => Promise<void>;
deleteProject: (projectId: string, deleteData?: boolean) => Promise<void>;
}
const useWorkspaceStore = create<WorkspaceStore>((set, get) => ({
workspace: null,
projects: [],
activeProject: null,
async initWorkspace() {
const workspace = new WorkspaceFileSystem();
await workspace.initialize(); // Auto-restores state
// Setup reactive events
workspace.events.on("project:loaded", ({ project }) => {
set(state => ({
projects: [...state.projects, project]
}));
});
workspace.events.on("project:activated", ({ projectId }) => {
const project = workspace.getProject(projectId);
set({ activeProject: project });
});
workspace.events.on("project:deleted", ({ projectId }) => {
set(state => ({
projects: state.projects.filter(p => p.id !== projectId),
activeProject: state.activeProject?.id === projectId ? null : state.activeProject
}));
});
set({
workspace,
projects: workspace.getProjects(),
activeProject: workspace.getActiveProject()
});
},
async createProject(name: string, type: string) {
const { workspace } = get();
if (!workspace) return;
const project = await workspace.loadProject({
id: `project-${Date.now()}`,
name,
source: { type, config: {} }
});
await workspace.setActiveProject(project.id);
},
async switchProject(projectId: string) {
const { workspace } = get();
if (!workspace) return;
await workspace.setActiveProject(projectId);
},
async deleteProject(projectId: string, deleteData = false) {
const { workspace } = get();
if (!workspace) return;
await workspace.deleteProject(projectId, { deleteData });
}
}));
// React component
function MultiProjectIDE() {
const {
workspace,
projects,
activeProject,
initWorkspace,
createProject,
switchProject,
deleteProject
} = useWorkspaceStore();
useEffect(() => {
initWorkspace();
}, []);
return (
<div className="ide-container">
{/* Project tabs - all remain active */}
<div className="project-tabs">
{projects.map(project => (
<button
key={project.id}
onClick={() => switchProject(project.id)}
className={activeProject?.id === project.id ? 'active' : ''}
>
{project.name}
<span className="type">({project.source.type})</span>
</button>
))}
</div>
{/* File explorer for active project */}
{activeProject && (
<FileExplorer project={activeProject} />
)}
{/* Cross-project operations */}
<CrossProjectPanel
projects={projects}
onCopyFiles={(source, target, pattern) =>
workspace?.copyFiles(source, pattern, target, "/")
}
onSyncProjects={(source, target) =>
workspace?.syncProjects(source, target)
}
/>
</div>
);
}Vue 3 Composition API
import { ref, reactive, onMounted } from 'vue';
import { WorkspaceFileSystem } from '@firesystem/workspace';
export function useWorkspace() {
const workspace = ref<WorkspaceFileSystem | null>(null);
const projects = ref<Project[]>([]);
const activeProject = ref<Project | null>(null);
const initWorkspace = async () => {
workspace.value = new WorkspaceFileSystem();
await workspace.value.initialize();
workspace.value.events.on('project:loaded', ({ project }) => {
projects.value.push(project);
});
workspace.value.events.on('project:activated', ({ projectId }) => {
activeProject.value = workspace.value!.getProject(projectId);
});
workspace.value.events.on('project:deleted', ({ projectId }) => {
projects.value = projects.value.filter(p => p.id !== projectId);
if (activeProject.value?.id === projectId) {
activeProject.value = null;
}
});
projects.value = workspace.value.getProjects();
activeProject.value = workspace.value.getActiveProject();
};
const createProject = async (name: string, type: string) => {
if (!workspace.value) return;
const project = await workspace.value.loadProject({
id: `project-${Date.now()}`,
name,
source: { type, config: {} }
});
await workspace.value.setActiveProject(project.id);
};
const deleteProject = async (projectId: string, deleteData = false) => {
if (!workspace.value) return;
await workspace.value.deleteProject(projectId, { deleteData });
};
return {
workspace,
projects,
activeProject,
initWorkspace,
createProject,
deleteProject
};
}🌍 Real-World Use Cases
1. Multi-Environment Development
class DevelopmentWorkspace {
async setup() {
// Load all environments simultaneously
const local = await workspace.loadProject({
id: "local-dev",
source: { type: "memory", config: {} }
});
const staging = await workspace.loadProject({
id: "staging-mirror",
source: { type: "indexeddb", config: { dbName: "staging-cache" } }
});
const production = await workspace.loadProject({
id: "prod-readonly",
source: { type: "s3", config: { bucket: "prod-backup" } }
});
// Copy production config to development
const prodConfig = await production.fs.readFile("/config/app.json");
await local.fs.writeFile("/config/prod-like.json", prodConfig.content);
// Sync staging data for testing
await workspace.syncProjects("staging-mirror", "local-dev", {
include: ["data/**/*.json"],
exclude: ["data/sensitive/**"]
});
}
}2. Content Management Pipeline
class ContentPipeline {
async processContent() {
// Multiple content sources active simultaneously
const cms = await workspace.loadProject({
id: "cms-api",
source: { type: "api", config: { baseUrl: "https://cms.example.com" } }
});
const assets = await workspace.loadProject({
id: "cdn-assets",
source: { type: "s3", config: { bucket: "cdn-assets" } }
});
const cache = await workspace.loadProject({
id: "local-cache",
source: { type: "indexeddb", config: { dbName: "content-cache" } }
});
// Process content across all sources
const articles = await cms.fs.glob("/articles/*.json");
for (const article of articles) {
const content = await cms.fs.readFile(article);
const processed = await this.processArticle(content);
// Cache processed content
await cache.fs.writeFile(article, processed);
// Upload assets
if (processed.images) {
for (const image of processed.images) {
await assets.fs.writeFile(`/images/${image.name}`, image.data);
}
}
}
}
}3. Data Analysis Workspace
class DataAnalysisWorkspace {
async analyzeData() {
// Load datasets from different sources
const rawData = await workspace.loadProject({
id: "data-lake",
source: { type: "s3", config: { bucket: "analytics-data" } }
});
const processing = await workspace.loadProject({
id: "analysis-memory",
source: { type: "memory", config: {} }
});
const results = await workspace.loadProject({
id: "results-db",
source: { type: "indexeddb", config: { dbName: "analysis-results" } }
});
// Cross-dataset analysis
const datasets = await rawData.fs.glob("datasets/**/*.csv");
const analysis = {
totalRows: 0,
correlations: {},
summaries: {}
};
// Process each dataset in memory for speed
for (const dataset of datasets) {
const data = await rawData.fs.readFile(dataset);
const processed = await this.analyzeDataset(data.content);
// Store intermediate results in memory
await processing.fs.writeFile(`/processed/${dataset}`, processed);
// Aggregate analysis
analysis.totalRows += processed.rows;
analysis.summaries[dataset] = processed.summary;
}
// Save final results
await results.fs.writeFile("/analysis/summary.json", JSON.stringify(analysis));
}
}📊 Performance & Best Practices
Memory Management
// Configure auto-optimization
const workspace = new WorkspaceFileSystem({
settings: {
maxActiveProjects: 5, // Auto-disable oldest
autoDisableAfter: 30 * 60 * 1000, // 30 minutes inactive
keepFocusedActive: true, // Never disable focused
memoryThreshold: 500 * 1024 * 1024 // 500MB threshold
}
});
// Manual optimization
const stats = await workspace.getProjectStats();
console.log(stats);
// {
// total: 15,
// active: 5,
// disabled: 10,
// memoryUsage: "245MB",
// connections: { s3: 2, api: 1 }
// }
// Optimize when needed
if (stats.memoryUsage > threshold) {
await workspace.optimizeMemoryUsage();
}Efficient File Operations
// Batch operations across projects
const operations = [
{ project: "dev", operation: () => dev.fs.writeFile("/a.txt", "content") },
{ project: "staging", operation: () => staging.fs.writeFile("/b.txt", "content") },
{ project: "prod", operation: () => prod.fs.readFile("/config.json") }
];
await Promise.all(operations.map(op => op.operation()));
// Use project capabilities efficiently
const provider = workspace.getProvider("s3");
if (provider?.getCapabilities().supportsGlob) {
const files = await s3Project.fs.glob("**/*.log");
} else {
// Fallback for providers without glob
const files = await this.manualGlob(s3Project, "**/*.log");
}Error Handling
// Robust multi-project error handling
async function robustOperation() {
const results = { success: [], failed: [] };
for (const project of workspace.getProjects()) {
try {
await project.fs.writeFile("/health-check.txt", new Date().toISOString());
results.success.push(project.id);
} catch (error) {
console.warn(`Health check failed for ${project.id}:`, error.message);
results.failed.push({ project: project.id, error: error.message });
// Auto-disable problematic projects
if (error.message.includes("network") || error.message.includes("credentials")) {
await workspace.disableProject(project.id);
}
}
}
return results;
}🧪 Testing
The workspace includes comprehensive test coverage with a focus on core functionality:
# Run all tests
pnpm test
# Core functionality tests
pnpm test tests/core/
# Specific test suites
pnpm test tests/core/ProjectManagement.test.ts # Load/unload/delete
pnpm test tests/core/ProjectStateManagement.test.ts # Enable/disable
pnpm test tests/core/CrossProjectOperations.test.ts # Multi-project ops
pnpm test tests/core/FileSystemProxy.test.ts # Transparent proxy
pnpm test tests/core/ProviderValidation.test.ts # Provider systemTest Philosophy
- Unit Tests: Core workspace logic with mocked providers (fast, deterministic)
- Integration Tests: Real provider implementations (in individual packages)
- Mocked Providers: Complete FileSystem mocks with configurable capabilities
- Edge Cases: Orphaned references, capability limitations, error scenarios
🏗️ Architecture
The workspace system follows a modular architecture with clear separation of concerns:
Core Modules
src/
├── WorkspaceFileSystem.ts # Main orchestrator class (~900 lines)
├── managers/ # Business logic managers
│ ├── ProjectManager.ts # Project lifecycle and loading
│ ├── PerformanceManager.ts # Memory optimization and metrics
│ ├── PersistenceManager.ts # State persistence and restoration
│ └── EventManager.ts # Event forwarding and enrichment
├── operations/ # Cross-cutting operations
│ ├── ProjectOperations.ts # Copy, sync, compare between projects
│ └── FileSystemProxy.ts # IReactiveFileSystem delegation
├── credentials/ # Credential management (provider-agnostic)
├── import-export/ # Workspace import/export functionality
└── interfaces/ # Core interfaces and types
Design Principles
- Single Responsibility: Each module handles one specific aspect
- Provider Agnostic: Core knows nothing about specific implementations
- Dependency Injection: Modules receive dependencies via constructor
- Event-Driven: Reactive architecture with typed events
- Testable: Small, focused modules that are easy to test
Module Responsibilities
- ProjectManager: Handles project loading, unloading, and conversion
- PerformanceManager: Monitors memory usage and optimizes resources
- PersistenceManager: Manages database operations and state restoration
- EventManager: Forwards filesystem events with project context
- ProjectOperations: Implements cross-project operations
- FileSystemProxy: Delegates all filesystem calls to active project
📚 Documentation
Explore our comprehensive guides to master Firesystem Workspace:
- Project Lifecycle Management - Complete guide to loading, enabling/disabling, and deleting projects
- Event System Guide - Master the reactive event system for real-time updates
- Provider System Guide - Learn how to work with providers and create custom storage implementations
- Cross-Project Operations - Unlock the power of multi-project workflows
- React Integration Guide - Build modern React apps with Firesystem Workspace
For code examples, check out the examples directory.
📚 API Reference
WorkspaceFileSystem
class WorkspaceFileSystem implements IReactiveFileSystem {
// Project Management
async loadProject(config: ProjectConfig): Promise<Project>
async unloadProject(projectId: string): Promise<void>
getProject(projectId: string): Project | null
getProjects(): Project[]
async setActiveProject(projectId: string): Promise<void>
getActiveProject(): Project | null
// Project Lifecycle
async deleteProject(projectId: string, options?: DeleteProjectOptions): Promise<void>
async disableProject(projectId: string): Promise<void>
async enableProject(projectId: string): Promise<void>
async disableProjects(projectIds: string[]): Promise<void>
async enableProjects(projectIds: string[]): Promise<void>
isProjectEnabled(projectId: string): boolean
async getDisabledProjects(): Promise<StoredProject[]>
// Cross-Project Operations
async copyFiles(sourceId: string, pattern: string, targetId: string, targetPath: string): Promise<void>
async syncProjects(sourceId: string, targetId: string, options?: SyncOptions): Promise<void>
async compareProjects(projectId1: string, projectId2: string): Promise<ProjectDiff>
// Provider Management
registerProvider(provider: SourceProvider): void
unregisterProvider(scheme: string): void
getProvider(scheme: string): SourceProvider | undefined
getRegisteredProviders(): SourceProvider[]
// Performance & Statistics
async getProjectStats(): Promise<WorkspaceStats>
async getProjectMetrics(projectId: string): Promise<ProjectMetrics>
async optimizeMemoryUsage(): Promise<OptimizationReport>
// Discovery & Persistence
async discoverIndexedDBProjects(): Promise<ProjectConfig[]>
async exportWorkspace(options?: ExportOptions): Promise<WorkspaceExport>
async importFromUrl(url: string): Promise<void>
// All IReactiveFileSystem methods (proxied to active project)
async readFile(path: string): Promise<FileEntry>
async writeFile(path: string, content: any): Promise<FileEntry>
// ... (all file system operations)
// Event System
readonly events: TypedEventEmitter<WorkspaceEventPayloads>
// Lifecycle
async initialize(config?: WorkspaceConfig): Promise<void>
async clear(): Promise<void>
async close(): Promise<void>
}Key Interfaces
interface Project {
id: string
name: string
source: ProjectSource
fs: IReactiveFileSystem
metadata: ProjectMetadata
state: "loading" | "loaded" | "error" | "disabled"
lastAccessed: Date
accessCount: number
memoryUsage: number
}
interface ProjectSource {
type: "memory" | "indexeddb" | "s3" | "github" | "api" | "json-url"
config: any
auth?: SourceAuth
}
interface DeleteProjectOptions {
deleteData?: boolean // Also delete project data (provider-specific)
skipConfirmation?: boolean // Skip confirmation event
}
interface SourceProvider {
readonly scheme: string
readonly displayName: string
createFileSystem(config: any): Promise<IReactiveFileSystem>
getCapabilities(): IFileSystemCapabilities
validateConfiguration?(config: any): Promise<{ valid: boolean; errors?: string[] }>
dispose?(fs: IReactiveFileSystem): Promise<void>
// Optional data management methods
deleteProjectData?(config: any): Promise<void>
hasLocalData?(): boolean
estimateDataSize?(config: any): Promise<number | null>
}
interface WorkspaceSettings {
maxActiveProjects?: number // Auto-disable when exceeded
autoDisableAfter?: number // Ms of inactivity before auto-disable
keepFocusedActive?: boolean // Never auto-disable focused project
autoSave?: boolean
autoSaveInterval?: number
memoryThreshold?: number // Bytes before optimization
}🔧 Configuration
Environment Variables
# S3 Configuration
FIRESYSTEM_S3_ACCESS_KEY_ID=your_access_key
FIRESYSTEM_S3_SECRET_ACCESS_KEY=your_secret_key
FIRESYSTEM_S3_REGION=us-east-1
# GitHub Configuration
FIRESYSTEM_GITHUB_TOKEN=ghp_xxxxxxxxxxxx
GITHUB_TOKEN=ghp_xxxxxxxxxxxx # Fallback
# API Configuration
FIRESYSTEM_API_API_KEY=your_api_key
FIRESYSTEM_API_BASE_URL=https://api.example.comWorkspace Settings
const workspace = new WorkspaceFileSystem({
settings: {
maxActiveProjects: 10, // Keep 10 projects active max
autoDisableAfter: 1800000, // 30 minutes
keepFocusedActive: true, // Protect focused project
autoSave: true, // Auto-save state changes
autoSaveInterval: 60000, // Save every minute
memoryThreshold: 512 * 1024 * 1024 // 512MB threshold
}
});🚨 Migration Guide
From Traditional Single-Project Tools
// Before: Single project at a time
class OldWorkflow {
async switchToProject(projectId: string) {
await this.closeCurrentProject(); // Lost from memory
await this.openProject(projectId); // Load from scratch
}
async copyToOtherProject(file: string, targetProject: string) {
const content = await this.readFile(file);
await this.closeCurrentProject(); // Lose current context
await this.openProject(targetProject);
await this.writeFile(file, content);
await this.closeCurrentProject(); // Lose target context
await this.openOriginalProject(); // Reload original
}
}
// After: Multi-project simultaneous access
class NewWorkflow {
async switchToProject(projectId: string) {
await workspace.setActiveProject(projectId); // Just change focus
// All other projects remain active and accessible!
}
async copyToOtherProject(file: string, targetProjectId: string) {
const source = workspace.getActiveProject();
const target = workspace.getProject(targetProjectId);
const content = await source.fs.readFile(file);
await target.fs.writeFile(file, content);
// Both projects remain active, no context loss!
}
}Version History
| Version | Release Date | Key Features | |---------|-------------|--------------| | v1.0.8 | 2025-07-03 | 🏗️ Modular architecture refactoring, improved separation of concerns | | v1.0.7 | 2025-07-02 | 🗑️ Complete deleteProject, provider data management | | v1.0.4 | 2025-06-30 | 🧪 Comprehensive test suite, robust validation | | v1.0.3 | 2025-06-30 | 🧹 Auto-cleanup, empty-by-design, robust validation | | v1.0.2 | 2025-06-30 | 🚀 Complete workspace system with persistence | | v1.0.1 | 2025-06-30 | 📦 Initial release with multi-project support |
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/firesystem/firesystem.git
cd firesystem/packages/workspace
# Install dependencies
pnpm install
# Run tests
pnpm test
# Type checking
pnpm typecheck
# Build
pnpm build📋 Roadmap
v1.1.0 - Enhanced Cloud Support
- [ ] Native S3FileSystem implementation
- [ ] Google Drive source provider
- [ ] Azure Blob Storage support
- [ ] Enhanced GitHub integration with commit/push
v1.2.0 - Advanced Operations
- [ ] Project templates and scaffolding
- [ ] Advanced sync strategies
- [ ] Workspace-level search across all projects
- [ ] Project dependency management
v1.3.0 - Performance & Scale
- [ ] Virtual file system for large projects
- [ ] Streaming file operations
- [ ] Background sync and preloading
- [ ] Advanced caching strategies
v2.0.0 - Collaboration Features
- [ ] Real-time collaboration protocol
- [ ] Multi-user workspace sharing
- [ ] Role-based access control
- [ ] Audit logging and analytics
📄 License
MIT © Anderson D. Rosa
🙏 Acknowledgments
- Built on top of the robust @firesystem/core foundation
- Inspired by VSCode's multi-root workspace concept, but taken to the next level
- Thanks to the TypeScript team for excellent type system
- IndexedDB API for reliable browser persistence
- The open source community for continuous inspiration
🔥 Transform your development workflow with true multi-project parallelism!
