@coho-ai/sdk
Version:
Coho AI SDK for Web Applications
292 lines (285 loc) • 8.62 kB
JavaScript
// src/utils/Region.ts
var Region = /* @__PURE__ */ ((Region2) => {
Region2["US"] = "US";
Region2["EU"] = "EU";
return Region2;
})(Region || {});
var RegionEndpoints = {
["US" /* US */]: "https://app.us.coho.ai/api/raw-data/custom",
["EU" /* EU */]: "https://app.coho.ai/api/raw-data/custom"
};
// src/CohoSDK.ts
import axios from "axios";
// src/CohoSDKOptions.ts
var DEFAULT_OPTIONS = {
region: "EU" /* EU */,
retries: 0,
retryDelay: 100,
enableLogging: false
};
// 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"
};
// src/CohoSDK.ts
var CohoSDK = class {
constructor(options) {
this.uid = null;
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.client = axios.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}`);
}
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,
...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"
);
}
}
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;
}
isBrowser() {
return typeof window !== "undefined" && typeof window.document !== "undefined";
}
async fetchWithRetry(payload) {
var _a;
let attempts = 0;
const endpoint = RegionEndpoints[this.options.region];
const maxAttempts = this.options.retries + 1;
while (attempts < maxAttempts) {
try {
await this.client.post(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}`);
}
}
};
export {
Region,
CohoSDK
};