@teamsparta/cross-platform-logger
Version:
```typescript import * as CPL from "@teamsparta/cross-platform-logger";
1,275 lines (1,266 loc) • 42.1 kB
JavaScript
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 };