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.
- ✅ Parse and validate Zap Lang files (
api-collection
,api-folder
,api-request
,api-global
,api-environment
) - ✅ File System Discovery Mode: No
items
arrays 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
type
field 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
- 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
npm install zap-lang
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);
}
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' }
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: 'john@example.com'
};
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: 'john@example.com'
// }
// }
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)
}
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 }
Serializes a Zap object to a JSON string.
const json = serializeZap(zapObject, 2); // Pretty-printed with 2-space indent
Quick check if a string contains valid Zap Lang JSON.
if (isValidZapString(fileContent)) {
// Safe to parse
}
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[] }
Quick boolean check for object validity.
if (isValidZapObject(obj)) {
// Object is valid
}
Creates a new API request object with defaults.
const request = createApiRequest({
meta: { name: 'Get Users' },
request: {
method: 'GET',
url: '{{baseUrl}}/users'
}
});
Creates a new API collection object.
const collection = createApiCollection({
meta: { name: 'My API Collection' },
variables: { baseUrl: 'https://api.example.com' }
});
Creates a new API folder object.
const folder = createApiFolder({
meta: { name: 'User Management' },
variables: { endpoint: '/users' }
});
Creates a new environment object.
const env = createApiEnvironment({
meta: { name: 'Development' },
variables: { baseUrl: 'https://api.dev.example.com' }
});
Creates a new global variables object.
const global = createApiGlobal({
variables: { timeout: 5000, retries: 3 }
});
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
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'
Extracts variable names from a template string.
const vars = extractVariables('{{baseUrl}}/users/{{userId}}');
// Result: ['baseUrl', 'userId']
Loads all .zap files from a directory tree.
const files = loadZapFiles('./my-collection');
// Result: { 'folder/request.zap': requestObject, ... }
Filters loaded objects by type.
const requests = filterZapObjectsByType(files, 'api-request');
const collections = filterZapObjectsByType(files, 'api-collection');
Finds the root collection object.
const rootCollection = findCollectionRoot(files);
Gets all request objects from loaded files.
const requests = getAllRequests(files);
Gets all folder objects from loaded files.
const folders = getAllFolders(files);
Gets all environment objects.
const environments = getEnvironments(files);
Gets the global variables object.
const global = getGlobalVariables(files);
Recursively finds all requests within a collection or folder.
const requests = findAllRequests(collectionObject);
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
The current version of this zap-lang package.
The Zap Lang specification version supported (currently 1).
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: 'john@example.com'
});
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));
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);
}
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" } }
The package automatically detects request types based on structure:
-
HTTP Request: Has
method
andurl
fields -
GraphQL Request: Has
url
andbody.type: 'graphql'
fields -
WebSocket Request: Has
url
andwsMessage
fields -
File Request: Has
fileOp
field
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
# 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 dev
This package is built for browser environments and works in:
- VS Code workbench/webview environment
- Modern browsers (ES2020+)
- Node.js 16+ (with node build)
- 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)
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
MIT