@yourgpt/widget-web-sdk
Version:
Official YourGPT SDK for JavaScript/TypeScript and React applications
584 lines (578 loc) • 16.3 kB
JavaScript
/* YourGPT SDK - https://yourgpt.ai */
// src/types/core.ts
var YourGPTError = class extends Error {
constructor(message, code) {
super(message);
this.code = code;
this.name = "YourGPTError";
}
};
// src/utils/index.ts
var isBrowser = () => {
return typeof window !== "undefined" && typeof document !== "undefined";
};
var isDevelopment = () => {
return typeof process !== "undefined" && process.env?.NODE_ENV === "development";
};
var createDebugLogger = (namespace) => {
return {
log: (message, ...args) => {
if (isDevelopment()) {
console.log(`[YourGPT SDK:${namespace}] ${message}`, ...args);
}
},
warn: (message, ...args) => {
if (isDevelopment()) {
console.warn(`[YourGPT SDK:${namespace}] ${message}`, ...args);
}
},
error: (message, ...args) => {
if (isDevelopment()) {
console.error(`[YourGPT SDK:${namespace}] ${message}`, ...args);
}
}
};
};
var waitFor = async (condition, timeout = 5e3, interval = 100) => {
const start = Date.now();
while (!condition()) {
if (Date.now() - start > timeout) {
throw new YourGPTError(`Timeout after ${timeout}ms waiting for condition`);
}
await new Promise((resolve) => setTimeout(resolve, interval));
}
};
var withRetry = async (operation, maxRetries = 3, baseDelay = 1e3) => {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (attempt === maxRetries) {
throw lastError;
}
const delay = baseDelay * Math.pow(2, attempt);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw lastError;
};
var deepMerge = (target, source) => {
const result = { ...target };
for (const key in source) {
if (source.hasOwnProperty(key)) {
if (typeof source[key] === "object" && source[key] !== null && !Array.isArray(source[key]) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) {
result[key] = deepMerge(result[key], source[key]);
} else {
result[key] = source[key];
}
}
}
return result;
};
var generateId = () => {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
};
var validateWidgetId = (widgetId) => {
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
return uuidRegex.test(widgetId);
};
var validateUrl = (url) => {
try {
new URL(url);
return true;
} catch {
return false;
}
};
var sanitizeHtml = (html) => {
if (!isBrowser()) return html;
const div = document.createElement("div");
div.textContent = html;
return div.innerHTML;
};
var debounce = (func, wait) => {
let timeout = null;
return (...args) => {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => func(...args), wait);
};
};
var throttle = (func, wait) => {
let lastCallTime = 0;
return (...args) => {
const now = Date.now();
if (now - lastCallTime >= wait) {
lastCallTime = now;
func(...args);
}
};
};
var isInViewport = (element) => {
if (!isBrowser()) return false;
const rect = element.getBoundingClientRect();
return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
};
var loadScript = (src, async = true) => {
return new Promise((resolve, reject) => {
if (!isBrowser()) {
reject(new YourGPTError("Cannot load script in non-browser environment"));
return;
}
if (document.querySelector(`script[src="${src}"]`)) {
resolve();
return;
}
const script = document.createElement("script");
script.src = src;
script.async = async;
script.onload = () => resolve();
script.onerror = () => reject(new YourGPTError(`Failed to load script: ${src}`));
document.head.appendChild(script);
});
};
var loadCSS = (href) => {
return new Promise((resolve, reject) => {
if (!isBrowser()) {
reject(new YourGPTError("Cannot load CSS in non-browser environment"));
return;
}
if (document.querySelector(`link[href="${href}"]`)) {
resolve();
return;
}
const link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = href;
link.onload = () => resolve();
link.onerror = () => reject(new YourGPTError(`Failed to load CSS: ${href}`));
document.head.appendChild(link);
});
};
var EventEmitter = class {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
return () => {
this.off(event, callback);
};
}
off(event, callback) {
if (!this.events[event]) return;
if (callback) {
this.events[event] = this.events[event].filter((cb) => cb !== callback);
} else {
delete this.events[event];
}
}
emit(event, data) {
if (!this.events[event]) return;
this.events[event].forEach((callback) => {
try {
callback(data);
} catch (error) {
console.error(`Error in event handler for ${String(event)}:`, error);
}
});
}
removeAllListeners() {
this.events = {};
}
};
// src/types/enum.ts
var WidgetRenderModeE = /* @__PURE__ */ ((WidgetRenderModeE2) => {
WidgetRenderModeE2["floating"] = "floating";
WidgetRenderModeE2["embedded"] = "embedded";
WidgetRenderModeE2["iframe"] = "iframe";
return WidgetRenderModeE2;
})(WidgetRenderModeE || {});
// src/core/YourGPT.ts
var _YourGPTSDK = class _YourGPTSDK extends EventEmitter {
constructor() {
super();
this.config = null;
this.isInitialized = false;
this.logger = createDebugLogger("Core");
this.state = {
isOpen: false,
isVisible: true,
isConnected: false,
isLoaded: false,
messageCount: 0,
connectionRetries: 0
};
this.aiActionHandlers = /* @__PURE__ */ new Map();
this.setupGlobalAPI();
}
/**
* Get singleton instance
*/
static getInstance() {
if (!_YourGPTSDK.instance) {
_YourGPTSDK.instance = new _YourGPTSDK();
}
return _YourGPTSDK.instance;
}
/**
* Initialize the SDK
*/
async init(config) {
window.YGC_WIDGET_RENDER_MODE = config.mode || "floating" /* floating */;
if (this.isInitialized) {
this.logger.warn("SDK already initialized");
return this;
}
this.validateConfig(config);
this.config = config;
this.logger.log("Initializing SDK with config:", config);
this.setupGlobalVariables();
if (config.autoLoad !== false) {
await this.loadWidget();
}
this.isInitialized = true;
this.logger.log("SDK initialized successfully");
return this;
}
/**
* Validate configuration
*/
validateConfig(config) {
if (!config.widgetId) {
throw new YourGPTError("Widget ID is required", "MISSING_WIDGET_ID");
}
if (!validateWidgetId(config.widgetId)) {
throw new YourGPTError("Invalid widget ID format", "INVALID_WIDGET_ID");
}
if (config.endpoint && !validateUrl(config.endpoint)) {
throw new YourGPTError("Invalid endpoint URL", "INVALID_ENDPOINT");
}
}
/**
* Set up global variables
*/
setupGlobalVariables() {
if (!isBrowser()) return;
window.YOURGPT_WIDGET_UID = this.config.widgetId;
if (!window.$yourgptChatbot) {
window.$yourgptChatbot = {};
}
window.$yourgptChatbot.WIDGET_ENDPOINT = this.getEndpoint();
}
/**
* Get endpoint URL
*/
getEndpoint() {
if (this.config?.endpoint) {
return this.config.endpoint;
}
return this.config?.whitelabel ? "https://widget.d4ai.chat" : "";
}
/**
* Load widget assets
*/
async loadWidget() {
if (!isBrowser()) {
throw new YourGPTError("Cannot load widget in non-browser environment", "NOT_BROWSER");
}
const endpoint = this.getEndpoint();
try {
this.createRootContainer();
await loadScript(`${endpoint}/chatbot.js`);
await waitFor(() => this.isWidgetReady(), 1e4);
this.updateState({ isLoaded: true });
this.logger.log("Widget loaded successfully");
} catch (error) {
this.logger.error("Failed to load widget:", error);
throw new YourGPTError("Failed to load widget", "WIDGET_LOAD_FAILED");
}
}
/**
* Create root container for widget
*/
createRootContainer() {
if (document.getElementById("yourgpt_root")) {
return;
}
const root = document.createElement("div");
root.id = "yourgpt_root";
root.style.cssText = `
position: fixed;
z-index: 99999;
`;
document.body.appendChild(root);
}
/**
* Check if widget is ready
*/
isWidgetReady() {
return Boolean(window.$yourgptChatbot?.execute && window.$yourgptChatbot?.on && window.$yourgptChatbot?.set);
}
/**
* Set up global API for backwards compatibility
*/
setupGlobalAPI() {
if (!isBrowser()) return;
if (!window.$yourgptChatbot) {
window.$yourgptChatbot = {
q: [],
execute: (action, ...args) => {
window.$yourgptChatbot.q.push(["execute", action, ...args]);
},
on: (event, callback) => {
window.$yourgptChatbot.q.push(["on", event, callback]);
},
off: (event, callback) => {
window.$yourgptChatbot.q.push(["off", event, callback]);
},
set: (key, value) => {
window.$yourgptChatbot.q.push(["set", key, value]);
}
};
}
}
/**
* Update widget state
*/
updateState(updates) {
this.state = { ...this.state, ...updates };
this.emit("stateChange", this.state);
}
/**
* Execute widget command
*/
executeCommand(command, ...args) {
if (!this.isWidgetReady()) {
this.logger.warn("Widget not ready, queueing command:", command);
window.$yourgptChatbot?.q?.push(["execute", command, ...args]);
return;
}
window.$yourgptChatbot.execute(command, ...args);
}
/**
* Register event listener
*/
registerEventListener(event, callback) {
if (!this.isWidgetReady()) {
this.logger.warn("Widget not ready, queueing event listener:", event);
window.$yourgptChatbot?.q?.push(["on", event, callback]);
return;
}
window.$yourgptChatbot.on(event, callback);
}
/**
* Set widget data
*/
setWidgetData(key, value) {
if (!this.isWidgetReady()) {
this.logger.warn("Widget not ready, queueing data:", key);
window.$yourgptChatbot?.q?.push(["set", key, value]);
return;
}
window.$yourgptChatbot.set(key, value);
}
/**
* Get current configuration
*/
getConfig() {
return this.config;
}
/**
* Get current state
*/
getState() {
return { ...this.state };
}
/**
* Check if SDK is initialized
*/
isReady() {
return this.isInitialized;
}
/**
* Widget Controls
*/
open() {
this.executeCommand("widget:open");
this.updateState({ isOpen: true });
}
close() {
this.executeCommand("widget:close");
this.updateState({ isOpen: false });
}
toggle() {
if (this.state.isOpen) {
this.close();
} else {
this.open();
}
}
show() {
this.executeCommand("widget:show");
this.updateState({ isVisible: true });
}
hide() {
this.executeCommand("widget:hide");
this.updateState({ isVisible: false });
}
/**
* Messaging
*/
sendMessage(text, autoSend = true) {
this.executeCommand("message:send", { text, send: autoSend });
}
/**
* Advanced Features
*/
openBottomSheet(url) {
this.executeCommand("bottomSheet:open", { url });
}
startGame(gameId, options = {}) {
this.executeCommand("game:start", { id: gameId, ...options });
}
/**
* Data Management
*/
setSessionData(data) {
this.setWidgetData("session:data", data);
}
setVisitorData(data) {
this.setWidgetData("visitor:data", data);
}
setContactData(data) {
this.setWidgetData("contact:data", data);
}
/**
* Event Listeners
*/
onInit(callback) {
const wrappedCallback = () => {
this.updateState({ isConnected: true });
callback();
};
this.registerEventListener("init", wrappedCallback);
return () => window.$yourgptChatbot?.off?.("init", wrappedCallback);
}
onMessageReceived(callback) {
const wrappedCallback = (data) => {
this.updateState({ messageCount: this.state.messageCount + 1 });
callback(data);
};
this.registerEventListener("message:received", wrappedCallback);
return () => window.$yourgptChatbot?.off?.("message:received", wrappedCallback);
}
onEscalatedToHuman(callback) {
this.registerEventListener("escalatedToHuman", callback);
return () => window.$yourgptChatbot?.off?.("escalatedToHuman", callback);
}
onWidgetPopup(callback) {
const wrappedCallback = (isOpen) => {
this.updateState({ isOpen });
callback(isOpen);
};
this.registerEventListener("widget:popup", wrappedCallback);
return () => window.$yourgptChatbot?.off?.("widget:popup", wrappedCallback);
}
/**
* AI Actions
*/
registerAIAction(actionName, handler) {
this.aiActionHandlers.set(actionName, handler);
this.registerEventListener(`ai:action:${actionName}`, handler);
}
unregisterAIAction(actionName) {
this.aiActionHandlers.delete(actionName);
window.$yourgptChatbot?.off?.(`ai:action:${actionName}`);
}
getRegisteredAIActions() {
return Array.from(this.aiActionHandlers.keys());
}
/**
* Create complete chatbot API
*/
createChatbotAPI() {
return {
// State
...this.getState(),
// Widget Controls
open: this.open.bind(this),
close: this.close.bind(this),
toggle: this.toggle.bind(this),
show: this.show.bind(this),
hide: this.hide.bind(this),
// Messaging
sendMessage: this.sendMessage.bind(this),
// Advanced Features
openBottomSheet: this.openBottomSheet.bind(this),
startGame: this.startGame.bind(this),
// Data Management
setSessionData: this.setSessionData.bind(this),
setVisitorData: this.setVisitorData.bind(this),
setContactData: this.setContactData.bind(this),
// Event Listeners
onInit: this.onInit.bind(this),
onMessageReceived: this.onMessageReceived.bind(this),
onEscalatedToHuman: this.onEscalatedToHuman.bind(this),
onWidgetPopup: this.onWidgetPopup.bind(this)
};
}
/**
* Create AI Actions API
*/
createAIActionsAPI() {
return {
registerAction: this.registerAIAction.bind(this),
unregisterAction: this.unregisterAIAction.bind(this),
registerActions: (actions) => {
Object.entries(actions).forEach(([name, handler]) => {
this.registerAIAction(name, handler);
});
},
getRegisteredActions: this.getRegisteredAIActions.bind(this),
get registeredActions() {
return this.getRegisteredActions();
}
};
}
/**
* Cleanup
*/
destroy() {
this.removeAllListeners();
this.aiActionHandlers.clear();
this.isInitialized = false;
this.config = null;
_YourGPTSDK.instance = null;
}
};
_YourGPTSDK.instance = null;
var YourGPTSDK = _YourGPTSDK;
var YourGPT = {
/**
* Initialize the SDK
*/
init: (config) => {
const sdk = YourGPTSDK.getInstance();
return sdk.init(config);
},
/**
* Get the SDK instance
*/
getInstance: () => {
return YourGPTSDK.getInstance();
}
};
var YourGPT_default = YourGPT;
// src/index.ts
var VERSION = "1.0.0";
export { EventEmitter, VERSION, WidgetRenderModeE, YourGPT_default as YourGPT, YourGPTError, YourGPTSDK, createDebugLogger, debounce, deepMerge, YourGPT_default as default, generateId, isBrowser, isDevelopment, isInViewport, loadCSS, loadScript, sanitizeHtml, throttle, validateUrl, validateWidgetId, waitFor, withRetry };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map