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

@http-forge/core

v0.2.3

Published

Headless HTTP testing engine with Postman collection support, dynamic variables, and script-based automation.

Readme

@http-forge/core

Standalone HTTP testing engine with Postman collection support and JavaScript-based automation.

npm version License: MIT

📦 What is @http-forge/core?

@http-forge/core is a headless, framework-agnostic HTTP execution engine with full Postman collection compatibility. Execute complex API workflows, test suites, and automated flows without the overhead of a UI.

Core Features:

  • 🚀 Postman Collections - Load and execute .postman_collection.json and .forge.json files
  • 📝 JavaScript Scripting - Pre-request and post-response scripts with full pm.* API (variables, assertions, execution flow, visualizer)
  • 🔄 Dynamic Variables - Built-in generators: {{$randomInt}}, {{$timestamp}}, {{$uuid}}, {{$guid}}, etc.
  • 🌍 Environments - Full variable scoping (globals, collection, environment, session, iterationData)
  • 🍪 Cookie Persistence - Automatic cookie storage and reuse, pm.cookies.jar() and .toObject()
  • 📊 Test Assertions - BDD-style testing with pm.test() (sync/async) and full Chai expect() chains
  • 🔐 CryptoJS - Full crypto library: hash, HMAC, AES/DES/TripleDES, PBKDF2, encoding helpers
  • 🎯 Execution Flow - pm.setNextRequest(), pm.execution.skipRequest() for suite runner flow control
  • 📈 Visualizer - pm.visualizer.set(template, data) for custom Handlebars-based HTML output
  • 🔌 Extensible - Custom interceptors, HTTP clients, and module loaders

Ideal for:

  • CI/CD pipeline integration (GitHub Actions, GitLab CI, Jenkins)
  • Headless API testing and contract validation
  • Building custom API testing CLIs
  • Load testing and performance monitoring
  • Automated integration test suites

🎯 Installation

npm install @http-forge/core

Or using the tarball:

npm install ./http-forge-core-0.1.0.tgz

⚡ Quick Start

Basic Usage

import { ForgeContainer } from '@http-forge/core';

// Create a container with default settings
const forge = new ForgeContainer();

// Load a collection
const collection = await forge.loadCollection('./my-api.forge.json');

// Execute a request
const result = await forge.execute(collection.items[0], collection);

console.log(result.response.status);        // 200
console.log(result.response.body);          // Response data
console.log(result.postResponseResult?.assertions);  // Test results

With Environment Variables

const forge = new ForgeContainer();

// Set environment variables
forge.setEnvironment({
    baseUrl: 'https://api.example.com',
    apiKey: 'your-api-key',
    timeout: '5000'
});

// Variables are automatically interpolated in requests
// URL: {{baseUrl}}/users -> https://api.example.com/users
const result = await forge.execute(request, collection);

With Custom Configuration

const forge = new ForgeContainer({
    // Use native Node.js http/https instead of fetch
    useNativeHttp: true,
    
    // Enable automatic cookie management
    enableCookies: true,
    
    // Set request timeout
    requestTimeout: 10000,
    
    // Enable request history
    enableHistory: true,
    maxHistoryEntries: 50,
    
    // Storage format
    storageFormat: 'folder'  // or 'file'
});

📚 Core Concepts

ForgeContainer

The main entry point - a dependency injection container that wires up all components.

const forge = new ForgeContainer(options);

// Load collections
const collection = await forge.loadCollection(path);
const folderCollection = await forge.loadFolderCollection(path);

// Execute requests
const result = await forge.execute(request, collection, options);

// Manage environments
forge.setEnvironment(variables);
forge.setActiveEnvironment(name);
const resolved = forge.getResolvedEnvironment();

// Access services
const executor = forge.getRequestExecutor();
const loader = forge.getCollectionLoader();

Request Execution

Execute requests with full control over the execution pipeline:

const result = await forge.execute(request, collection, {
    environment: 'production',
    overrides: {
        url: 'https://override.com/api',
        headers: { 'X-Custom': 'value' }
    },
    skipPreRequest: false,
    skipPostResponse: false,
    timeout: 5000
});

// Access results
console.log(result.response);           // HTTP response
console.log(result.preRequestResult);   // Pre-request script output
console.log(result.postResponseResult); // Test results

Dynamic Variables

Automatic variable generation within request templates:

// URL with dynamic timestamp
https://api.example.com/events?timestamp={{$timestamp}}

// Headers with unique ID
X-Request-ID: {{$uuid}}

// Query parameters with random value
?page=1&seed={{$randomInt:1:100}}

Supported dynamic variables:

  • {{$randomInt}} - Random integer (0-2147483647)
  • {{$randomInt:min:max}} - Random integer in range
  • {{$timestamp}} - Current Unix timestamp (seconds)
  • {{$uuid}} - UUID v4
  • {{$guid}} - GUID (alias for uuid)
  • {{$randomString}} - 10-char alphanumeric string
  • {{$randomHexadecimal}} - Random hex string
  • {{$isoTimestamp}} - ISO 8601 timestamp

Script Execution

Run pre-request and post-response scripts with full Postman API compatibility:

Pre-request script - Set variables & modify request:

// Set variables across scopes
pm.variables.set('requestId', pm.variables.randomUUID());
pm.environment.set('token', 'abc-123');
pm.collectionVariables.set('counter', '1');

// Modify request headers
pm.request.headers.add({
    name: 'X-Request-ID',
    value: pm.variables.get('requestId')
});
pm.request.headers.update({
    name: 'Authorization',
    value: 'Bearer ' + pm.environment.get('token')
});

// Modify URL and body
pm.request.url = 'https://api.example.com' + pm.request.url;
pm.request.body.raw = JSON.stringify({ timestamp: Date.now() });

// Set cookies for next request
pm.cookies.set('sessionId', 'sess_abc123');

Post-response script - Test & extract data:

// Run assertions
pm.test('Status is 200', () => {
    pm.expect(pm.response.code).to.equal(200);
});
pm.test('Response time under 1s', () => {
    pm.expect(pm.response.responseTime).to.be.below(1000);
});

// Extract data for next request
const data = pm.response.json();
pm.environment.set('userId', data.id);
pm.environment.set('authToken', data.token);

// Store cookies from response
if (pm.response.headers.has('Set-Cookie')) {
    pm.cookies.set('authCookie', data.authCookie);
}

Environment Management

// Define multiple environments
forge.setEnvironmentConfig({
    dev: { baseUrl: 'https://dev.api.com', apiKey: 'dev-key' },
    staging: { baseUrl: 'https://staging.api.com', apiKey: 'staging-key' },
    prod: { baseUrl: 'https://api.com', apiKey: 'prod-key' }
});

// Switch environments
forge.setActiveEnvironment('prod');

// Get resolved variables (with inheritance and overrides)
const vars = forge.getResolvedEnvironment('prod');

🔧 Advanced Features

Custom HTTP Client

Implement your own HTTP client:

import { IHttpClient, HttpRequest, HttpResponse } from '@http-forge/core';

class CustomHttpClient implements IHttpClient {
    async send(request: HttpRequest): Promise<HttpResponse> {
        // Your custom HTTP logic
        return {
            status: 200,
            statusText: 'OK',
            headers: {},
            body: {},
            duration: 100,
            size: 1024
        };
    }
}

const forge = new ForgeContainer({
    httpClient: new CustomHttpClient()
});

Request/Response Interceptors

Add custom interceptors to modify requests and responses:

import { IRequestInterceptor, IResponseInterceptor } from '@http-forge/core';

// Request interceptor
class AuthInterceptor implements IRequestInterceptor {
    async intercept(request: HttpRequest): Promise<HttpRequest> {
        request.headers['Authorization'] = `Bearer ${getToken()}`;
        return request;
    }
}

// Response interceptor
class LoggingInterceptor implements IResponseInterceptor {
    async intercept(response: HttpResponse, request: HttpRequest): Promise<HttpResponse> {
        console.log(`${request.method} ${request.url} -> ${response.status}`);
        return response;
    }
}

const forge = new ForgeContainer({
    requestInterceptors: [new AuthInterceptor()],
    responseInterceptors: [new LoggingInterceptor()]
});

Cookie Management & Persistence

Automatic cookie storage and reuse across multi-request flows:

const forge = new ForgeContainer({
    enableCookies: true  // Cookies persist across requests in session
});

// Login - response sets Session-ID cookie
const loginResult = await forge.execute(loginRequest, collection);

// Subsequent requests automatically include Session-ID
// No need to manually extract and re-add cookies
const dataResult = await forge.execute(dataRequest, collection);
const updateResult = await forge.execute(updateRequest, collection);

Access cookies in scripts:

// Pre-request script - read stored cookies
if (pm.cookies.has('sessionId')) {
    const sid = pm.cookies.get('sessionId');
    pm.request.headers.add({
        name: 'Cookie',
        value: 'sessionId=' + sid
    });
}

// Post-response script - store new cookies
pm.response.cookies.forEach(cookie => {
    pm.cookies.set(cookie.name, cookie.value);
});

// List all active cookies
const allCookies = pm.cookies.list();  // [{name, value}, ...]

// Clear cookies
pm.cookies.clear();  // When switching users/sessions

Cookies are automatically extracted from Set-Cookie response headers and reused in subsequent Cookie request headers.

Request History

Track all executed requests:

const forge = new ForgeContainer({
    enableHistory: true,
    maxHistoryEntries: 100
});

// Execute requests
await forge.execute(request1, collection);
await forge.execute(request2, collection);

// Access history
const history = forge.getRequestHistory();
const entries = history.getAll();         // All requests
const byId = history.getByRequestId(id);  // Specific request history

📖 API Reference

ForgeContainer

Constructor Options:

interface ForgeContainerOptions {
    forgeRoot?: string;              // Path to http-forge folder
    storageFormat?: 'file' | 'folder'; // Collection storage format
    
    // HTTP Settings
    useNativeHttp?: boolean;         // Use native http/https
    httpClient?: IHttpClient;        // Custom HTTP client
    httpSettings?: RequestSettings;  // Default HTTP settings
    requestTimeout?: number;         // Request timeout (ms)
    
    // Cookie Settings
    enableCookies?: boolean;         // Enable cookie jar
    cookieJar?: ICookieJar;         // Custom cookie jar
    
    // Interceptors
    requestInterceptors?: IRequestInterceptor[];
    responseInterceptors?: IResponseInterceptor[];
    errorInterceptors?: IErrorInterceptor[];
    
    // Script Settings
    scriptRunner?: IScriptRunner;    // Custom script runner
    scriptTimeout?: number;          // Script timeout (ms)
    
    // History
    enableHistory?: boolean;         // Enable request history
    maxHistoryEntries?: number;      // Max history size
}

Methods:

// Collection loading
loadCollection(path: string): Promise<UnifiedCollection>
loadFolderCollection(path: string): Promise<UnifiedCollection>

// Request execution
execute(
    request: UnifiedRequest,
    collection: UnifiedCollection,
    options?: ExecuteOptions
): Promise<ExecuteResult>

// Environment management
setEnvironment(variables: Record<string, string>): void
setEnvironmentConfig(config: EnvironmentConfig): void
setActiveEnvironment(name: string): void
getResolvedEnvironment(name?: string): Record<string, string>

// Service access
getRequestExecutor(): RequestExecutor
getCollectionLoader(): ICollectionLoader
getEnvironmentResolver(): EnvironmentResolver
getRequestHistory(): IRequestHistory

ExecuteResult

interface ExecuteResult {
    response: HttpResponse;          // HTTP response
    preRequestResult?: ScriptResult; // Pre-request script output
    postResponseResult?: ScriptResult; // Test results
    requestId: string;               // Unique request ID
    timestamp: number;               // Execution timestamp
}

HttpResponse

interface HttpResponse {
    status: number;                  // HTTP status code
    statusText: string;              // Status text
    headers: Record<string, string>; // Response headers
    body: any;                       // Parsed response body
    cookies?: Cookie[];              // Response cookies
    duration: number;                // Request duration (ms)
    size: number;                    // Response size (bytes)
    redirected?: boolean;            // Whether redirected
}

🛠️ Use Cases

CLI Tool

#!/usr/bin/env node
import { ForgeContainer } from '@http-forge/core';

async function runTests(collectionPath: string) {
    const forge = new ForgeContainer();
    const collection = await forge.loadCollection(collectionPath);
    
    for (const request of collection.items) {
        const result = await forge.execute(request, collection);
        const tests = result.postResponseResult?.assertions || [];
        
        console.log(`\n${request.name}: ${result.response.status}`);
        tests.forEach(test => {
            console.log(`  ${test.passed ? '✓' : '✗'} ${test.name}`);
        });
    }
}

runTests(process.argv[2]);

CI/CD Integration

import { ForgeContainer } from '@http-forge/core';

async function ciTest() {
    const forge = new ForgeContainer({
        enableCookies: true,
        requestTimeout: 30000
    });
    
    forge.setEnvironment({
        baseUrl: process.env.API_URL,
        apiKey: process.env.API_KEY
    });
    
    const collection = await forge.loadCollection('./api-tests.forge.json');
    
    let failedTests = 0;
    for (const request of collection.items) {
        const result = await forge.execute(request, collection);
        const failed = result.postResponseResult?.assertions?.filter(t => !t.passed) || [];
        failedTests += failed.length;
    }
    
    process.exit(failedTests > 0 ? 1 : 0);
}

Custom Testing Framework

import { ForgeContainer } from '@http-forge/core';

class ApiTestRunner {
    private forge: ForgeContainer;
    
    constructor() {
        this.forge = new ForgeContainer({
            enableCookies: true,
            enableHistory: true
        });
    }
    
    async runSuite(suites: TestSuite[]) {
        for (const suite of suites) {
            await this.runTests(suite);
        }
    }
    
    async runTests(suite: TestSuite) {
        const collection = await this.forge.loadCollection(suite.collection);
        
        for (const request of collection.items) {
            const result = await this.forge.execute(request, collection);
            suite.results.push(result);
        }
    }
}

Multi-Request Workflows

Execute dependent request chains with automatic cookie and variable management:

import { ForgeContainer } from '@http-forge/core';

async function apiAuthWorkflow() {
    const forge = new ForgeContainer({
        enableCookies: true,  // Cookies persist across requests
        enableHistory: true
    });

    // Request 1: Login (sets session cookie)
    const loginReq = {
        name: 'Login',
        method: 'POST',
        url: 'https://api.example.com/auth/login',
        body: { type: 'raw', content: JSON.stringify({ 
            username: '{{email}}',
            password: '{{password}}'
        })},
        scripts: {
            postResponse: `
                const { token, userId } = pm.response.json();
                pm.environment.set('authToken', token);
                pm.environment.set('userId', userId);
            `
        }
    };
    
    const loginResult = await forge.execute(loginReq, collection);
    console.log('✓ Logged in, token:', forge.getResolvedEnvironment()['authToken']);
    
    // Request 2: Fetch user profile (uses session cookie automatically)
    const profileReq = {
        name: 'Get Profile',
        method: 'GET',
        url: 'https://api.example.com/users/{{userId}}',
        headers: {
            'Authorization': 'Bearer {{authToken}}'  // Uses token from login
        }
    };
    
    const profileResult = await forge.execute(profileReq, collection);
    console.log('✓ Profile:', profileResult.response.body);
    
    // Request 3: Update profile (session cookie still active)
    const updateReq = {
        name: 'Update Profile',
        method: 'PATCH',
        url: 'https://api.example.com/users/{{userId}}',
        headers: {
            'Authorization': 'Bearer {{authToken}}'
        },
        body: { type: 'raw', content: JSON.stringify({ status: 'active' })}
    };
    
    const updateResult = await forge.execute(updateReq, collection);
    console.log('✓ Updated profile');
    
    // Session automatically logged out - cookies cleared
    forge.getRequestHistory().clear();
}

📦 Storage Formats

File Format (Single JSON)

my-api.forge.json

Folder Format (Directory Structure)

my-api/
  collection.json
  requests/
    login/
      request.json
    users/
      get-users/
        request.json
      create-user/
        request.json

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT © Henry Huang

🔗 Links

📝 Changelog

0.2.0 (Postman API Parity)

  • Async pm.test() support - Tests with async callbacks are properly awaited
  • Expect chain assertions - .a(type), .an(type), .deep.equal(), .lengthOf(), .exist, .members(), .keys(), .string()
  • pm.iterationData scope - Data-driven testing with iteration variables
  • Response status getters - response.to.be.ok, .error, .clientError, .serverError work as getters and functions
  • Response headers HeaderList - .get(), .has(), .toObject(), .each() on pm.response.headers
  • pm.cookies.toObject() - Flat {name: value} cookie map
  • replaceIn() on all scopes - Available on pm.variables, pm.environment, pm.collectionVariables, pm.globals
  • Sandbox globals - xml2Json(), jsonStringify(), jsonParse() available in scripts
  • Request headers API - .toObject(), .each() on pm.request.headers
  • pm.execution.setNextRequest() - Runner flow control (jump to named request or stop with null)
  • pm.execution.skipRequest() - Skip HTTP call from pre-request scripts
  • pm.setNextRequest() - Top-level alias for flow control
  • pm.visualizer.set(template, data) - Custom HTML visualization with Handlebars templates
  • pm.request.url as Url object - Postman SDK-compatible Url with getHost(), getPath(), getQueryString(), protocol, host, port, path, query, hash
  • Full CryptoJS - AES/DES/TripleDES encrypt/decrypt, PBKDF2, all hash algorithms (SHA1/256/384/512/SHA3/MD5/RIPEMD160), HMAC, encoding helpers (Hex/Base64/Utf8/Latin1/Utf16)

0.1.0 (Initial Release)

  • Core request execution with Postman collection support
  • Dynamic variables - 7 generators for on-the-fly value generation
  • Postman-compatible scripting - pm.* API with full feature parity
  • Variable scoping - globals, collection, environment, session, flow-level
  • Cookie persistence - automatic storage and reuse across request chains
  • Pre-request & post-response scripts with shared VM context
  • Test assertions with BDD-style pm.test() and expect chains
  • Environment management with variable substitution
  • Cookie jar with domain-aware matching
  • Request/response interceptors for customization
  • Request history tracking
  • File and folder collection formats (Postman-compatible)
  • Full TypeScript support with comprehensive types
  • Dependency injection for extensibility
  • Comprehensive test coverage