UNPKG

zapsea

Version:

Official JavaScript SDK for ZapSEA Intelligence Engine API

1,031 lines (1,022 loc) 33.5 kB
/** * Impact Analysis Resource */ class ImpactResource { constructor(client) { Object.defineProperty(this, "client", { enumerable: true, configurable: true, writable: true, value: client }); } /** * Submit policy impact simulation * * @example * ```typescript * const job = await client.impact.simulate({ * policyDescription: 'Federal AI regulation requiring algorithmic transparency', * analysisDepth: 'comprehensive', * focusAreas: ['financial_services', 'artificial_intelligence'], * timeHorizon: '12_months' * }); * * console.log(`Job submitted: ${job.jobId}`); * ``` */ async simulate(request) { this._validateSimulationRequest(request); return this.client.request('POST', '/v2/impact/simulate', request); } /** * Compare multiple policy scenarios * * @example * ```typescript * const job = await client.impact.compareScenarios({ * scenarios: [ * { * name: 'Strict Regulation', * policyDescription: 'Mandatory AI audits for all financial algorithms' * }, * { * name: 'Self-Regulation', * policyDescription: 'Industry-led voluntary AI transparency standards' * } * ], * comparisonCriteria: ['implementation_cost', 'effectiveness', 'industry_acceptance'] * }); * ``` */ async compareScenarios(request) { this._validateScenarioComparisonRequest(request); return this.client.request('POST', '/v2/impact/compare', request); } /** * Get impact simulation result by job ID * * @example * ```typescript * const result = await client.impact.getResult('job_abc123'); * console.log(`Success probability: ${result.successProbability}`); * ``` */ async getResult(jobId) { if (!jobId) { throw new Error('Job ID is required'); } return this.client.request('GET', `/v2/impact/results/${jobId}`); } /** * Validate impact simulation request */ _validateSimulationRequest(request) { if (!request.policyDescription || typeof request.policyDescription !== 'string') { throw new Error('Policy description is required and must be a string'); } if (request.policyDescription.trim().length < 10) { throw new Error('Policy description must be at least 10 characters long'); } if (request.analysisDepth && !['quick', 'standard', 'comprehensive'].includes(request.analysisDepth)) { throw new Error('Analysis depth must be one of: quick, standard, comprehensive'); } if (request.focusAreas && !Array.isArray(request.focusAreas)) { throw new Error('Focus areas must be an array of strings'); } } /** * Validate scenario comparison request */ _validateScenarioComparisonRequest(request) { if (!request.scenarios || !Array.isArray(request.scenarios)) { throw new Error('Scenarios must be an array'); } if (request.scenarios.length < 2) { throw new Error('At least 2 scenarios are required for comparison'); } if (request.scenarios.length > 5) { throw new Error('Maximum 5 scenarios allowed for comparison'); } for (const [index, scenario] of request.scenarios.entries()) { if (!scenario.name || typeof scenario.name !== 'string') { throw new Error(`Scenario ${index + 1}: name is required and must be a string`); } if (!scenario.policyDescription || typeof scenario.policyDescription !== 'string') { throw new Error(`Scenario ${index + 1}: policy description is required and must be a string`); } if (scenario.policyDescription.trim().length < 10) { throw new Error(`Scenario ${index + 1}: policy description must be at least 10 characters long`); } } if (request.comparisonCriteria && !Array.isArray(request.comparisonCriteria)) { throw new Error('Comparison criteria must be an array of strings'); } } } /** * Influence Analysis Resource */ class InfluenceResource { constructor(client) { Object.defineProperty(this, "client", { enumerable: true, configurable: true, writable: true, value: client }); } /** * Find influence path between entities * * @example * ```typescript * const job = await client.influence.findPath({ * sourceEntity: 'Congress', * targetEntity: 'Tech Industry', * maxPathLength: 5, * minInfluenceThreshold: 0.3 * }); * * console.log(`Job submitted: ${job.jobId}`); * ``` */ async findPath(request) { this._validateInfluencePathRequest(request); return this.client.request('POST', '/v2/influence/path', request); } /** * Get influence network for an entity * * @example * ```typescript * const job = await client.influence.getNetwork({ * entity: 'Federal Reserve', * depth: 2, * minInfluenceThreshold: 0.2 * }); * ``` */ async getNetwork(request) { this._validateNetworkRequest(request); return this.client.request('POST', '/v2/influence/network', request); } /** * Get influence path result by job ID * * @example * ```typescript * const result = await client.influence.getPathResult('job_abc123'); * console.log(`Path length: ${result.pathLength}`); * ``` */ async getPathResult(jobId) { if (!jobId) { throw new Error('Job ID is required'); } return this.client.request('GET', `/v2/influence/results/${jobId}`); } /** * Get top influencers in a domain * * @example * ```typescript * const influencers = await client.influence.getTopInfluencers({ * domain: 'technology_policy', * limit: 10 * }); * ``` */ async getTopInfluencers(request) { this._validateTopInfluencersRequest(request); return this.client.request('GET', '/v2/influence/top-influencers', request); } /** * Validate influence path request */ _validateInfluencePathRequest(request) { if (!request.sourceEntity || typeof request.sourceEntity !== 'string') { throw new Error('Source entity is required and must be a string'); } if (!request.targetEntity || typeof request.targetEntity !== 'string') { throw new Error('Target entity is required and must be a string'); } if (request.sourceEntity.trim().length < 2) { throw new Error('Source entity must be at least 2 characters long'); } if (request.targetEntity.trim().length < 2) { throw new Error('Target entity must be at least 2 characters long'); } if (request.sourceEntity === request.targetEntity) { throw new Error('Source and target entities must be different'); } if (request.maxPathLength !== undefined) { if (typeof request.maxPathLength !== 'number' || request.maxPathLength < 1 || request.maxPathLength > 10) { throw new Error('Max path length must be a number between 1 and 10'); } } if (request.minInfluenceThreshold !== undefined) { if (typeof request.minInfluenceThreshold !== 'number' || request.minInfluenceThreshold < 0 || request.minInfluenceThreshold > 1) { throw new Error('Min influence threshold must be a number between 0 and 1'); } } } /** * Validate network request */ _validateNetworkRequest(request) { if (!request.entity || typeof request.entity !== 'string') { throw new Error('Entity is required and must be a string'); } if (request.entity.trim().length < 2) { throw new Error('Entity must be at least 2 characters long'); } if (request.depth !== undefined) { if (typeof request.depth !== 'number' || request.depth < 1 || request.depth > 5) { throw new Error('Depth must be a number between 1 and 5'); } } if (request.minInfluenceThreshold !== undefined) { if (typeof request.minInfluenceThreshold !== 'number' || request.minInfluenceThreshold < 0 || request.minInfluenceThreshold > 1) { throw new Error('Min influence threshold must be a number between 0 and 1'); } } } /** * Validate top influencers request */ _validateTopInfluencersRequest(request) { if (!request.domain || typeof request.domain !== 'string') { throw new Error('Domain is required and must be a string'); } if (request.domain.trim().length < 2) { throw new Error('Domain must be at least 2 characters long'); } if (request.limit !== undefined) { if (typeof request.limit !== 'number' || request.limit < 1 || request.limit > 100) { throw new Error('Limit must be a number between 1 and 100'); } } if (request.timeframe !== undefined) { const validTimeframes = ['1d', '7d', '30d', '90d', '1y']; if (!validTimeframes.includes(request.timeframe)) { throw new Error(`Timeframe must be one of: ${validTimeframes.join(', ')}`); } } } } /** * Error classes for the ZapSEA JavaScript SDK */ class ZapSEAError extends Error { constructor(message, status, code) { super(message); Object.defineProperty(this, "status", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "code", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.name = 'ZapSEAError'; this.status = status ?? undefined; this.code = code ?? undefined; // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, ZapSEAError); } } static async fromResponse(response) { let message = `HTTP ${response.status}`; let code; try { const errorData = await response.json(); message = errorData.detail || errorData.message || message; code = errorData.code; } catch { message = `HTTP ${response.status}: ${response.statusText}`; } // Map status codes to specific error types switch (response.status) { case 401: return new AuthenticationError(message, response.status, code); case 403: return new PermissionError(message, response.status, code); case 404: return new NotFoundError(message, response.status, code); case 429: return new RateLimitError(message, response.status, code); case 422: return new ValidationError(message, response.status, code); default: return new ZapSEAError(message, response.status, code); } } } class AuthenticationError extends ZapSEAError { constructor(message, status, code) { super(message, status, code); this.name = 'AuthenticationError'; } } class PermissionError extends ZapSEAError { constructor(message, status, code) { super(message, status, code); this.name = 'PermissionError'; } } class NotFoundError extends ZapSEAError { constructor(message, status, code) { super(message, status, code); this.name = 'NotFoundError'; } } class RateLimitError extends ZapSEAError { constructor(message, status, code) { super(message, status, code); this.name = 'RateLimitError'; } } class JobNotFoundError extends NotFoundError { constructor(message) { super(message, 404); this.name = 'JobNotFoundError'; } } class TimeoutError extends ZapSEAError { constructor(message) { super(message); this.name = 'TimeoutError'; } } class ValidationError extends ZapSEAError { constructor(message, status, code) { super(message, status, code); this.name = 'ValidationError'; } } class NetworkError extends ZapSEAError { constructor(message, cause) { super(message); Object.defineProperty(this, "cause", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.name = 'NetworkError'; this.cause = cause; } } /** * Utility functions for the ZapSEA JavaScript SDK */ /** * Sleep for a specified number of milliseconds */ function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Check if we're running in a browser environment */ function isBrowser() { return typeof window !== 'undefined' && typeof window.document !== 'undefined'; } /** * Validate API key format */ function validateApiKey(apiKey) { if (!apiKey || typeof apiKey !== 'string') { return false; } // API keys should start with pk_live_ or pk_test_ return /^pk_(live|test)_[a-zA-Z0-9]{32,}$/.test(apiKey); } /** * Sanitize URL by removing trailing slashes */ function sanitizeUrl(url) { return url.replace(/\/+$/, ''); } /** * Create a user agent string for the SDK */ function createUserAgent(version = '1.0.0') { const platform = isBrowser() ? 'browser' : 'node'; return `zapsea-js/${version} (${platform})`; } /** * Determine if an error should be retried */ function shouldRetryError(error) { // Retry on network errors if (error.name === 'AbortError' || error.name === 'NetworkError') { return true; } // Retry on 5xx status codes if (error.status >= 500 && error.status < 600) { return true; } // Retry on 429 (rate limit) with backoff if (error.status === 429) { return true; } return false; } /** * Calculate exponential backoff delay */ function calculateBackoffDelay(attempt, baseDelay = 1000) { return Math.min(baseDelay * Math.pow(2, attempt), 30000); // Max 30 seconds } /** * Validate job ID format */ function validateJobId(jobId) { if (!jobId || typeof jobId !== 'string') { return false; } // Job IDs should be UUIDs or similar format return /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(jobId) || /^job_[a-zA-Z0-9]{16,}$/.test(jobId); } /** * Jobs Resource for managing asynchronous operations */ class JobsResource { constructor(client) { Object.defineProperty(this, "client", { enumerable: true, configurable: true, writable: true, value: client }); } /** * Get job status and results * * @example * ```typescript * const status = await client.jobs.get('job_abc123'); * * if (status.status === 'completed') { * console.log('Results:', status.resultData); * } * ``` */ async get(jobId) { if (!validateJobId(jobId)) { throw new Error('Invalid job ID format'); } try { return await this.client.request('GET', `/v2/jobs/${jobId}`); } catch (error) { if (error.status === 404) { throw new JobNotFoundError(`Job ${jobId} not found`); } throw error; } } /** * Wait for job completion with polling * * @example * ```typescript * const result = await client.jobs.waitForCompletion('job_abc123', { * timeout: 300000, // 5 minutes * pollInterval: 5000 // 5 seconds * }); * * console.log('Final result:', result.resultData); * ``` */ async waitForCompletion(jobId, options = {}) { const { timeout = 300000, pollInterval = 5000, onProgress } = options; if (!validateJobId(jobId)) { throw new Error('Invalid job ID format'); } if (timeout < 1000) { throw new Error('Timeout must be at least 1000ms'); } if (pollInterval < 1000) { throw new Error('Poll interval must be at least 1000ms'); } const startTime = Date.now(); while (Date.now() - startTime < timeout) { const status = await this.get(jobId); if (onProgress) { onProgress(status); } if (status.status === 'completed') { return status; } else if (status.status === 'failed') { throw new Error(`Job ${jobId} failed: ${status.errorMessage || 'Unknown error'}`); } else if (status.status === 'cancelled') { throw new Error(`Job ${jobId} was cancelled`); } await delay(pollInterval); } throw new TimeoutError(`Job ${jobId} did not complete within ${timeout}ms`); } /** * List user's jobs * * @example * ```typescript * const jobs = await client.jobs.list({ * statusFilter: 'completed', * limit: 10 * }); * * jobs.forEach(job => { * console.log(`${job.jobId}: ${job.status}`); * }); * ``` */ async list(options = {}) { this._validateListRequest(options); const params = new URLSearchParams(); if (options.statusFilter) { params.append('status', options.statusFilter); } if (options.limit) { params.append('limit', options.limit.toString()); } if (options.offset) { params.append('offset', options.offset.toString()); } const queryString = params.toString(); const endpoint = `/v2/jobs${queryString ? `?${queryString}` : ''}`; const response = await this.client.request('GET', endpoint); return response.jobs; } /** * Cancel a running job * * @example * ```typescript * await client.jobs.cancel('job_abc123'); * console.log('Job cancelled successfully'); * ``` */ async cancel(jobId) { if (!validateJobId(jobId)) { throw new Error('Invalid job ID format'); } await this.client.request('POST', `/v2/jobs/${jobId}/cancel`); } /** * Get job logs for debugging * * @example * ```typescript * const logs = await client.jobs.getLogs('job_abc123'); * console.log('Job logs:', logs); * ``` */ async getLogs(jobId) { if (!validateJobId(jobId)) { throw new Error('Invalid job ID format'); } return this.client.request('GET', `/v2/jobs/${jobId}/logs`); } /** * Stream job progress updates * * @example * ```typescript * for await (const update of client.jobs.streamProgress('job_abc123')) { * console.log(`Progress: ${update.progressPercentage}%`); * if (update.status === 'completed') break; * } * ``` */ async *streamProgress(jobId, pollInterval = 3000) { if (!validateJobId(jobId)) { throw new Error('Invalid job ID format'); } if (pollInterval < 1000) { throw new Error('Poll interval must be at least 1000ms'); } while (true) { const status = await this.get(jobId); yield status; if (['completed', 'failed', 'cancelled'].includes(status.status)) { break; } await delay(pollInterval); } } /** * Validate list request parameters */ _validateListRequest(options) { if (options.statusFilter) { const validStatuses = ['processing', 'completed', 'failed', 'cancelled']; if (!validStatuses.includes(options.statusFilter)) { throw new Error(`Status filter must be one of: ${validStatuses.join(', ')}`); } } if (options.limit !== undefined) { if (typeof options.limit !== 'number' || options.limit < 1 || options.limit > 100) { throw new Error('Limit must be a number between 1 and 100'); } } if (options.offset !== undefined) { if (typeof options.offset !== 'number' || options.offset < 0) { throw new Error('Offset must be a non-negative number'); } } } } /** * Feedback Resource for collecting user feedback */ class FeedbackResource { constructor(client) { Object.defineProperty(this, "client", { enumerable: true, configurable: true, writable: true, value: client }); } /** * Submit user feedback * * @example * ```typescript * const response = await client.feedback.submit({ * page: 'impact-simulation', * rating: 5, * comment: 'Great feature! Very helpful for policy analysis.', * feedbackType: 'general', * category: 'usability' * }); * * console.log('Feedback submitted:', response.feedbackId); * ``` */ async submit(request) { this._validateFeedbackRequest(request); return this.client.request('POST', '/v2/feedback', request); } /** * Submit bug report * * @example * ```typescript * const response = await client.feedback.reportBug({ * page: 'dashboard', * comment: 'Charts not loading properly on mobile devices', * apiEndpoint: '/v2/impact/simulate', * sessionId: 'session_123' * }); * ``` */ async reportBug(request) { const feedbackRequest = { ...request, feedbackType: 'bug_report', category: 'technical' }; return this.submit(feedbackRequest); } /** * Submit feature request * * @example * ```typescript * const response = await client.feedback.requestFeature({ * page: 'general', * comment: 'Would love to see real-time collaboration features', * customerEmail: 'user@company.com', * customerOrganization: 'Acme Corp' * }); * ``` */ async requestFeature(request) { const feedbackRequest = { ...request, feedbackType: 'feature_request', category: 'enhancement' }; return this.submit(feedbackRequest); } /** * Submit rating only (quick feedback) * * @example * ```typescript * const response = await client.feedback.rate({ * page: 'api-documentation', * rating: 4 * }); * ``` */ async rate(request) { const feedbackRequest = { ...request, feedbackType: 'general' }; return this.submit(feedbackRequest); } /** * Get feedback status * * @example * ```typescript * const status = await client.feedback.getStatus('feedback_abc123'); * console.log('Feedback status:', status); * ``` */ async getStatus(feedbackId) { if (!feedbackId) { throw new Error('Feedback ID is required'); } return this.client.request('GET', `/v2/feedback/${feedbackId}`); } /** * Validate feedback request */ _validateFeedbackRequest(request) { if (!request.page || typeof request.page !== 'string') { throw new Error('Page is required and must be a string'); } if (request.page.trim().length < 1) { throw new Error('Page must not be empty'); } if (request.rating !== undefined) { if (typeof request.rating !== 'number' || !Number.isInteger(request.rating) || request.rating < 1 || request.rating > 5) { throw new Error('Rating must be an integer between 1 and 5'); } } if (request.comment !== undefined) { if (typeof request.comment !== 'string') { throw new Error('Comment must be a string'); } if (request.comment.trim().length > 5000) { throw new Error('Comment must be 5000 characters or less'); } } if (request.feedbackType !== undefined) { const validTypes = ['general', 'bug_report', 'feature_request', 'support']; if (!validTypes.includes(request.feedbackType)) { throw new Error(`Feedback type must be one of: ${validTypes.join(', ')}`); } } if (request.customerEmail !== undefined) { if (typeof request.customerEmail !== 'string') { throw new Error('Customer email must be a string'); } // Basic email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(request.customerEmail)) { throw new Error('Customer email must be a valid email address'); } } if (request.customerOrganization !== undefined) { if (typeof request.customerOrganization !== 'string') { throw new Error('Customer organization must be a string'); } if (request.customerOrganization.trim().length > 255) { throw new Error('Customer organization must be 255 characters or less'); } } // Ensure either rating or comment is provided if (!request.rating && (!request.comment || request.comment.trim().length === 0)) { throw new Error('Either rating or comment must be provided'); } } } /** * Main ZapSEA API client */ class ZapSEA { /** * ZapSEA API Client * * @example * ```typescript * const client = new ZapSEA({ * apiKey: 'pk_live_...', * baseUrl: 'https://api.politycs.ai' * }); * * const job = await client.impact.simulate({ * policyDescription: 'AI regulation for financial services', * analysisDepth: 'comprehensive' * }); * ``` */ constructor(config) { Object.defineProperty(this, "apiKey", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "baseUrl", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "timeout", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "maxRetries", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "userAgent", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "impact", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "influence", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "jobs", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "feedback", { enumerable: true, configurable: true, writable: true, value: void 0 }); // Validate configuration if (!config.apiKey) { throw new Error('API key is required'); } if (!validateApiKey(config.apiKey)) { throw new Error('Invalid API key format. API key should start with pk_live_ or pk_test_'); } this.apiKey = config.apiKey; this.baseUrl = sanitizeUrl(config.baseUrl || 'https://api.politycs.ai'); this.timeout = config.timeout || 60000; this.maxRetries = Math.max(0, config.maxRetries || 3); this.userAgent = createUserAgent(); // Initialize resource clients this.impact = new ImpactResource(this); this.influence = new InfluenceResource(this); this.jobs = new JobsResource(this); this.feedback = new FeedbackResource(this); } /** * Make authenticated HTTP request */ async request(method, endpoint, data, options) { const url = `${this.baseUrl}${endpoint}`; const config = { method, headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'User-Agent': this.userAgent, ...options?.headers }, ...options }; if (data && (method === 'POST' || method === 'PUT')) { config.body = JSON.stringify(data); } // Implement retry logic with exponential backoff let lastError; for (let attempt = 0; attempt <= this.maxRetries; attempt++) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); const response = await fetch(url, { ...config, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { throw await ZapSEAError.fromResponse(response); } // Handle empty responses const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } else { return {}; } } catch (error) { lastError = error; // Don't retry on the last attempt or if error shouldn't be retried if (attempt === this.maxRetries || !shouldRetryError(error)) { break; } // Calculate backoff delay const backoffDelay = calculateBackoffDelay(attempt); await delay(backoffDelay); } } // Wrap network errors if (lastError.name === 'AbortError') { throw new NetworkError(`Request timeout after ${this.timeout}ms`, lastError); } throw lastError; } /** * Check API health status */ async healthCheck() { return this.request('GET', '/v2/health'); } /** * Get current API configuration */ getConfig() { return { baseUrl: this.baseUrl, timeout: this.timeout, maxRetries: this.maxRetries, userAgent: this.userAgent }; } /** * Update client configuration */ updateConfig(updates) { if (updates.baseUrl !== undefined) { this.baseUrl = sanitizeUrl(updates.baseUrl); } if (updates.timeout !== undefined) { this.timeout = Math.max(1000, updates.timeout); // Minimum 1 second } if (updates.maxRetries !== undefined) { this.maxRetries = Math.max(0, updates.maxRetries); } } } /** * ZapSEA JavaScript SDK * * A production-ready JavaScript SDK for the ZapSEA Intelligence Engine API V2. * Provides simple, async-first interfaces for policy impact simulation and analysis. * * @example * ```typescript * import { ZapSEA } from 'zapsea'; * * // Initialize client * const client = new ZapSEA({ * apiKey: 'pk_live_your_api_key_here' * }); * * // Run impact simulation * const job = await client.impact.simulate({ * policyDescription: 'AI regulation for financial services', * analysisDepth: 'comprehensive' * }); * * // Wait for completion * const result = await client.jobs.waitForCompletion(job.jobId); * console.log('Analysis complete:', result.resultData); * ``` */ // Main client // Version const VERSION = '1.0.0'; export { AuthenticationError, FeedbackResource, ImpactResource, InfluenceResource, JobNotFoundError, JobsResource, NetworkError, NotFoundError, PermissionError, RateLimitError, TimeoutError, VERSION, ValidationError, ZapSEA, ZapSEAError, validateApiKey, validateJobId }; //# sourceMappingURL=index.esm.js.map