UNPKG

osmos-web-sdk

Version:

OnlineSales.ai Web SDK for Vue and React projects

785 lines (777 loc) 26.7 kB
'use strict'; class DeviceDetector { constructor(mobileMediaQuery = "(max-width: 767px)", tabletMediaQuery = "(min-width: 768px) and (max-width: 1023px)", detectDeviceViaUA = false) { this.mobileMediaQuery = mobileMediaQuery; this.tabletMediaQuery = tabletMediaQuery; this.detectDeviceViaUA = detectDeviceViaUA; } /** * 检测当前设备类型 */ detectDevice() { if (this.detectDeviceViaUA) { return this.detectDeviceViaUserAgent(); } return this.detectDeviceViaMediaQuery(); } /** * 通过媒体查询检测设备类型 */ detectDeviceViaMediaQuery() { if (typeof window === 'undefined') { return 'DESKTOP'; } if (window.matchMedia(this.mobileMediaQuery).matches) { return 'MOBILE'; } if (window.matchMedia(this.tabletMediaQuery).matches) { return 'TABLET'; } return 'DESKTOP'; } /** * 通过用户代理检测设备类型 */ detectDeviceViaUserAgent() { if (typeof navigator === 'undefined') { return 'DESKTOP'; } const userAgent = navigator.userAgent.toLowerCase(); // 检测移动设备 if (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent)) { // 进一步区分平板和手机 if (/ipad|android(?=.*\b(?:tablet|playbook)\b)/i.test(userAgent)) { return 'TABLET'; } return 'MOBILE'; } return 'DESKTOP'; } /** * 检查是否为移动设备 */ isMobile() { return this.detectDevice() === 'MOBILE'; } /** * 检查是否为平板设备 */ isTablet() { return this.detectDevice() === 'TABLET'; } /** * 检查是否为桌面设备 */ isDesktop() { return this.detectDevice() === 'DESKTOP'; } } class HttpClient { constructor(retryCount = 0, debugMode = false) { this.retryCount = retryCount; this.debugMode = debugMode; } /** * 发送HTTP请求 */ async request(url, options = {}, retryAttempt = 0) { try { if (this.debugMode) { console.log(`[OsmosSDK] Making request to: ${url}`, options); } const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...options.headers, }, }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (this.debugMode) { console.log(`[OsmosSDK] Response from ${url}:`, data); } return data; } catch (error) { if (retryAttempt < this.retryCount) { if (this.debugMode) { console.log(`[OsmosSDK] Retry attempt ${retryAttempt + 1} for ${url}`); } // 指数退避重试 const delay = Math.pow(2, retryAttempt) * 1000; await this.sleep(delay); return this.request(url, options, retryAttempt + 1); } if (this.debugMode) { console.error(`[OsmosSDK] Request failed after ${retryAttempt + 1} attempts:`, error); } throw error; } } /** * GET请求 */ async get(url, params) { const queryString = params ? this.buildQueryString(params) : ''; const fullUrl = queryString ? `${url}?${queryString}` : url; return this.request(fullUrl, { method: 'GET', }); } /** * POST请求 */ async post(url, data) { return this.request(url, { method: 'POST', body: data ? JSON.stringify(data) : undefined, }); } /** * 构建查询字符串 */ buildQueryString(params) { return Object.entries(params) .filter(([_, value]) => value !== undefined && value !== null) .map(([key, value]) => { if (Array.isArray(value)) { return value.map(v => `${encodeURIComponent(key)}=${encodeURIComponent(v)}`).join('&'); } return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; }) .join('&'); } /** * 延迟函数 */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } class AdFetcher { constructor(productAdsHost, displayAdsHost, retryCount = 0, debugMode = false) { this.productAdsHost = productAdsHost; this.displayAdsHost = displayAdsHost; this.httpClient = new HttpClient(retryCount, debugMode); } /** * 获取展示广告 */ async fetchDisplayAds(query) { try { const url = `https://${this.displayAdsHost}/api/display-ads`; // 处理adUnit参数 const params = { ...query, adUnit: Array.isArray(query.adUnit) ? query.adUnit.join(',') : query.adUnit }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * 获取产品页面广告 */ async fetchPLAProductPageAds(query) { try { const url = `https://${this.productAdsHost}/api/pla/product-page`; const params = { ...query, productCount: query.productCount || 2, skuIds: query.skuIds.join(',') }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * 获取搜索页面广告 */ async fetchPLASearchPageAds(query) { try { const url = `https://${this.productAdsHost}/api/pla/search-page`; const params = { ...query, productCount: query.productCount || 2 }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * 获取分类页面广告 */ async fetchPLACategoryPageAd(query) { try { const url = `https://${this.productAdsHost}/api/pla/category-page`; const params = { ...query, productCount: query.productCount || 2, categoryId: query.categoryId, categories: query.categories ? query.categories.join(',') : undefined }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * 获取TPA页面广告 */ async fetchPLATPAPageAd(query) { try { const url = `https://${this.productAdsHost}/api/pla/tpa-page`; const params = { ...query, productCount: query.productCount || 2, skuIds: query.skuIds ? query.skuIds.join(',') : undefined }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * 获取购买页面广告 */ async fetchPLAPurchasePageAd(query) { try { const url = `https://${this.productAdsHost}/api/pla/purchase-page`; const params = { ...query, productCount: query.productCount || 2, skuIds: query.skuIds.join(',') }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * 获取首页广告 */ async fetchPLAHomePageAd(query) { try { const url = `https://${this.productAdsHost}/api/pla/home-page`; const params = { ...query, productCount: query.productCount || 2 }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * 获取自定义页面广告 */ async fetchPLACustomPageAd(query) { try { const url = `https://${this.productAdsHost}/api/pla/custom-page`; const params = { ...query, productCount: query.productCount || 2, mcategoriesIds: query.mcategoriesIds ? query.mcategoriesIds.join(',') : undefined, brands: query.brands ? query.brands.join(',') : undefined, categories: query.categories ? query.categories.join(',') : undefined }; const response = await this.httpClient.get(url, params); return { success: true, data: response }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } } class TrackingService { constructor(eventTrackingHost, clientId, options = {}) { var _a, _b, _c, _d, _e, _f, _g, _h; this.eventTrackingHost = eventTrackingHost; this.clientId = clientId; this.cliUbid = options.cliUbid; this.enableTracking = (_a = options.enableTracking) !== null && _a !== void 0 ? _a : true; this.fireDefaultPixel = (_b = options.fireDefaultPixel) !== null && _b !== void 0 ? _b : true; this.trackingParams = options.trackingParams; this.searchKeywordKey = (_c = options.searchKeywordKey) !== null && _c !== void 0 ? _c : 'keyword'; this.customQueryParams = options.customQueryParams; this.queryToParamMapping = (_d = options.queryToParamMapping) !== null && _d !== void 0 ? _d : {}; this.urlToParamMapping = (_e = options.urlToParamMapping) !== null && _e !== void 0 ? _e : {}; this.videoView = (_f = options.videoView) !== null && _f !== void 0 ? _f : 5; this.defaultTrackSeconds = (_g = options.defaultTrackSeconds) !== null && _g !== void 0 ? _g : [1, 3]; this.debugMode = (_h = options.debugMode) !== null && _h !== void 0 ? _h : false; this.httpClient = new HttpClient(0, this.debugMode); } /** * 发送跟踪事件 */ async trackEvent(event) { if (!this.enableTracking) { if (this.debugMode) { console.log('[OsmosSDK] Tracking disabled, skipping event:', event); } return; } try { const trackingData = { clientId: this.clientId, cliUbid: this.cliUbid, timestamp: event.timestamp || Date.now(), ...event, ...this.getTrackingParams(), ...this.getCustomQueryParams() }; const url = `https://${this.eventTrackingHost}/api/track`; if (this.debugMode) { console.log('[OsmosSDK] Sending tracking event:', trackingData); } await this.httpClient.post(url, trackingData); } catch (error) { if (this.debugMode) { console.error('[OsmosSDK] Failed to send tracking event:', error); } } } /** * 跟踪产品查看事件 */ async trackProductView(skuId, additionalData) { await this.trackEvent({ eventType: 'PRODUCT_VIEW', skuId, ...additionalData }); } /** * 跟踪添加到购物车事件 */ async trackAddToCart(skuId, quantity = 1, additionalData) { await this.trackEvent({ eventType: 'ADD_TO_CART', skuId, quantity, ...additionalData }); } /** * 跟踪购买事件 */ async trackPurchase(skuIds, total, additionalData) { await this.trackEvent({ eventType: 'PURCHASE', skuIds, total, ...additionalData }); } /** * 跟踪广告点击事件 */ async trackAdClick(adId, adUnit, additionalData) { await this.trackEvent({ eventType: 'AD_CLICK', adId, adUnit, ...additionalData }); } /** * 跟踪视频观看事件 */ async trackVideoView(videoId, duration, additionalData) { await this.trackEvent({ eventType: 'VIDEO_VIEW', videoId, duration, viewed: duration >= this.videoView, ...additionalData }); } /** * 跟踪搜索事件 */ async trackSearch(keyword, resultsCount, additionalData) { await this.trackEvent({ eventType: 'SEARCH', keyword, resultsCount, ...additionalData }); } /** * 跟踪页面浏览事件 */ async trackPageView(pageName, additionalData) { await this.trackEvent({ eventType: 'PAGE_VIEW', pageName, url: typeof window !== 'undefined' ? window.location.href : undefined, ...additionalData }); } /** * 获取跟踪参数 */ getTrackingParams() { if (typeof this.trackingParams === 'function') { return this.trackingParams(); } return this.trackingParams || {}; } /** * 获取自定义查询参数 */ getCustomQueryParams() { if (typeof this.customQueryParams === 'function') { return this.customQueryParams(); } return this.customQueryParams || {}; } /** * 从URL获取搜索关键词 */ getSearchKeyword() { if (typeof window === 'undefined') { return null; } const urlParams = new URLSearchParams(window.location.search); return urlParams.get(this.searchKeywordKey); } /** * 从URL映射参数 */ getMappedParams() { if (typeof window === 'undefined') { return {}; } const params = {}; const urlParams = new URLSearchParams(window.location.search); const pathname = window.location.pathname; // 从查询参数映射 Object.entries(this.queryToParamMapping).forEach(([queryKey, paramKey]) => { const value = urlParams.get(queryKey); if (value) { params[paramKey] = value; } }); // 从URL路径映射 Object.entries(this.urlToParamMapping).forEach(([pattern, paramKey]) => { const regex = new RegExp(pattern); const match = pathname.match(regex); if (match && match[1]) { params[paramKey] = match[1]; } }); return params; } /** * 发送默认像素 */ async fireDefaultTrackingPixel() { if (!this.fireDefaultPixel || !this.enableTracking) { return; } try { const pixelUrl = `https://${this.eventTrackingHost}/pixel.gif`; const params = { clientId: this.clientId, cliUbid: this.cliUbid, timestamp: Date.now(), ...this.getTrackingParams() }; const queryString = new URLSearchParams(params).toString(); const fullUrl = `${pixelUrl}?${queryString}`; if (this.debugMode) { console.log('[OsmosSDK] Firing default pixel:', fullUrl); } // 创建图片元素来发送像素 const img = new Image(); img.src = fullUrl; } catch (error) { if (this.debugMode) { console.error('[OsmosSDK] Failed to fire default pixel:', error); } } } } class OsmosSDK { constructor() { this.isInitialized = false; // 初始化时不做任何操作,等待initialize调用 } /** * 初始化SDK */ initialize(options) { if (this.isInitialized) { console.warn('[OsmosSDK] SDK already initialized'); return; } // 验证必需参数 if (!options.clientId) { throw new Error('clientId is required'); } if (!options.productAdsHost) { throw new Error('productAdsHost is required'); } if (!options.displayAdsHost) { throw new Error('displayAdsHost is required'); } this.options = { // 默认值 device: 'DESKTOP', mobileMediaQuery: "(max-width: 767px)", tabletMediaQuery: "(min-width: 768px) and (max-width: 1023px)", detectDeviceViaUA: false, enableTracking: true, fireDefaultPixel: true, searchKeywordKey: "keyword", queryToParamMapping: {}, urlToParamMapping: {}, videoView: 5, defaultTrackSeconds: [1, 3], enableOMSdk: false, omSdkOptions: {}, selectorPrefix: "os", retryCount: 0, debugMode: false, ...options }; // 初始化设备检测器 this.deviceDetector = new DeviceDetector(this.options.mobileMediaQuery, this.options.tabletMediaQuery, this.options.detectDeviceViaUA); // 初始化广告获取器 this._adFetcher = new AdFetcher(this.options.productAdsHost, this.options.displayAdsHost, this.options.retryCount, this.options.debugMode); // 初始化跟踪服务 const eventTrackingHost = this.options.eventTrackingHost || 'tracking.onlinesales.ai'; this.trackingService = new TrackingService(eventTrackingHost, this.options.clientId, { cliUbid: this.options.cliUbid, enableTracking: this.options.enableTracking, fireDefaultPixel: this.options.fireDefaultPixel, trackingParams: this.options.trackingParams, searchKeywordKey: this.options.searchKeywordKey, customQueryParams: this.options.customQueryParams, queryToParamMapping: this.options.queryToParamMapping, urlToParamMapping: this.options.urlToParamMapping, videoView: this.options.videoView, defaultTrackSeconds: this.options.defaultTrackSeconds, debugMode: this.options.debugMode }); this.isInitialized = true; if (this.options.debugMode) { console.log('[OsmosSDK] SDK initialized successfully', this.options); } // 发送默认跟踪像素 this.trackingService.fireDefaultTrackingPixel(); } /** * 更新SDK选项 */ updateOptions(options) { if (!this.isInitialized) { throw new Error('SDK must be initialized before updating options'); } this.options = { ...this.options, ...options }; // 重新初始化相关服务 if (options.mobileMediaQuery || options.tabletMediaQuery || options.detectDeviceViaUA) { this.deviceDetector = new DeviceDetector(this.options.mobileMediaQuery, this.options.tabletMediaQuery, this.options.detectDeviceViaUA); } if (options.productAdsHost || options.displayAdsHost || options.retryCount || options.debugMode) { this._adFetcher = new AdFetcher(this.options.productAdsHost, this.options.displayAdsHost, this.options.retryCount, this.options.debugMode); } if (this.options.debugMode) { console.log('[OsmosSDK] Options updated', this.options); } } /** * 获取当前设备类型 */ getDevice() { if (!this.isInitialized) { throw new Error('SDK must be initialized before getting device type'); } return this.options.device || this.deviceDetector.detectDevice(); } /** * 获取广告获取器 */ get adFetcher() { if (!this.isInitialized) { throw new Error('SDK must be initialized before accessing adFetcher'); } return this._adFetcher; } /** * 获取跟踪服务 */ get tracking() { if (!this.isInitialized) { throw new Error('SDK must be initialized before accessing tracking service'); } return this.trackingService; } /** * 检查SDK是否已初始化 */ isReady() { return this.isInitialized; } /** * 获取当前配置 */ getConfig() { if (!this.isInitialized) { throw new Error('SDK must be initialized before getting config'); } return { ...this.options }; } } // 创建全局实例(用于浏览器环境) let globalOsmos = null; /** * 获取全局Osmos实例 */ function getOsmos() { if (!globalOsmos) { globalOsmos = new OsmosSDK(); } return globalOsmos; } /** * 初始化全局Osmos实例 */ function initializeOsmos(options) { const osmos = getOsmos(); osmos.initialize(options); return osmos; } // 浏览器环境下的全局对象 if (typeof window !== 'undefined') { window.osmos = { initialize: (options) => { const osmos = getOsmos(); osmos.initialize(options); return osmos; }, updateOptions: (options) => { const osmos = getOsmos(); osmos.updateOptions(options); }, getDevice: () => { const osmos = getOsmos(); return osmos.getDevice(); }, isReady: () => { const osmos = getOsmos(); return osmos.isReady(); }, getConfig: () => { const osmos = getOsmos(); return osmos.getConfig(); }, adFetcher: { fetchDisplayAds: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchDisplayAds(query); }, fetchPLAProductPageAds: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchPLAProductPageAds(query); }, fetchPLASearchPageAds: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchPLASearchPageAds(query); }, fetchPLACategoryPageAd: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchPLACategoryPageAd(query); }, fetchPLATPAPageAd: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchPLATPAPageAd(query); }, fetchPLAPurchasePageAd: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchPLAPurchasePageAd(query); }, fetchPLAHomePageAd: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchPLAHomePageAd(query); }, fetchPLACustomPageAd: (query) => { const osmos = getOsmos(); return osmos.adFetcher.fetchPLACustomPageAd(query); } }, tracking: { trackEvent: (event) => { const osmos = getOsmos(); return osmos.tracking.trackEvent(event); }, trackProductView: (skuId, additionalData) => { const osmos = getOsmos(); return osmos.tracking.trackProductView(skuId, additionalData); }, trackAddToCart: (skuId, quantity, additionalData) => { const osmos = getOsmos(); return osmos.tracking.trackAddToCart(skuId, quantity, additionalData); }, trackPurchase: (skuIds, total, additionalData) => { const osmos = getOsmos(); return osmos.tracking.trackPurchase(skuIds, total, additionalData); }, trackAdClick: (adId, adUnit, additionalData) => { const osmos = getOsmos(); return osmos.tracking.trackAdClick(adId, adUnit, additionalData); }, trackVideoView: (videoId, duration, additionalData) => { const osmos = getOsmos(); return osmos.tracking.trackVideoView(videoId, duration, additionalData); }, trackSearch: (keyword, resultsCount, additionalData) => { const osmos = getOsmos(); return osmos.tracking.trackSearch(keyword, resultsCount, additionalData); }, trackPageView: (pageName, additionalData) => { const osmos = getOsmos(); return osmos.tracking.trackPageView(pageName, additionalData); }, getSearchKeyword: () => { const osmos = getOsmos(); return osmos.tracking.getSearchKeyword(); }, getMappedParams: () => { const osmos = getOsmos(); return osmos.tracking.getMappedParams(); } } }; } exports.AdFetcher = AdFetcher; exports.DeviceDetector = DeviceDetector; exports.HttpClient = HttpClient; exports.OsmosSDK = OsmosSDK; exports.TrackingService = TrackingService; exports.getOsmos = getOsmos; exports.initializeOsmos = initializeOsmos; //# sourceMappingURL=index.js.map