@hlmr/sdk-js 1.0.27 → 1.0.28

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.
@@ -13,6 +13,6 @@ export type { HttpClient } from './utils/http';
13
13
  export { isJSendResponse, isJSendSuccess, isJSendFail, isJSendError } from './utils/jsend';
14
14
  export { clearAllCache, preventBackNavigation, performLogout } from './utils/auth';
15
15
  export type { LogoutOptions } from './utils/auth';
16
- export declare const SDK_VERSION = "1.0.26";
16
+ export declare const SDK_VERSION = "1.0.27";
17
17
  export declare const SDK_NAME = "hlmr-sdk-js";
18
18
  //# sourceMappingURL=index.d.ts.map
package/dist/cjs/index.js CHANGED
@@ -18,6 +18,6 @@ var auth_1 = require("./utils/auth");
18
18
  Object.defineProperty(exports, "clearAllCache", { enumerable: true, get: function () { return auth_1.clearAllCache; } });
19
19
  Object.defineProperty(exports, "preventBackNavigation", { enumerable: true, get: function () { return auth_1.preventBackNavigation; } });
20
20
  Object.defineProperty(exports, "performLogout", { enumerable: true, get: function () { return auth_1.performLogout; } });
21
- exports.SDK_VERSION = '1.0.26';
21
+ exports.SDK_VERSION = '1.0.27';
22
22
  exports.SDK_NAME = 'hlmr-sdk-js';
23
23
  //# sourceMappingURL=index.js.map
@@ -13,6 +13,6 @@ export type { HttpClient } from './utils/http';
13
13
  export { isJSendResponse, isJSendSuccess, isJSendFail, isJSendError } from './utils/jsend';
14
14
  export { clearAllCache, preventBackNavigation, performLogout } from './utils/auth';
15
15
  export type { LogoutOptions } from './utils/auth';
16
- export declare const SDK_VERSION = "1.0.26";
16
+ export declare const SDK_VERSION = "1.0.27";
17
17
  export declare const SDK_NAME = "hlmr-sdk-js";
18
18
  //# sourceMappingURL=index.d.ts.map
package/dist/esm/index.js CHANGED
@@ -4,6 +4,6 @@ export { EventsModule } from './modules/events';
4
4
  export { HlmrApiError } from './types/errors';
5
5
  export { isJSendResponse, isJSendSuccess, isJSendFail, isJSendError } from './utils/jsend';
6
6
  export { clearAllCache, preventBackNavigation, performLogout } from './utils/auth';
7
- export const SDK_VERSION = '1.0.26';
7
+ export const SDK_VERSION = '1.0.27';
8
8
  export const SDK_NAME = 'hlmr-sdk-js';
9
9
  //# sourceMappingURL=index.js.map
@@ -13,6 +13,6 @@ export type { HttpClient } from './utils/http';
13
13
  export { isJSendResponse, isJSendSuccess, isJSendFail, isJSendError } from './utils/jsend';
14
14
  export { clearAllCache, preventBackNavigation, performLogout } from './utils/auth';
15
15
  export type { LogoutOptions } from './utils/auth';
16
- export declare const SDK_VERSION = "1.0.26";
16
+ export declare const SDK_VERSION = "1.0.27";
17
17
  export declare const SDK_NAME = "hlmr-sdk-js";
18
18
  //# sourceMappingURL=index.d.ts.map
@@ -958,7 +958,7 @@
958
958
  window.location.replace(finalRedirectUrl);
959
959
  }
960
960
 
961
- const SDK_VERSION = '1.0.26';
961
+ const SDK_VERSION = '1.0.27';
962
962
  const SDK_NAME = 'hlmr-sdk-js';
963
963
 
964
964
  exports.AppsModule = AppsModule;
@@ -1,2 +1,2 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).HlmrSDK={})}(this,function(t){"use strict";const e={production:{name:"production",url:"https://api.hlmr.io",description:"Environnement de production"},development:{name:"development",url:"https://api.dev.hlmr.io",description:"Environnement de développement"},staging:{name:"staging",url:"https://api.stg.hlmr.io",description:"Environnement de staging"}};class n extends Error{constructor(t,e,s,i,o){super(t),this.name="HlmrApiError",this.statusCode=e,this.code=s,this.response=i,this.originalError=o,Error.captureStackTrace&&Error.captureStackTrace(this,n)}static fromJSendResponse(t,e){if("fail"===t.status){return new n(t.data.detail||"Validation failed",e,"VALIDATION_ERROR",t)}if("error"===t.status){const s=t;return new n(s.message||"Server error",e,s.code||"SERVER_ERROR",t)}return new n("Unknown API error",e,"UNKNOWN_ERROR",t)}static networkError(t){return new n(`Network error: ${t.message}`,0,"NETWORK_ERROR",void 0,t)}static timeoutError(t){return new n(`Request timeout after ${t}ms`,0,"TIMEOUT_ERROR")}static configError(t){return new n(`Configuration error: ${t}`,0,"CONFIG_ERROR")}isAuthError(){return 401===this.statusCode}isPermissionError(){return 403===this.statusCode}isValidationError(){return this.statusCode>=400&&this.statusCode<500&&"VALIDATION_ERROR"===this.code}isServerError(){return this.statusCode>=500}isNetworkError(){return"NETWORK_ERROR"===this.code}isTimeoutError(){return"TIMEOUT_ERROR"===this.code}}function s(t){return"object"==typeof t&&null!==t&&"string"==typeof t.status&&["success","fail","error"].includes(t.status)}function i(t){return"success"===t.status&&"data"in t}class o{constructor(t){this.config=t}updateConfig(t){this.config={...this.config,...t}}setBearerToken(t){this.config.bearerToken=t}clearBearerToken(){delete this.config.bearerToken}setAppSecret(t){this.config.appSecret=t}clearAppSecret(){delete this.config.appSecret}buildUrl(t,e="v1"){if(t.startsWith("http://")||t.startsWith("https://"))return t;const n=this.config.baseUrl.replace(/\/$/,""),s=t.replace(/^\//,"");return s.match(/^v\d+\//)?`${n}/${s}`:e?`${n}/${e}/${s}`:`${n}/${s}`}buildHeaders(t={}){const e={"Content-Type":"application/json","X-App-ID":this.config.appId,"X-SDK-Referrer":"hlmr-sdk-js@1.0.6",...this.config.customHeaders,...t.headers};return this.config.appSecret&&!t.skipAuth?e["X-App-Secret"]=this.config.appSecret:this.config.bearerToken&&!t.skipAuth&&(e.Authorization=`Bearer ${this.config.bearerToken}`),e}createTimeoutController(t){const e=new AbortController;return setTimeout(()=>e.abort(),t),e}async processResponse(t){const e={};let o;t.headers.forEach((t,n)=>{e[n]=t});const r=t.headers.get("content-type");if(r&&r.includes("application/json"))try{o=await t.json()}catch(e){o=await t.text()}else o=await t.text();if(s(o)){if(!t.ok)throw n.fromJSendResponse(o,t.status);if(i(o))return{status:t.status,statusText:t.statusText,headers:e,data:o.data}}if(!t.ok)throw new n(`HTTP ${t.status}: ${t.statusText}`,t.status,"HTTP_ERROR");return{status:t.status,statusText:t.statusText,headers:e,data:o}}async request(t,e,s,i={}){const o=this.buildUrl(e,i.apiVersion),r=this.buildHeaders(i),c=i.timeout||this.config.timeout||3e4;this.config.debug&&console.log(`[HlmrSDK] ${t} ${o}`,{headers:r,body:s});const a={method:t,headers:r,signal:this.createTimeoutController(c).signal};s&&["POST","PUT","PATCH"].includes(t.toUpperCase())&&(a.body="string"==typeof s?s:JSON.stringify(s));try{let t=await fetch(o,a);if(307===t.status||301===t.status||302===t.status){const e=t.headers.get("location");if(e){console.warn(`[HlmrSDK] Received ${t.status} redirect for ${o}. Following redirect to ${e}. Please update your code to use the correct URL with trailing slash.`);const n=e.startsWith("http")?e:new URL(e,o).toString();t=await fetch(n,a)}}const e=await this.processResponse(t);return this.config.debug&&console.log("[HlmrSDK] Response:",e),e}catch(t){if(t instanceof n)throw t;if(t instanceof Error){if("AbortError"===t.name)throw n.timeoutError(c);throw n.networkError(t)}throw new n("Unknown error occurred",0,"UNKNOWN_ERROR")}}async get(t,e){return this.request("GET",t,void 0,e)}async post(t,e,n){return this.request("POST",t,e,n)}async put(t,e,n){return this.request("PUT",t,e,n)}async patch(t,e,n){return this.request("PATCH",t,e,n)}async delete(t,e){return this.request("DELETE",t,void 0,e)}async directRequest(t,e="GET",s,i={}){const o={"Content-Type":"application/json",...i.headers},r=i.timeout||this.config.timeout||3e4,c=this.createTimeoutController(r);this.config.debug&&console.log(`[HlmrSDK] Direct ${e} ${t}`,{headers:o,body:s});const a={method:e,headers:o,signal:c.signal};s&&["POST","PUT","PATCH"].includes(e)&&(a.body="string"==typeof s?s:JSON.stringify(s));try{const e=await fetch(t,a),n=await this.processResponse(e);return this.config.debug&&console.log("[HlmrSDK] Direct Response:",n),n}catch(t){if(t instanceof n)throw t;if(t instanceof Error){if("AbortError"===t.name)throw n.timeoutError(r);throw n.networkError(t)}throw new n("Unknown error occurred",0,"UNKNOWN_ERROR")}}}class r{constructor(t){this.http=t}async validateRedirect(t,e){const n={app_id:t,redirect_uri:e};return(await this.http.post(`apps/${t}/validate-redirect`,n,{skipAuth:!0})).data}}class c{constructor(t){this.http=t}async getProfile(){return(await this.http.get("users/profile")).data}}class a{constructor(t){this.http=t}async ping(t){let e="ping";if(t){const n=new URLSearchParams;void 0!==t.idle&&n.append("idle",String(t.idle)),void 0!==t.app&&n.append("app",String(t.app)),void 0!==t.lat&&n.append("lat",String(t.lat)),void 0!==t.lng&&n.append("lng",String(t.lng)),void 0!==t.device&&n.append("device",String(t.device));const s=n.toString();s&&(e=`${e}?${s}`)}return(await this.http.get(e,{apiVersion:""})).data}async version(){return(await this.http.get("version",{apiVersion:"",skipAuth:!0})).data}async checkServiceReady(t){return(await this.http.get(`ready/${t}`,{apiVersion:"",skipAuth:!0})).data}}class h{constructor(t){this.http=t}async logSession(t,e){return(await this.http.post(`apps/${t}/sessions`,e||{})).data}}const u={pingInterval:3e4,autoReconnect:!0,maxReconnectAttempts:5,reconnectDelay:1e3,maxReconnectDelay:3e4,debug:!1};class l{constructor(t,e){this.ws=null,this.pingIntervalId=null,this.reconnectAttempts=0,this.reconnectTimeoutId=null,this.isConnecting=!1,this.isManualDisconnect=!1,this.subscriptions=new Map,this.pendingSubscriptions=new Map,this.listeners=new Map,this.userId=null,this.bearerToken=null,this.authResolve=null,this.authReject=null,this.http=t,this.config={...u,...e};const n=t.config;this.bearerToken=n.bearerToken||null}async connect(){if(this.ws&&this.ws.readyState===WebSocket.OPEN)throw new Error("Already connected");if(this.isConnecting)throw new Error("Connection in progress");return this.isConnecting=!0,this.isManualDisconnect=!1,this.reconnectAttempts=0,this._connect()}async _connect(){return new Promise((t,e)=>{try{this.authResolve=t,this.authReject=e;const n=this._buildWebSocketUrl();this.config.debug&&console.log("[EventsModule] Connecting to:",n),this.ws=new WebSocket(n);const s=setTimeout(()=>{this.ws&&this.ws.readyState!==WebSocket.OPEN&&(this.ws.close(),this.isConnecting=!1,this.authResolve=null,this.authReject=null,e(new Error("Connection timeout")))},1e4);this.ws.onopen=()=>{if(this.config.debug&&console.log("[EventsModule] WebSocket connected, sending auth..."),clearTimeout(s),this.bearerToken){const t={type:"auth",token:this.bearerToken};this.ws.send(JSON.stringify(t)),this.config.debug&&console.log("[EventsModule] Sent auth message")}else this.isConnecting=!1,this.authResolve=null,this.authReject=null,this.ws.close(1008,"Missing authentication token"),e(new Error("Missing authentication token"))},this.ws.onmessage=t=>{try{const e=JSON.parse(t.data);this._handleMessage(e)}catch(t){this.config.debug&&console.error("[EventsModule] Failed to parse message:",t)}},this.ws.onerror=t=>{this.config.debug&&console.error("[EventsModule] WebSocket error:",t),clearTimeout(s),this.isConnecting=!1,this.authResolve=null,this.authReject=null,this._emit("error",new Error("WebSocket error")),e(new Error("WebSocket error"))},this.ws.onclose=t=>{this.config.debug&&console.log("[EventsModule] WebSocket closed:",t.code,t.reason),clearTimeout(s),this._stopPingInterval(),this.isConnecting=!1,this.authReject&&(this.authReject(new Error(t.reason||"Connection closed before authentication")),this.authResolve=null,this.authReject=null),this._emit("disconnect",t.reason||"Connection closed"),!this.isManualDisconnect&&this.config.autoReconnect&&this._scheduleReconnect()}}catch(t){this.isConnecting=!1,this.authResolve=null,this.authReject=null,e(t)}})}disconnect(){this.isManualDisconnect=!0,this._cleanup(),this.ws&&(this.ws.close(1e3,"Client disconnect"),this.ws=null),this.config.debug&&console.log("[EventsModule] Disconnected")}async subscribe(t,e,n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected");const s=this._generateId();return new Promise((i,o)=>{const r=n?{domain:t,resource:e,options:n}:{domain:t,resource:e},c={resolve:t=>{this.subscriptions.set(t.id,r),i(t)},reject:o,...r};this.pendingSubscriptions.set(s,c);const a={type:"subscribe",subscription_id:s,domain:t,resource:e,options:{filters:(null==n?void 0:n.filters)||{},include_data:(null==n?void 0:n.include_data)||!1}};this.ws.send(JSON.stringify(a)),this.config.debug&&console.log("[EventsModule] Subscribing:",a),setTimeout(()=>{this.pendingSubscriptions.has(s)&&(this.pendingSubscriptions.delete(s),o(new Error("Subscription timeout")))},1e4)})}async unsubscribe(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected");return new Promise((e,n)=>{const s={type:"unsubscribe",subscription_id:t};this.ws.send(JSON.stringify(s)),this.subscriptions.delete(t),this.config.debug&&console.log("[EventsModule] Unsubscribing:",t),e()})}on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e)}off(t,e){const n=this.listeners.get(t);n&&n.delete(e)}sendTypingEvent(t,e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return void(this.config.debug&&console.warn("[EventsModule] Cannot send typing event: not connected"));const n={type:"typing_event",chat_id:t,event:e};this.ws.send(JSON.stringify(n)),this.config.debug&&console.log("[EventsModule] Sent typing event:",n)}get isConnected(){return null!==this.ws&&this.ws.readyState===WebSocket.OPEN}get currentUserId(){return this.userId}getSubscriptions(){return new Map(this.subscriptions)}ping(){this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify({type:"ping"}))}updateConfig(t){this.config={...this.config,...t},this.ws&&this.ws.readyState===WebSocket.OPEN&&(this._stopPingInterval(),this._startPingInterval())}_buildWebSocketUrl(){return`${this.http.config.baseUrl.replace("https://","wss://").replace("http://","ws://")}/v1/events/ws`}_handleMessage(t){var e;switch(this.config.debug&&console.log("[EventsModule] Message received:",t),t.type){case"connected":this.isConnecting=!1,this.userId=t.user_id,this._emit("connect",t),this.authResolve&&(this.authResolve(t),this.authResolve=null,this.authReject=null),this._startPingInterval(),this.reconnectAttempts>0&&this._resubscribeAll(),this.reconnectAttempts=0;break;case"auth_error":this.isConnecting=!1;const n=t.error||"Authentication failed";this.authReject&&(this.authReject(new Error(n)),this.authResolve=null,this.authReject=null),null===(e=this.ws)||void 0===e||e.close(1008,n);break;case"subscription_confirmed":this._handleSubscriptionConfirmed(t);break;case"unsubscribed":this.subscriptions.delete(t.subscription_id);break;case"notification":this._emit("notification",t);break;case"system_notification":this._emit("system_notification",t);break;case"pong":break;case"error":this._emit("error",t);break;default:this.config.debug&&console.warn("[EventsModule] Unknown message type:",t.type)}}_handleSubscriptionConfirmed(t){const e=this.pendingSubscriptions.get(t.subscription_id);if(e){this.pendingSubscriptions.delete(t.subscription_id);const n={id:t.subscription_id,domain:t.domain,resource:t.resource,filters:t.filters};e.resolve(n)}this._emit("subscription_confirmed",t)}_emit(t,...e){const n=this.listeners.get(t);n&&n.forEach(n=>{try{n(...e)}catch(e){console.error(`[EventsModule] Error in ${t} listener:`,e)}})}_startPingInterval(){this.config.pingInterval>0&&(this.pingIntervalId=setInterval(()=>{this.ping()},this.config.pingInterval),this.config.debug&&console.log("[EventsModule] Ping interval started:",this.config.pingInterval))}_stopPingInterval(){this.pingIntervalId&&(clearInterval(this.pingIntervalId),this.pingIntervalId=null)}_scheduleReconnect(){if(this.reconnectAttempts>=this.config.maxReconnectAttempts)return this.config.debug&&console.log("[EventsModule] Max reconnect attempts reached"),void this._emit("error",new Error("Max reconnect attempts reached"));this.reconnectAttempts++;const t=Math.min(this.config.reconnectDelay*Math.pow(2,this.reconnectAttempts-1),this.config.maxReconnectDelay);this.config.debug&&console.log(`[EventsModule] Reconnecting in ${t}ms (attempt ${this.reconnectAttempts})`),this._emit("reconnecting",this.reconnectAttempts),this.reconnectTimeoutId=setTimeout(async()=>{try{await this._connect()}catch(t){this.config.debug&&console.error("[EventsModule] Reconnect failed:",t)}},t)}async _resubscribeAll(){this.config.debug&&console.log("[EventsModule] Re-subscribing to",this.subscriptions.size,"subscriptions");const t=new Map(this.subscriptions);this.subscriptions.clear();for(const[e,n]of t)try{await this.subscribe(n.domain,n.resource,n.options),this.config.debug&&console.log("[EventsModule] Re-subscribed:",n.domain,n.resource)}catch(t){this.config.debug&&console.error("[EventsModule] Failed to re-subscribe:",t)}}_cleanup(){this._stopPingInterval(),this.reconnectTimeoutId&&(clearTimeout(this.reconnectTimeoutId),this.reconnectTimeoutId=null);for(const t of this.pendingSubscriptions.values())t.reject(new Error("Connection closed"));this.pendingSubscriptions.clear()}_generateId(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)})}}class d{constructor(t){const e=this.buildConfig(t);this.httpClient=new o(e),this.auth=new r(this.httpClient),this.user=new c(this.httpClient),this.system=new a(this.httpClient),this.apps=new h(this.httpClient),this.events=new l(this.httpClient,t.eventsConfig)}buildConfig(t){var s,i,o;let r;if("string"==typeof t.environment){const s=e[t.environment];if(!s)throw n.configError(`Unknown environment: ${t.environment}`);r=s.url}else r=t.environment&&"object"==typeof t.environment?t.environment.url:e.production.url;if(!t.appId||"string"!=typeof t.appId)throw n.configError("appId is required and must be a string");return{baseUrl:r,appId:t.appId,bearerToken:t.bearerToken,appSecret:t.appSecret,timeout:(null===(s=t.config)||void 0===s?void 0:s.timeout)||3e4,customHeaders:(null===(i=t.config)||void 0===i?void 0:i.customHeaders)||{},debug:(null===(o=t.config)||void 0===o?void 0:o.debug)||!1}}setBearerToken(t){if(!t||"string"!=typeof t)throw n.configError("Bearer token must be a non-empty string");this.httpClient.setBearerToken(t)}clearBearerToken(){this.httpClient.clearBearerToken()}setAppSecret(t){if(!t||"string"!=typeof t)throw n.configError("App secret must be a non-empty string");this.httpClient.setAppSecret(t)}clearAppSecret(){this.httpClient.clearAppSecret()}getConfig(){return{baseUrl:this.httpClient.config.baseUrl,appId:this.httpClient.config.appId,timeout:this.httpClient.config.timeout,customHeaders:{...this.httpClient.config.customHeaders},debug:this.httpClient.config.debug}}updateConfig(t){this.httpClient.updateConfig(t)}setDebug(t){this.httpClient.updateConfig({debug:t})}static forEnvironment(t,e,n){return new d({environment:t,appId:e,bearerToken:n})}static forProduction(t="default",e){return d.forEnvironment("production",t,e)}static forDevelopment(t="default",e){return d.forEnvironment("development",t,e)}static forStaging(t="default",e){return d.forEnvironment("staging",t,e)}async directRequest(t,e="GET",n,s){return this.httpClient.directRequest(t,e,n,s)}async get(t,e){return this.httpClient.get(t,e)}async post(t,e,n){return this.httpClient.post(t,e,n)}async put(t,e,n){return this.httpClient.put(t,e,n)}async patch(t,e,n){return this.httpClient.patch(t,e,n)}async delete(t,e){return this.httpClient.delete(t,e)}}function p(t){if(t)try{t()}catch(t){console.warn("[AuthUtils] Error clearing app-specific cache:",t)}try{localStorage.clear()}catch(t){}try{sessionStorage.clear()}catch(t){}try{document.cookie.split(";").forEach(t=>{const e=t.indexOf("="),n=e>-1?t.substr(0,e).trim():t.trim();document.cookie=`${n}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`,document.cookie=`${n}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=${window.location.hostname}`,document.cookie=`${n}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=.${window.location.hostname}`})}catch(t){}}function g(){window.history.pushState(null,"",window.location.href);const t=()=>{window.history.pushState(null,"",window.location.href)};window.addEventListener("popstate",t),setTimeout(()=>{window.removeEventListener("popstate",t)},5e3)}t.AppsModule=h,t.EventsModule=l,t.HlmrApiError=n,t.HlmrClient=d,t.SDK_NAME="hlmr-sdk-js",t.SDK_VERSION="1.0.26",t.clearAllCache=p,t.isJSendError=function(t){return"error"===t.status&&"message"in t},t.isJSendFail=function(t){return"fail"===t.status&&"data"in t},t.isJSendResponse=s,t.isJSendSuccess=i,t.performLogout=function(t={}){const{redirectUrl:e,onClearCache:n,idpBaseUrl:s,clientId:i,callbackPath:o}=t;let r;if(p(n),g(),e)r=e;else if(s&&i&&o){const t=`${s}/login?client_id=${i}&redirect_uri=${encodeURIComponent(window.location.origin+o)}`;r=`${s}/logout?redirect_uri=${encodeURIComponent(t)}`}else r="/login";window.location.replace(r)},t.preventBackNavigation=g});
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).HlmrSDK={})}(this,function(t){"use strict";const e={production:{name:"production",url:"https://api.hlmr.io",description:"Environnement de production"},development:{name:"development",url:"https://api.dev.hlmr.io",description:"Environnement de développement"},staging:{name:"staging",url:"https://api.stg.hlmr.io",description:"Environnement de staging"}};class n extends Error{constructor(t,e,s,i,o){super(t),this.name="HlmrApiError",this.statusCode=e,this.code=s,this.response=i,this.originalError=o,Error.captureStackTrace&&Error.captureStackTrace(this,n)}static fromJSendResponse(t,e){if("fail"===t.status){return new n(t.data.detail||"Validation failed",e,"VALIDATION_ERROR",t)}if("error"===t.status){const s=t;return new n(s.message||"Server error",e,s.code||"SERVER_ERROR",t)}return new n("Unknown API error",e,"UNKNOWN_ERROR",t)}static networkError(t){return new n(`Network error: ${t.message}`,0,"NETWORK_ERROR",void 0,t)}static timeoutError(t){return new n(`Request timeout after ${t}ms`,0,"TIMEOUT_ERROR")}static configError(t){return new n(`Configuration error: ${t}`,0,"CONFIG_ERROR")}isAuthError(){return 401===this.statusCode}isPermissionError(){return 403===this.statusCode}isValidationError(){return this.statusCode>=400&&this.statusCode<500&&"VALIDATION_ERROR"===this.code}isServerError(){return this.statusCode>=500}isNetworkError(){return"NETWORK_ERROR"===this.code}isTimeoutError(){return"TIMEOUT_ERROR"===this.code}}function s(t){return"object"==typeof t&&null!==t&&"string"==typeof t.status&&["success","fail","error"].includes(t.status)}function i(t){return"success"===t.status&&"data"in t}class o{constructor(t){this.config=t}updateConfig(t){this.config={...this.config,...t}}setBearerToken(t){this.config.bearerToken=t}clearBearerToken(){delete this.config.bearerToken}setAppSecret(t){this.config.appSecret=t}clearAppSecret(){delete this.config.appSecret}buildUrl(t,e="v1"){if(t.startsWith("http://")||t.startsWith("https://"))return t;const n=this.config.baseUrl.replace(/\/$/,""),s=t.replace(/^\//,"");return s.match(/^v\d+\//)?`${n}/${s}`:e?`${n}/${e}/${s}`:`${n}/${s}`}buildHeaders(t={}){const e={"Content-Type":"application/json","X-App-ID":this.config.appId,"X-SDK-Referrer":"hlmr-sdk-js@1.0.6",...this.config.customHeaders,...t.headers};return this.config.appSecret&&!t.skipAuth?e["X-App-Secret"]=this.config.appSecret:this.config.bearerToken&&!t.skipAuth&&(e.Authorization=`Bearer ${this.config.bearerToken}`),e}createTimeoutController(t){const e=new AbortController;return setTimeout(()=>e.abort(),t),e}async processResponse(t){const e={};let o;t.headers.forEach((t,n)=>{e[n]=t});const r=t.headers.get("content-type");if(r&&r.includes("application/json"))try{o=await t.json()}catch(e){o=await t.text()}else o=await t.text();if(s(o)){if(!t.ok)throw n.fromJSendResponse(o,t.status);if(i(o))return{status:t.status,statusText:t.statusText,headers:e,data:o.data}}if(!t.ok)throw new n(`HTTP ${t.status}: ${t.statusText}`,t.status,"HTTP_ERROR");return{status:t.status,statusText:t.statusText,headers:e,data:o}}async request(t,e,s,i={}){const o=this.buildUrl(e,i.apiVersion),r=this.buildHeaders(i),c=i.timeout||this.config.timeout||3e4;this.config.debug&&console.log(`[HlmrSDK] ${t} ${o}`,{headers:r,body:s});const a={method:t,headers:r,signal:this.createTimeoutController(c).signal};s&&["POST","PUT","PATCH"].includes(t.toUpperCase())&&(a.body="string"==typeof s?s:JSON.stringify(s));try{let t=await fetch(o,a);if(307===t.status||301===t.status||302===t.status){const e=t.headers.get("location");if(e){console.warn(`[HlmrSDK] Received ${t.status} redirect for ${o}. Following redirect to ${e}. Please update your code to use the correct URL with trailing slash.`);const n=e.startsWith("http")?e:new URL(e,o).toString();t=await fetch(n,a)}}const e=await this.processResponse(t);return this.config.debug&&console.log("[HlmrSDK] Response:",e),e}catch(t){if(t instanceof n)throw t;if(t instanceof Error){if("AbortError"===t.name)throw n.timeoutError(c);throw n.networkError(t)}throw new n("Unknown error occurred",0,"UNKNOWN_ERROR")}}async get(t,e){return this.request("GET",t,void 0,e)}async post(t,e,n){return this.request("POST",t,e,n)}async put(t,e,n){return this.request("PUT",t,e,n)}async patch(t,e,n){return this.request("PATCH",t,e,n)}async delete(t,e){return this.request("DELETE",t,void 0,e)}async directRequest(t,e="GET",s,i={}){const o={"Content-Type":"application/json",...i.headers},r=i.timeout||this.config.timeout||3e4,c=this.createTimeoutController(r);this.config.debug&&console.log(`[HlmrSDK] Direct ${e} ${t}`,{headers:o,body:s});const a={method:e,headers:o,signal:c.signal};s&&["POST","PUT","PATCH"].includes(e)&&(a.body="string"==typeof s?s:JSON.stringify(s));try{const e=await fetch(t,a),n=await this.processResponse(e);return this.config.debug&&console.log("[HlmrSDK] Direct Response:",n),n}catch(t){if(t instanceof n)throw t;if(t instanceof Error){if("AbortError"===t.name)throw n.timeoutError(r);throw n.networkError(t)}throw new n("Unknown error occurred",0,"UNKNOWN_ERROR")}}}class r{constructor(t){this.http=t}async validateRedirect(t,e){const n={app_id:t,redirect_uri:e};return(await this.http.post(`apps/${t}/validate-redirect`,n,{skipAuth:!0})).data}}class c{constructor(t){this.http=t}async getProfile(){return(await this.http.get("users/profile")).data}}class a{constructor(t){this.http=t}async ping(t){let e="ping";if(t){const n=new URLSearchParams;void 0!==t.idle&&n.append("idle",String(t.idle)),void 0!==t.app&&n.append("app",String(t.app)),void 0!==t.lat&&n.append("lat",String(t.lat)),void 0!==t.lng&&n.append("lng",String(t.lng)),void 0!==t.device&&n.append("device",String(t.device));const s=n.toString();s&&(e=`${e}?${s}`)}return(await this.http.get(e,{apiVersion:""})).data}async version(){return(await this.http.get("version",{apiVersion:"",skipAuth:!0})).data}async checkServiceReady(t){return(await this.http.get(`ready/${t}`,{apiVersion:"",skipAuth:!0})).data}}class h{constructor(t){this.http=t}async logSession(t,e){return(await this.http.post(`apps/${t}/sessions`,e||{})).data}}const u={pingInterval:3e4,autoReconnect:!0,maxReconnectAttempts:5,reconnectDelay:1e3,maxReconnectDelay:3e4,debug:!1};class l{constructor(t,e){this.ws=null,this.pingIntervalId=null,this.reconnectAttempts=0,this.reconnectTimeoutId=null,this.isConnecting=!1,this.isManualDisconnect=!1,this.subscriptions=new Map,this.pendingSubscriptions=new Map,this.listeners=new Map,this.userId=null,this.bearerToken=null,this.authResolve=null,this.authReject=null,this.http=t,this.config={...u,...e};const n=t.config;this.bearerToken=n.bearerToken||null}async connect(){if(this.ws&&this.ws.readyState===WebSocket.OPEN)throw new Error("Already connected");if(this.isConnecting)throw new Error("Connection in progress");return this.isConnecting=!0,this.isManualDisconnect=!1,this.reconnectAttempts=0,this._connect()}async _connect(){return new Promise((t,e)=>{try{this.authResolve=t,this.authReject=e;const n=this._buildWebSocketUrl();this.config.debug&&console.log("[EventsModule] Connecting to:",n),this.ws=new WebSocket(n);const s=setTimeout(()=>{this.ws&&this.ws.readyState!==WebSocket.OPEN&&(this.ws.close(),this.isConnecting=!1,this.authResolve=null,this.authReject=null,e(new Error("Connection timeout")))},1e4);this.ws.onopen=()=>{if(this.config.debug&&console.log("[EventsModule] WebSocket connected, sending auth..."),clearTimeout(s),this.bearerToken){const t={type:"auth",token:this.bearerToken};this.ws.send(JSON.stringify(t)),this.config.debug&&console.log("[EventsModule] Sent auth message")}else this.isConnecting=!1,this.authResolve=null,this.authReject=null,this.ws.close(1008,"Missing authentication token"),e(new Error("Missing authentication token"))},this.ws.onmessage=t=>{try{const e=JSON.parse(t.data);this._handleMessage(e)}catch(t){this.config.debug&&console.error("[EventsModule] Failed to parse message:",t)}},this.ws.onerror=t=>{this.config.debug&&console.error("[EventsModule] WebSocket error:",t),clearTimeout(s),this.isConnecting=!1,this.authResolve=null,this.authReject=null,this._emit("error",new Error("WebSocket error")),e(new Error("WebSocket error"))},this.ws.onclose=t=>{this.config.debug&&console.log("[EventsModule] WebSocket closed:",t.code,t.reason),clearTimeout(s),this._stopPingInterval(),this.isConnecting=!1,this.authReject&&(this.authReject(new Error(t.reason||"Connection closed before authentication")),this.authResolve=null,this.authReject=null),this._emit("disconnect",t.reason||"Connection closed"),!this.isManualDisconnect&&this.config.autoReconnect&&this._scheduleReconnect()}}catch(t){this.isConnecting=!1,this.authResolve=null,this.authReject=null,e(t)}})}disconnect(){this.isManualDisconnect=!0,this._cleanup(),this.ws&&(this.ws.close(1e3,"Client disconnect"),this.ws=null),this.config.debug&&console.log("[EventsModule] Disconnected")}async subscribe(t,e,n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected");const s=this._generateId();return new Promise((i,o)=>{const r=n?{domain:t,resource:e,options:n}:{domain:t,resource:e},c={resolve:t=>{this.subscriptions.set(t.id,r),i(t)},reject:o,...r};this.pendingSubscriptions.set(s,c);const a={type:"subscribe",subscription_id:s,domain:t,resource:e,options:{filters:(null==n?void 0:n.filters)||{},include_data:(null==n?void 0:n.include_data)||!1}};this.ws.send(JSON.stringify(a)),this.config.debug&&console.log("[EventsModule] Subscribing:",a),setTimeout(()=>{this.pendingSubscriptions.has(s)&&(this.pendingSubscriptions.delete(s),o(new Error("Subscription timeout")))},1e4)})}async unsubscribe(t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected");return new Promise((e,n)=>{const s={type:"unsubscribe",subscription_id:t};this.ws.send(JSON.stringify(s)),this.subscriptions.delete(t),this.config.debug&&console.log("[EventsModule] Unsubscribing:",t),e()})}on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e)}off(t,e){const n=this.listeners.get(t);n&&n.delete(e)}sendTypingEvent(t,e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return void(this.config.debug&&console.warn("[EventsModule] Cannot send typing event: not connected"));const n={type:"typing_event",chat_id:t,event:e};this.ws.send(JSON.stringify(n)),this.config.debug&&console.log("[EventsModule] Sent typing event:",n)}get isConnected(){return null!==this.ws&&this.ws.readyState===WebSocket.OPEN}get currentUserId(){return this.userId}getSubscriptions(){return new Map(this.subscriptions)}ping(){this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify({type:"ping"}))}updateConfig(t){this.config={...this.config,...t},this.ws&&this.ws.readyState===WebSocket.OPEN&&(this._stopPingInterval(),this._startPingInterval())}_buildWebSocketUrl(){return`${this.http.config.baseUrl.replace("https://","wss://").replace("http://","ws://")}/v1/events/ws`}_handleMessage(t){var e;switch(this.config.debug&&console.log("[EventsModule] Message received:",t),t.type){case"connected":this.isConnecting=!1,this.userId=t.user_id,this._emit("connect",t),this.authResolve&&(this.authResolve(t),this.authResolve=null,this.authReject=null),this._startPingInterval(),this.reconnectAttempts>0&&this._resubscribeAll(),this.reconnectAttempts=0;break;case"auth_error":this.isConnecting=!1;const n=t.error||"Authentication failed";this.authReject&&(this.authReject(new Error(n)),this.authResolve=null,this.authReject=null),null===(e=this.ws)||void 0===e||e.close(1008,n);break;case"subscription_confirmed":this._handleSubscriptionConfirmed(t);break;case"unsubscribed":this.subscriptions.delete(t.subscription_id);break;case"notification":this._emit("notification",t);break;case"system_notification":this._emit("system_notification",t);break;case"pong":break;case"error":this._emit("error",t);break;default:this.config.debug&&console.warn("[EventsModule] Unknown message type:",t.type)}}_handleSubscriptionConfirmed(t){const e=this.pendingSubscriptions.get(t.subscription_id);if(e){this.pendingSubscriptions.delete(t.subscription_id);const n={id:t.subscription_id,domain:t.domain,resource:t.resource,filters:t.filters};e.resolve(n)}this._emit("subscription_confirmed",t)}_emit(t,...e){const n=this.listeners.get(t);n&&n.forEach(n=>{try{n(...e)}catch(e){console.error(`[EventsModule] Error in ${t} listener:`,e)}})}_startPingInterval(){this.config.pingInterval>0&&(this.pingIntervalId=setInterval(()=>{this.ping()},this.config.pingInterval),this.config.debug&&console.log("[EventsModule] Ping interval started:",this.config.pingInterval))}_stopPingInterval(){this.pingIntervalId&&(clearInterval(this.pingIntervalId),this.pingIntervalId=null)}_scheduleReconnect(){if(this.reconnectAttempts>=this.config.maxReconnectAttempts)return this.config.debug&&console.log("[EventsModule] Max reconnect attempts reached"),void this._emit("error",new Error("Max reconnect attempts reached"));this.reconnectAttempts++;const t=Math.min(this.config.reconnectDelay*Math.pow(2,this.reconnectAttempts-1),this.config.maxReconnectDelay);this.config.debug&&console.log(`[EventsModule] Reconnecting in ${t}ms (attempt ${this.reconnectAttempts})`),this._emit("reconnecting",this.reconnectAttempts),this.reconnectTimeoutId=setTimeout(async()=>{try{await this._connect()}catch(t){this.config.debug&&console.error("[EventsModule] Reconnect failed:",t)}},t)}async _resubscribeAll(){this.config.debug&&console.log("[EventsModule] Re-subscribing to",this.subscriptions.size,"subscriptions");const t=new Map(this.subscriptions);this.subscriptions.clear();for(const[e,n]of t)try{await this.subscribe(n.domain,n.resource,n.options),this.config.debug&&console.log("[EventsModule] Re-subscribed:",n.domain,n.resource)}catch(t){this.config.debug&&console.error("[EventsModule] Failed to re-subscribe:",t)}}_cleanup(){this._stopPingInterval(),this.reconnectTimeoutId&&(clearTimeout(this.reconnectTimeoutId),this.reconnectTimeoutId=null);for(const t of this.pendingSubscriptions.values())t.reject(new Error("Connection closed"));this.pendingSubscriptions.clear()}_generateId(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)})}}class d{constructor(t){const e=this.buildConfig(t);this.httpClient=new o(e),this.auth=new r(this.httpClient),this.user=new c(this.httpClient),this.system=new a(this.httpClient),this.apps=new h(this.httpClient),this.events=new l(this.httpClient,t.eventsConfig)}buildConfig(t){var s,i,o;let r;if("string"==typeof t.environment){const s=e[t.environment];if(!s)throw n.configError(`Unknown environment: ${t.environment}`);r=s.url}else r=t.environment&&"object"==typeof t.environment?t.environment.url:e.production.url;if(!t.appId||"string"!=typeof t.appId)throw n.configError("appId is required and must be a string");return{baseUrl:r,appId:t.appId,bearerToken:t.bearerToken,appSecret:t.appSecret,timeout:(null===(s=t.config)||void 0===s?void 0:s.timeout)||3e4,customHeaders:(null===(i=t.config)||void 0===i?void 0:i.customHeaders)||{},debug:(null===(o=t.config)||void 0===o?void 0:o.debug)||!1}}setBearerToken(t){if(!t||"string"!=typeof t)throw n.configError("Bearer token must be a non-empty string");this.httpClient.setBearerToken(t)}clearBearerToken(){this.httpClient.clearBearerToken()}setAppSecret(t){if(!t||"string"!=typeof t)throw n.configError("App secret must be a non-empty string");this.httpClient.setAppSecret(t)}clearAppSecret(){this.httpClient.clearAppSecret()}getConfig(){return{baseUrl:this.httpClient.config.baseUrl,appId:this.httpClient.config.appId,timeout:this.httpClient.config.timeout,customHeaders:{...this.httpClient.config.customHeaders},debug:this.httpClient.config.debug}}updateConfig(t){this.httpClient.updateConfig(t)}setDebug(t){this.httpClient.updateConfig({debug:t})}static forEnvironment(t,e,n){return new d({environment:t,appId:e,bearerToken:n})}static forProduction(t="default",e){return d.forEnvironment("production",t,e)}static forDevelopment(t="default",e){return d.forEnvironment("development",t,e)}static forStaging(t="default",e){return d.forEnvironment("staging",t,e)}async directRequest(t,e="GET",n,s){return this.httpClient.directRequest(t,e,n,s)}async get(t,e){return this.httpClient.get(t,e)}async post(t,e,n){return this.httpClient.post(t,e,n)}async put(t,e,n){return this.httpClient.put(t,e,n)}async patch(t,e,n){return this.httpClient.patch(t,e,n)}async delete(t,e){return this.httpClient.delete(t,e)}}function p(t){if(t)try{t()}catch(t){console.warn("[AuthUtils] Error clearing app-specific cache:",t)}try{localStorage.clear()}catch(t){}try{sessionStorage.clear()}catch(t){}try{document.cookie.split(";").forEach(t=>{const e=t.indexOf("="),n=e>-1?t.substr(0,e).trim():t.trim();document.cookie=`${n}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`,document.cookie=`${n}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=${window.location.hostname}`,document.cookie=`${n}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=.${window.location.hostname}`})}catch(t){}}function g(){window.history.pushState(null,"",window.location.href);const t=()=>{window.history.pushState(null,"",window.location.href)};window.addEventListener("popstate",t),setTimeout(()=>{window.removeEventListener("popstate",t)},5e3)}t.AppsModule=h,t.EventsModule=l,t.HlmrApiError=n,t.HlmrClient=d,t.SDK_NAME="hlmr-sdk-js",t.SDK_VERSION="1.0.27",t.clearAllCache=p,t.isJSendError=function(t){return"error"===t.status&&"message"in t},t.isJSendFail=function(t){return"fail"===t.status&&"data"in t},t.isJSendResponse=s,t.isJSendSuccess=i,t.performLogout=function(t={}){const{redirectUrl:e,onClearCache:n,idpBaseUrl:s,clientId:i,callbackPath:o}=t;let r;if(p(n),g(),e)r=e;else if(s&&i&&o){const t=`${s}/login?client_id=${i}&redirect_uri=${encodeURIComponent(window.location.origin+o)}`;r=`${s}/logout?redirect_uri=${encodeURIComponent(t)}`}else r="/login";window.location.replace(r)},t.preventBackNavigation=g});
2
2
  //# sourceMappingURL=hlmr-sdk.min.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hlmr/sdk-js",
3
- "version": "1.0.27",
3
+ "version": "1.0.28",
4
4
  "description": "SDK JavaScript officiel pour l'API Mira - Routes publiques et sécurisées",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",