@coho-ai/sdk
Version:
Coho AI SDK for Web Applications
386 lines (375 loc) • 12.3 kB
JavaScript
"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
});