@http-forge/core
v0.2.3
Published
Headless HTTP testing engine with Postman collection support, dynamic variables, and script-based automation.
Maintainers
Readme
@http-forge/core
Standalone HTTP testing engine with Postman collection support and JavaScript-based automation.
📦 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.jsonand.forge.jsonfiles - 📝 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 Chaiexpect()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/coreOr 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 resultsWith 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 resultsDynamic 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/sessionsCookies 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(): IRequestHistoryExecuteResult
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.jsonFolder 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.iterationDatascope - Data-driven testing with iteration variables - ✅ Response status getters -
response.to.be.ok,.error,.clientError,.serverErrorwork as getters and functions - ✅ Response headers HeaderList -
.get(),.has(),.toObject(),.each()onpm.response.headers - ✅
pm.cookies.toObject()- Flat{name: value}cookie map - ✅
replaceIn()on all scopes - Available onpm.variables,pm.environment,pm.collectionVariables,pm.globals - ✅ Sandbox globals -
xml2Json(),jsonStringify(),jsonParse()available in scripts - ✅ Request headers API -
.toObject(),.each()onpm.request.headers - ✅
pm.execution.setNextRequest()- Runner flow control (jump to named request or stop withnull) - ✅
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.urlas Url object - Postman SDK-compatible Url withgetHost(),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
