zap-lang-suite
v1.0.0
Published
Zap Lang parser and validator - A human-friendly, JSON-based language for describing HTTP requests, collections, environments, and variables for API testing
Downloads
10
Maintainers
Readme
Zap Lang Package
A TypeScript utility library for parsing, validating, and transforming Zap Lang API collection files. It enables filesystem-first, portable, and human-readable API collections for use in VS Code, CLI, or browser-based API testing and automation tools.
Features
- ✅ Parse and validate Zap Lang files (
api-collection,api-folder,api-request,api-global,api-environment) - ✅ File System Discovery Mode: No
itemsarrays required; hierarchy is inferred from directory structure - ✅ Variable resolution and merging across all scopes (global, environment, collection, folder, request, session)
- ✅ Transforms Zap requests to Axios-compatible config objects (no Axios dependency - axios is dev dependency only)
- ✅ Supports HTTP, GraphQL, WebSocket, and File operations (protocol is inferred by structure, not a
typefield in request) - ✅ Web-compatible build - works in browser environments like VS Code workbench
- ✅ Comprehensive test coverage with real sample collections and live API execution
- ✅ Strictly follows the Zap Lang Specification
Test Coverage
- 56 passing tests covering all functionality
- Live API execution tests against JSONPlaceholder API
- End-to-end validation of all sample collection files
- Variable resolution at all scopes (collection, folder, request)
- Axios conversion for all HTTP methods (GET, POST, PUT, DELETE)
- Error handling and edge cases
Installation
npm install zap-langQuick Start
Basic Parsing and Validation
import { parseZap, validateZapObject } from 'zap-lang';
// Parse a Zap Lang file
const zapFileContent = `{
"zapType": "api-request",
"zapVersion": 1,
"meta": { "name": "Get Users" },
"request": {
"method": "GET",
"url": "{{baseUrl}}/users",
"headers": [
{ "name": "Authorization", "value": "Bearer {{token}}" }
]
}
}`;
const result = parseZap(zapFileContent);
if (result.validation.valid) {
console.log('Valid Zap request:', result.object);
} else {
console.error('Validation errors:', result.validation.errors);
}Variable Merging
import { mergeZapVariables } from 'zap-lang';
const variables = mergeZapVariables({
global: { zapType: 'api-global', zapVersion: 1, variables: { timeout: 5000 } },
environment: { zapType: 'api-environment', zapVersion: 1, variables: { baseUrl: 'https://api.dev.example.com' } },
collection: { zapType: 'api-collection', zapVersion: 1, meta: { name: 'Users' }, variables: { version: 'v1' } },
sessionVars: { token: 'abc123' }
});
// Result: { timeout: 5000, baseUrl: 'https://api.dev.example.com', version: 'v1', token: 'abc123' }Converting to Axios Config
import { zapRequestToAxiosConfig } from 'zap-lang';
const zapRequest = {
zapType: 'api-request',
zapVersion: 1,
meta: { name: 'Create User' },
request: {
method: 'POST',
url: '{{baseUrl}}/users',
headers: [
{ name: 'Content-Type', value: 'application/json' },
{ name: 'Authorization', value: 'Bearer {{token}}' }
],
body: {
type: 'json',
content: '{"name": "{{userName}}", "email": "{{userEmail}}"}'
}
}
};
const variables = {
baseUrl: 'https://api.example.com',
token: 'abc123',
userName: 'John Doe',
userEmail: '[email protected]'
};
const axiosConfig = zapRequestToAxiosConfig(zapRequest, variables);
// Result: Ready-to-use Axios config object
console.log(axiosConfig);
// {
// method: 'post',
// url: 'https://api.example.com/users',
// headers: {
// 'Content-Type': 'application/json',
// 'Authorization': 'Bearer abc123'
// },
// data: {
// name: 'John Doe',
// email: '[email protected]'
// }
// }File System Discovery
import { loadZapFiles, getAllRequests, mergeZapVariables } from 'zap-lang';
// Load all .zap files from a directory structure
const files = loadZapFiles('./my-api-collection');
// Returns a flat object with all parsed .zap files keyed by relative path
// Get all requests from the loaded files
const requests = getAllRequests(files);
// Example: Execute all requests in a collection
for (const [path, request] of Object.entries(requests)) {
const variables = mergeZapVariables({
collection: files['collection.zap'],
request: request,
sessionVars: { token: 'your-token' }
});
const axiosConfig = zapRequestToAxiosConfig(request, variables);
// Execute with axios: await axios(axiosConfig)
}API Reference
Parsing Functions
parseZap(content: string): ZapParseResult
Parses a JSON string into a validated Zap object.
const result = parseZap('{"zapType": "api-request", ...}');
if (result.validation.valid) {
console.log(result.object); // Parsed Zap object
} else {
console.log(result.validation.errors); // Validation errors
}Returns: { object: ZapObject | null, validation: ZapValidationResult }
serializeZap(obj: ZapObject, indent?: number): string
Serializes a Zap object to a JSON string.
const json = serializeZap(zapObject, 2); // Pretty-printed with 2-space indentisValidZapString(content: string): boolean
Quick check if a string contains valid Zap Lang JSON.
if (isValidZapString(fileContent)) {
// Safe to parse
}Validation Functions
validateZapObject(obj: any): ZapValidationResult
Validates any object against Zap Lang schema.
const validation = validateZapObject(someObject);
if (validation.valid) {
// Object is valid
} else {
console.log(validation.errors); // Array of error messages
}Returns: { valid: boolean, errors: string[] }
isValidZapObject(obj: any): boolean
Quick boolean check for object validity.
if (isValidZapObject(obj)) {
// Object is valid
}Creator Functions
createApiRequest(options: Partial<ZapApiRequest>): ZapApiRequest
Creates a new API request object with defaults.
const request = createApiRequest({
meta: { name: 'Get Users' },
request: {
method: 'GET',
url: '{{baseUrl}}/users'
}
});createApiCollection(options: Partial<ZapApiCollection>): ZapApiCollection
Creates a new API collection object.
const collection = createApiCollection({
meta: { name: 'My API Collection' },
variables: { baseUrl: 'https://api.example.com' }
});createApiFolder(options: Partial<ZapApiFolder>): ZapApiFolder
Creates a new API folder object.
const folder = createApiFolder({
meta: { name: 'User Management' },
variables: { endpoint: '/users' }
});createApiEnvironment(options: Partial<ZapApiEnvironment>): ZapApiEnvironment
Creates a new environment object.
const env = createApiEnvironment({
meta: { name: 'Development' },
variables: { baseUrl: 'https://api.dev.example.com' }
});createApiGlobal(options: Partial<ZapApiGlobal>): ZapApiGlobal
Creates a new global variables object.
const global = createApiGlobal({
variables: { timeout: 5000, retries: 3 }
});Variable Functions
mergeZapVariables(options: MergeVariablesOptions): Record<string, any>
Merges variables from all scopes with proper precedence.
const variables = mergeZapVariables({
global: globalVarsObject, // Lowest precedence
environment: environmentObject,
collection: collectionObject,
folder: folderObject,
request: requestObject,
sessionVars: { token: 'abc123' } // Highest precedence
});Precedence (lowest to highest): global → environment → collection → folder → request → session
replaceVariables(template: string, variables: Record<string, any>): string
Replaces {{variable}} placeholders in a string.
const url = replaceVariables('{{baseUrl}}/users/{{userId}}', {
baseUrl: 'https://api.example.com',
userId: '123'
});
// Result: 'https://api.example.com/users/123'extractVariables(template: string): string[]
Extracts variable names from a template string.
const vars = extractVariables('{{baseUrl}}/users/{{userId}}');
// Result: ['baseUrl', 'userId']Discovery Functions
loadZapFiles(directory: string): Record<string, ZapObject>
Loads all .zap files from a directory tree.
const files = loadZapFiles('./my-collection');
// Result: { 'folder/request.zap': requestObject, ... }filterZapObjectsByType<T>(objects: Record<string, ZapObject>, zapType: ZapType): Record<string, T>
Filters loaded objects by type.
const requests = filterZapObjectsByType(files, 'api-request');
const collections = filterZapObjectsByType(files, 'api-collection');findCollectionRoot(objects: Record<string, ZapObject>): ZapApiCollection | null
Finds the root collection object.
const rootCollection = findCollectionRoot(files);getAllRequests(objects: Record<string, ZapObject>): Record<string, ZapApiRequest>
Gets all request objects from loaded files.
const requests = getAllRequests(files);getAllFolders(objects: Record<string, ZapObject>): Record<string, ZapApiFolder>
Gets all folder objects from loaded files.
const folders = getAllFolders(files);getEnvironments(objects: Record<string, ZapObject>): Record<string, ZapApiEnvironment>
Gets all environment objects.
const environments = getEnvironments(files);getGlobalVariables(objects: Record<string, ZapObject>): ZapApiGlobal | null
Gets the global variables object.
const global = getGlobalVariables(files);findAllRequests(obj: ZapObject): ZapApiRequest[]
Recursively finds all requests within a collection or folder.
const requests = findAllRequests(collectionObject);Axios Adapter
zapRequestToAxiosConfig(zapRequest: ZapApiRequest, variables: Record<string, any>): AxiosRequestConfig
Converts a Zap request to an Axios-compatible configuration object.
const axiosConfig = zapRequestToAxiosConfig(zapRequest, variables);
// Use with axios: axios(axiosConfig)Features:
- Handles all HTTP methods (GET, POST, PUT, DELETE, PATCH, etc.)
- Processes all body types (json, raw, form, urlencoded, binary, graphql)
- Converts headers array to object format
- Applies variable substitution
- Handles authentication (Bearer, Basic, API Key)
- Sets appropriate content-type headers
Utility Exports
ZAP_LANG_VERSION: string
The current version of this zap-lang package.
ZAP_SPEC_VERSION: number
The Zap Lang specification version supported (currently 1).
Advanced Examples
Complete End-to-End Workflow
import {
loadZapFiles,
getAllRequests,
mergeZapVariables,
zapRequestToAxiosConfig,
getEnvironments,
getGlobalVariables
} from 'zap-lang';
import axios from 'axios';
// 1. Load collection from filesystem
const files = loadZapFiles('./my-api-collection');
// 2. Get environment and global variables
const environments = getEnvironments(files);
const global = getGlobalVariables(files);
const environment = environments['development.zap']; // or user selection
// 3. Get all requests
const requests = getAllRequests(files);
// 4. Execute a specific request
async function executeRequest(requestPath: string, sessionVars: Record<string, any> = {}) {
const request = requests[requestPath];
if (!request) throw new Error(`Request not found: ${requestPath}`);
// Merge variables from all scopes
const variables = mergeZapVariables({
global,
environment,
collection: files['collection.zap'],
request,
sessionVars
});
// Convert to axios config
const axiosConfig = zapRequestToAxiosConfig(request, variables);
// Execute the request
try {
const response = await axios(axiosConfig);
console.log(`✅ ${request.meta.name}:`, response.status, response.data);
return response;
} catch (error) {
console.error(`❌ ${request.meta.name}:`, error.message);
throw error;
}
}
// Execute requests
await executeRequest('users/create-user.zap', {
userName: 'John Doe',
userEmail: '[email protected]'
});Creating Collections Programmatically
import {
createApiCollection,
createApiFolder,
createApiRequest,
serializeZap
} from 'zap-lang';
// Create a new collection
const collection = createApiCollection({
meta: {
name: 'Users API',
description: 'CRUD operations for user management'
},
variables: {
baseUrl: 'https://api.example.com',
version: 'v1'
}
});
// Create a folder
const usersFolder = createApiFolder({
meta: { name: 'User Operations' },
variables: { endpoint: '/users' }
});
// Create requests
const createUserRequest = createApiRequest({
meta: { name: 'Create User' },
request: {
method: 'POST',
url: '{{baseUrl}}/{{version}}{{endpoint}}',
headers: [
{ name: 'Content-Type', value: 'application/json' },
{ name: 'Authorization', value: 'Bearer {{token}}' }
],
body: {
type: 'json',
content: JSON.stringify({
name: '{{userName}}',
email: '{{userEmail}}'
})
}
}
});
// Serialize to JSON
console.log(serializeZap(collection, 2));
console.log(serializeZap(createUserRequest, 2));Dynamic Variable Resolution
import { mergeZapVariables, replaceVariables, extractVariables } from 'zap-lang';
// Complex variable merging with session state
const variables = mergeZapVariables({
global: { timeout: 5000, retries: 3 },
environment: { baseUrl: 'https://api.dev.example.com' },
collection: { version: 'v1' },
folder: { endpoint: '/users' },
request: { userId: '{{createdUserId}}' }, // Dynamic from previous request
sessionVars: {
token: 'session-token',
createdUserId: '12345' // From previous request response
}
});
// Template processing
const urlTemplate = '{{baseUrl}}/{{version}}{{endpoint}}/{{userId}}';
const resolvedUrl = replaceVariables(urlTemplate, variables);
console.log(resolvedUrl); // 'https://api.dev.example.com/v1/users/12345'
// Find missing variables
const requiredVars = extractVariables('{{baseUrl}}/{{version}}/{{missingVar}}');
const missingVars = requiredVars.filter(v => !(v in variables));
if (missingVars.length > 0) {
console.warn('Missing variables:', missingVars);
}GraphQL Request Example
const graphqlRequest = createApiRequest({
meta: { name: 'Get User Profile' },
request: {
url: '{{graphqlEndpoint}}',
headers: [
{ name: 'Content-Type', value: 'application/json' },
{ name: 'Authorization', value: 'Bearer {{token}}' }
],
body: {
type: 'graphql',
content: `
query GetUser($userId: ID!) {
user(id: $userId) {
id
name
email
profile {
avatar
bio
}
}
}
`
}
},
variables: {
userId: '{{currentUserId}}'
}
});
// Convert to axios config
const axiosConfig = zapRequestToAxiosConfig(graphqlRequest, {
graphqlEndpoint: 'https://api.example.com/graphql',
token: 'your-token',
currentUserId: '123'
});
// The body will be properly formatted as:
// { query: "...", variables: { userId: "123" } }Request Types
The package automatically detects request types based on structure:
- HTTP Request: Has
methodandurlfields - GraphQL Request: Has
urlandbody.type: 'graphql'fields - WebSocket Request: Has
urlandwsMessagefields - File Request: Has
fileOpfield
Body Types
Supported body types in requests:
{ type: 'json', content: '...' }- JSON content{ type: 'raw', content: '...' }- Raw text content{ type: 'form', form: [{ name: '...', value: '...' }] }- Form data{ type: 'urlencoded', urlencoded: [{ name: '...', value: '...' }] }- URL-encoded form{ type: 'binary', file: '...' }- Binary file{ type: 'none' }- No body{ type: 'graphql', content: '...' }- GraphQL query
Development
# Install dependencies
npm install
# Run tests
npm test
# Build for production
npm run build
# Build for Node.js environment
npm run build:node
# Development mode with watch
npm run devBrowser Compatibility
This package is built for browser environments and works in:
- VS Code workbench/webview environment
- Modern browsers (ES2020+)
- Node.js 16+ (with node build)
What this package does NOT do
- Does NOT perform HTTP requests (no fetch/axios runtime dependency)
- Does NOT manage request execution or test running
- Does NOT provide CLI tools (focused on library functionality)
- Does NOT require Node.js specific APIs (web-compatible)
Testing
The package includes comprehensive tests covering:
- Parsing and validation of all Zap Lang object types
- Variable merging and precedence
- Axios adapter functionality
- File system discovery
- Real-world sample collections
License
MIT
