connectbase-client 3.3.1 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/dist/connect-base.umd.js +3 -3
- package/dist/index.d.mts +79 -3
- package/dist/index.d.ts +79 -3
- package/dist/index.js +98 -5
- package/dist/index.mjs +97 -5
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7994,6 +7994,29 @@ var SessionManager = class {
|
|
|
7994
7994
|
} catch {
|
|
7995
7995
|
}
|
|
7996
7996
|
}
|
|
7997
|
+
/**
|
|
7998
|
+
* visitor_uid 를 새로 발급하고 세션도 함께 초기화.
|
|
7999
|
+
*
|
|
8000
|
+
* 사용 시점:
|
|
8001
|
+
* - 사용자 로그아웃 (이전 사용자 활동이 다음 사용자에 attribution 되는 것 방지)
|
|
8002
|
+
* - 다른 사용자 로그인 감지 (link-member 가 `VISITOR_LINKED_TO_OTHER_MEMBER` 응답)
|
|
8003
|
+
*
|
|
8004
|
+
* localStorage 의 `__cb_visitor_uid` 가 새 UUID 로 교체되며 sessionStorage 의
|
|
8005
|
+
* 세션 키도 같이 비워진다. 이후의 모든 batch 는 새 visitor 로 기록되어 멤버 간
|
|
8006
|
+
* 데이터 오염이 차단된다.
|
|
8007
|
+
*/
|
|
8008
|
+
regenerateVisitorUid() {
|
|
8009
|
+
const newUid = generateId();
|
|
8010
|
+
this._visitorUid = newUid;
|
|
8011
|
+
try {
|
|
8012
|
+
if (typeof localStorage !== "undefined") {
|
|
8013
|
+
localStorage.setItem(VISITOR_KEY, newUid);
|
|
8014
|
+
}
|
|
8015
|
+
} catch {
|
|
8016
|
+
}
|
|
8017
|
+
this.reset();
|
|
8018
|
+
return newUid;
|
|
8019
|
+
}
|
|
7997
8020
|
ensureSession() {
|
|
7998
8021
|
const now = Date.now();
|
|
7999
8022
|
if (!this._sessionId) {
|
|
@@ -8199,6 +8222,11 @@ var AnalyticsAPI = class {
|
|
|
8199
8222
|
* 백엔드 `link-member` 엔드포인트를 한 번 호출합니다 (1.11.0+). 호출 실패는
|
|
8200
8223
|
* silent — 다음 batch 가 닿을 때 백엔드 자동 매핑이 동일하게 처리하므로 자가 복구.
|
|
8201
8224
|
*
|
|
8225
|
+
* **사용자 전환 자동 처리 (1.13.0+)**: 동일 브라우저에서 다른 사용자가 로그인해
|
|
8226
|
+
* 백엔드가 `VISITOR_LINKED_TO_OTHER_MEMBER` 응답을 보내면, 큐를 비우고
|
|
8227
|
+
* `visitor_uid` 를 새로 발급한 뒤 link-member 를 한 번 더 호출해 새 visitor 가
|
|
8228
|
+
* 즉시 회원으로 기록되도록 자가 복구한다.
|
|
8229
|
+
*
|
|
8202
8230
|
* 산업 표준(GA4 User-ID, Mixpanel/PostHog `identify`) 과 동작 정합.
|
|
8203
8231
|
*
|
|
8204
8232
|
* @example
|
|
@@ -8209,14 +8237,42 @@ var AnalyticsAPI = class {
|
|
|
8209
8237
|
* ```
|
|
8210
8238
|
*/
|
|
8211
8239
|
identify(memberId) {
|
|
8240
|
+
if (this.memberId && this.memberId !== memberId) {
|
|
8241
|
+
this.reset();
|
|
8242
|
+
}
|
|
8212
8243
|
this.setMemberId(memberId);
|
|
8213
8244
|
this.linkMemberSilent(memberId);
|
|
8214
8245
|
}
|
|
8246
|
+
/**
|
|
8247
|
+
* 로그아웃 / 사용자 전환 시 호출. 익명 상태로 복귀하면서 visitor_uid 를 새로
|
|
8248
|
+
* 발급해 다음 사용자의 활동이 이전 사용자에게 attribution 되는 데이터 오염을 차단.
|
|
8249
|
+
*
|
|
8250
|
+
* 동작:
|
|
8251
|
+
* 1. memberId 를 null 로 설정 (이후 batch 는 익명으로 기록)
|
|
8252
|
+
* 2. 큐에 쌓인 미전송 이벤트 폐기 (이전 visitor 로 가는 것을 막기 위함)
|
|
8253
|
+
* 3. localStorage `__cb_visitor_uid` 새 UUID 로 교체 + sessionStorage 세션 키 정리
|
|
8254
|
+
* 4. heatmap 큐도 함께 비움
|
|
8255
|
+
*
|
|
8256
|
+
* @example
|
|
8257
|
+
* ```ts
|
|
8258
|
+
* // 로그아웃 핸들러
|
|
8259
|
+
* await cb.auth.signOut()
|
|
8260
|
+
* cb.analytics.reset()
|
|
8261
|
+
* ```
|
|
8262
|
+
*/
|
|
8263
|
+
reset() {
|
|
8264
|
+
this.setMemberId(null);
|
|
8265
|
+
this.eventQueue = [];
|
|
8266
|
+
this.heatmapQueue = [];
|
|
8267
|
+
const newUid = this.session.regenerateVisitorUid();
|
|
8268
|
+
this.log("Analytics reset", { newVisitorUid: newUid });
|
|
8269
|
+
}
|
|
8215
8270
|
/**
|
|
8216
8271
|
* 방문자 트래커에 현재 회원 ID 설정 (로그인/게스트 가입 시 호출).
|
|
8217
8272
|
*
|
|
8218
8273
|
* `identify()` 와 달리 즉시 backfill 호출은 하지 않습니다 — 단순히 이후 batch 의
|
|
8219
|
-
* `app_member_id` 값만 갱신. null 을 넘기면 익명 상태로 복귀 (로그아웃
|
|
8274
|
+
* `app_member_id` 값만 갱신. null 을 넘기면 익명 상태로 복귀 (로그아웃 시에는
|
|
8275
|
+
* 데이터 오염 방지를 위해 `reset()` 를 권장).
|
|
8220
8276
|
*/
|
|
8221
8277
|
setMemberId(memberId) {
|
|
8222
8278
|
this.memberId = memberId || null;
|
|
@@ -8238,9 +8294,12 @@ var AnalyticsAPI = class {
|
|
|
8238
8294
|
*
|
|
8239
8295
|
* - 첫 페이지뷰가 아직 백엔드에 닿기 전이면 visitor 가 없어 404. 무시 — 다음 batch 가
|
|
8240
8296
|
* 가면 백엔드 BatchRecordVisit 가 자동 LinkMember 를 호출.
|
|
8297
|
+
* - 다른 멤버에 이미 연결된 visitor 인 경우 (`VISITOR_LINKED_TO_OTHER_MEMBER`)
|
|
8298
|
+
* visitor_uid 를 자동 재발급하고 link-member 를 한 번 더 호출 — 한 단계의 자가
|
|
8299
|
+
* 복구로 끝나며 무한 재귀를 막기 위해 두 번째 시도는 응답을 더 보지 않는다.
|
|
8241
8300
|
* - storage_web_id 가 init 안 됐거나 모든 종류의 네트워크 오류 — silent fail.
|
|
8242
8301
|
*/
|
|
8243
|
-
linkMemberSilent(memberId) {
|
|
8302
|
+
linkMemberSilent(memberId, isRetry = false) {
|
|
8244
8303
|
if (!this.storageWebId) return;
|
|
8245
8304
|
const prefix = this.http.hasPublicKey() ? "/v1/public" : "/v1";
|
|
8246
8305
|
this.http.post(
|
|
@@ -8249,7 +8308,18 @@ var AnalyticsAPI = class {
|
|
|
8249
8308
|
visitor_uid: this.session.visitorUid,
|
|
8250
8309
|
app_member_id: memberId
|
|
8251
8310
|
}
|
|
8252
|
-
).
|
|
8311
|
+
).then((resp) => {
|
|
8312
|
+
if (!resp) return;
|
|
8313
|
+
this.log("link-member response", resp);
|
|
8314
|
+
if (!isRetry && resp.success === false && resp.code === "VISITOR_LINKED_TO_OTHER_MEMBER") {
|
|
8315
|
+
this.log("user-switch detected \u2014 regenerating visitor_uid");
|
|
8316
|
+
this.eventQueue = [];
|
|
8317
|
+
this.heatmapQueue = [];
|
|
8318
|
+
this.session.regenerateVisitorUid();
|
|
8319
|
+
this.linkMemberSilent(memberId, true);
|
|
8320
|
+
}
|
|
8321
|
+
}).catch((err) => {
|
|
8322
|
+
this.log("link-member silent fail", err);
|
|
8253
8323
|
});
|
|
8254
8324
|
}
|
|
8255
8325
|
/** 현재 설정된 회원 ID 조회 (미설정 시 null) */
|
|
@@ -8483,6 +8553,7 @@ var AnalyticsAPI = class {
|
|
|
8483
8553
|
createBaseEvent(type) {
|
|
8484
8554
|
return {
|
|
8485
8555
|
type,
|
|
8556
|
+
event_id: generateId(),
|
|
8486
8557
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8487
8558
|
session_id: this.session.sessionId,
|
|
8488
8559
|
visitor_uid: this.session.visitorUid
|
|
@@ -8505,6 +8576,7 @@ var AnalyticsAPI = class {
|
|
|
8505
8576
|
visitor_uid: this.session.visitorUid,
|
|
8506
8577
|
...this.memberId ? { app_member_id: this.memberId } : {},
|
|
8507
8578
|
events: events.map((e) => ({
|
|
8579
|
+
event_id: e.event_id,
|
|
8508
8580
|
timestamp: e.timestamp,
|
|
8509
8581
|
page_path: e.page_path || "",
|
|
8510
8582
|
page_url: e.page_url || "",
|
|
@@ -8534,7 +8606,13 @@ var AnalyticsAPI = class {
|
|
|
8534
8606
|
this.log("Flush failed", err);
|
|
8535
8607
|
}
|
|
8536
8608
|
}
|
|
8537
|
-
/**
|
|
8609
|
+
/**
|
|
8610
|
+
* sendBeacon 으로 동기 flush (beforeunload 용).
|
|
8611
|
+
*
|
|
8612
|
+
* sendBeacon 은 일반 fetch 와 달리 User-Agent 헤더가 신뢰할 수 있지만, 백엔드 봇
|
|
8613
|
+
* 탐지가 body 의 `user_agent` 필드 첫 번째 값만 보므로 여기서도 동일하게 채워
|
|
8614
|
+
* 첫 방문이 unload 타이밍에 도달했을 때 봇으로 오판되는 것을 방지한다.
|
|
8615
|
+
*/
|
|
8538
8616
|
flushSync() {
|
|
8539
8617
|
if (this.eventQueue.length === 0 || !this.storageWebId) return;
|
|
8540
8618
|
if (typeof navigator === "undefined" || !navigator.sendBeacon) return;
|
|
@@ -8542,20 +8620,30 @@ var AnalyticsAPI = class {
|
|
|
8542
8620
|
const prefix = this.http.hasPublicKey() ? "/v1/public" : "/v1";
|
|
8543
8621
|
const baseUrl = this.http.getBaseUrl();
|
|
8544
8622
|
const url = `${baseUrl}${prefix}/storages/web/${this.storageWebId}/visitors/batch`;
|
|
8623
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
8545
8624
|
const body = JSON.stringify({
|
|
8546
8625
|
visitor_uid: this.session.visitorUid,
|
|
8547
8626
|
...this.memberId ? { app_member_id: this.memberId } : {},
|
|
8548
8627
|
events: events.map((e) => ({
|
|
8628
|
+
event_id: e.event_id,
|
|
8549
8629
|
timestamp: e.timestamp,
|
|
8550
8630
|
page_path: e.page_path || "",
|
|
8551
8631
|
page_url: e.page_url || "",
|
|
8552
8632
|
page_title: e.page_title || "",
|
|
8553
8633
|
referrer: e.referrer || "",
|
|
8634
|
+
user_agent: ua,
|
|
8635
|
+
screen_width: e.screen_width || 0,
|
|
8636
|
+
screen_height: e.screen_height || 0,
|
|
8554
8637
|
session_id: e.session_id,
|
|
8555
8638
|
session_start: e.type === "session_start",
|
|
8556
8639
|
is_page_view: e.type === "page_view",
|
|
8557
8640
|
event_name: e.event_name,
|
|
8558
|
-
event_properties: e.event_properties
|
|
8641
|
+
event_properties: e.event_properties,
|
|
8642
|
+
utm_source: e.utm_source,
|
|
8643
|
+
utm_medium: e.utm_medium,
|
|
8644
|
+
utm_campaign: e.utm_campaign,
|
|
8645
|
+
utm_content: e.utm_content,
|
|
8646
|
+
utm_term: e.utm_term
|
|
8559
8647
|
}))
|
|
8560
8648
|
});
|
|
8561
8649
|
try {
|
|
@@ -8727,6 +8815,9 @@ var AnalyticsAPI = class {
|
|
|
8727
8815
|
}
|
|
8728
8816
|
};
|
|
8729
8817
|
|
|
8818
|
+
// src/types/knowledge.ts
|
|
8819
|
+
var AUTH_MEMBER_ID_TOKEN = "$auth.member_id";
|
|
8820
|
+
|
|
8730
8821
|
// src/api/game-transport.ts
|
|
8731
8822
|
var WebTransportTransport = class {
|
|
8732
8823
|
constructor(config, onMessage, onClose, onError) {
|
|
@@ -9435,6 +9526,7 @@ var ConnectBase = class {
|
|
|
9435
9526
|
var index_default = ConnectBase;
|
|
9436
9527
|
export {
|
|
9437
9528
|
AIAPI,
|
|
9529
|
+
AUTH_MEMBER_ID_TOKEN,
|
|
9438
9530
|
AdsAPI,
|
|
9439
9531
|
ApiError,
|
|
9440
9532
|
AuthError,
|