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

@jucie.io/engine

v1.1.62

Published

A powerful, extensible service engine with middleware, async operations, and lifecycle management (includes core + services)

Readme

@jucie.io/engine

A powerful, extensible service engine for JavaScript applications with middleware support, async operations, and comprehensive lifecycle management.

Important Notice

This package is made publicly available for use but comes with no support, maintenance commitments, or warranty of any kind. It is provided strictly "as-is" under the MIT license.

  • Issues and feature requests may not be addressed
  • No timeline or commitment for updates
  • Not actively seeking contributions
  • Use at your own risk

If you choose to use this package, please ensure it meets your needs through your own testing and evaluation.

Features

  • 🚀 Service Architecture: Clean, modular service system with lifecycle management
  • 🔧 Middleware Support: Composable middleware with sync/async support
  • Async Operations: Full async/await support for actions and middleware
  • 🛡️ Security: Built-in protection against prototype pollution and namespace conflicts
  • 🔗 Dependency Management: Automatic service dependency resolution
  • ⚙️ Configuration: Flexible service configuration with defaults merging
  • 🧪 Well Tested: Comprehensive test suite with 33+ test cases
  • 📦 TypeScript Ready: Full TypeScript definitions included

Installation

npm install @jucie.io/engine

Quick Start

Method Pattern

import { Engine, ServiceProvider } from '@jucie.io/engine';

// Create an engine instance
const engine = Engine.create();

// Define a service using method pattern
class MyService extends ServiceProvider {
  static manifest = {
    name: 'My Service',
    namespace: 'myService',
    version: '1.0.0'
  };

  actions(useContext, config) {
    return {
      greet: (name) => `Hello, ${name}!`,
      fetchData: async () => {
        // Async operations supported
        return await fetch('/api/data').then(r => r.json());
      }
    };
  }

  middleware(useContext, config) {
    return (action, ctx, next) => {
      console.log(`Executing action: ${action}`);
      return next();
    };
  }
}

// Install the service
await engine.install(MyService);

// Use the service
const greeting = engine.myService.greet('World');
console.log(greeting); // "Hello, World!"

Setup Pattern

import { Engine, ServiceProvider } from '@jucie.io/engine';

// Create an engine instance
const engine = Engine.create();

// Define a service using setup pattern
class MyService extends ServiceProvider {
  static manifest = {
    name: 'My Service',
    namespace: 'myService',
    version: '1.0.0'
  };

  setup({ defineActions, defineMiddleware }) {
    defineActions((useContext, config) => {
      return {
        greet: (name) => `Hello, ${name}!`,
        fetchData: async () => {
          return await fetch('/api/data').then(r => r.json());
        }
      };
    });

    defineMiddleware((useContext, config) => {
      return (action, ctx, next) => {
        console.log(`Executing action: ${action}`);
        return next();
      };
    });
  }
}

// Install the service
await engine.install(MyService);

// Use the service
const greeting = engine.myService.greet('World');
console.log(greeting); // "Hello, World!"

Service Architecture

ServiceProvider Base Class

All services extend the ServiceProvider base class and implement the following lifecycle methods:

Required Properties

static manifest = {
  name: 'Service Name',
  namespace: 'serviceName',  // Must be valid JS identifier
  version: '1.0.0'
};

Service Definition Patterns

The ServiceProvider supports two patterns for defining service functionality:

Method Pattern (Traditional approach):

  • actions(useContext, config) - Define service actions (required)
  • middleware(useContext, config) - Define middleware functions (optional)
  • getters(useContext, config) - Define read-only properties (optional)
  • initialize(useContext, config) - Setup logic called directly (optional)
  • uninstall(useContext, config) - Cleanup logic called directly (optional)

Setup Pattern (Declarative approach):

  • setup({ defineActions, defineMiddleware, defineGetters, defineInitialize, defineUninstall }) - Declarative service definition

Method Pattern Example

class DatabaseService extends ServiceProvider {
  static manifest = {
    name: 'Database Service',
    namespace: 'db',
    version: '1.0.0',
    defaults: { connectionString: 'sqlite:memory:' }
  };

  #connection = null;

  initialize(useContext, config) {
    this.#connection = createConnection(this.config.connectionString);
  }

  getters(useContext, config) {
    return {
      isConnected: () => this.#connection !== null
    };
  }

  middleware(useContext, config) {
    return (action, ctx, next) => {
      console.log(`Database action: ${action}`);
      return next();
    };
  }

  actions(useContext, config) {
    return {
      async query: (sql, params = []) => {
        return await this.#connection.query(sql, params);
      },

      async transaction: async (callback) => {
        const tx = await this.#connection.beginTransaction();
        try {
          const result = await callback(tx);
          await tx.commit();
          return result;
        } catch (error) {
          await tx.rollback();
          throw error;
        }
      }
    };
  }

  uninstall(useContext, config) {
    if (this.#connection) {
      this.#connection.close();
      this.#connection = null;
    }
  }
}

Setup Pattern Example

class CacheService extends ServiceProvider {
  static manifest = {
    name: 'Cache Service',
    namespace: 'cache',
    version: '1.0.0',
    defaults: { ttl: 300000, maxSize: 1000 }
  };

  #cache = new Map();

  setup({ defineMiddleware, defineGetters, defineActions, defineInitialize, defineUninstall }) {
    // Define middleware
    defineMiddleware((useContext, config) => {
      return (action, ctx, next) => {
        console.log(`Cache action: ${action}`);
        return next();
      };
    });

    // Define getters
    defineGetters((useContext, config) => {
      return {
        size: () => this.#cache.size,
        isEnabled: () => this.config.maxSize > 0
      };
    });

    // Define actions
    defineActions((useContext, config) => {
      return {
        get: (key) => {
          const item = this.#cache.get(key);
          if (!item) return null;
          
          if (Date.now() > item.expires) {
            this.#cache.delete(key);
            return null;
          }
          
          return item.value;
        },

        set: (key, value, ttl = this.config.ttl) => {
          if (this.#cache.size >= this.config.maxSize) {
            // Remove oldest entry
            const firstKey = this.#cache.keys().next().value;
            this.#cache.delete(firstKey);
          }
          
          this.#cache.set(key, {
            value,
            expires: Date.now() + ttl
          });
        },

        clear: () => {
          this.#cache.clear();
        }
      };
    });

    // Define initialize
    defineInitialize((useContext, config) => {
      console.log('Cache service initialized');
    });

    // Define uninstall
    defineUninstall((useContext, config) => {
      this.#cache.clear();
      console.log('Cache service cleaned up');
    });
  }
}

Pattern Comparison

| Feature | Method Pattern | Setup Pattern | |---------|---------------|---------------| | Syntax | Class methods | Single setup function | | Organization | Separate methods | Centralized definition | | Flexibility | High - conditional methods | Medium - declarative | | Readability | Good for simple services | Excellent for complex services | | Type Safety | Standard class methods | Requires proper typing | | Use Case | Simple to medium services | Complex services with many definitions |

Middleware System

The engine supports powerful middleware composition for intercepting and processing action calls:

class LoggingService extends ServiceProvider {
  static manifest = {
    name: 'Logging Service',
    namespace: 'logging',
    version: '1.0.0'
  };

  middleware(useContext, config) {
    return [
      // Sync middleware
      (action, ctx, next) => {
        console.log(`[${new Date().toISOString()}] Starting ${action}`);
        const start = Date.now();
        
        const result = next();
        
        console.log(`[${new Date().toISOString()}] Completed ${action} in ${Date.now() - start}ms`);
        return result;
      },

      // Async middleware
      async (action, ctx, next) => {
        // Authentication check
        const user = await authenticateUser();
        if (!user) {
          throw new Error('Unauthorized');
        }
        
        ctx.user = user;
        return next();
      }
    ];
  }

  actions(useContext, config) {
    return {
      log: (message) => console.log(message)
    };
  }
}

Service Dependencies

Services can declare dependencies that are automatically installed:

class AuthService extends ServiceProvider {
  static manifest = {
    name: 'Auth Service',
    namespace: 'auth',
    version: '1.0.0'
  };

  actions(useContext, config) {
    const { engine } = useContext();
    return {
      login: (credentials) => {
        // Use dependency
        return engine.db.query('SELECT * FROM users WHERE email = ?', [credentials.email]);
      }
    };
  }
}

class ApiService extends ServiceProvider {
  static manifest = {
    name: 'API Service',
    namespace: 'api',
    version: '1.0.0',
    dependencies: [AuthService, DatabaseService] // Auto-install dependencies
  };

  actions(useContext, config) {
    const { engine } = useContext();
    return {
      handleRequest: (req, res) => {
        // Dependencies are available
        const user = engine.auth.getCurrentUser();
        return engine.db.query('SELECT * FROM posts WHERE userId = ?', [user.id]);
      }
    };
  }
}

Configuration

Services support configuration with defaults:

class ConfigurableService extends ServiceProvider {
  static manifest = {
    name: 'Configurable Service',
    namespace: 'configurable',
    version: '1.0.0',
    defaults: {
      timeout: 5000,
      retries: 3,
      debug: false
    }
  };

  actions(useContext, config) {
    return {
      getConfig: () => this.config,
      getTimeout: () => this.config.timeout
    };
  }
}

// Install with custom configuration
const configuredService = ConfigurableService.configure({
  timeout: 10000,
  debug: true
});

await engine.install(configuredService);
console.log(engine.configurable.getTimeout()); // 10000

Global Engine Context

The engine provides global context management:

import { provideEngine, useEngine, hasEngine } from '@jucie.io/engine';

// Provide engine globally
provideEngine(engine);

// Use engine from anywhere
const globalEngine = useEngine();
const hasGlobalEngine = hasEngine();

Error Handling

The engine provides comprehensive error handling with context:

class ErrorProneService extends ServiceProvider {
  static manifest = {
    name: 'Error Service',
    namespace: 'error',
    version: '1.0.0'
  };

  actions(useContext, config) {
    return {
      riskyOperation: () => {
        throw new Error('Something went wrong');
      }
    };
  }
}

try {
  await engine.install(ErrorProneService);
  engine.error.riskyOperation();
} catch (error) {
  console.log(error.action);     // 'riskyOperation'
  console.log(error.namespace);  // 'error'
  console.log(error.message);    // 'Something went wrong'
}

Security Features

The engine includes built-in security protections:

  • Prototype Pollution Prevention: Blocks __proto__ and constructor namespace usage
  • Namespace Validation: Ensures valid JavaScript identifiers
  • Reserved Namespace Protection: Prevents conflicts with engine methods
  • Input Sanitization: Validates service manifests and configurations

API Reference

Engine

Engine.create(config?)

Creates a new engine instance.

engine.install(...services)

Installs one or more services.

engine.uninstall(...namespaces)

Uninstalls services by namespace.

engine.use(...definitions)

Adds middleware or other definitions directly.

ServiceProvider

ServiceProvider.configure(options)

Creates a configured service instance.

Static Properties

  • manifest - Service metadata and configuration

Lifecycle Methods

  • actions(useContext, config) - Define service actions
  • middleware(useContext, config) - Define middleware functions
  • getters(useContext, config) - Define read-only properties
  • initialize(useContext, config) - Setup logic called directly
  • uninstall(useContext, config) - Cleanup logic called directly

Testing

The engine includes a comprehensive test suite:

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

Development

# Install dependencies
npm install

# Build the project
npm run build

# Watch for changes
npm run watch

# Run benchmarks
npm run bench

License

MIT License with Commons Clause - See LICENSE file for details.

TL;DR: Free to use in your projects, but you cannot sell this software as a competing product or service.