npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@workspace-fs/core

v1.0.8

Published

Multi-project workspace manager for Firesystem with support for multiple sources

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.

npm version TypeScript License Tests

🚀 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 accessible

Firesystem 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); // false

Creating 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 differ

Real-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/disabled

Smart 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 references

Project 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_key

Interactive 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 system

Test 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

  1. Single Responsibility: Each module handles one specific aspect
  2. Provider Agnostic: Core knows nothing about specific implementations
  3. Dependency Injection: Modules receive dependencies via constructor
  4. Event-Driven: Reactive architecture with typed events
  5. 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:

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.com

Workspace 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!