UNPKG

@coho-ai/sdk

Version:

Coho AI SDK for Web Applications

386 lines (375 loc) 12.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/react/index.ts var react_exports = {}; __export(react_exports, { CohoAiProvider: () => CohoAiProvider, useCohoAi: () => useCohoAi }); module.exports = __toCommonJS(react_exports); // src/react/CohoAiProvider.tsx var import_react = __toESM(require("react")); // src/CohoSDK.ts var import_axios = __toESM(require("axios")); // src/utils/Region.ts var RegionEndpoints = { ["US" /* US */]: "https://app.us.coho.ai/api/raw-data/custom", ["EU" /* EU */]: "https://app.coho.ai/api/raw-data/custom" }; // src/CohoSDKOptions.ts var DEFAULT_OPTIONS = { region: "EU" /* EU */, retries: 0, retryDelay: 100, enableLogging: false, proxyEndpoint: void 0 }; // src/utils/DeviceInfoCollector.ts var DeviceInfoCollector = class { isServer() { return typeof window === "undefined" || typeof navigator === "undefined"; } collectInfo() { if (this.isServer()) { return this.getServerInfo(); } const userAgent = navigator.userAgent; const language = navigator.language; const timeZone = this.getTimeZone(); return { os: this.getOS(userAgent), osVersion: this.getOSVersion(userAgent), device: this.getDevice(userAgent), manufacturer: this.getManufacturer(userAgent), deviceType: this.getDeviceType(), country: this.getCountry(language), language: this.getLanguage(language), clientTimestamp: (/* @__PURE__ */ new Date()).toISOString(), timeZone, localClientTime: (/* @__PURE__ */ new Date()).toISOString(), browserInfo: this.getBrowserInfo() }; } getServerInfo() { return { os: "Server", osVersion: (process == null ? void 0 : process.version) || "Unknown", device: "Server", manufacturer: "Unknown", deviceType: "Server", country: "", language: "", clientTimestamp: (/* @__PURE__ */ new Date()).toISOString(), timeZone: "UTC", localClientTime: (/* @__PURE__ */ new Date()).toISOString(), browserInfo: `Node.js/${(process == null ? void 0 : process.version) || "Unknown"}` }; } getTimeZone() { try { return Intl.DateTimeFormat().resolvedOptions().timeZone; } catch (e) { return "UTC"; } } getCountry(language) { try { return language.split("-")[1] || ""; } catch (e) { return ""; } } getLanguage(language) { try { return language.split("-")[0]; } catch (e) { return ""; } } getDeviceType() { if (this.isServer()) { return "Server"; } try { if (window.matchMedia) { const isMobile = window.matchMedia("(max-width: 767px)").matches || window.matchMedia("(pointer: coarse)").matches; const isTablet = window.matchMedia( "(min-width: 768px) and (max-width: 1024px)" ).matches; if (isTablet) return "Tablet"; if (isMobile) return "Mobile"; return "Desktop"; } const userAgent = navigator.userAgent.toLowerCase(); if (/mobile|android|iphone|ipad|ipod/i.test(userAgent)) { return "Mobile"; } return "Desktop"; } catch (e) { return "Unknown"; } } getOS(userAgent) { if (/Windows/i.test(userAgent)) return "Windows"; if (/Mac/i.test(userAgent)) return "MacOS"; if (/Linux/i.test(userAgent)) return "Linux"; if (/Android/i.test(userAgent)) return "Android"; if (/iPhone|iPad|iPod/i.test(userAgent)) return "iOS"; return "Unknown"; } getOSVersion(userAgent) { const matches = userAgent.match( /(?:Windows NT|Mac OS X|Android) ([._\d]+)/ ); return matches ? matches[1] : "Unknown"; } getDevice(userAgent) { var _a; if ("userAgentData" in navigator) { const uaData = navigator.userAgentData; return uaData.platform; } if (/iPhone|iPad|iPod/i.test(userAgent)) return "iOS"; if (/Android/i.test(userAgent)) return ((_a = userAgent.match(/Android.*?;.*?(\w+)\s+Build/)) == null ? void 0 : _a[1]) || "Android Device"; if (/Windows/i.test(userAgent)) return "Windows Device"; if (/Mac/i.test(userAgent)) return "Mac Device"; return "Unknown"; } getManufacturer(userAgent) { if (/iPhone|iPad|iPod/i.test(userAgent)) return "Apple"; if (/Android.*Samsung/i.test(userAgent)) return "Samsung"; return "Unknown"; } getBrowserInfo() { var _a, _b; try { if (this.isServer()) { return `Node.js/${(process == null ? void 0 : process.version) || "Unknown"}`; } if ("userAgentData" in navigator) { const uaData = navigator.userAgentData; return `${((_a = uaData.brands[0]) == null ? void 0 : _a.brand) || "Unknown"} ${((_b = uaData.brands[0]) == null ? void 0 : _b.version) || ""}`; } return navigator.userAgent.split(" ").pop() || "Unknown"; } catch (e) { return "Unknown"; } } }; // src/utils/Utils.ts var Utils = { MEDIA_TYPE_JSON: "application/json; charset=utf-8", HEADER_TENANT_ID: "X-Coho-TenantId", HEADER_USER_ID_KEY: "X-Coho-UserId-Key", HEADER_ACCEPT: "Accept", HEADER_CONTENT_TYPE: "Content-Type", LOG_PREFIX: "[CohoSDK] ", EVENTS_KEY: "events", USER_ID_KEY: "userId", DATASOURCE_CONTEXT: "X-Coho-DatasourceContext", TRANSIENT_ERRORS: [408, 429, 500, 502, 503, 504], LOCAL_STORAGE_USER_ID_KEY: "cohoUserId", LOCAL_STORAGE_GLOBAL_PROPERTIES_KEY: "cohoGlobalProperties" }; // src/CohoSDK.ts var CohoSDK = class { constructor(options) { this.uid = null; this.globalProperties = {}; this.options = { ...DEFAULT_OPTIONS, ...options }; this.deviceInfo = new DeviceInfoCollector(); if (this.isBrowser()) { this.uid = this.getUserIdFromLocalStorage(); if (!this.uid) { this.log("No user ID found in local storage."); } this.globalProperties = this.getGlobalPropertiesFromLocalStorage() || {}; if (!this.globalProperties) { this.log("No global properties were found in local storage."); } } this.endpoint = this.options.proxyEndpoint || RegionEndpoints[this.options.region]; this.client = import_axios.default.create({ headers: { [Utils.HEADER_TENANT_ID]: this.options.tenantId, [Utils.HEADER_USER_ID_KEY]: Utils.USER_ID_KEY, [Utils.HEADER_ACCEPT]: "*/*", [Utils.HEADER_CONTENT_TYPE]: "application/json", [Utils.DATASOURCE_CONTEXT]: "sdk" } }); } setUserId(userId) { this.uid = userId; this.saveUserIdToLocalStorage(userId); this.log(`UserId set to: ${userId}`); } setGlobalProperties(properties) { this.globalProperties = { ...this.globalProperties, ...properties }; this.saveGlobalPropertiesToLocalStorage(this.globalProperties); } async sendEvent(eventName, additionalProperties = {}) { this.log(`sendEvent called with eventName: ${eventName}`); if (!this.uid) { this.log( "The user id is missing or empty, event wasn't fired. Set up userId before firing events." ); return; } const deviceInfo = this.deviceInfo.collectInfo(); const event = { eventName, userId: this.uid, ...deviceInfo, ...this.globalProperties, ...additionalProperties }; const payload = { [Utils.EVENTS_KEY]: [event] }; this.log(`Sending payload: ${JSON.stringify(payload, null, 2)}`); try { await this.fetchWithRetry(payload); this.log("Event send process completed"); } catch (error) { this.onEventFailed( error instanceof Error ? error.message : "Unknown error occurred" ); } } saveGlobalPropertiesToLocalStorage(properties) { if (this.isBrowser()) { try { localStorage.setItem(Utils.LOCAL_STORAGE_GLOBAL_PROPERTIES_KEY, JSON.stringify(properties)); } catch (error) { this.log(`Failed to save global properties to local storage: ${error}`); } } } saveUserIdToLocalStorage(userId) { if (this.isBrowser()) { try { localStorage.setItem(Utils.LOCAL_STORAGE_USER_ID_KEY, userId); } catch (error) { this.log(`Failed to save user ID to local storage: ${error}`); } } } getUserIdFromLocalStorage() { if (this.isBrowser()) { try { return localStorage.getItem(Utils.LOCAL_STORAGE_USER_ID_KEY); } catch (error) { this.log(`Failed to retrieve user ID from local storage: ${error}`); } } return null; } getGlobalPropertiesFromLocalStorage() { if (this.isBrowser()) { try { const storedProperties = localStorage.getItem(Utils.LOCAL_STORAGE_GLOBAL_PROPERTIES_KEY); return storedProperties ? JSON.parse(storedProperties) : null; } catch (error) { this.log(`Failed to retrieve global properties from local storage: ${error}`); } } return null; } isBrowser() { return typeof window !== "undefined" && typeof window.document !== "undefined"; } async fetchWithRetry(payload) { var _a; let attempts = 0; const maxAttempts = this.options.retries + 1; while (attempts < maxAttempts) { try { await this.client.post(this.endpoint, payload); this.log("Request successful"); return; } catch (error) { attempts++; const axiosError = error; this.log(`Attempt ${attempts} failed: ${error}`); if (((_a = axiosError == null ? void 0 : axiosError.response) == null ? void 0 : _a.status) && !Utils.TRANSIENT_ERRORS.includes(axiosError.response.status)) { throw error; } if (attempts >= maxAttempts) { throw new Error(`Failed to send event after ${maxAttempts} attempts`); } await new Promise( (resolve) => setTimeout(resolve, this.options.retryDelay) ); } } } onEventFailed(errorMessage) { this.log(`Event failed: ${errorMessage}`); } log(message) { if (this.options.enableLogging) { console.log(`${Utils.LOG_PREFIX}${message}`); } } }; // src/react/CohoAiProvider.tsx var CohoAiContext = (0, import_react.createContext)(null); var CohoAiProvider = ({ children, userId, ...sdkOptions }) => { const sdk = (0, import_react.useMemo)(() => { const instance = new CohoSDK(sdkOptions); if (userId) { instance.setUserId(userId); } return instance; }, [sdkOptions, userId]); return /* @__PURE__ */ import_react.default.createElement(CohoAiContext.Provider, { value: sdk }, children); }; // src/react/hooks/useCohoAi.ts var import_react2 = require("react"); var useCohoAi = () => { const sdk = (0, import_react2.useContext)(CohoAiContext); if (!sdk) { throw new Error("useCohoAi must be used within a CohoAiProvider"); } return sdk; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CohoAiProvider, useCohoAi });