@yourgpt/widget-web-sdk
Version:
Official YourGPT SDK for JavaScript/TypeScript and React applications
848 lines (839 loc) • 23.5 kB
JavaScript
'use strict';
var react = require('react');
var jsxRuntime = require('react/jsx-runtime');
/* YourGPT SDK - React Package - 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 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 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.type = "module";
script.onload = () => resolve();
script.onerror = () => reject(new YourGPTError(`Failed to load script: ${src}`));
document.head.appendChild(script);
});
};
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/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/react/hooks/useYourGPT.ts
function useYourGPT(options = {}) {
const { config, autoInit = true } = options;
const [sdk, setSdk] = react.useState(null);
const [isInitialized, setIsInitialized] = react.useState(false);
const [isLoading, setIsLoading] = react.useState(false);
const [error, setError] = react.useState(null);
const [state, setState] = react.useState({
isOpen: false,
isVisible: true,
isConnected: false,
isLoaded: false,
messageCount: 0,
connectionRetries: 0
});
const sdkRef = react.useRef(null);
const init = react.useCallback(async (initConfig) => {
if (isInitialized) {
return;
}
setIsLoading(true);
setError(null);
try {
const sdkInstance = await YourGPT_default.init(initConfig);
const unsubscribe = sdkInstance.on("stateChange", (newState) => {
setState(newState);
});
sdkRef.current = sdkInstance;
setSdk(sdkInstance);
setIsInitialized(true);
setState(sdkInstance.getState());
sdkInstance._unsubscribeStateChange = unsubscribe;
} catch (err) {
const sdkError = err instanceof YourGPTError ? err : new YourGPTError(String(err));
setError(sdkError);
} finally {
setIsLoading(false);
}
}, [isInitialized]);
const destroy = react.useCallback(() => {
if (sdkRef.current) {
if (sdkRef.current._unsubscribeStateChange) {
sdkRef.current._unsubscribeStateChange();
}
sdkRef.current.destroy();
sdkRef.current = null;
setSdk(null);
setIsInitialized(false);
setState({
isOpen: false,
isVisible: true,
isConnected: false,
isLoaded: false,
messageCount: 0,
connectionRetries: 0
});
}
}, []);
react.useEffect(() => {
if (config && autoInit && !isInitialized && !isLoading) {
init(config);
}
}, [config, autoInit, isInitialized, isLoading, init]);
react.useEffect(() => {
return () => {
if (sdkRef.current) {
if (sdkRef.current._unsubscribeStateChange) {
sdkRef.current._unsubscribeStateChange();
}
}
};
}, []);
return {
sdk,
isInitialized,
isLoading,
error,
state,
init,
destroy
};
}
function useYourGPTChatbot() {
const [state, setState] = react.useState({
isOpen: false,
isVisible: true,
isConnected: false,
isLoaded: false,
messageCount: 0,
connectionRetries: 0
});
const sdkRef = react.useRef(null);
const unsubscribersRef = react.useRef([]);
react.useEffect(() => {
const sdk = YourGPT_default.getInstance();
sdkRef.current = sdk;
setState(sdk.getState());
const unsubscribe = sdk.on("stateChange", (newState) => {
setState(newState);
});
unsubscribersRef.current.push(unsubscribe);
return () => {
unsubscribersRef.current.forEach((unsub) => unsub());
unsubscribersRef.current = [];
};
}, []);
const widgetControls = react.useMemo(() => ({
open: () => sdkRef.current?.open(),
close: () => sdkRef.current?.close(),
toggle: () => sdkRef.current?.toggle(),
show: () => sdkRef.current?.show(),
hide: () => sdkRef.current?.hide()
}), []);
const messaging = react.useMemo(() => ({
sendMessage: (text, autoSend = true) => {
sdkRef.current?.sendMessage(text, autoSend);
}
}), []);
const advanced = react.useMemo(() => ({
openBottomSheet: (url) => {
sdkRef.current?.openBottomSheet(url);
},
startGame: (gameId, options = {}) => {
sdkRef.current?.startGame(gameId, options);
}
}), []);
const dataManagement = react.useMemo(() => ({
setSessionData: (data) => {
sdkRef.current?.setSessionData(data);
},
setVisitorData: (data) => {
sdkRef.current?.setVisitorData(data);
},
setContactData: (data) => {
sdkRef.current?.setContactData(data);
}
}), []);
const eventListeners = react.useMemo(() => ({
onInit: (callback) => {
const unsubscribe = sdkRef.current?.onInit(callback);
if (unsubscribe) {
unsubscribersRef.current.push(unsubscribe);
}
return unsubscribe || (() => {
});
},
onMessageReceived: (callback) => {
const unsubscribe = sdkRef.current?.onMessageReceived(callback);
if (unsubscribe) {
unsubscribersRef.current.push(unsubscribe);
}
return unsubscribe || (() => {
});
},
onEscalatedToHuman: (callback) => {
const unsubscribe = sdkRef.current?.onEscalatedToHuman(callback);
if (unsubscribe) {
unsubscribersRef.current.push(unsubscribe);
}
return unsubscribe || (() => {
});
},
onWidgetPopup: (callback) => {
const unsubscribe = sdkRef.current?.onWidgetPopup(callback);
if (unsubscribe) {
unsubscribersRef.current.push(unsubscribe);
}
return unsubscribe || (() => {
});
}
}), []);
return {
// State
...state,
// Functions
...widgetControls,
...messaging,
...advanced,
...dataManagement,
...eventListeners
};
}
function useAIActions() {
const [registeredActions, setRegisteredActions] = react.useState([]);
const handlersRef = react.useRef(/* @__PURE__ */ new Map());
const sdkRef = react.useRef(null);
react.useEffect(() => {
const sdk = YourGPT_default.getInstance();
sdkRef.current = sdk;
setRegisteredActions(sdk.getRegisteredAIActions());
}, []);
const registerAction = react.useCallback((actionName, handler) => {
if (!sdkRef.current) return;
handlersRef.current.set(actionName, handler);
sdkRef.current.registerAIAction(actionName, handler);
setRegisteredActions(Array.from(handlersRef.current.keys()));
}, []);
const unregisterAction = react.useCallback((actionName) => {
if (!sdkRef.current) return;
handlersRef.current.delete(actionName);
sdkRef.current.unregisterAIAction(actionName);
setRegisteredActions(Array.from(handlersRef.current.keys()));
}, []);
const getRegisteredActions = react.useCallback(() => {
return Array.from(handlersRef.current.keys());
}, []);
const registerActions = react.useCallback((actions) => {
Object.entries(actions).forEach(([name, handler]) => {
registerAction(name, handler);
});
}, [registerAction]);
react.useEffect(() => {
return () => {
handlersRef.current.forEach((_, actionName) => {
sdkRef.current?.unregisterAIAction(actionName);
});
handlersRef.current.clear();
};
}, []);
return {
registerAction,
unregisterAction,
registerActions,
getRegisteredActions,
registeredActions
};
}
var YourGPTContext = react.createContext(null);
function YourGPTProvider({ children, config, onError, onInitialized }) {
const [isBrowser2, setIsBrowser] = react.useState(false);
const [sdk, setSdk] = react.useState(null);
const [isInitialized, setIsInitialized] = react.useState(false);
const [isLoading, setIsLoading] = react.useState(false);
const [error, setError] = react.useState(null);
const [state, setState] = react.useState({
isOpen: false,
isVisible: true,
isConnected: false,
isLoaded: false,
messageCount: 0,
connectionRetries: 0
});
react.useEffect(() => {
setIsBrowser(true);
}, []);
react.useEffect(() => {
if (!isBrowser2) return;
const initializeSDK = async () => {
setIsLoading(true);
setError(null);
try {
const sdkInstance = await YourGPT_default.init(config);
const unsubscribe = sdkInstance.on("stateChange", (newState) => {
setState(newState);
});
setSdk(sdkInstance);
setIsInitialized(true);
setState(sdkInstance.getState());
sdkInstance._unsubscribeStateChange = unsubscribe;
if (onInitialized) {
onInitialized(sdkInstance);
}
} catch (err) {
const sdkError = err instanceof YourGPTError ? err : new YourGPTError(String(err));
setError(sdkError);
if (onError) {
onError(sdkError);
}
} finally {
setIsLoading(false);
}
};
initializeSDK();
return () => {
if (sdk) {
if (sdk._unsubscribeStateChange) {
sdk._unsubscribeStateChange();
}
}
};
}, [config, onError, onInitialized, isBrowser2]);
const value = {
sdk,
isInitialized,
isLoading,
error,
state
};
return /* @__PURE__ */ jsxRuntime.jsx(YourGPTContext.Provider, { value, children });
}
function useYourGPT2() {
const context = react.useContext(YourGPTContext);
if (!context) {
throw new YourGPTError("useYourGPT must be used within a YourGPTProvider");
}
return context;
}
function YourGPTWidget({ className, style, onMount, onUnmount }) {
const containerRef = react.useRef(null);
const { isInitialized } = useYourGPT2();
const [isBrowser2, setIsBrowser] = react.useState(false);
const mode = typeof window !== "undefined" ? window.YGC_WIDGET_RENDER_MODE : "floating" /* floating */;
react.useEffect(() => {
setIsBrowser(true);
}, []);
react.useEffect(() => {
if (!isInitialized || !isBrowser2) return void 0;
const container = containerRef.current;
if (!container || mode !== "embedded") return void 0;
window.YGC_WIDGET?.renderEmbedded(container);
onMount?.();
return () => {
window.YGC_WIDGET?.cleanup?.();
onUnmount?.();
};
}, [isInitialized, isBrowser2, mode]);
if (!isBrowser2) {
return null;
}
if (mode === "embedded") {
return /* @__PURE__ */ jsxRuntime.jsx(
"div",
{
ref: containerRef,
id: `yourgpt-container-${Math.random()}`,
className,
style: {
position: "relative",
width: "100%",
height: "100%",
...style
}
}
);
}
return null;
}
// src/react/index.ts
var VERSION = "1.0.0";
exports.VERSION = VERSION;
exports.YourGPT = YourGPT_default;
exports.YourGPTError = YourGPTError;
exports.YourGPTProvider = YourGPTProvider;
exports.YourGPTSDK = YourGPTSDK;
exports.YourGPTWidget = YourGPTWidget;
exports.useAIActions = useAIActions;
exports.useYourGPT = useYourGPT;
exports.useYourGPTChatbot = useYourGPTChatbot;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map