UNPKG

@teamsparta/cross-platform-logger

Version:

```typescript import * as CPL from "@teamsparta/cross-platform-logger";

1,275 lines (1,266 loc) 42.1 kB
import * as amplitude from '@amplitude/analytics-browser'; import { sessionReplayPlugin } from '@amplitude/plugin-session-replay-browser'; import * as braze from '@braze/web-sdk'; import { KinesisClient, PutRecordsCommand } from '@aws-sdk/client-kinesis'; import { fromCognitoIdentityPool } from '@aws-sdk/credential-providers'; import { Buffer } from 'buffer'; import * as Hackle from '@hackler/javascript-sdk'; // src/amplitude.ts // src/config.ts var config = { isDev: true }; function setConfig(newConfig) { Object.assign(config, newConfig); } // src/utils.ts var isClient = () => { return typeof window !== "undefined" && typeof document !== "undefined"; }; var isBrowser = typeof window !== "undefined"; var isNode = !isBrowser; var debugLog = (message, data, options) => { const isProduction = !config.isDev; if (options?.hideInProdBrowser && isProduction && isBrowser) { return; } const color = options?.color || "gray"; if (data) { if (typeof data === "object") { console.debug(`%c[CPL] ${message}`, `color: ${color}`, data); } else { console.debug(`%c[CPL] ${message}: ${data}`, `color: ${color}`); } } else { console.debug(`%c[CPL] ${message}`, `color: ${color}`); } }; var getCookie = (name) => { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) { let cookie = parts?.pop()?.split(";")?.shift(); return cookie ? decodeURIComponent(cookie) : ""; } }; var setCookie = (name, value, days = null) => { const SPARTA_DOMAIN = ".spartaclub.kr"; const domain = document.location.origin.includes(SPARTA_DOMAIN) ? SPARTA_DOMAIN : document.location.hostname; const sameSite = document.location.origin.includes(SPARTA_DOMAIN) ? "; SameSite=None; Secure" : ""; let date = /* @__PURE__ */ new Date(); date.setTime( days ? date.getTime() + days * 24 * 60 * 60 * 1e3 : date.getTime() ); const expires = `; domain=${domain}; expires=${date.toUTCString()}${sameSite}`; document.cookie = name + "=" + (value || "") + expires + "; path=/"; }; var getUserIdfromUserInfoCookie = () => { const userInfoStr = getCookie("userinfo") ?? ""; const entries = userInfoStr.split(/\s*&\s*/).map((pair) => pair.split("=")).map(([k, v = ""]) => [decodeURIComponent(k), decodeURIComponent(v)]); const userInfoObj = Object.fromEntries(entries); return userInfoObj["_id"] ?? ""; }; var getUrlParams = (key) => { if (typeof window === "undefined") return null; let urlParams = new URLSearchParams(window.location.search); let urlData = urlParams.get(key); return urlData ? encodeURIComponent(urlData) : null; }; var getUTMFromCookie = () => { if (!isBrowser) return null; try { const utmLastCookie = getCookie("utm_last"); if (!utmLastCookie) return null; return JSON.parse(utmLastCookie); } catch (error) { console.warn("UTM \uCFE0\uD0A4 \uD30C\uC2F1 \uC2E4\uD328:", error); return null; } }; var matchPlatform = (ua) => { return /(ipad)/.exec(ua) || /(ipod)/.exec(ua) || /(windows phone)/.exec(ua) || /(iphone)/.exec(ua) || /(kindle)/.exec(ua) || /(silk)/.exec(ua) || /(android)/.exec(ua) || /(win)/.exec(ua) || /(mac)/.exec(ua) || /(linux)/.exec(ua) || /(cros)/.exec(ua) || /(playbook)/.exec(ua) || /(bb)/.exec(ua) || /(blackberry)/.exec(ua) || []; }; var _AmplitudeLog = class _AmplitudeLog { // 이미 호출된 user_id 기록 constructor(amplitude_key, amplitude_browser_options) { this._client = null; this.isInitialized = false; this.sessionReplayConfig = null; this.isRecording = false; this.currentPath = null; // user_id cookie 모니터링 관련 this.cookieMonitorInterval = null; this.cookieCheckInterval = 300; // 0.3초마다 체크 this.lastUserId = null; this.setUserIdHistory = /* @__PURE__ */ new Set(); /** * 이벤트 날짜를 설정합니다. * @param date 설정할 날짜 */ this.setEventDate = (date) => { this.ensureInitialized(); if (!this._client) return; const identifyEvent = new this._client.Identify(); identifyEvent.set("event_date", date.toISOString().split("T")[0]); this._client.identify(identifyEvent); }; /** * 이벤트를 전송합니다. * @param key 이벤트 키 * @param data 이벤트 데이터 */ this.send = (key, data = {}) => { this.ensureInitialized(); if (!this._client) return; this.setEventDate(/* @__PURE__ */ new Date()); this._client.track(key, data); }; /** * Beacon 전송을 사용합니다. */ this.useSendBeacon = () => { this.ensureInitialized(); if (!this._client) return; this._client.setTransport("beacon"); this._client.flush(); }; /** * 사용자 속성을 설정합니다. * @param properties 설정할 사용자 속성 객체 */ this.setUserProperties = (properties) => { this.ensureInitialized(); if (!this._client) return; const identifyEvent = new this._client.Identify(); Object.entries(properties).forEach(([key, value]) => { identifyEvent.set(key, value); }); this._client.identify(identifyEvent); }; /** * 특정 사용자 속성을 설정합니다. * @param key 속성 키 * @param value 속성 값 */ this.setUserProperty = (key, value) => { this.ensureInitialized(); if (!this._client) return; const identifyEvent = new this._client.Identify(); identifyEvent.set(key, value); this._client.identify(identifyEvent); }; /** * 사용자 속성을 배열에 추가합니다. * @param key 배열 속성 키 * @param value 추가할 값 */ this.appendUserProperty = (key, value) => { this.ensureInitialized(); if (!this._client) return; const identifyEvent = new this._client.Identify(); identifyEvent.append(key, value); this._client.identify(identifyEvent); }; /** * 사용자 속성을 배열에서 제거합니다. * @param key 배열 속성 키 * @param value 제거할 값 */ this.removeUserProperty = (key, value) => { this.ensureInitialized(); if (!this._client) return; const identifyEvent = new this._client.Identify(); identifyEvent.remove(key, value); this._client.identify(identifyEvent); }; /** * 사용자 속성을 제거합니다. * @param key 제거할 속성 키 */ this.unsetUserProperty = (key) => { this.ensureInitialized(); if (!this._client) return; const identifyEvent = new this._client.Identify(); identifyEvent.unset(key); this._client.identify(identifyEvent); }; /** * Amplitude 클라이언트 인스턴스를 반환합니다. * @returns Amplitude 클라이언트 */ this.getInstance = () => { return this._client; }; /** * 디바이스 ID를 설정합니다. * @param deviceId 설정할 디바이스 ID */ this.setDeviceId = (deviceId) => { this.ensureInitialized(); if (!this._client) return; this._client.setDeviceId(deviceId); }; /** * 사용자 ID를 설정합니다. * @param userId 사용자 ID */ this.setUserId = (userId) => { this.ensureInitialized(); if (!this._client) return; if (this.setUserIdHistory.has(userId)) { return; } try { this._client.setUserId(userId); this.setUserIdHistory.add(userId); } catch (error) { console.error(`Amplitude setUserId \uC2E4\uD328: ${userId}`, error); } }; /** * 사용자 ID를 초기화합니다. (로그아웃 시 사용) * deviceId는 유지하고 userId만 초기화합니다. */ this.resetUserId = () => { this.ensureInitialized(); if (!this._client) return; try { this._client.setUserId(void 0); this.setUserIdHistory.clear(); this.lastUserId = null; } catch (error) { console.error("Amplitude resetUserId \uC2E4\uD328:", error); } }; /** * setUserId 히스토리를 초기화합니다. * 로그아웃 후 새로운 사용자로 로그인할 때 사용합니다. */ this.resetSetUserIdHistory = () => { this.setUserIdHistory.clear(); this.lastUserId = null; }; /** * Session Replay를 설정합니다. * @param config Session Replay 설정 */ this.setSessionReplayConfig = (config2) => { this.sessionReplayConfig = config2; }; /** * Session Replay를 시작합니다. */ this.startSessionReplay = () => { this.ensureInitialized(); if (!this._client) { console.warn("Session Replay\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uAC70\uB098 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."); return; } if (this.isRecording) { console.warn("Session Replay\uAC00 \uC774\uBBF8 \uC2DC\uC791\uB418\uC5C8\uC2B5\uB2C8\uB2E4."); return; } try { const sessionReplayOptions = { sampleRate: this.sessionReplayConfig?.sampleRate || 1 }; this._client.add(sessionReplayPlugin(sessionReplayOptions)); this.isRecording = true; console.log("Session Replay\uAC00 \uC2DC\uC791\uB418\uC5C8\uC2B5\uB2C8\uB2E4."); } catch (error) { console.error("Session Replay \uC2DC\uC791 \uC2E4\uD328:", error); } }; /** * Session Replay를 중지합니다. */ this.stopSessionReplay = () => { if (!this.isRecording) { console.warn("Session Replay\uAC00 \uC2E4\uD589 \uC911\uC774 \uC544\uB2D9\uB2C8\uB2E4."); return; } this.isRecording = false; console.log("Session Replay\uAC00 \uC911\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4."); }; /** * 현재 endpoint가 recording 대상인지 확인합니다. * @param currentPath 현재 경로 * @returns recording 여부 */ this.shouldRecordSession = (currentPath) => { if (!this.sessionReplayConfig?.recordingEndpoints) { return true; } return this.sessionReplayConfig.recordingEndpoints.some((endpoint) => { if (endpoint.includes("*")) { const pattern = endpoint.replace(/\*/g, ".*"); const regex = new RegExp(`^${pattern}$`); return regex.test(currentPath); } return currentPath === endpoint || currentPath.startsWith(endpoint); }); }; /** * 특정 endpoint에서 Session Replay를 시작합니다. * @param currentPath 현재 경로 */ this.startSessionReplayForEndpoint = (currentPath) => { if (this.currentPath === currentPath) { return; } this.currentPath = currentPath; if (this.shouldRecordSession(currentPath)) { if (!this.isRecording) { this.startSessionReplay(); } } else { if (this.isRecording) { this.stopSessionReplay(); } } }; this.amplitude_key = amplitude_key; this.amplitude_browser_options = amplitude_browser_options; } /** * Amplitude 인스턴스를 생성하고 반환합니다. (Singleton 패턴) * 기본 옵션이 제공되지 않은 경우 자동으로 설정됩니다. * @param amplitude_key Amplitude API 키 * @param amplitude_browser_options 브라우저 옵션 (선택사항) * @returns AmplitudeLog 인스턴스 */ static getInstance(amplitude_key, amplitude_browser_options) { if (!amplitude_key) { throw new Error("Amplitude \uC778\uC2A4\uD134\uC2A4\uB97C \uC0DD\uC131\uD558\uB824\uBA74 amplitude_key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."); } if (!_AmplitudeLog.instance) { const options = amplitude_browser_options || { autocapture: { attribution: { resetSessionOnNewCampaign: true, excludeReferrers: [ // https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2#exclude-referrers /spartaclub\.kr$/, /spartacodingclub\.kr$/, "kauth.kakao.com", "logins.daum.net", "accounts.kakao.com" ] }, pageViews: { trackOn: "attribution" } } // 디버깅 시 로그 레벨을 Debug로 설정하면 브라우저 콘솔에서 자세한 로그를 볼 수 있습니다. // logLevel: Types.LogLevel.Debug, }; _AmplitudeLog.instance = new _AmplitudeLog(amplitude_key, options); } return _AmplitudeLog.instance; } /** * 기존 인스턴스를 초기화합니다. */ static resetInstance() { _AmplitudeLog.instance = null; } /** * Amplitude SDK를 초기화합니다. */ initialize() { if (this.isInitialized) { console.warn("Amplitude\uAC00 \uC774\uBBF8 \uCD08\uAE30\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4."); return; } if (isClient()) { try { amplitude.init(this.amplitude_key, this.amplitude_browser_options); this._client = amplitude; this.isInitialized = true; this.startCookieMonitoring(); const initialUserId = this.getUserIdFromCookie(); if (initialUserId) { this.lastUserId = initialUserId; this.setUserId(initialUserId); } } catch (err) { console.error("Amplitude init failed:", err); } } } /** * 쿠키에서 user_id를 가져옵니다. * @returns user_id 값 또는 null */ getUserIdFromCookie() { if (!isClient()) return null; const cookies = document.cookie.split(";"); for (const cookie of cookies) { const [name, value] = cookie.trim().split("="); if (name === "user_id" && value) { return value; } } return null; } /** * 쿠키 모니터링을 시작합니다. * user_id 쿠키 변경을 감지하여 자동으로 setUserId를 호출합니다. */ startCookieMonitoring() { if (!isClient() || this.cookieMonitorInterval) return; this.cookieMonitorInterval = setInterval(() => { const currentUserId = this.getUserIdFromCookie(); if (!currentUserId && this.lastUserId) { this.resetUserId(); return; } if (currentUserId && currentUserId !== this.lastUserId) { this.lastUserId = currentUserId; this.setUserId(currentUserId); } }, this.cookieCheckInterval); } /** * 쿠키 모니터링을 중지합니다. */ stopCookieMonitoring() { if (this.cookieMonitorInterval) { clearInterval(this.cookieMonitorInterval); this.cookieMonitorInterval = null; this.lastUserId = null; this.setUserIdHistory.clear(); } } /** * 초기화 상태를 확인합니다. */ ensureInitialized() { if (!this.isInitialized) { throw new Error("Amplitude\uAC00 \uCD08\uAE30\uD654\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. initialize() \uBA54\uC11C\uB4DC\uB97C \uBA3C\uC800 \uD638\uCD9C\uD558\uC138\uC694."); } } }; _AmplitudeLog.instance = null; var AmplitudeLog = _AmplitudeLog; var _Braze = class _Braze { // 이미 호출된 user_id 기록 constructor(apiKey, baseUrl, enableLogging = false, allowUserSuppliedJavascript = false) { this.isInitialized = false; this.cookieMonitorInterval = null; this.cookieCheckInterval = 300; // 0.3초마다 체크 this.lastUserId = null; this.changeUserHistory = /* @__PURE__ */ new Set(); this.apiKey = apiKey; this.baseUrl = baseUrl; this.enableLogging = enableLogging; this.allowUserSuppliedJavascript = allowUserSuppliedJavascript; } /** * Braze 인스턴스를 가져옵니다. (Singleton 패턴) * @param apiKey Braze API 키 * @param baseUrl Braze SDK 엔드포인트 * @param enableLogging 로깅 활성화 여부 * @param allowUserSuppliedJavascript 사용자 제공 JavaScript 허용 여부 * @returns Braze 인스턴스 */ static getInstance(apiKey, baseUrl, enableLogging = false, allowUserSuppliedJavascript = true) { if (!_Braze.instance) { if (!apiKey || !baseUrl) { throw new Error("Braze \uC778\uC2A4\uD134\uC2A4\uB97C \uC0DD\uC131\uD558\uB824\uBA74 apiKey\uC640 baseUrl\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."); } _Braze.instance = new _Braze(apiKey, baseUrl, enableLogging, allowUserSuppliedJavascript); } return _Braze.instance; } /** * 쿠키에서 user_id를 가져옵니다. * @returns user_id 값 또는 null */ getUserIdFromCookie() { if (!isClient()) return null; const cookies = document.cookie.split(";"); for (const cookie of cookies) { const [name, value] = cookie.trim().split("="); if (name === "user_id" && value) { return value; } } return null; } /** * 쿠키 모니터링을 시작합니다. */ startCookieMonitoring() { if (!isClient() || this.cookieMonitorInterval) return; this.cookieMonitorInterval = setInterval(() => { const currentUserId = this.getUserIdFromCookie(); if (!currentUserId && this.lastUserId) { if (this.enableLogging) { console.log("Braze \uB85C\uADF8\uC544\uC6C3 \uAC10\uC9C0: \uC775\uBA85 \uC0AC\uC6A9\uC790\uB85C \uBCC0\uACBD"); } try { braze.changeUser(""); if (this.enableLogging) { console.log("Braze \uC775\uBA85 \uC0AC\uC6A9\uC790\uB85C \uBCC0\uACBD \uC644\uB8CC"); } } catch (error) { console.error("Braze \uC775\uBA85 \uC0AC\uC6A9\uC790 \uBCC0\uACBD \uC2E4\uD328:", error); } this.resetChangeUserHistory(); return; } if (currentUserId && currentUserId !== this.lastUserId) { this.lastUserId = currentUserId; if (this.enableLogging) { console.log(`Braze \uC0AC\uC6A9\uC790 \uBCC0\uACBD \uAC10\uC9C0: ${currentUserId}`); } this.changeUser(currentUserId); } }, this.cookieCheckInterval); if (this.enableLogging) { console.log("Braze \uCFE0\uD0A4 \uBAA8\uB2C8\uD130\uB9C1 \uC2DC\uC791"); } } /** * 쿠키 모니터링을 중지합니다. */ stopCookieMonitoring() { if (this.cookieMonitorInterval) { clearInterval(this.cookieMonitorInterval); this.cookieMonitorInterval = null; this.lastUserId = null; this.changeUserHistory.clear(); if (this.enableLogging) { console.log("Braze \uCFE0\uD0A4 \uBAA8\uB2C8\uD130\uB9C1 \uC911\uC9C0"); } } } /** * 쿠키 모니터링 간격을 설정합니다. * @param interval 체크 간격 (밀리초) */ setCookieCheckInterval(interval) { this.cookieCheckInterval = interval; if (this.cookieMonitorInterval) { this.stopCookieMonitoring(); this.startCookieMonitoring(); } } /** * changeUser 히스토리를 초기화합니다. * 로그아웃 후 새로운 사용자로 로그인할 때 사용합니다. */ resetChangeUserHistory() { this.changeUserHistory.clear(); this.lastUserId = null; if (this.enableLogging) { console.log("Braze changeUser \uD788\uC2A4\uD1A0\uB9AC \uCD08\uAE30\uD654 \uC644\uB8CC"); } } /** * Braze SDK를 초기화합니다. */ initialize() { if (this.isInitialized) { console.warn("Braze\uAC00 \uC774\uBBF8 \uCD08\uAE30\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4."); return; } if (isClient()) { try { braze.initialize(this.apiKey, { baseUrl: this.baseUrl, enableLogging: this.enableLogging, allowUserSuppliedJavascript: this.allowUserSuppliedJavascript, minimumIntervalBetweenTriggerActionsInSeconds: 5 }); braze.automaticallyShowInAppMessages(); this.isInitialized = true; this.startCookieMonitoring(); const initialUserId = this.getUserIdFromCookie(); if (initialUserId) { this.lastUserId = initialUserId; this.changeUser(initialUserId); } braze.openSession(); } catch (err) { console.error("Braze init failed:", err); } } } /** * 사용자 지정 이벤트를 로깅합니다. * @param eventName 이벤트 이름 * @param brazeProperties 이벤트 속성 */ logCustomEvent(eventName, brazeProperties) { this.ensureInitialized(); try { braze.logCustomEvent(eventName, brazeProperties); if (this.enableLogging) { console.log(`Braze \uC774\uBCA4\uD2B8 \uB85C\uAE45: ${eventName}`, brazeProperties || ""); } } catch (error) { console.error(`Braze \uC774\uBCA4\uD2B8 \uB85C\uAE45 \uC2E4\uD328: ${eventName}`, error); } } /** * 현재 사용자의 외부 ID를 설정합니다. * @param userIdentifier 사용자 식별자 */ changeUser(userIdentifier) { this.ensureInitialized(); if (this.changeUserHistory.has(userIdentifier)) { if (this.enableLogging) { console.log(`Braze changeUser \uC911\uBCF5 \uD638\uCD9C \uBC29\uC9C0: ${userIdentifier}`); } return; } try { braze.changeUser(userIdentifier); this.changeUserHistory.add(userIdentifier); if (this.enableLogging) { console.log(`Braze changeUser \uC131\uACF5: ${userIdentifier}`); } } catch (error) { console.error(`Braze changeUser \uC2E4\uD328: ${userIdentifier}`, error); } } /** * 사용자 커스텀 속성을 설정합니다. * @param attributeKey 속성 키 * @param attributeValue 속성 값 */ setCustomUserAttribute(attributeKey, attributeValue) { this.ensureInitialized(); try { const user = braze.getUser(); if (user) { user.setCustomUserAttribute(attributeKey, attributeValue); if (this.enableLogging) { console.log(`Braze \uC0AC\uC6A9\uC790 \uC18D\uC131 \uC124\uC815: ${attributeKey} = ${attributeValue}`); } } else { console.warn("Braze \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."); } } catch (error) { console.error(`Braze \uC0AC\uC6A9\uC790 \uC18D\uC131 \uC124\uC815 \uC2E4\uD328: ${attributeKey}`, error); } } /** * Braze 푸시 알림 권한을 요청합니다. * @returns Promise<boolean> 권한 허용 여부 */ async requestNotificationPermission() { this.ensureInitialized(); try { if (this.enableLogging) { console.log("Braze \uD478\uC2DC \uC54C\uB9BC \uAD8C\uD55C\uC744 \uC694\uCCAD\uD569\uB2C8\uB2E4..."); } await braze.requestPushPermission(); if (this.enableLogging) { console.log("Braze \uD478\uC2DC \uC54C\uB9BC \uAD8C\uD55C \uC694\uCCAD \uC644\uB8CC"); } await braze.isPushSupported(); return true; } catch (error) { console.error("Braze \uD478\uC2DC \uC54C\uB9BC \uAD8C\uD55C \uC694\uCCAD \uC2E4\uD328:", error); return false; } } /** * 초기화 상태를 확인합니다. */ ensureInitialized() { if (!this.isInitialized) { throw new Error("Braze\uAC00 \uCD08\uAE30\uD654\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. initialize() \uBA54\uC11C\uB4DC\uB97C \uBA3C\uC800 \uD638\uCD9C\uD558\uC138\uC694."); } } /** * Braze SDK를 비활성화합니다. * 탈퇴 시 사용자 데이터 수집을 중단합니다. */ disableSDK() { this.ensureInitialized(); try { this.stopCookieMonitoring(); braze.disableSDK(); braze.wipeData(); this.isInitialized = false; if (this.enableLogging) { console.log("Braze SDK \uBE44\uD65C\uC131\uD654 \uC644\uB8CC"); } } catch (error) { console.error("Braze SDK \uBE44\uD65C\uC131\uD654 \uC2E4\uD328:", error); } } /** * Braze SDK를 다시 활성화합니다. */ enableSDK() { try { braze.enableSDK(); this.initialize(); if (this.enableLogging) { console.log("Braze SDK \uD65C\uC131\uD654 \uC644\uB8CC"); } } catch (error) { console.error("Braze SDK \uD65C\uC131\uD654 \uC2E4\uD328:", error); } } /** * Braze 인스턴스를 정리합니다. */ destroy() { this.stopCookieMonitoring(); this.isInitialized = false; if (this.enableLogging) { console.log("Braze \uC778\uC2A4\uD134\uC2A4 \uC815\uB9AC \uC644\uB8CC"); } } /** * Braze의 디바이스 ID를 반환합니다. * @returns 디바이스 ID 문자열 또는 undefined */ getDeviceId() { if (isClient()) this.ensureInitialized(); try { const deviceId = braze.getDeviceId(); if (this.enableLogging) { console.log(`Braze Device ID: ${deviceId}`); } return deviceId; } catch (error) { console.error("Braze Device ID \uAC00\uC838\uC624\uAE30 \uC2E4\uD328:", error); return void 0; } } }; _Braze.instance = null; var Braze = _Braze; // src/utm.ts var getUTMCookie = () => { let utm_last = getCookie("utm_last"); if (utm_last == void 0) return; return JSON.parse(utm_last); }; var setUTMCookies = () => { const utm_keys = ["utm_source", "utm_medium", "utm_term", "utm_content", "utm_campaign"]; let current_utm_values = utm_keys.map(getUrlParams); if (current_utm_values.every((element) => element === null)) return; let utm_dict = utm_keys.reduce((dict, utm_value, idx) => { dict[utm_value] = current_utm_values[idx]; return dict; }, {}); setCookie("utm_last", JSON.stringify(utm_dict), 7); }; var setGclidCookie = () => { let gclid = getUrlParams("gclid"); if (gclid == null) return; setCookie("gclid", gclid, 90); }; // src/events.ts var CustomData = class { constructor() { this.created_at = new Date((/* @__PURE__ */ new Date()).getTime() + 9 * 60 * 60 * 1e3); this.referrer = document.referrer; this.utm_last = getUTMCookie(); this.gclid = getCookie("gclid"); this.url = document.location.href; this.user_id = getCookie("user_id"); this.device_id = getCookie("device_id"); let ua = window.navigator.userAgent?.toLowerCase(); this.platform = matchPlatform(ua)[0] || ""; } }; var _FlounderLog = class _FlounderLog { constructor(flounder_key, is_dev = true) { this._client = null; this.isInitialized = false; this.initKinesis = (flounder_key) => { if (isNode) { debugLog("Kinesis: Client initialized (Node)"); this._client = new KinesisClient({ region: this._region, credentials: fromCognitoIdentityPool({ identityPoolId: flounder_key, clientConfig: { region: this._region } }) }); } }; this._getStream = () => { return this._is_dev ? "TEST.online_lead_kinesis" : "PROD.sparta_online"; }; this.push_events = (key, data = {}) => { if (isBrowser) { this._addToBrowserQueue(key, data); return; } const record = { Data: Buffer.from( JSON.stringify({ key, ...data }) ), PartitionKey: String(Math.floor(Math.random() * 10) + 1) }; this._recordData.push(record); }; // 브라우저 환경용 큐 추가 메소드 this._addToBrowserQueue = (key, data = {}) => { try { const MAX_QUEUE_SIZE = 20; const payload = { key, data, timestamp: Date.now() }; if (typeof localStorage !== "undefined") { const existingLogs = JSON.parse( localStorage.getItem("cpl_event_queue") || "[]" ); existingLogs.push(payload); localStorage.setItem( "cpl_event_queue", JSON.stringify(existingLogs.slice(-MAX_QUEUE_SIZE)) ); } this._recordData.push({ Data: Buffer.from(JSON.stringify(payload)), PartitionKey: String(Math.floor(Math.random() * 10) + 1) }); if (this._recordData.length > MAX_QUEUE_SIZE) { this._recordData = this._recordData.slice(-MAX_QUEUE_SIZE); debugLog("Flounder: Queue size limit exceeded", { queue_size: this._recordData.length }); } } catch (e) { console.error("Browser queue add failed:", e); } }; this.get_failed_events = (result) => { const failedCounts = result.FailedRecordCount ?? 0; const recordsList = result.Records ?? []; let failed_array = []; if (recordsList && failedCounts > 0) { failed_array = recordsList.reduce( (array, record, idx) => { if (!!record.ErrorCode) array.push(this._recordData[idx]); return array; }, [] ); } return failed_array; }; this.send = async () => { this.ensureInitialized(); if (isBrowser) { return this._sendBrowserEvents(); } if (!this._client) { debugLog("Kinesis: Client not initialized"); return; } const command = new PutRecordsCommand({ Records: this._recordData, StreamName: this._stream_name }); this._recordData = []; try { return await this._client.send(command); } catch (e) { console.log(e); return; } }; // 브라우저 환경에서 이벤트 전송 this._sendBrowserEvents = async () => { if (this._recordData.length === 0) return null; this._recordData = []; return { FailedRecordCount: 0, Records: [] }; }; this.sendInterval = () => { if (typeof window !== "undefined") { window.setInterval(async () => { if (!this._recordData.length) return; if (this._recordData.length > 10) { debugLog( `Flounder: Current queue size`, { size: this._recordData.length }, { hideInProdBrowser: true } ); } const result = await this.send(); if (result) { let failed_events = this.get_failed_events(result); if (failed_events.length > 0) { this._recordData = [...this._recordData, ...failed_events]; debugLog("Flounder: Kinesis send failed (retrying)", { count: failed_events.length }); } } }, 1e3); } }; this.send_events_keep_alive = async (key, data = {}) => { this.ensureInitialized(); if (isBrowser) { return this._sendBrowserBeacon(key, data); } let stream_name = this._getStream(); const kinesis_api_gateway_url = `https://a6wkbhd6x8.execute-api.ap-northeast-2.amazonaws.com/main/streams/${stream_name}`; const headers = { "Content-Type": "application/json" }; const mergedData = { ...data, key }; const payload = { PartitionKey: "3", Data: mergedData }; const payload_blob = new Blob([JSON.stringify(payload)]); debugLog("Beacon: Send request", mergedData); try { const response = await fetch(kinesis_api_gateway_url, { method: "PUT", headers, body: payload_blob, keepalive: true }); if (!response.ok) { debugLog("Beacon: Send failed (HTTP)", { status: response.status }); } else { const responseData = await response.json(); debugLog("Beacon: Send success", responseData); } } catch (error) { debugLog("Beacon: Send failed (Network)", { message: error.message }); } }; // 브라우저 환경에서 Beacon API로 전송 this._sendBrowserBeacon = (key, data = {}) => { if (!navigator.sendBeacon) { debugLog("Beacon: API not supported"); return false; } try { let stream_name = this._getStream(); const kinesis_api_gateway_url = `https://a6wkbhd6x8.execute-api.ap-northeast-2.amazonaws.com/main/streams/${stream_name}`; const mergedData = { ...data, key }; const payload = { PartitionKey: "3", Data: mergedData }; const blob = new Blob([JSON.stringify(payload)], { type: "application/json" }); const result = navigator.sendBeacon(kinesis_api_gateway_url, blob); debugLog("Beacon: Send result", { success: result, key }); return result; } catch (e) { console.error("Browser beacon send failed:", e); return false; } }; this._is_dev = is_dev; this._region = "ap-northeast-2"; this._stream_name = this._getStream(); if (isNode) { this.initKinesis(flounder_key); } this._recordData = []; this.sendInterval(); this.isInitialized = true; } /** * Flounder 인스턴스를 가져옵니다. (Singleton 패턴) * @param flounder_key Flounder API 키 * @param is_dev 개발 환경 여부 * @returns FlounderLog 인스턴스 */ static getInstance(flounder_key, is_dev = true) { if (!_FlounderLog.instance) { _FlounderLog.instance = new _FlounderLog(flounder_key, is_dev); } return _FlounderLog.instance; } /** * 기존 인스턴스를 초기화합니다. */ static resetInstance() { _FlounderLog.instance = null; } /** * 초기화 상태를 확인합니다. */ ensureInitialized() { if (!this.isInitialized) { throw new Error("Flounder\uAC00 \uCD08\uAE30\uD654\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. getInstance() \uBA54\uC11C\uB4DC\uB85C \uBA3C\uC800 \uCD08\uAE30\uD654\uD558\uC138\uC694."); } } }; _FlounderLog.instance = null; var FlounderLog = _FlounderLog; // src/gtm.ts var sendGTM = (key, data, attempt = 0) => { if (typeof window.dataLayer === "undefined") { if (attempt >= 10) { return; } return setTimeout(() => sendGTM(key, data, attempt + 1), 300); } try { data["event"] = key; window.dataLayer.push(data); } catch (error) { console.error("send gtm error: ", error); } }; var _HackleLog = class _HackleLog { constructor(hackle_key) { this.isInitialized = false; this.getHackleUserData = () => { this.ensureInitialized(); const userId = getUserIdfromUserInfoCookie(); const defaultObj = { "id": getCookie("device_id"), "deviceId": getCookie("device_id") }; if (!userId.length) { return defaultObj; } return { ...defaultObj, userId }; }; this.send = (key, data = {}) => { this.ensureInitialized(); let event = { key, properties: data }; let user = this.getHackleUserData(); this._client.track(event, user); }; this._client = Hackle.createInstance(hackle_key, { debug: false, auto_track_page_view: true }); this.isInitialized = true; } /** * Hackle 인스턴스를 가져옵니다. (Singleton 패턴) * @param hackle_key Hackle API 키 * @returns HackleLog 인스턴스 */ static getInstance(hackle_key) { if (!_HackleLog.instance) { _HackleLog.instance = new _HackleLog(hackle_key); } return _HackleLog.instance; } /** * 기존 인스턴스를 초기화합니다. */ static resetInstance() { _HackleLog.instance = null; } /** * 초기화 상태를 확인합니다. */ ensureInitialized() { if (!this.isInitialized) { throw new Error("Hackle\uC774 \uCD08\uAE30\uD654\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. getInstance() \uBA54\uC11C\uB4DC\uB85C \uBA3C\uC800 \uCD08\uAE30\uD654\uD558\uC138\uC694."); } } }; _HackleLog.instance = null; var HackleLog = _HackleLog; // src/index.ts var loggerInstances = {}; var initCPLog = (flounder_key, amplitude_key, hackle_key, braze_key, braze_endpoint, is_dev = true, amplitude_browser_options) => { setConfig({ isDev: is_dev }); debugLog("Initialization complete", { environment: isBrowser ? "browser" : "node", is_dev }); loggerInstances.flounder = FlounderLog.getInstance(flounder_key, is_dev); loggerInstances.amplitude = AmplitudeLog.getInstance(amplitude_key, amplitude_browser_options); loggerInstances.amplitude.initialize(); loggerInstances.hackle = HackleLog.getInstance(hackle_key); if (braze_key && braze_endpoint) { loggerInstances.braze = Braze.getInstance(braze_key, braze_endpoint, is_dev); loggerInstances.braze.initialize(); const brazeDeviceId = loggerInstances.braze.getDeviceId(); if (brazeDeviceId) { loggerInstances.amplitude.setDeviceId(brazeDeviceId); debugLog("Amplitude Device ID\uAC00 Braze Device ID\uB85C \uC124\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", { deviceId: brazeDeviceId }); } } if (isBrowser) { try { setUTMCookies(); setGclidCookie(); } catch (e) { debugLog("Browser: Cookie setup failed", e); } } console.log("Cross Platform Logger \uCD08\uAE30\uD654 \uC644\uB8CC"); }; var pageUnloading = false; if (typeof window !== "undefined") { window.addEventListener("beforeunload", () => { pageUnloading = true; debugLog("Browser: Page unloading detected", null, { hideInProdBrowser: true }); }); window.addEventListener("load", () => { pageUnloading = false; debugLog("Browser: Page load complete", null, { hideInProdBrowser: true }); }); } var sendCPLog = (key, data = {}, is_gtm = false) => { try { debugLog( "Event dispatch request received", { key, is_gtm }, { hideInProdBrowser: true } ); let customData = new CustomData(); let mergedData = { ...customData, ...data }; let nestedData = { ...customData, data }; if (isBrowser) { const utmData = getUTMFromCookie(); if (utmData) { mergedData = { ...mergedData, ...utmData }; debugLog(`UTM \uC815\uBCF4\uAC00 Amplitude \uC774\uBCA4\uD2B8\uC5D0 \uCD94\uAC00\uB428`, utmData, { color: "blue", hideInProdBrowser: true }); } } loggerInstances.amplitude?.send(key, mergedData); debugLog(`Amplitude: "${key}" sent successfully`, null, { color: "green", hideInProdBrowser: true }); loggerInstances.hackle?.send(key, mergedData); debugLog(`Hackle: "${key}" sent successfully`, null, { color: "green", hideInProdBrowser: true }); if (is_gtm && isBrowser) { sendGTM(key, mergedData); debugLog(`GTM: "${key}" sent successfully`, null, { color: "green", hideInProdBrowser: true }); } setTimeout(() => { if (pageUnloading) { debugLog("Page unload handling started", { key }); loggerInstances.amplitude?.useSendBeacon(); loggerInstances.flounder?.send_events_keep_alive(key, nestedData); } if (!pageUnloading) { debugLog( "Flounder: Event added to queue", { key }, { hideInProdBrowser: true } ); loggerInstances.flounder?.push_events(key, nestedData); } }, 0); } catch (e) { console.error("CPLog: Event send failed", e); } }; var getAmplitudeInstance = () => { if (!loggerInstances.amplitude) { throw new Error("Amplitude\uAC00 \uCD08\uAE30\uD654\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. initCPLog()\uB97C \uBA3C\uC800 \uD638\uCD9C\uD558\uC138\uC694."); } return loggerInstances.amplitude.getInstance(); }; var setAmplitudeUserProperty = (key, value) => { loggerInstances.amplitude?.setUserProperty(key, value); }; var setAmplitudeUserProperties = (properties) => { loggerInstances.amplitude?.setUserProperties(properties); }; var appendAmplitudeUserProperty = (key, value) => { loggerInstances.amplitude?.appendUserProperty(key, value); }; var removeAmplitudeUserProperty = (key, value) => { loggerInstances.amplitude?.removeUserProperty(key, value); }; var unsetAmplitudeUserProperty = (key) => { loggerInstances.amplitude?.unsetUserProperty(key); }; var getBrazeInstance = () => { return loggerInstances.braze || null; }; var destroyBrazeInstance = () => { if (loggerInstances.braze) { loggerInstances.braze.destroy(); } }; var resetBrazeChangeUserHistory = () => { if (loggerInstances.braze) { loggerInstances.braze.resetChangeUserHistory(); } }; var requestBrazeNotificationPermission = async () => { if (loggerInstances.braze) { return await loggerInstances.braze.requestNotificationPermission(); } return false; }; var disableBrazeSDK = () => { if (loggerInstances.braze) { loggerInstances.braze.disableSDK(); } }; var enableBrazeSDK = () => { if (loggerInstances.braze) { loggerInstances.braze.enableSDK(); } }; var setAmplitudeSessionReplayConfig = (config2) => { loggerInstances.amplitude?.setSessionReplayConfig(config2); }; var startAmplitudeSessionReplay = () => { loggerInstances.amplitude?.startSessionReplay(); }; var stopAmplitudeSessionReplay = () => { loggerInstances.amplitude?.stopSessionReplay(); }; var startAmplitudeSessionReplayForEndpoint = (currentPath) => { loggerInstances.amplitude?.startSessionReplayForEndpoint(currentPath); }; var setAmplitudeUserId = (userId) => { loggerInstances.amplitude?.setUserId(userId); }; var resetAmplitudeUserId = () => { loggerInstances.amplitude?.resetUserId(); }; var resetAmplitudeSetUserIdHistory = () => { loggerInstances.amplitude?.resetSetUserIdHistory(); }; export { appendAmplitudeUserProperty, destroyBrazeInstance, disableBrazeSDK, enableBrazeSDK, getAmplitudeInstance, getBrazeInstance, initCPLog, removeAmplitudeUserProperty, requestBrazeNotificationPermission, resetAmplitudeSetUserIdHistory, resetAmplitudeUserId, resetBrazeChangeUserHistory, sendCPLog, setAmplitudeSessionReplayConfig, setAmplitudeUserId, setAmplitudeUserProperties, setAmplitudeUserProperty, startAmplitudeSessionReplay, startAmplitudeSessionReplayForEndpoint, stopAmplitudeSessionReplay, unsetAmplitudeUserProperty };