mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-09-24 17:12:00 +00:00
* Introduced `headers` property in `ClientConfig` interface. * Updated `HeaderSpecClient` and `ResolveSpecClient` to utilize custom headers. * Implemented `mergeHeaders` function to handle case-insensitive header merging. * Added tests for custom header functionality in clients.
2 lines
11 KiB
JavaScript
2 lines
11 KiB
JavaScript
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("uuid");function u(...r){const e={};for(const t of r)for(const[s,n]of Object.entries(t)){for(const i of Object.keys(e))i.toLowerCase()===s.toLowerCase()&&delete e[i];Object.defineProperty(e,s,{value:n,enumerable:!0,configurable:!0,writable:!0})}return e}function f(r){return u({"Content-Type":"application/json"},r.headers??{},r.token?{Authorization:`Bearer ${r.token}`}:{})}function y(r){const e=Object.entries(f(r)).map(([t,s])=>[t.toLowerCase(),s]).sort(([t],[s])=>t.localeCompare(s));return JSON.stringify([r.baseUrl,e])}const m=new Map;function v(r){const e=y(r);let t=m.get(e);return t||(t=new w(r),m.set(e,t)),t}class w{constructor(e){this.config={...e,headers:{...e.headers}}}buildUrl(e,t,s){let n=`${this.config.baseUrl}/${e}/${t}`;return s&&(n+=`/${s}`),n}baseHeaders(){return f(this.config)}async fetchWithError(e,t){const s=await fetch(e,t),n=await s.json();if(!s.ok)throw new Error(n.error?.message||"An error occurred");return n}async getMetadata(e,t){const s=this.buildUrl(e,t);return this.fetchWithError(s,{method:"GET",headers:this.baseHeaders()})}async read(e,t,s,n){const i=typeof s=="number"||typeof s=="string"?String(s):void 0,a=this.buildUrl(e,t,i),c={operation:"read",id:Array.isArray(s)?s:void 0,options:n};return this.fetchWithError(a,{method:"POST",headers:this.baseHeaders(),body:JSON.stringify(c)})}async create(e,t,s,n){const i=this.buildUrl(e,t),a={operation:"create",data:s,options:n};return this.fetchWithError(i,{method:"POST",headers:this.baseHeaders(),body:JSON.stringify(a)})}async update(e,t,s,n,i){const a=typeof n=="number"||typeof n=="string"?String(n):void 0,c=this.buildUrl(e,t,a),o={operation:"update",id:Array.isArray(n)?n:void 0,data:s,options:i};return this.fetchWithError(c,{method:"POST",headers:this.baseHeaders(),body:JSON.stringify(o)})}async delete(e,t,s){const n=this.buildUrl(e,t,String(s)),i={operation:"delete"};return this.fetchWithError(n,{method:"POST",headers:this.baseHeaders(),body:JSON.stringify(i)})}}const b=new Map;function k(r){const e=r.url;let t=b.get(e);return t||(t=new S(r),b.set(e,t)),t}class S{constructor(e){this.ws=null,this.messageHandlers=new Map,this.subscriptions=new Map,this.eventListeners={},this.state="disconnected",this.reconnectAttempts=0,this.reconnectTimer=null,this.heartbeatTimer=null,this.isManualClose=!1,this.config={url:e.url,reconnect:e.reconnect??!0,reconnectInterval:e.reconnectInterval??3e3,maxReconnectAttempts:e.maxReconnectAttempts??10,heartbeatInterval:e.heartbeatInterval??3e4,debug:e.debug??!1}}async connect(){if(this.ws?.readyState===WebSocket.OPEN){this.log("Already connected");return}return this.isManualClose=!1,this.setState("connecting"),new Promise((e,t)=>{try{this.ws=new WebSocket(this.config.url),this.ws.onopen=()=>{this.log("Connected to WebSocket server"),this.setState("connected"),this.reconnectAttempts=0,this.startHeartbeat(),this.emit("connect"),e()},this.ws.onmessage=s=>{this.handleMessage(s.data)},this.ws.onerror=s=>{this.log("WebSocket error:",s);const n=new Error("WebSocket connection error");this.emit("error",n),t(n)},this.ws.onclose=s=>{this.log("WebSocket closed:",s.code,s.reason),this.stopHeartbeat(),this.setState("disconnected"),this.emit("disconnect",s),this.config.reconnect&&!this.isManualClose&&this.reconnectAttempts<this.config.maxReconnectAttempts&&(this.reconnectAttempts++,this.log(`Reconnection attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts}`),this.setState("reconnecting"),this.reconnectTimer=setTimeout(()=>{this.connect().catch(n=>{this.log("Reconnection failed:",n)})},this.config.reconnectInterval))}}catch(s){t(s)}})}disconnect(){this.isManualClose=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.stopHeartbeat(),this.ws&&(this.setState("disconnecting"),this.ws.close(),this.ws=null),this.setState("disconnected"),this.messageHandlers.clear()}async request(e,t,s){this.ensureConnected();const n=h.v4(),i={id:n,type:"request",operation:e,entity:t,schema:s?.schema,record_id:s?.record_id,data:s?.data,options:s?.options};return new Promise((a,c)=>{this.messageHandlers.set(n,o=>{o.success?a(o.data):c(new Error(o.error?.message||"Request failed"))}),this.send(i),setTimeout(()=>{this.messageHandlers.has(n)&&(this.messageHandlers.delete(n),c(new Error("Request timeout")))},3e4)})}async read(e,t){return this.request("read",e,{schema:t?.schema,record_id:t?.record_id,options:{filters:t?.filters,columns:t?.columns,sort:t?.sort,preload:t?.preload,limit:t?.limit,offset:t?.offset}})}async create(e,t,s){return this.request("create",e,{schema:s?.schema,data:t})}async update(e,t,s,n){return this.request("update",e,{schema:n?.schema,record_id:t,data:s})}async delete(e,t,s){await this.request("delete",e,{schema:s?.schema,record_id:t})}async meta(e,t){return this.request("meta",e,{schema:t?.schema})}async subscribe(e,t,s){this.ensureConnected();const n=h.v4(),i={id:n,type:"subscription",operation:"subscribe",entity:e,schema:s?.schema,options:{filters:s?.filters}};return new Promise((a,c)=>{this.messageHandlers.set(n,o=>{if(o.success&&o.data?.subscription_id){const l=o.data.subscription_id;this.subscriptions.set(l,{id:l,entity:e,schema:s?.schema,options:{filters:s?.filters},callback:t}),this.log(`Subscribed to ${e} with ID: ${l}`),a(l)}else c(new Error(o.error?.message||"Subscription failed"))}),this.send(i),setTimeout(()=>{this.messageHandlers.has(n)&&(this.messageHandlers.delete(n),c(new Error("Subscription timeout")))},1e4)})}async unsubscribe(e){this.ensureConnected();const t=h.v4(),s={id:t,type:"subscription",operation:"unsubscribe",subscription_id:e};return new Promise((n,i)=>{this.messageHandlers.set(t,a=>{a.success?(this.subscriptions.delete(e),this.log(`Unsubscribed from ${e}`),n()):i(new Error(a.error?.message||"Unsubscribe failed"))}),this.send(s),setTimeout(()=>{this.messageHandlers.has(t)&&(this.messageHandlers.delete(t),i(new Error("Unsubscribe timeout")))},1e4)})}getSubscriptions(){return Array.from(this.subscriptions.values())}getState(){return this.state}isConnected(){return this.ws?.readyState===WebSocket.OPEN}on(e,t){this.eventListeners[e]=t}off(e){delete this.eventListeners[e]}handleMessage(e){try{const t=JSON.parse(e);switch(this.log("Received message:",t),this.emit("message",t),t.type){case"response":this.handleResponse(t);break;case"notification":this.handleNotification(t);break;case"pong":break;default:this.log("Unknown message type:",t.type)}}catch(t){this.log("Error parsing message:",t)}}handleResponse(e){const t=this.messageHandlers.get(e.id);t&&(t(e),this.messageHandlers.delete(e.id))}handleNotification(e){const t=this.subscriptions.get(e.subscription_id);t?.callback&&t.callback(e)}send(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");const t=JSON.stringify(e);this.log("Sending message:",e),this.ws.send(t)}startHeartbeat(){this.heartbeatTimer||(this.heartbeatTimer=setInterval(()=>{if(this.isConnected()){const e={id:h.v4(),type:"ping"};this.send(e)}},this.config.heartbeatInterval))}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}setState(e){this.state!==e&&(this.state=e,this.emit("stateChange",e))}ensureConnected(){if(!this.isConnected())throw new Error("WebSocket is not connected. Call connect() first.")}emit(e,...t){const s=this.eventListeners[e];s&&s(...t)}log(...e){this.config.debug&&console.log("[WebSocketClient]",...e)}}function E(r){return typeof btoa=="function"?"ZIP_"+btoa(r):"ZIP_"+Buffer.from(r,"utf-8").toString("base64")}function H(r){let e=r;return e.startsWith("ZIP_")?(e=e.slice(4).replace(/[\n\r ]/g,""),e=g(e)):e.startsWith("__")&&(e=e.slice(2).replace(/[\n\r ]/g,""),e=g(e)),(e.startsWith("ZIP_")||e.startsWith("__"))&&(e=H(e)),e}function g(r){return typeof atob=="function"?atob(r):Buffer.from(r,"base64").toString("utf-8")}function d(r){const e={};if(r.columns?.length&&(e["X-Select-Fields"]=r.columns.join(",")),r.omit_columns?.length&&(e["X-Not-Select-Fields"]=r.omit_columns.join(",")),r.filters?.length)for(const t of r.filters){const s=t.logic_operator??"AND",n=_(t.operator),i=O(t);t.operator==="eq"&&s==="AND"?e[`X-FieldFilter-${t.column}`]=i:s==="OR"?e[`X-SearchOr-${n}-${t.column}`]=i:e[`X-SearchOp-${n}-${t.column}`]=i}if(r.sort?.length){const t=r.sort.map(s=>s.direction.toUpperCase()==="DESC"?`-${s.column}`:`+${s.column}`);e["X-Sort"]=t.join(",")}if(r.limit!==void 0&&(e["X-Limit"]=String(r.limit)),r.offset!==void 0&&(e["X-Offset"]=String(r.offset)),r.cursor_forward&&(e["X-Cursor-Forward"]=r.cursor_forward),r.cursor_backward&&(e["X-Cursor-Backward"]=r.cursor_backward),r.preload?.length){const t=r.preload.map(s=>s.columns?.length?`${s.relation}:${s.columns.join(",")}`:s.relation);e["X-Preload"]=t.join("|")}if(r.fetch_row_number&&(e["X-Fetch-RowNumber"]=r.fetch_row_number),r.computedColumns?.length)for(const t of r.computedColumns)e[`X-CQL-SEL-${t.name}`]=t.expression;if(r.customOperators?.length){const t=r.customOperators.map(s=>s.sql);e["X-Custom-SQL-W"]=t.join(" AND ")}return e}function _(r){switch(r){case"eq":return"equals";case"neq":return"notequals";case"gt":return"greaterthan";case"gte":return"greaterthanorequal";case"lt":return"lessthan";case"lte":return"lessthanorequal";case"like":case"ilike":case"contains":return"contains";case"startswith":return"beginswith";case"endswith":return"endswith";case"in":return"in";case"between":return"between";case"between_inclusive":return"betweeninclusive";case"is_null":return"empty";case"is_not_null":return"notempty";default:return r}}function O(r){return r.value===null||r.value===void 0?"":Array.isArray(r.value)?r.value.join(","):String(r.value)}const p=new Map;function W(r){const e=y(r);let t=p.get(e);return t||(t=new C(r),p.set(e,t)),t}class C{constructor(e){this.config={...e,headers:{...e.headers}}}buildUrl(e,t,s){let n=`${this.config.baseUrl}/${e}/${t}`;return s&&(n+=`/${s}`),n}baseHeaders(){return f(this.config)}async fetchWithError(e,t){const s=await fetch(e,t),n=await s.json();if(!s.ok)throw new Error(n.error?.message||`${s.statusText} (${s.status})`);return{data:n,success:!0,error:n.error?n.error:void 0,metadata:{count:s.headers.get("content-range")?Number(s.headers.get("content-range")?.split("/")[1]):0,total:s.headers.get("content-range")?Number(s.headers.get("content-range")?.split("/")[1]):0,filtered:s.headers.get("content-range")?Number(s.headers.get("content-range")?.split("/")[1]):0,offset:s.headers.get("content-range")?Number(s.headers.get("content-range")?.split("/")[0].split("-")[0]):0,limit:s.headers.get("x-limit")?Number(s.headers.get("x-limit")):0}}}async read(e,t,s,n){const i=this.buildUrl(e,t,s),a=n?d(n):{};return this.fetchWithError(i,{method:"GET",headers:u(this.baseHeaders(),a)})}async create(e,t,s,n){const i=this.buildUrl(e,t),a=n?d(n):{};return this.fetchWithError(i,{method:"POST",headers:u(this.baseHeaders(),a),body:JSON.stringify(s)})}async update(e,t,s,n,i){const a=this.buildUrl(e,t,s),c=i?d(i):{};return this.fetchWithError(a,{method:"PUT",headers:u(this.baseHeaders(),c),body:JSON.stringify(n)})}async delete(e,t,s){const n=this.buildUrl(e,t,s);return this.fetchWithError(n,{method:"DELETE",headers:this.baseHeaders()})}}exports.HeaderSpecClient=C;exports.ResolveSpecClient=w;exports.WebSocketClient=S;exports.buildHeaders=d;exports.decodeHeaderValue=H;exports.encodeHeaderValue=E;exports.getHeaderSpecClient=W;exports.getResolveSpecClient=v;exports.getWebSocketClient=k;
|