@0xshariq/voxa-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +250 -0
  2. package/LICENSE +21 -0
  3. package/README.md +782 -0
  4. package/dist/config/index.d.ts +1 -0
  5. package/dist/config/index.js +1 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +1 -0
  8. package/dist/lib/client/http-methods.d.ts +1 -0
  9. package/dist/lib/client/http-methods.js +1 -0
  10. package/dist/lib/client/logging.d.ts +1 -0
  11. package/dist/lib/client/logging.js +1 -0
  12. package/dist/lib/client/request.d.ts +1 -0
  13. package/dist/lib/client/request.js +1 -0
  14. package/dist/lib/client/security.d.ts +1 -0
  15. package/dist/lib/client/security.js +1 -0
  16. package/dist/lib/client/utils.d.ts +1 -0
  17. package/dist/lib/client/utils.js +1 -0
  18. package/dist/lib/client/voxa.d.ts +1 -0
  19. package/dist/lib/client/voxa.js +1 -0
  20. package/dist/lib/features/cache/file-storage.d.ts +1 -0
  21. package/dist/lib/features/cache/file-storage.js +1 -0
  22. package/dist/lib/features/cache/manager.d.ts +1 -0
  23. package/dist/lib/features/cache/manager.js +1 -0
  24. package/dist/lib/features/deduplication/manager.d.ts +1 -0
  25. package/dist/lib/features/deduplication/manager.js +1 -0
  26. package/dist/lib/features/errors/classifier.d.ts +1 -0
  27. package/dist/lib/features/errors/classifier.js +1 -0
  28. package/dist/lib/features/interceptors/manager.d.ts +1 -0
  29. package/dist/lib/features/interceptors/manager.js +1 -0
  30. package/dist/lib/features/logging/file-logger.d.ts +1 -0
  31. package/dist/lib/features/logging/file-logger.js +1 -0
  32. package/dist/lib/features/metadata/manager.d.ts +1 -0
  33. package/dist/lib/features/metadata/manager.js +1 -0
  34. package/dist/lib/features/queue/manager.d.ts +1 -0
  35. package/dist/lib/features/queue/manager.js +1 -0
  36. package/dist/lib/features/rate/limiter.d.ts +1 -0
  37. package/dist/lib/features/rate/limiter.js +1 -0
  38. package/dist/lib/features/retry/manager.d.ts +1 -0
  39. package/dist/lib/features/retry/manager.js +1 -0
  40. package/dist/lib/features/schema/validator.d.ts +1 -0
  41. package/dist/lib/features/schema/validator.js +1 -0
  42. package/dist/lib/index.d.ts +1 -0
  43. package/dist/lib/index.js +1 -0
  44. package/dist/lib/types/client-types.d.ts +1 -0
  45. package/dist/lib/types/client-types.js +1 -0
  46. package/dist/lib/version.d.ts +1 -0
  47. package/dist/lib/version.js +1 -0
  48. package/package.json +76 -0
@@ -0,0 +1 @@
1
+ export declare class ConfigLoader{private static getEnv;private static getEnvInt;private static getEnvBool;static getCacheConfig():{enabled:boolean;ttl:number;storage:"memory"|"file"|"custom";};static getQueueConfig():{enabled:boolean;maxConcurrent:number;};static getRetryConfig():{count:number;delay:number;exponentialBackoff:boolean;};static getHttpConfig():{timeout:number;baseURL:string;};static getDefaultConfig():{timeout:number;baseURL:string|undefined;cache:{enabled:boolean;ttl:number;storage:"memory"|"file"|"custom";};queue:{enabled:boolean;maxConcurrent:number;};retry:{count:number;delay:number;exponentialBackoff:boolean;};};static getDebugInfo():{cache:{enabled:boolean;ttl:number;storage:"memory"|"file"|"custom";};queue:{enabled:boolean;maxConcurrent:number;};retry:{count:number;delay:number;exponentialBackoff:boolean;};http:{timeout:number;baseURL:string;};};}
@@ -0,0 +1 @@
1
+ export class ConfigLoader{static getEnv(t,e=""){return process.env[`VOXA_${t}`]||process.env[t]||e}static getEnvInt(t,e){const n=this.getEnv(t),i=parseInt(n);return isNaN(i)?e:i}static getEnvBool(t,e=!1){const n=this.getEnv(t);return n?"true"===n.toLowerCase()||"1"===n:e}static getCacheConfig(){return{enabled:this.getEnvBool("CACHE_ENABLED",!1),ttl:this.getEnvInt("CACHE_TTL",3e5),storage:this.getEnv("CACHE_STORAGE","memory")}}static getQueueConfig(){return{enabled:this.getEnvBool("QUEUE_ENABLED",!1),maxConcurrent:this.getEnvInt("QUEUE_MAX_CONCURRENT",6)}}static getRetryConfig(){return{count:this.getEnvInt("RETRY_COUNT",5),delay:this.getEnvInt("RETRY_DELAY",1e3),exponentialBackoff:this.getEnvBool("RETRY_EXPONENTIAL_BACKOFF",!0)}}static getHttpConfig(){return{timeout:this.getEnvInt("HTTP_TIMEOUT",5e3)||this.getEnvInt("DEFAULT_TIMEOUT",5e3),baseURL:this.getEnv("HTTP_BASE_URL")||this.getEnv("DEFAULT_BASE_URL")}}static getDefaultConfig(){const t=this.getHttpConfig();return{timeout:t.timeout,baseURL:t.baseURL||void 0,cache:this.getCacheConfig(),queue:this.getQueueConfig(),retry:this.getRetryConfig()}}static getDebugInfo(){return{cache:this.getCacheConfig(),queue:this.getQueueConfig(),retry:this.getRetryConfig(),http:this.getHttpConfig()}}}
@@ -0,0 +1 @@
1
+ export * from './lib/index.js';export{default}from './lib/index.js';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export*from"./lib/index.js";export{default}from"./lib/index.js";
@@ -0,0 +1 @@
1
+ import type{VoxaConfig,HttpMethod,VoxaResponse}from '../types/client-types.js';export declare class HttpMethods{protected request<T = any>(_method:HttpMethod,_url:string,_data:any,_config:VoxaConfig):Promise<VoxaResponse<T>>;get<T = any>(endpoint:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;post<T = any>(endpoint:string,data?:any,config?:VoxaConfig):Promise<VoxaResponse<T>>;put<T = any>(endpoint:string,data?:any,config?:VoxaConfig):Promise<VoxaResponse<T>>;delete<T = any>(endpoint:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;patch<T = any>(endpoint:string,data?:any,config?:VoxaConfig):Promise<VoxaResponse<T>>;head<T = any>(endpoint:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;options<T = any>(endpoint:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;}
@@ -0,0 +1 @@
1
+ export class HttpMethods{request(e,t,s,r){throw new Error("request method must be implemented")}async get(e,t={}){return this.request("GET",e,null,t)}async post(e,t,s={}){return this.request("POST",e,t,s)}async put(e,t,s={}){return this.request("PUT",e,t,s)}async delete(e,t={}){return this.request("DELETE",e,null,t)}async patch(e,t,s={}){return this.request("PATCH",e,t,s)}async head(e,t={}){return this.request("HEAD",e,null,t)}async options(e,t={}){return this.request("OPTIONS",e,null,t)}}
@@ -0,0 +1 @@
1
+ export declare function setDebugEnabled(enabled:boolean):void;export declare function isDebugEnabled():boolean;export declare function setFileLoggingEnabled(enabled:boolean):void;export declare function sanitizeLog<T = any>(obj:T):T;export declare function logSafe(...args:any[]):void;export declare function warnSafe(...args:any[]):void;export declare function errorSafe(...args:any[]):void;export declare function debugLog(...args:any[]):void;
@@ -0,0 +1 @@
1
+ import{getFileLogger as e}from"../features/logging/file-logger.js";const o=["authorization","token","access_token","refresh_token","password","secret","apiKey","apikey","x-api-key","set-cookie","cookie","email","ssn","creditcard","cardnumber","cvv","pin"];let n=!1,t=!1;export function setDebugEnabled(e){n=e}export function isDebugEnabled(){return n}export function setFileLoggingEnabled(e){t=e}export function sanitizeLog(e){if("object"!=typeof e||null===e)return e;if(Array.isArray(e))return e.map(sanitizeLog);const n={};for(const[t,i]of Object.entries(e))o.includes(t.toLowerCase())?n[t]="[REDACTED]":n[t]="object"==typeof i&&null!==i?sanitizeLog(i):i;return n}export function logSafe(...o){const n=o.map(sanitizeLog);console.log(...n),t&&e().log("info",o[0],...o.slice(1)).catch(()=>{})}export function warnSafe(...o){const n=o.map(sanitizeLog);console.warn(...n),t&&e().log("warn",o[0],...o.slice(1)).catch(()=>{})}export function errorSafe(...e){const o=e.map(sanitizeLog);console.error(...o)}export function debugLog(...e){if(n){const o=e.map(sanitizeLog);console.log("[Voxa Debug]",...o)}}
@@ -0,0 +1 @@
1
+ import type{HttpMethod,VoxaConfig}from '../types/client-types.js';export declare function dispatchRequest(method:HttpMethod,endpoint:string,data:any,config:VoxaConfig,baseURL?:string,signal?:AbortSignal):Promise<Response>;
@@ -0,0 +1 @@
1
+ import{RetryManager as t}from"../features/retry/manager.js";import{isPrivateIp as e,isValidUrl as o,filterHeaders as r}from"./security.js";export async function dispatchRequest(a,s,n,i,c,l){i.debug&&console.log("[Voxa Debug] Request:",{method:a,endpoint:s,baseURL:c,hasData:!!n});const u=new t(i.retry),d=u.getRetryConfig();let f;const m=d.count+1;for(let t=0;m>t;t++){const g=new AbortController,p=i.timeout||5e3,h=setTimeout(()=>g.abort(),p),y=l||g.signal;try{if(y.aborted)throw new DOMException("Aborted","AbortError");const l=c?`${c}${s}`:s;if(!o(l))throw new Error("Blocked: Invalid or malformed URL");if(await e(l))throw new Error("Blocked: SSRF attempt to private IP");const f=r(i.headers),m=await fetch(l,{method:a,headers:f,body:n?JSON.stringify(n):void 0,signal:y});if(clearTimeout(h),m.redirected&&await e(m.url))throw new Error("Blocked: Redirected to private IP (SSRF protection)");if(i.debug&&console.log("[Voxa Debug] Response:",{status:m.status,statusText:m.statusText,redirected:m.redirected,attempt:t+1}),!m.ok&&u.shouldRetryStatus(m.status,d.statusCodes)&&t<d.count){const e=u.calculateRetryDelay(t,d.delay,d.exponentialBackoff,d.maxRetry);import("./logging.js").then(({logSafe:o})=>o(`\u26a0\ufe0f Request failed with status ${m.status}. Retrying in ${e}ms... (attempt ${t+1}/${d.count})`)),await u.sleep(e,y);continue}return m}catch(e){if(clearTimeout(h),f=e,y.aborted)throw e;if(("AbortError"===e.name||"TypeError"===e.name||e.message.includes("fetch"))&&t<d.count){const o=u.calculateRetryDelay(t,d.delay,d.exponentialBackoff,d.maxRetry);import("./logging.js").then(({logSafe:r})=>r(`\u26a0\ufe0f Request failed: ${e.message}. Retrying in ${o}ms... (attempt ${t+1}/${d.count})`)),await u.sleep(o,y);continue}throw t<d.count||import("./logging.js").then(({errorSafe:t})=>t(`\u274c Request failed after ${m} attempts`)),e}}throw f||new Error("Request failed after all retry attempts")}
@@ -0,0 +1 @@
1
+ export declare function isPrivateIp(urlStr:string):Promise<boolean>;export declare function isValidUrl(urlStr:string):boolean;export declare function filterHeaders(headers:any):Record<string,string>;
@@ -0,0 +1 @@
1
+ function f(f){return f.includes(":")?BigInt("0x"+f.split(":").map(f=>f.padStart(4,"0")).join("")):f.split(".").reduce((f,t)=>(f<<8n)+BigInt(t),0n)}function t(t,e,n){try{const r=f(t);return r>=f(e)&&r<=f(n)}catch{return!1}}import e from"dns/promises";import n from"net";const r=[["10.0.0.0","10.255.255.255"],["172.16.0.0","172.31.255.255"],["192.168.0.0","192.168.255.255"],["127.0.0.0","127.255.255.255"],["::1","::1"],["fc00::","fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"],["fe80::","febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]];export async function isPrivateIp(f){try{const o=new URL(f).hostname;return n.isIP(o)?r.some(([f,e])=>t(o,f,e)):(await e.lookup(o,{all:!0})).some(f=>r.some(([e,n])=>t(f.address,e,n)))}catch{return!1}}export function isValidUrl(f){try{return new URL(f),!0}catch{return!1}}const o=["accept","accept-language","content-type","content-length","user-agent","authorization","x-requested-with","x-api-key","x-xsrf-token"];export function filterHeaders(f){const t={};if(!f)return t;for(const[e,n]of Object.entries(f))o.includes(e.toLowerCase())&&(t[e]=n);return t}
@@ -0,0 +1 @@
1
+ import type{VoxaConfig}from '../types/client-types.js';export declare function mergeConfig(baseConfig:VoxaConfig,requestConfig:VoxaConfig):VoxaConfig;export declare function buildURL(baseURL:string|undefined,endpoint:string):string;
@@ -0,0 +1 @@
1
+ export function mergeConfig(e,r){return{...e,...r,headers:{...e.headers||{},...r?.headers||{}},retry:r.retry||e.retry}}export function buildURL(e,r){return e?`${e.replace(/\/$/,"")}/${r.replace(/^\//,"")}`:r}
@@ -0,0 +1 @@
1
+ import type{VoxaConfig,RequestMetadata,HttpMethod,VoxaResponse}from '../types/client-types.js';import{HttpMethods}from './http-methods.js';import type{SchemaValidator}from '../features/schema/validator.js';import type{RateLimiter}from '../features/rate/limiter.js';declare class Voxa extends HttpMethods{static version:string;static checkVersionCompatibility(apiVersion?:string):void;protected request<T = any>(method:HttpMethod,endpoint:string,data:any,config?:VoxaConfig):Promise<VoxaResponse<T>>;private schemaValidator?;private rateLimiter?;private errorClassifier?;private config;private interceptorManager;private deduplicationManager;private cacheManager;private queueManager;private metadataManager;constructor(config?:VoxaConfig);classifyError(error:unknown):string|undefined;rate():RateLimiter|undefined;schema():SchemaValidator|undefined;get interceptors():{request:{use:(successFn:any,errorFn?:any)=>void;};response:{use:(successFn:any,errorFn?:any)=>void;};};getConfig():Readonly<VoxaConfig>;isDebugEnabled():boolean;getCacheStats():Promise<{storage:string;size:number;location:string;adapterProvided?:undefined;entries?:undefined;connected?:undefined;}|{storage:string;adapterProvided:boolean;size?:undefined;location?:undefined;entries?:undefined;connected?:undefined;}|{storage:string;size:number;entries:string[];location?:undefined;adapterProvided?:undefined;connected?:undefined;}|{storage:string;connected:boolean;size?:undefined;location?:undefined;adapterProvided?:undefined;entries?:undefined;}>;getQueueStats():{enabled:boolean|undefined;queueSize:number;activeRequests:number;maxConcurrent:number|undefined;queuedByPriority:{critical:number;high:number;normal:number;low:number;};};getDeduplicationStats():{pendingCount:number;ttl:number;enabled:boolean;};getMetadataStats():{totalRequests:number;requests:RequestMetadata[];};getRequestMetadata(requestId:string):RequestMetadata|undefined;clearCache():Promise<void>;clearQueue():void;destroy():void;static get<T = any>(url:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;static post<T = any>(url:string,data?:any,config?:VoxaConfig):Promise<VoxaResponse<T>>;static put<T = any>(url:string,data?:any,config?:VoxaConfig):Promise<VoxaResponse<T>>;static delete<T = any>(url:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;static patch<T = any>(url:string,data?:any,config?:VoxaConfig):Promise<VoxaResponse<T>>;static head<T = any>(url:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;static options<T = any>(url:string,config?:VoxaConfig):Promise<VoxaResponse<T>>;}declare function create(config?:VoxaConfig):Voxa;export default create;export{Voxa};export type{VoxaConfig,VoxaResponse,CacheAdapter}from '../types/client-types.js';
@@ -0,0 +1 @@
1
+ function e(e={}){return new Voxa(e)}import{randomUUID as t}from"node:crypto";import{VOXA_VERSION as r}from"../version.js";import{mergeConfig as a}from"./utils.js";import{CacheManager as s}from"../features/cache/manager.js";import{QueueManager as i}from"../features/queue/manager.js";import{InterceptorManager as n}from"../features/interceptors/manager.js";import{DeduplicationManager as o}from"../features/deduplication/manager.js";import{MetadataManager as c}from"../features/metadata/manager.js";import{HttpMethods as g}from"./http-methods.js";class Voxa extends g{static version=r;static checkVersionCompatibility(e){e&&e!==r&&console.warn(`\u26a0\ufe0f Voxa client version (${r}) does not match API/SDK version (${e}). Please ensure compatibility.`)}async request(e,r,s,i={}){const n=a(this.config,i),o=Date.now();let c=i?.requestId,g=i?.metadata;const h=c||t(),u={id:h,method:e,endpoint:r,priority:n.priority||"normal",timestamp:o,expiresAt:o+3e5,...g||{}};this.metadataManager.set(h,u);let d=this.errorClassifier,l={method:e,endpoint:r,data:s,config:n,requestId:h,metadata:u};try{for(const{successFn:e,errorFn:t}of this.interceptorManager.getRequestInterceptors())try{e&&(l=await Promise.resolve(e(l)))}catch(e){if(!t){if(d){const t=d.classifyWithMessage(e);throw new Error(`[Request Interceptor Error] ${t.category}: ${t.message}`)}throw e}l=await Promise.resolve(t(e,l))}let e;try{const t=i?.signal;e=await import("./request.js").then(e=>e.dispatchRequest(l.method,l.endpoint,l.data,l.config,l.config.baseURL,t))}catch(e){if(d){const t=d.classifyWithMessage(e);throw new Error(`[Network Error] ${t.category}: ${t.message}`)}throw e}let t=e;for(const{successFn:e,errorFn:r}of this.interceptorManager.getResponseInterceptors())try{e&&(t=await Promise.resolve(e(t)))}catch(e){if(!r){if(d){const t=d.classifyWithMessage(e);throw new Error(`[Response Interceptor Error] ${t.category}: ${t.message}`)}throw e}t=await Promise.resolve(r(e,t))}let r=null;try{const e=t.headers.get("content-type")||"";r=i?.stream?t.body:e.includes("application/json")?await t.json():await t.text()}catch(e){if(d){const t=d.classifyWithMessage(e);throw new Error(`[Parse Error] ${t.category}: ${t.message}`)}r=null}return{response:t,data:r,metadata:this.metadataManager.get(h),requestId:h,status:t.status,statusText:t.statusText}}catch(e){if(d){const t=d.classifyWithMessage(e);throw new Error(`[Voxa Error] ${t.category}: ${t.message}`,{cause:e})}throw e}}schemaValidator;rateLimiter;errorClassifier;config={timeout:5e3,headers:{"Content-Type":"application/json"},errors:{enabled:!0}};interceptorManager;deduplicationManager;cacheManager;queueManager;metadataManager;constructor(e={}){super(),this.config=a({deduplication:{enabled:!0},retry:{enabled:!0,count:5},errors:{enabled:!0},metadata:{enabled:!0},cache:{enabled:!0},rate:{enabled:!0}},e),this.config.debug&&import("./logging.js").then(({setDebugEnabled:e})=>{e(!0)}),!1!==this.config.retry?.enabled&&(this.config.retry={...this.config.retry||{},enabled:!0,count:5}),this.interceptorManager=new n;const t={...this.config.deduplication||{},enabled:!1!==this.config.deduplication?.enabled};this.deduplicationManager=new o(t),this.cacheManager=new s(this.config.cache),this.queueManager=new i(this.config.queue),this.metadataManager=new c(this.config.metadata||{}),!1!==this.config.errors?.enabled&&import("../features/errors/classifier.js").then(({ErrorClassifier:e})=>{this.errorClassifier=new e(this.config.errors)})}classifyError(e){if(this.errorClassifier&&"function"==typeof this.errorClassifier.classifyWithMessage){const t=this.errorClassifier.classifyWithMessage(e);return`${t.category}: ${t.message}`}}rate(){return this.rateLimiter}schema(){return this.schemaValidator}get interceptors(){return this.interceptorManager.getAPI()}getConfig(){return{...this.config}}isDebugEnabled(){return!0===this.config.debug}getCacheStats(){return this.cacheManager.getStats()}getQueueStats(){return this.queueManager.getStats()}getDeduplicationStats(){return this.deduplicationManager.getStats()}getMetadataStats(){return this.metadataManager.getStats()}getRequestMetadata(e){return this.metadataManager.get(e)}async clearCache(){await this.cacheManager.clear()}clearQueue(){this.queueManager.clear()}destroy(){this.deduplicationManager.destroy(),this.interceptorManager.clear(),this.metadataManager.clear()}static async get(e,t={}){return new Voxa(t).get(e,t)}static async post(e,t,r={}){return new Voxa(r).post(e,t,r)}static async put(e,t,r={}){return new Voxa(r).put(e,t,r)}static async delete(e,t={}){return new Voxa(t).delete(e,t)}static async patch(e,t,r={}){return new Voxa(r).patch(e,t,r)}static async head(e,t={}){return new Voxa(t).head(e,t)}static async options(e,t={}){return new Voxa(t).options(e,t)}}export default e;export{Voxa};
@@ -0,0 +1 @@
1
+ export declare class FileStorage{private cacheDir;private initialized;constructor();private init;private getFilePath;get(key:string):Promise<string|null>;set(key:string,value:string,ttlSeconds?:number):Promise<void>;delete(key:string):Promise<void>;clear():Promise<void>;cleanup():Promise<void>;getStats():Promise<{size:number;files:string[];}>;}
@@ -0,0 +1 @@
1
+ import{promises as e}from"fs";import{join as i}from"path";import{homedir as t}from"os";import{logSafe as a,errorSafe as r}from"../../client/logging.js";export class FileStorage{cacheDir;initialized=!1;constructor(){this.cacheDir=i(t(),".voxa","cache")}async init(){if(!this.initialized)try{await e.mkdir(this.cacheDir,{recursive:!0}),this.initialized=!0,a("\u{1f4c1} File cache directory initialized:",this.cacheDir)}catch(e){throw r("Failed to initialize cache directory:",e.message),e}}getFilePath(e){const t=Buffer.from(e).toString("base64").replace(/[/+=]/g,"_").substring(0,255);return i(this.cacheDir,`${t}.json`)}async get(i){await this.init();try{const t=this.getFilePath(i),a=await e.readFile(t,"utf-8"),r=JSON.parse(a);return r.expiresAt&&Date.now()>r.expiresAt?(await this.delete(i),null):r.value}catch(e){return"ENOENT"===e.code||r("File cache get error:",e.message),null}}async set(i,t,a){await this.init();try{const r=this.getFilePath(i),c={value:t,timestamp:Date.now(),expiresAt:a?Date.now()+1e3*a:null};await e.writeFile(r,JSON.stringify(c),"utf-8")}catch(e){r("File cache set error:",e.message)}}async delete(i){await this.init();try{const t=this.getFilePath(i);await e.unlink(t)}catch(e){"ENOENT"!==e.code&&r("File cache delete error:",e.message)}}async clear(){await this.init();try{const t=await e.readdir(this.cacheDir);await Promise.all(t.map(t=>e.unlink(i(this.cacheDir,t)))),a("\u{1f5d1}\ufe0f File cache cleared")}catch(e){r("File cache clear error:",e.message)}}async cleanup(){await this.init();try{const t=await e.readdir(this.cacheDir),a=Date.now();for(const r of t)try{const t=i(this.cacheDir,r),c=await e.readFile(t,"utf-8"),s=JSON.parse(c);s.expiresAt&&a>s.expiresAt&&await e.unlink(t)}catch(e){}}catch(e){r("File cache cleanup error:",e.message)}}async getStats(){await this.init();try{const i=await e.readdir(this.cacheDir);return{size:i.length,files:i}}catch(e){return{size:0,files:[]}}}}
@@ -0,0 +1 @@
1
+ import type{CacheConfig}from '../../types/client-types.js';export declare class CacheManager{dispose():Promise<void>;private memoryCache;private config;private customAdapter;private fileStorage;constructor(config?:CacheConfig);getCacheKey(method:string,endpoint:string,data?:any):string;private isValid;get(key:string):Promise<Response|null>;set(key:string,response:Response,requestId:string,ttl?:number):Promise<void>;delete(key:string):Promise<void>;clear():Promise<void>;getStats():Promise<{storage:string;size:number;location:string;adapterProvided?:undefined;entries?:undefined;connected?:undefined;}|{storage:string;adapterProvided:boolean;size?:undefined;location?:undefined;entries?:undefined;connected?:undefined;}|{storage:string;size:number;entries:string[];location?:undefined;adapterProvided?:undefined;connected?:undefined;}|{storage:string;connected:boolean;size?:undefined;location?:undefined;adapterProvided?:undefined;entries?:undefined;}>;disconnect():Promise<void>;}
@@ -0,0 +1 @@
1
+ import{warnSafe as e,logSafe as t,errorSafe as s}from"../../client/logging.js";import{FileStorage as r}from"./file-storage.js";export class CacheManager{async dispose(){this.fileStorage&&await this.fileStorage.cleanup(),await this.clear(),this.customAdapter=null,this.fileStorage=null}memoryCache=new Map;config;customAdapter=null;fileStorage=null;constructor(s={}){this.config={enabled:s.enabled??!1,ttl:s.ttl??3e5,storage:s.storage??"memory",...s},this.config.enabled&&("file"===this.config.storage?(this.fileStorage=new r,t("\u2705 Using file-based cache storage in ~/.voxa/cache/")):"custom"===this.config.storage&&(this.config.adapter?(this.customAdapter=this.config.adapter,t("\u2705 Using custom cache adapter")):(e("\u26a0\ufe0f Custom storage selected but no adapter provided. Falling back to memory cache."),this.config.storage="memory")))}getCacheKey(e,t,s){return`voxa:${e}:${t}:${JSON.stringify(s||{})}`}isValid(e){return Date.now()<e.expiresAt}async get(e){if(!this.config.enabled)return null;if("file"===this.config.storage&&this.fileStorage)try{const s=await this.fileStorage.get(e);if(!s)return null;const r=JSON.parse(s);return this.isValid(r)?(t("\u{1f4e6} Cache HIT (File):",e),new Response(JSON.stringify(r.response),{status:200,headers:{"X-Cache":"HIT"}})):(await this.delete(e),null)}catch(e){return s("File storage get error:",e instanceof Error?e.message:String(e)),null}if("custom"===this.config.storage&&this.customAdapter)try{const s=await this.customAdapter.get(e);if(!s)return null;const r=JSON.parse(s);return this.isValid(r)?(t("\u{1f4e6} Cache HIT (Custom):",e),new Response(JSON.stringify(r.response),{status:200,headers:{"X-Cache":"HIT"}})):(await this.delete(e),null)}catch(e){return s("Custom adapter get error:",e instanceof Error?e.message:String(e)),null}const r=this.memoryCache.get(e);return r?this.isValid(r)?(t("\u{1f4e6} Cache HIT (Memory):",e),r.response.clone()):(this.memoryCache.delete(e),null):null}async set(e,r,a,i){if(!this.config.enabled)return;const o=i||this.config.ttl||3e5,c=Date.now(),n={response:r.clone(),timestamp:c,ttl:o,expiresAt:c+o,requestId:a};if("file"===this.config.storage&&this.fileStorage)try{const s=r.clone(),i={response:{body:await s.text(),status:s.status,statusText:s.statusText,headers:Object.fromEntries(s.headers.entries())},timestamp:n.timestamp,ttl:n.ttl,expiresAt:n.expiresAt,requestId:a};await this.fileStorage.set(e,JSON.stringify(i),Math.ceil(o/1e3)),t("\u{1f4be} Cached in File:",e,`(TTL: ${o}ms)`)}catch(e){s("File storage set error:",e instanceof Error?e.message:String(e))}else if("custom"===this.config.storage&&this.customAdapter)try{const s=r.clone(),a={response:{body:await s.text(),status:s.status,statusText:s.statusText,headers:Object.fromEntries(s.headers.entries())},timestamp:n.timestamp,ttl:n.ttl,expiresAt:n.expiresAt};await this.customAdapter.set(e,JSON.stringify(a),Math.ceil(o/1e3)),t("\u{1f4be} Cached with custom adapter:",e,`(TTL: ${o}ms)`)}catch(e){s("Custom adapter set error:",e instanceof Error?e.message:String(e))}else this.memoryCache.set(e,n),t("\u{1f4be} Cached in Memory:",e,`(TTL: ${o}ms)`),setTimeout(()=>{if(this.memoryCache.has(e)){const t=this.memoryCache.get(e);t&&!this.isValid(t)&&this.memoryCache.delete(e)}},o)}async delete(e){if("file"===this.config.storage&&this.fileStorage)try{await this.fileStorage.delete(e)}catch(e){s("File storage delete error:",e instanceof Error?e.message:String(e))}else if("custom"===this.config.storage&&this.customAdapter)try{await this.customAdapter.delete(e)}catch(e){s("Custom adapter delete error:",e instanceof Error?e.message:String(e))}else this.memoryCache.delete(e)}async clear(){if("file"===this.config.storage&&this.fileStorage)try{await this.fileStorage.clear()}catch(e){s("File storage clear error:",e instanceof Error?e.message:String(e))}else if("custom"===this.config.storage&&this.customAdapter)try{await this.customAdapter.clear()}catch(e){s("Custom adapter clear error:",e instanceof Error?e.message:String(e))}else this.memoryCache.clear()}async getStats(){return"file"===this.config.storage&&this.fileStorage?{storage:"file",size:(await this.fileStorage.getStats()).size,location:"~/.voxa/cache/"}:"custom"===this.config.storage?{storage:"custom",adapterProvided:!!this.customAdapter}:"memory"===this.config.storage?{storage:"memory",size:this.memoryCache.size,entries:Array.from(this.memoryCache.keys())}:{storage:"none",connected:!1}}async disconnect(){await this.dispose()}}
@@ -0,0 +1 @@
1
+ import{DeduplicationConfig}from "../../types/client-types.js";export declare class DeduplicationManager{private pendingRequests;private readonly TTL;private readonly enabled;private cleanupInterval;constructor(config?:DeduplicationConfig);generateKey(method:string,endpoint:string,data?:any):string;getPending(key:string):Promise<Response>|null;setPending(key:string,promise:Promise<Response>):void;removePending(key:string):void;private cleanup;getStats():{pendingCount:number;ttl:number;enabled:boolean;};destroy():void;}
@@ -0,0 +1 @@
1
+ import{logSafe as e}from"../../client/logging.js";export class DeduplicationManager{pendingRequests=new Map;TTL;enabled;cleanupInterval=null;constructor(e={}){this.TTL="number"==typeof e.ttl?e.ttl:3e5,this.enabled=!1!==e.enabled,this.enabled&&(this.cleanupInterval=setInterval(()=>this.cleanup(),6e4))}generateKey(e,t,n){return`${e}:${t}:${JSON.stringify(n||{})}`}getPending(e){if(!this.enabled)return null;const t=this.pendingRequests.get(e);return t?Date.now()-t.timestamp<this.TTL?t.promise:(this.pendingRequests.delete(e),null):null}setPending(e,t){this.enabled&&this.pendingRequests.set(e,{promise:t,timestamp:Date.now()})}removePending(e){this.enabled&&this.pendingRequests.delete(e)}cleanup(){if(!this.enabled)return;const t=Date.now();for(const[n,s]of this.pendingRequests.entries())t-s.timestamp<this.TTL||(this.pendingRequests.delete(n),e(`\u{1f9f9} Cleaned up stale pending request: ${n}`))}getStats(){return{pendingCount:this.pendingRequests.size,ttl:this.TTL,enabled:this.enabled}}destroy(){this.cleanupInterval&&clearInterval(this.cleanupInterval),this.pendingRequests.clear()}}
@@ -0,0 +1 @@
1
+ export type ErrorCategory = 'network'|'timeout'|'server'|'client'|'auth'|'offline'|'unknown';export interface ErrorClassifierConfig{enabled?:boolean;}export declare class ErrorClassifier{private config;constructor(config?:ErrorClassifierConfig);classifyWithMessage(error:any):{category:ErrorCategory;message:string;};}
@@ -0,0 +1 @@
1
+ export class ErrorClassifier{config;constructor(e={}){this.config=e}classifyWithMessage(e){let s="unknown",t="Unknown error occurred.";return this.config.enabled?"AbortError"===e?.name||e?.message?.includes("timeout")?(s="timeout",t=`Timeout: The request took too long to complete. Details: ${e?.message??e}`):401===e?.status||403===e?.status?(s="auth",t=`Authentication error: Access denied (status ${e?.status}). Details: ${e?.message??e}`):0===e?.status||e?.message?.includes("NetworkError")?(s="network",t=`Network error: Unable to reach the server. Details: ${e?.message??e}`):500>e?.status?400>e?.status?"undefined"!=typeof navigator&&!1===navigator.onLine&&(s="offline",t="Offline: The browser is offline."):(s="client",t=`Client error: The request was invalid (status ${e?.status}). Details: ${e?.message??e}`):(s="server",t=`Server error: The server responded with status ${e?.status}. Details: ${e?.message??e}`):t="Error classification is disabled.",{category:s,message:t}}}
@@ -0,0 +1 @@
1
+ import type{Interceptor}from '../../types/client-types.js';export declare class InterceptorManager{dispose():void;private requestInterceptors;private responseInterceptors;addRequestInterceptor(successFn:any,errorFn?:any):void;addResponseInterceptor(successFn:any,errorFn?:any):void;getRequestInterceptors():Interceptor[];getResponseInterceptors():Interceptor[];clear():void;getAPI():{request:{use:(successFn:any,errorFn?:any)=>void;};response:{use:(successFn:any,errorFn?:any)=>void;};};}
@@ -0,0 +1 @@
1
+ export class InterceptorManager{dispose(){this.clear()}requestInterceptors=[];responseInterceptors=[];addRequestInterceptor(e,s){this.requestInterceptors.push({successFn:e,errorFn:s||null})}addResponseInterceptor(e,s){this.responseInterceptors.push({successFn:e,errorFn:s||null})}getRequestInterceptors(){return this.requestInterceptors}getResponseInterceptors(){return this.responseInterceptors}clear(){this.requestInterceptors=[],this.responseInterceptors=[]}getAPI(){return{request:{use:(e,s)=>this.addRequestInterceptor(e,s)},response:{use:(e,s)=>this.addResponseInterceptor(e,s)}}}}
@@ -0,0 +1 @@
1
+ export declare class FileLogger{private logDir;private initialized;private maxLogSize;private maxLogFiles;constructor();private init;private getLogFilePath;private rotateLogsIfNeeded;private cleanupOldLogs;log(level:'info'|'warn'|'error'|'debug',message:string,...args:any[]):Promise<void>;readLogs(limit?:number):Promise<any[]>;clearLogs():Promise<void>;}export declare function getFileLogger():FileLogger;
@@ -0,0 +1 @@
1
+ import{promises as t}from"fs";import{join as i}from"path";import{homedir as a}from"os";export class FileLogger{logDir;initialized=!1;maxLogSize=10485760;maxLogFiles=5;constructor(){this.logDir=i(a(),".voxa","logs")}async init(){if(!this.initialized)try{await t.mkdir(this.logDir,{recursive:!0}),this.initialized=!0}catch(t){console.error("Failed to initialize log directory:",t)}}getLogFilePath(){const t=(new Date).toISOString().split("T")[0];return i(this.logDir,`voxa-${t}.log`)}async rotateLogsIfNeeded(){try{const a=this.getLogFilePath();if((await t.stat(a)).size>this.maxLogSize){const e=Date.now(),o=i(this.logDir,`voxa-${e}.log`);await t.rename(a,o),await this.cleanupOldLogs()}}catch(t){t.code}}async cleanupOldLogs(){try{const a=(await t.readdir(this.logDir)).filter(t=>t.startsWith("voxa-")&&t.endsWith(".log")).map(t=>({name:t,path:i(this.logDir,t)}));if(a.length>this.maxLogFiles){a.sort((t,i)=>t.name.localeCompare(i.name));const i=a.slice(0,a.length-this.maxLogFiles);for(const a of i)await t.unlink(a.path)}}catch(t){}}async log(i,a,...e){await this.init();try{await this.rotateLogsIfNeeded();const o={timestamp:(new Date).toISOString(),level:i,message:a,data:e.length>0?e:void 0},r=JSON.stringify(o)+"\n",s=this.getLogFilePath();await t.appendFile(s,r,"utf-8")}catch(t){}}async readLogs(i=100){await this.init();try{const a=this.getLogFilePath();return(await t.readFile(a,"utf-8")).trim().split("\n").filter(t=>t).slice(-i).map(t=>{try{return JSON.parse(t)}catch{return null}}).filter(Boolean)}catch(t){return t.code,[]}}async clearLogs(){await this.init();try{const a=await t.readdir(this.logDir);await Promise.all(a.filter(t=>t.startsWith("voxa-")&&t.endsWith(".log")).map(a=>t.unlink(i(this.logDir,a))))}catch(t){}}}let e=null;export function getFileLogger(){return e||(e=new FileLogger),e}
@@ -0,0 +1 @@
1
+ import type{RequestMetadata,MetadataConfig}from '../../types/client-types.js';export declare class MetadataManager{dispose():void;private metadata;private config;private enabled;constructor(config?:MetadataConfig);set(id:string,metadata:RequestMetadata):void;get(id:string):RequestMetadata;update(id:string,updates:Partial<RequestMetadata>):void;delete(id:string):void;getAll():Map<string,RequestMetadata>;clear():void;getStats():{totalRequests:number;requests:RequestMetadata[];};getRecent(n:number):RequestMetadata[];findByField<K extends keyof RequestMetadata>(field:K,value:RequestMetadata[K]):RequestMetadata[];}
@@ -0,0 +1 @@
1
+ import{logSafe as t}from"../../client/logging.js";export class MetadataManager{dispose(){this.clear()}metadata=new Map;config;enabled;constructor(t={}){this.config=t,this.enabled=!1!==t.enabled}set(e,a){if(!this.enabled)return;let i=a;if(this.config.fields&&this.config.fields.length>0&&(i=Object.fromEntries(Object.entries(a).filter(([t])=>this.config.fields.includes(t)))),this.config.customHandler&&this.config.customHandler(i),this.config.log&&t("[Metadata]",i),"number"==typeof this.config.maxEntries&&this.metadata.size>=this.config.maxEntries){const t=this.metadata.keys().next().value;"string"==typeof t&&this.metadata.delete(t)}this.metadata.set(e,i)}get(t){return this.enabled&&this.metadata.get(t)||{id:t,method:"GET",endpoint:"",priority:"normal",timestamp:Date.now()}}update(t,e){if(!this.enabled)return;const a=this.metadata.get(t);if(a){const i={...a,...e};this.set(t,i)}}delete(t){this.enabled&&this.metadata.delete(t)}getAll(){return this.enabled?new Map(this.metadata):new Map}clear(){this.enabled&&this.metadata.clear()}getStats(){if(!this.enabled)return{totalRequests:0,requests:[]};let t=Array.from(this.metadata.values());return this.config.fields&&this.config.fields.length>0&&(t=t.map(t=>Object.fromEntries(Object.entries(t).filter(([t])=>this.config.fields.includes(t))))),{totalRequests:this.metadata.size,requests:t}}getRecent(t){return this.enabled?Array.from(this.metadata.values()).slice(-t):[]}findByField(t,e){return this.enabled?Array.from(this.metadata.values()).filter(a=>a[t]===e):[]}}
@@ -0,0 +1 @@
1
+ import type{QueueConfig}from '../../types/client-types.js';export declare class QueueManager{dispose():void;private queue;private activeRequests;private config;constructor(config?:QueueConfig);private sortQueue;private processQueue;enqueue(execute:()=>Promise<Response>,priority:"critical"|"high"|"normal"|"low"|undefined,requestId:string):Promise<Response>;getStats():{enabled:boolean|undefined;queueSize:number;activeRequests:number;maxConcurrent:number|undefined;queuedByPriority:{critical:number;high:number;normal:number;low:number;};};clear():void;}
@@ -0,0 +1 @@
1
+ import{logSafe as e}from"../../client/logging.js";export class QueueManager{dispose(){this.clear(),this.activeRequests=0}queue=[];activeRequests=0;config;constructor(e={}){this.config={enabled:e.enabled??!1,maxConcurrent:e.maxConcurrent??6}}sortQueue(){this.queue.sort((e,t)=>{const i={critical:4,high:3,normal:2,low:1},s=(i[t.priority]||2)-(i[e.priority]||2);return 0===s?e.timestamp-t.timestamp:s})}async processQueue(){const t=this.config.maxConcurrent||6;for(;this.activeRequests<t&&this.queue.length>0;){const i=this.queue.shift();if(!i)break;this.activeRequests++,e(`\u{1f680} Processing request ${i.id} (priority: ${i.priority}, active: ${this.activeRequests}/${t})`),i.execute().then(e=>{i.resolve(e)}).catch(e=>{i.reject(new Error(e?.message||String(e),{cause:e}))}).finally(()=>{this.activeRequests--,this.processQueue()})}}async enqueue(t,i="normal",s){if(!this.config.enabled)return t();const r=this.config.maxConcurrent||6,u=s;if(this.activeRequests<r){this.activeRequests++,e(`\u26a1 Executing request ${u} immediately (priority: ${i}, active: ${this.activeRequests}/${r})`);try{return await t()}finally{this.activeRequests--,this.processQueue()}}return e(`\u23f3 Queueing request ${u} (priority: ${i}, queue size: ${this.queue.length+1})`),new Promise((e,s)=>{this.queue.push({id:u,priority:i,execute:t,resolve:e,reject:s,timestamp:Date.now()}),this.sortQueue()})}getStats(){return{enabled:this.config.enabled,queueSize:this.queue.length,activeRequests:this.activeRequests,maxConcurrent:this.config.maxConcurrent,queuedByPriority:{critical:this.queue.filter(e=>"critical"===e.priority).length,high:this.queue.filter(e=>"high"===e.priority).length,normal:this.queue.filter(e=>"normal"===e.priority).length,low:this.queue.filter(e=>"low"===e.priority).length}}}clear(){this.queue=[]}}
@@ -0,0 +1 @@
1
+ export interface RateLimiterConfig{enabled?:boolean;maxRequests?:number;perMilliseconds?:number;}export declare class RateLimiter{private config;private requestTimestamps;private queue;private timer;constructor(config?:RateLimiterConfig);enqueue(fn:()=>Promise<any>,requestId?:string):Promise<any>;private processQueue;}
@@ -0,0 +1 @@
1
+ export class RateLimiter{config;requestTimestamps=[];queue=[];timer;constructor(e={}){this.config=e}async enqueue(e,s){return this.config.enabled?new Promise((t,i)=>{this.queue.push({fn:e,resolve:t,reject:i,requestId:s||""}),this.processQueue()}):e()}processQueue(){if(!this.config.enabled)return;const e=Date.now();for(this.requestTimestamps=this.requestTimestamps.filter(s=>e-s<(this.config.perMilliseconds??6e4));this.queue.length>0&&this.requestTimestamps.length<(this.config.maxRequests??100);){const s=this.queue.shift();this.requestTimestamps.push(e),s.fn().then(s.resolve).catch(s.reject)}this.queue.length>0&&!this.timer&&(this.timer=setTimeout(()=>{this.timer=null,this.processQueue()},100))}}
@@ -0,0 +1 @@
1
+ export interface RetryConfig{count?:number;delay?:number;exponentialBackoff?:boolean;maxRetry?:number;statusCodes?:number[];}export declare class RetryManager{private config;constructor(config?:RetryConfig);getRetryConfig(override?:RetryConfig):Required<RetryConfig>;shouldRetryStatus(statusCode:number,statusCodes?:number[]):boolean;calculateRetryDelay(attempt:number,delay?:number,exponentialBackoff?:boolean,maxRetry?:number):number;sleep(ms:number,signal?:AbortSignal):Promise<void>;}
@@ -0,0 +1 @@
1
+ export class RetryManager{config;constructor(e={}){this.config=e}getRetryConfig(e){const t={...this.config,...e};return{count:Math.min(t.count??5,5),delay:t.delay??1e3,exponentialBackoff:t.exponentialBackoff??!0,maxRetry:t.maxRetry??3e4,statusCodes:t.statusCodes??[429,500,502,503,504]}}shouldRetryStatus(e,t){return(t??[429,500,502,503,504]).includes(e)}calculateRetryDelay(e,t=1e3,r=!0,o=3e4){let n;return n=r?t*Math.pow(2,e):t,Math.min(n,o)}async sleep(e,t){return new Promise((r,o)=>{function n(){clearTimeout(a),o(new DOMException("Aborted","AbortError"))}if(t?.aborted)return o(new DOMException("Aborted","AbortError"));const a=setTimeout(()=>{t?.removeEventListener("abort",n),r()},e);t&&t.addEventListener("abort",n)})}}
@@ -0,0 +1 @@
1
+ export interface SchemaValidatorConfig{enabled?:boolean;requestSchema?:any;responseSchema?:any;library?:'zod'|'yup';}export declare class SchemaValidator{private config;private zod?;private yup?;constructor(config?:SchemaValidatorConfig);validateRequest(data:any):any;validateResponse(data:any):any;}
@@ -0,0 +1 @@
1
+ export class SchemaValidator{config;zod;yup;constructor(i={}){if(this.config=i,"zod"===i.library)try{this.zod=require("zod")}catch{}if("yup"===i.library)try{this.yup=require("yup")}catch{}}validateRequest(i){if(!this.config.enabled||!this.config.requestSchema)return!0;if("zod"===this.config.library&&this.zod)return this.config.requestSchema.safeParse(i).success;if("yup"===this.config.library&&this.yup)try{return this.config.requestSchema.validateSync(i),!0}catch{return!1}return!0}validateResponse(i){if(!this.config.enabled||!this.config.responseSchema)return!0;if("zod"===this.config.library&&this.zod)return this.config.responseSchema.safeParse(i).success;if("yup"===this.config.library&&this.yup)try{return this.config.responseSchema.validateSync(i),!0}catch{return!1}return!0}}
@@ -0,0 +1 @@
1
+ export{Voxa}from './client/voxa.js';export{default}from './client/voxa.js';export type{VoxaConfig,VoxaResponse,CacheAdapter,CacheConfig,QueueConfig,RetryConfig,DeduplicationConfig,RateLimiterConfig,SchemaValidatorConfig,ErrorClassifierConfig,MetadataConfig,RequestPriority,RequestMetadata,HttpMethod}from './types/client-types.js';export{CacheManager}from './features/cache/manager.js';export{QueueManager}from './features/queue/manager.js';export declare const lazyLoadInterceptorManager:()=>Promise<typeof import("./features/interceptors/manager.js").InterceptorManager>;export declare const lazyLoadDeduplicationManager:()=>Promise<typeof import("./features/deduplication/manager.js").DeduplicationManager>;export declare const lazyLoadMetadataManager:()=>Promise<typeof import("./features/metadata/manager.js").MetadataManager>;export declare const lazyLoadRetryManager:()=>Promise<typeof import("./features/retry/manager.js").RetryManager>;export{InterceptorManager}from './features/interceptors/manager.js';export{DeduplicationManager}from './features/deduplication/manager.js';export{MetadataManager}from './features/metadata/manager.js';export{mergeConfig,buildURL}from './client/utils.js';
@@ -0,0 +1 @@
1
+ export{Voxa}from"./client/voxa.js";export{default}from"./client/voxa.js";export{CacheManager}from"./features/cache/manager.js";export{QueueManager}from"./features/queue/manager.js";export const lazyLoadInterceptorManager=()=>import("./features/interceptors/manager.js").then(e=>e.InterceptorManager);export const lazyLoadDeduplicationManager=()=>import("./features/deduplication/manager.js").then(e=>e.DeduplicationManager);export const lazyLoadMetadataManager=()=>import("./features/metadata/manager.js").then(e=>e.MetadataManager);export const lazyLoadRetryManager=()=>import("./features/retry/manager.js").then(e=>e.RetryManager);export{InterceptorManager}from"./features/interceptors/manager.js";export{DeduplicationManager}from"./features/deduplication/manager.js";export{MetadataManager}from"./features/metadata/manager.js";export{mergeConfig,buildURL}from"./client/utils.js";
@@ -0,0 +1 @@
1
+ export type RequestPriority = 'critical'|'high'|'normal'|'low';export type HttpMethod = 'GET'|'POST'|'PUT'|'DELETE'|'PATCH'|'HEAD'|'OPTIONS';export type ResponseType = 'basic'|'cors'|'default'|'error'|'opaque'|'opaqueredirect';export interface VoxaConfig{baseURL?:string;timeout?:number;headers?:Record<string,string>;debug?:boolean;deduplication?:DeduplicationConfig;retry?:RetryConfig;priority?:RequestPriority;cache?:CacheConfig;queue?:QueueConfig;rate?:RateLimiterConfig;schema?:SchemaValidatorConfig;errors?:ErrorClassifierConfig;metadata?:MetadataConfig;}export interface MetadataConfig{enabled?:boolean;log?:boolean;fields?:Array<'id'|'method'|'endpoint'|'priority'|'timestamp'|'startTime'|'endTime'>;maxEntries?:number;customHandler?:(metadata:RequestMetadata)=>void;}export interface SchemaValidatorConfig{enabled?:boolean;requestSchema?:any;responseSchema?:any;library?:'zod'|'yup';}export interface ErrorClassifierConfig{enabled?:boolean;}export interface RetryConfig{enabled?:boolean;count?:number;delay?:number;exponentialBackoff?:boolean;statusCodes?:number[];maxRetry?:number;}export interface CacheAdapter{get(key:string):Promise<string|null>;set(key:string,value:string,ttlSeconds:number):Promise<void>;delete(key:string):Promise<void>;clear():Promise<void>;}export interface CacheConfig{enabled?:boolean;ttl?:number;storage?:'memory'|'file'|'custom';adapter?:CacheAdapter;cacheEnvs?:{CACHE_URL?:string;CACHE_PASSWORD?:string;CACHE_HOST?:string;CACHE_PORT?:number;CACHE_DB?:number;};}export interface QueueConfig{enabled?:boolean;maxConcurrent?:number;}export interface QueuedRequest{id:string;priority:'critical'|'high'|'normal'|'low';execute:(signal?:AbortSignal)=>Promise<Response>;resolve:(value:Response)=>void;reject:(error:any)=>void;timestamp:number;signal?:AbortSignal;}export interface RequestMetadata{id:string;method:HttpMethod;endpoint:string;priority:'critical'|'high'|'normal'|'low';timestamp:number;expiresAt?:number;startTime?:number;endTime?:number;}export interface CacheEntry{response:Response;timestamp:number;ttl:number;expiresAt:number;requestId?:string;}export interface DeduplicationConfig{enabled?:boolean;ttl?:number;}export interface RateLimiterConfig{enabled?:boolean;maxRequests?:number;perMilliseconds?:number;}export interface Interceptor{successFn:any;errorFn:any;}export interface VoxaResponse<T = any>{response:Response;data:T|null;metadata:RequestMetadata;requestId:string;status:number;statusText:string;}
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1 @@
1
+ export declare const VOXA_VERSION:string;
@@ -0,0 +1 @@
1
+ import{readFileSync as o}from"fs";import{join as r}from"path";let t="unknown";try{t=JSON.parse(o(r(__dirname,"../../package.json"),"utf-8")).version||t}catch(o){}export const VOXA_VERSION=t;
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@0xshariq/voxa-core",
3
+ "version": "1.0.0",
4
+ "description": "Core HTTP client library built on native Fetch API with essential features like caching, retry, and request deduplication.",
5
+ "keywords": [
6
+ "http",
7
+ "http-client",
8
+ "fetch",
9
+ "axios",
10
+ "request",
11
+ "cache",
12
+ "retry",
13
+ "typescript"
14
+ ],
15
+ "homepage": "https://github.com/0xshariq/voxa#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/0xshariq/voxa/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/0xshariq/voxa.git",
22
+ "directory": "packages/voxa"
23
+ },
24
+ "license": "MIT",
25
+ "author": "Sharique Chaudhary",
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js",
34
+ "default": "./dist/index.js"
35
+ },
36
+ "./client": {
37
+ "types": "./dist/lib/client/voxa.d.ts",
38
+ "import": "./dist/lib/client/voxa.js"
39
+ },
40
+ "./cache": {
41
+ "types": "./dist/lib/features/cache/manager.d.ts",
42
+ "import": "./dist/lib/features/cache/manager.js"
43
+ },
44
+ "./streaming": {
45
+ "types": "./dist/lib/features/streaming-images/manager.d.ts",
46
+ "import": "./dist/lib/features/streaming-images/manager.js"
47
+ },
48
+ "./http2": {
49
+ "types": "./dist/lib/features/http2-push/manager.d.ts",
50
+ "import": "./dist/lib/features/http2-push/manager.js"
51
+ },
52
+ "./types": {
53
+ "types": "./dist/lib/types/client-types.d.ts"
54
+ }
55
+ },
56
+ "files": [
57
+ "dist",
58
+ "README.md",
59
+ "LICENSE",
60
+ "CHANGELOG.md"
61
+ ],
62
+ "sideEffects": false,
63
+ "scripts": {
64
+ "build": "node scripts/build.js",
65
+ "clean": "rm -rf dist",
66
+ "test": "tsx src/test.ts",
67
+ "prepublishOnly": "pnpm run clean && pnpm run build",
68
+ "typecheck": "tsc --noEmit"
69
+ },
70
+ "devDependencies": {
71
+ "@types/node": "^24.10.1",
72
+ "terser": "^5.44.1",
73
+ "tsx": "^4.20.6",
74
+ "typescript": "^5.9.3"
75
+ }
76
+ }