connectbase-client 1.9.0 → 1.10.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/dist/index.d.mts CHANGED
@@ -172,6 +172,77 @@ interface AnalyticsEvent {
172
172
  properties?: Record<string, unknown>;
173
173
  timestamp?: number;
174
174
  }
175
+ /**
176
+ * 인기 페이지 목록 응답.
177
+ *
178
+ * 백엔드 `dto.PopularPagesResponse` 와 1:1 매핑.
179
+ */
180
+ interface PopularPagesResponse {
181
+ pages: Array<{
182
+ page_path: string;
183
+ page_views: number;
184
+ }>;
185
+ start_date: number;
186
+ end_date: number;
187
+ }
188
+ /**
189
+ * 방문자 목록 응답.
190
+ *
191
+ * 백엔드 `dto.VisitorListResponse` 와 1:1 매핑. `app_member_id` 는 게스트 방문자에는
192
+ * 없을 수 있다.
193
+ */
194
+ interface VisitorListResponse {
195
+ visitors: Array<{
196
+ id: string;
197
+ visitor_uid: string;
198
+ app_member_id?: string;
199
+ total_visits: number;
200
+ total_page_views: number;
201
+ referrer?: string;
202
+ last_ip?: string;
203
+ country?: string;
204
+ is_bot: boolean;
205
+ first_visit_at: string;
206
+ last_visit_at: string;
207
+ }>;
208
+ total: number;
209
+ limit: number;
210
+ offset: number;
211
+ has_more: boolean;
212
+ }
213
+ /**
214
+ * 페이지 전환 플로우(Sankey) 응답.
215
+ *
216
+ * 백엔드 `dto.NavigationFlowResponse` 와 1:1 매핑. `nodes` 는 페이지, `links` 는 전환.
217
+ */
218
+ interface NavigationFlowResponse {
219
+ nodes: Array<{
220
+ id: string;
221
+ label: string;
222
+ value: number;
223
+ }>;
224
+ links: Array<{
225
+ source: string;
226
+ target: string;
227
+ value: number;
228
+ }>;
229
+ }
230
+ /**
231
+ * 조회 메서드 공통 옵션.
232
+ *
233
+ * `start_date` / `end_date` 는 백엔드가 정수형 epoch-day 또는 yyyymmdd 로 다루는 값
234
+ * (서비스 메서드 시그니처 기준 int). 0 또는 미지정 시 기본 기간을 사용.
235
+ */
236
+ interface AnalyticsRangeOptions {
237
+ start_date?: number;
238
+ end_date?: number;
239
+ limit?: number;
240
+ }
241
+ interface VisitorListOptions {
242
+ limit?: number;
243
+ offset?: number;
244
+ sort_by?: 'last_visit' | 'total_visits' | 'total_page_views';
245
+ }
175
246
  declare class SessionManager {
176
247
  private _sessionId;
177
248
  private _visitorUid;
@@ -270,6 +341,33 @@ declare class AnalyticsAPI {
270
341
  * ```
271
342
  */
272
343
  flush(): Promise<void>;
344
+ /**
345
+ * 인기 페이지 조회 (콘솔 JWT 또는 User Secret Key `cb_sk_` 인증 필요).
346
+ *
347
+ * 브라우저 환경에서 Public Key(`cb_pk_`) 만 가진 SDK 인스턴스로는 호출이 차단된다.
348
+ * Functions / 어드민 앱 등 cb_sk_ 환경에서 사용하라.
349
+ *
350
+ * @param storageWebId 조회할 웹스토리지 ID. 미지정 시 `init()` 에 전달된 ID 사용.
351
+ * @param options 기간/limit 옵션
352
+ * @example
353
+ * ```ts
354
+ * const { pages } = await cb.analytics.getPopularPages('019d8...', { limit: 20 })
355
+ * ```
356
+ */
357
+ getPopularPages(storageWebId?: string, options?: AnalyticsRangeOptions): Promise<PopularPagesResponse>;
358
+ /**
359
+ * 페이지 전환 플로우(Sankey) 조회 — JWT/cb_sk_ 인증 필요.
360
+ */
361
+ getNavigationFlow(storageWebId?: string, options?: AnalyticsRangeOptions): Promise<NavigationFlowResponse>;
362
+ /**
363
+ * 방문자 목록 조회 — JWT/cb_sk_ 인증 필요.
364
+ */
365
+ getVisitors(storageWebId?: string, options?: VisitorListOptions): Promise<VisitorListResponse>;
366
+ /**
367
+ * 조회 메서드 공통 가드 — Public Key 인증 SDK 인스턴스에서는 명확한 에러를 던진다.
368
+ * 백엔드 라우트는 cb_pk_ 를 거부하므로 호출 자체를 막는 것이 디버깅에 유리.
369
+ */
370
+ private requireServerSideStorageId;
273
371
  /**
274
372
  * 세션 매니저 접근 (고급 사용자용).
275
373
  *
@@ -3174,12 +3272,17 @@ interface GetAuthorizationURLResponse {
3174
3272
  }
3175
3273
  /**
3176
3274
  * OAuth 콜백 응답
3275
+ *
3276
+ * `email` 은 1.10 부터 추가된 선택적 필드. Apple private relay 또는 이메일 권한 미동의
3277
+ * 시 빈 문자열 / undefined 일 수 있다. 값이 있으면 첫 로그인 시점에 이메일을 확보할 수 있어
3278
+ * 별도 `getMember()` 호출이 불필요하다.
3177
3279
  */
3178
3280
  interface OAuthCallbackResponse {
3179
3281
  member_id: string;
3180
3282
  access_token: string;
3181
3283
  refresh_token: string;
3182
3284
  is_new_member: boolean;
3285
+ email?: string;
3183
3286
  }
3184
3287
 
3185
3288
  /**
@@ -3284,6 +3387,7 @@ declare class OAuthAPI {
3284
3387
  refresh_token?: string;
3285
3388
  member_id?: string;
3286
3389
  is_new_member?: boolean;
3390
+ email?: string;
3287
3391
  state?: string;
3288
3392
  error?: string;
3289
3393
  } | null;
@@ -3308,6 +3412,7 @@ declare class OAuthAPI {
3308
3412
  refresh_token: string;
3309
3413
  member_id: string;
3310
3414
  is_new_member: boolean;
3415
+ email?: string;
3311
3416
  state?: string;
3312
3417
  } | null>;
3313
3418
  }
@@ -4095,6 +4200,35 @@ interface WebPushSubscription {
4095
4200
  };
4096
4201
  }
4097
4202
 
4203
+ /**
4204
+ * `cb.push.sendToMembers` 페이로드. 백엔드 `dto.SendPushRequest` 의 멤버 발송 케이스
4205
+ * 에 매핑되며, target_type / target_member_ids 는 SDK 가 자동 채운다.
4206
+ */
4207
+ interface SendToMembersPayload {
4208
+ title: string;
4209
+ body: string;
4210
+ imageUrl?: string;
4211
+ data?: Record<string, unknown>;
4212
+ /** 'ios' | 'android' | 'web' 중 선택. 빈 배열/미지정 시 전체 플랫폼 발송. */
4213
+ platforms?: Array<'ios' | 'android' | 'web'>;
4214
+ /** TTL(초). 기본 86400 (24h). */
4215
+ ttlSeconds?: number;
4216
+ priority?: 'normal' | 'high';
4217
+ clickAction?: string;
4218
+ /** ISO8601 형식 예약 발송 시각. 미지정 시 즉시 발송. */
4219
+ scheduledAt?: string;
4220
+ }
4221
+ /**
4222
+ * 발송 결과. 백엔드 `dto.SendResultResponse` 와 1:1 매핑.
4223
+ */
4224
+ interface SendPushResult {
4225
+ message_id: string;
4226
+ total_target: number;
4227
+ sent_count: number;
4228
+ failed_count: number;
4229
+ status: string;
4230
+ error_message?: string;
4231
+ }
4098
4232
  /**
4099
4233
  * 푸시 알림 API
4100
4234
  *
@@ -4233,6 +4367,29 @@ declare class PushAPI {
4233
4367
  * @param deviceToken Web Push endpoint URL (등록 시 사용한 endpoint)
4234
4368
  */
4235
4369
  unregisterWebPush(deviceToken: string): Promise<void>;
4370
+ /**
4371
+ * 회원 ID 목록으로 푸시 발송 — Functions / cb_sk_ 인증 환경 전용.
4372
+ *
4373
+ * 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT 또는 User Secret Key
4374
+ * (`cb_sk_`) 만 받는다. 브라우저 SDK 의 Public Key(`cb_pk_`) 인스턴스에서는 호출
4375
+ * 자체가 차단되며, 권한 누설을 막기 위해 명확한 에러를 던진다.
4376
+ *
4377
+ * @param appId 발송 대상 앱 ID (cb_sk_ 환경은 user 단위 권한이므로 명시 전달).
4378
+ * @param memberIds 받는 회원 ID 배열 (UUID).
4379
+ * @param payload 푸시 내용/옵션.
4380
+ *
4381
+ * @example
4382
+ * ```ts
4383
+ * // ConnectBase Function (Node.js, secrets.CB_SECRET_KEY 주입)
4384
+ * const result = await cb.push.sendToMembers(APP_ID, [memberId], {
4385
+ * title: '새 알림',
4386
+ * body: '확인해보세요',
4387
+ * data: { route: '/notifications' },
4388
+ * })
4389
+ * console.log(result.message_id, result.sent_count)
4390
+ * ```
4391
+ */
4392
+ sendToMembers(appId: string, memberIds: string[], payload: SendToMembersPayload): Promise<SendPushResult>;
4236
4393
  /**
4237
4394
  * 브라우저 고유 ID 생성 (localStorage에 저장)
4238
4395
  */
@@ -6714,10 +6871,10 @@ interface ConnectBaseConfig {
6714
6871
  onTokenExpired?: () => void;
6715
6872
  /**
6716
6873
  * 토큰 저장 방식.
6717
- * - 'localStorage': 브라우저 종료 후에도 유지 (기본값)
6718
- * - 'sessionStorage': 탭 종료 시 삭제
6719
- * - 'none': 메모리에만 저장 (페이지 새로고침 삭제)
6720
- * @default 'localStorage'
6874
+ * - 'none' (기본·권장): 메모리에만 저장. 새로고침 재로그인 필요. XSS 노출 최소화
6875
+ * - 'sessionStorage': 탭 종료 시 삭제 (XSS 시 현재 탭 세션 탈취 가능)
6876
+ * - 'localStorage': 브라우저 종료 후에도 유지 (XSS토큰 영구 탈취 가능 — 콘솔 경고 출력)
6877
+ * @default 'none'
6721
6878
  */
6722
6879
  persistence?: TokenPersistence;
6723
6880
  /**
package/dist/index.d.ts CHANGED
@@ -172,6 +172,77 @@ interface AnalyticsEvent {
172
172
  properties?: Record<string, unknown>;
173
173
  timestamp?: number;
174
174
  }
175
+ /**
176
+ * 인기 페이지 목록 응답.
177
+ *
178
+ * 백엔드 `dto.PopularPagesResponse` 와 1:1 매핑.
179
+ */
180
+ interface PopularPagesResponse {
181
+ pages: Array<{
182
+ page_path: string;
183
+ page_views: number;
184
+ }>;
185
+ start_date: number;
186
+ end_date: number;
187
+ }
188
+ /**
189
+ * 방문자 목록 응답.
190
+ *
191
+ * 백엔드 `dto.VisitorListResponse` 와 1:1 매핑. `app_member_id` 는 게스트 방문자에는
192
+ * 없을 수 있다.
193
+ */
194
+ interface VisitorListResponse {
195
+ visitors: Array<{
196
+ id: string;
197
+ visitor_uid: string;
198
+ app_member_id?: string;
199
+ total_visits: number;
200
+ total_page_views: number;
201
+ referrer?: string;
202
+ last_ip?: string;
203
+ country?: string;
204
+ is_bot: boolean;
205
+ first_visit_at: string;
206
+ last_visit_at: string;
207
+ }>;
208
+ total: number;
209
+ limit: number;
210
+ offset: number;
211
+ has_more: boolean;
212
+ }
213
+ /**
214
+ * 페이지 전환 플로우(Sankey) 응답.
215
+ *
216
+ * 백엔드 `dto.NavigationFlowResponse` 와 1:1 매핑. `nodes` 는 페이지, `links` 는 전환.
217
+ */
218
+ interface NavigationFlowResponse {
219
+ nodes: Array<{
220
+ id: string;
221
+ label: string;
222
+ value: number;
223
+ }>;
224
+ links: Array<{
225
+ source: string;
226
+ target: string;
227
+ value: number;
228
+ }>;
229
+ }
230
+ /**
231
+ * 조회 메서드 공통 옵션.
232
+ *
233
+ * `start_date` / `end_date` 는 백엔드가 정수형 epoch-day 또는 yyyymmdd 로 다루는 값
234
+ * (서비스 메서드 시그니처 기준 int). 0 또는 미지정 시 기본 기간을 사용.
235
+ */
236
+ interface AnalyticsRangeOptions {
237
+ start_date?: number;
238
+ end_date?: number;
239
+ limit?: number;
240
+ }
241
+ interface VisitorListOptions {
242
+ limit?: number;
243
+ offset?: number;
244
+ sort_by?: 'last_visit' | 'total_visits' | 'total_page_views';
245
+ }
175
246
  declare class SessionManager {
176
247
  private _sessionId;
177
248
  private _visitorUid;
@@ -270,6 +341,33 @@ declare class AnalyticsAPI {
270
341
  * ```
271
342
  */
272
343
  flush(): Promise<void>;
344
+ /**
345
+ * 인기 페이지 조회 (콘솔 JWT 또는 User Secret Key `cb_sk_` 인증 필요).
346
+ *
347
+ * 브라우저 환경에서 Public Key(`cb_pk_`) 만 가진 SDK 인스턴스로는 호출이 차단된다.
348
+ * Functions / 어드민 앱 등 cb_sk_ 환경에서 사용하라.
349
+ *
350
+ * @param storageWebId 조회할 웹스토리지 ID. 미지정 시 `init()` 에 전달된 ID 사용.
351
+ * @param options 기간/limit 옵션
352
+ * @example
353
+ * ```ts
354
+ * const { pages } = await cb.analytics.getPopularPages('019d8...', { limit: 20 })
355
+ * ```
356
+ */
357
+ getPopularPages(storageWebId?: string, options?: AnalyticsRangeOptions): Promise<PopularPagesResponse>;
358
+ /**
359
+ * 페이지 전환 플로우(Sankey) 조회 — JWT/cb_sk_ 인증 필요.
360
+ */
361
+ getNavigationFlow(storageWebId?: string, options?: AnalyticsRangeOptions): Promise<NavigationFlowResponse>;
362
+ /**
363
+ * 방문자 목록 조회 — JWT/cb_sk_ 인증 필요.
364
+ */
365
+ getVisitors(storageWebId?: string, options?: VisitorListOptions): Promise<VisitorListResponse>;
366
+ /**
367
+ * 조회 메서드 공통 가드 — Public Key 인증 SDK 인스턴스에서는 명확한 에러를 던진다.
368
+ * 백엔드 라우트는 cb_pk_ 를 거부하므로 호출 자체를 막는 것이 디버깅에 유리.
369
+ */
370
+ private requireServerSideStorageId;
273
371
  /**
274
372
  * 세션 매니저 접근 (고급 사용자용).
275
373
  *
@@ -3174,12 +3272,17 @@ interface GetAuthorizationURLResponse {
3174
3272
  }
3175
3273
  /**
3176
3274
  * OAuth 콜백 응답
3275
+ *
3276
+ * `email` 은 1.10 부터 추가된 선택적 필드. Apple private relay 또는 이메일 권한 미동의
3277
+ * 시 빈 문자열 / undefined 일 수 있다. 값이 있으면 첫 로그인 시점에 이메일을 확보할 수 있어
3278
+ * 별도 `getMember()` 호출이 불필요하다.
3177
3279
  */
3178
3280
  interface OAuthCallbackResponse {
3179
3281
  member_id: string;
3180
3282
  access_token: string;
3181
3283
  refresh_token: string;
3182
3284
  is_new_member: boolean;
3285
+ email?: string;
3183
3286
  }
3184
3287
 
3185
3288
  /**
@@ -3284,6 +3387,7 @@ declare class OAuthAPI {
3284
3387
  refresh_token?: string;
3285
3388
  member_id?: string;
3286
3389
  is_new_member?: boolean;
3390
+ email?: string;
3287
3391
  state?: string;
3288
3392
  error?: string;
3289
3393
  } | null;
@@ -3308,6 +3412,7 @@ declare class OAuthAPI {
3308
3412
  refresh_token: string;
3309
3413
  member_id: string;
3310
3414
  is_new_member: boolean;
3415
+ email?: string;
3311
3416
  state?: string;
3312
3417
  } | null>;
3313
3418
  }
@@ -4095,6 +4200,35 @@ interface WebPushSubscription {
4095
4200
  };
4096
4201
  }
4097
4202
 
4203
+ /**
4204
+ * `cb.push.sendToMembers` 페이로드. 백엔드 `dto.SendPushRequest` 의 멤버 발송 케이스
4205
+ * 에 매핑되며, target_type / target_member_ids 는 SDK 가 자동 채운다.
4206
+ */
4207
+ interface SendToMembersPayload {
4208
+ title: string;
4209
+ body: string;
4210
+ imageUrl?: string;
4211
+ data?: Record<string, unknown>;
4212
+ /** 'ios' | 'android' | 'web' 중 선택. 빈 배열/미지정 시 전체 플랫폼 발송. */
4213
+ platforms?: Array<'ios' | 'android' | 'web'>;
4214
+ /** TTL(초). 기본 86400 (24h). */
4215
+ ttlSeconds?: number;
4216
+ priority?: 'normal' | 'high';
4217
+ clickAction?: string;
4218
+ /** ISO8601 형식 예약 발송 시각. 미지정 시 즉시 발송. */
4219
+ scheduledAt?: string;
4220
+ }
4221
+ /**
4222
+ * 발송 결과. 백엔드 `dto.SendResultResponse` 와 1:1 매핑.
4223
+ */
4224
+ interface SendPushResult {
4225
+ message_id: string;
4226
+ total_target: number;
4227
+ sent_count: number;
4228
+ failed_count: number;
4229
+ status: string;
4230
+ error_message?: string;
4231
+ }
4098
4232
  /**
4099
4233
  * 푸시 알림 API
4100
4234
  *
@@ -4233,6 +4367,29 @@ declare class PushAPI {
4233
4367
  * @param deviceToken Web Push endpoint URL (등록 시 사용한 endpoint)
4234
4368
  */
4235
4369
  unregisterWebPush(deviceToken: string): Promise<void>;
4370
+ /**
4371
+ * 회원 ID 목록으로 푸시 발송 — Functions / cb_sk_ 인증 환경 전용.
4372
+ *
4373
+ * 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT 또는 User Secret Key
4374
+ * (`cb_sk_`) 만 받는다. 브라우저 SDK 의 Public Key(`cb_pk_`) 인스턴스에서는 호출
4375
+ * 자체가 차단되며, 권한 누설을 막기 위해 명확한 에러를 던진다.
4376
+ *
4377
+ * @param appId 발송 대상 앱 ID (cb_sk_ 환경은 user 단위 권한이므로 명시 전달).
4378
+ * @param memberIds 받는 회원 ID 배열 (UUID).
4379
+ * @param payload 푸시 내용/옵션.
4380
+ *
4381
+ * @example
4382
+ * ```ts
4383
+ * // ConnectBase Function (Node.js, secrets.CB_SECRET_KEY 주입)
4384
+ * const result = await cb.push.sendToMembers(APP_ID, [memberId], {
4385
+ * title: '새 알림',
4386
+ * body: '확인해보세요',
4387
+ * data: { route: '/notifications' },
4388
+ * })
4389
+ * console.log(result.message_id, result.sent_count)
4390
+ * ```
4391
+ */
4392
+ sendToMembers(appId: string, memberIds: string[], payload: SendToMembersPayload): Promise<SendPushResult>;
4236
4393
  /**
4237
4394
  * 브라우저 고유 ID 생성 (localStorage에 저장)
4238
4395
  */
@@ -6714,10 +6871,10 @@ interface ConnectBaseConfig {
6714
6871
  onTokenExpired?: () => void;
6715
6872
  /**
6716
6873
  * 토큰 저장 방식.
6717
- * - 'localStorage': 브라우저 종료 후에도 유지 (기본값)
6718
- * - 'sessionStorage': 탭 종료 시 삭제
6719
- * - 'none': 메모리에만 저장 (페이지 새로고침 삭제)
6720
- * @default 'localStorage'
6874
+ * - 'none' (기본·권장): 메모리에만 저장. 새로고침 재로그인 필요. XSS 노출 최소화
6875
+ * - 'sessionStorage': 탭 종료 시 삭제 (XSS 시 현재 탭 세션 탈취 가능)
6876
+ * - 'localStorage': 브라우저 종료 후에도 유지 (XSS토큰 영구 탈취 가능 — 콘솔 경고 출력)
6877
+ * @default 'none'
6721
6878
  */
6722
6879
  persistence?: TokenPersistence;
6723
6880
  /**
package/dist/index.js CHANGED
@@ -4474,11 +4474,13 @@ var OAuthAPI = class {
4474
4474
  reject(new Error(event.data.error));
4475
4475
  return;
4476
4476
  }
4477
+ const rawEmail = event.data.email;
4477
4478
  const result = {
4478
4479
  member_id: event.data.member_id,
4479
4480
  access_token: event.data.access_token,
4480
4481
  refresh_token: event.data.refresh_token,
4481
- is_new_member: event.data.is_new_member === "true" || event.data.is_new_member === true
4482
+ is_new_member: event.data.is_new_member === "true" || event.data.is_new_member === true,
4483
+ ...typeof rawEmail === "string" && rawEmail.length > 0 ? { email: rawEmail } : {}
4482
4484
  };
4483
4485
  this.http.setTokens(result.access_token, result.refresh_token);
4484
4486
  resolve(result);
@@ -4546,11 +4548,13 @@ var OAuthAPI = class {
4546
4548
  if (!accessToken || !refreshToken || !memberId) {
4547
4549
  return null;
4548
4550
  }
4551
+ const emailParam = params.get("email");
4549
4552
  const result = {
4550
4553
  access_token: accessToken,
4551
4554
  refresh_token: refreshToken,
4552
4555
  member_id: memberId,
4553
4556
  is_new_member: params.get("is_new_member") === "true",
4557
+ ...emailParam ? { email: emailParam } : {},
4554
4558
  state: params.get("state") || void 0
4555
4559
  };
4556
4560
  if (window.opener) {
@@ -4588,6 +4592,7 @@ var OAuthAPI = class {
4588
4592
  refresh_token: resp.refresh_token,
4589
4593
  member_id: resp.member_id,
4590
4594
  is_new_member: resp.is_new_member,
4595
+ ...resp.email ? { email: resp.email } : {},
4591
4596
  state: params.get("state") || void 0
4592
4597
  };
4593
4598
  if (window.opener) {
@@ -5264,6 +5269,53 @@ var PushAPI = class {
5264
5269
  const prefix = this.getPublicPrefix();
5265
5270
  await this.http.delete(`${prefix}/push/devices/${encodeURIComponent(deviceToken)}`);
5266
5271
  }
5272
+ // ============ Server-side Send (Functions / cb_sk_ 전용) ============
5273
+ /**
5274
+ * 회원 ID 목록으로 푸시 발송 — Functions / cb_sk_ 인증 환경 전용.
5275
+ *
5276
+ * 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT 또는 User Secret Key
5277
+ * (`cb_sk_`) 만 받는다. 브라우저 SDK 의 Public Key(`cb_pk_`) 인스턴스에서는 호출
5278
+ * 자체가 차단되며, 권한 누설을 막기 위해 명확한 에러를 던진다.
5279
+ *
5280
+ * @param appId 발송 대상 앱 ID (cb_sk_ 환경은 user 단위 권한이므로 명시 전달).
5281
+ * @param memberIds 받는 회원 ID 배열 (UUID).
5282
+ * @param payload 푸시 내용/옵션.
5283
+ *
5284
+ * @example
5285
+ * ```ts
5286
+ * // ConnectBase Function (Node.js, secrets.CB_SECRET_KEY 주입)
5287
+ * const result = await cb.push.sendToMembers(APP_ID, [memberId], {
5288
+ * title: '새 알림',
5289
+ * body: '확인해보세요',
5290
+ * data: { route: '/notifications' },
5291
+ * })
5292
+ * console.log(result.message_id, result.sent_count)
5293
+ * ```
5294
+ */
5295
+ async sendToMembers(appId, memberIds, payload) {
5296
+ if (this.http.hasPublicKey()) {
5297
+ throw new Error(
5298
+ "cb.push.sendToMembers() \uB294 User Secret Key(cb_sk_) \uB610\uB294 \uCF58\uC194 JWT \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694."
5299
+ );
5300
+ }
5301
+ if (memberIds.length === 0) {
5302
+ throw new Error("cb.push.sendToMembers(): memberIds \uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4.");
5303
+ }
5304
+ const body = {
5305
+ target_type: "members",
5306
+ target_member_ids: memberIds,
5307
+ title: payload.title,
5308
+ body: payload.body,
5309
+ ...payload.imageUrl !== void 0 ? { image_url: payload.imageUrl } : {},
5310
+ ...payload.data !== void 0 ? { data: payload.data } : {},
5311
+ ...payload.platforms !== void 0 ? { platforms: payload.platforms } : {},
5312
+ ...payload.ttlSeconds !== void 0 ? { ttl: payload.ttlSeconds } : {},
5313
+ ...payload.priority !== void 0 ? { priority: payload.priority } : {},
5314
+ ...payload.clickAction !== void 0 ? { click_action: payload.clickAction } : {},
5315
+ ...payload.scheduledAt !== void 0 ? { scheduled_at: payload.scheduledAt } : {}
5316
+ };
5317
+ return this.http.post(`/v1/apps/${appId}/push/send`, body);
5318
+ }
5267
5319
  // ============ Helper Methods ============
5268
5320
  /**
5269
5321
  * 브라우저 고유 ID 생성 (localStorage에 저장)
@@ -8173,6 +8225,15 @@ var SessionManager = class {
8173
8225
  return generateId();
8174
8226
  }
8175
8227
  };
8228
+ function buildAnalyticsQuery(options) {
8229
+ if (!options) return "";
8230
+ const params = new URLSearchParams();
8231
+ if (options.start_date !== void 0) params.set("start_date", String(options.start_date));
8232
+ if (options.end_date !== void 0) params.set("end_date", String(options.end_date));
8233
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
8234
+ const qs = params.toString();
8235
+ return qs ? `?${qs}` : "";
8236
+ }
8176
8237
  function parseUTM() {
8177
8238
  if (typeof window === "undefined") return {};
8178
8239
  try {
@@ -8441,6 +8502,60 @@ var AnalyticsAPI = class {
8441
8502
  async flush() {
8442
8503
  await this.flushQueue();
8443
8504
  }
8505
+ /**
8506
+ * 인기 페이지 조회 (콘솔 JWT 또는 User Secret Key `cb_sk_` 인증 필요).
8507
+ *
8508
+ * 브라우저 환경에서 Public Key(`cb_pk_`) 만 가진 SDK 인스턴스로는 호출이 차단된다.
8509
+ * Functions / 어드민 앱 등 cb_sk_ 환경에서 사용하라.
8510
+ *
8511
+ * @param storageWebId 조회할 웹스토리지 ID. 미지정 시 `init()` 에 전달된 ID 사용.
8512
+ * @param options 기간/limit 옵션
8513
+ * @example
8514
+ * ```ts
8515
+ * const { pages } = await cb.analytics.getPopularPages('019d8...', { limit: 20 })
8516
+ * ```
8517
+ */
8518
+ async getPopularPages(storageWebId, options) {
8519
+ const id = this.requireServerSideStorageId(storageWebId, "getPopularPages");
8520
+ const qs = buildAnalyticsQuery(options);
8521
+ return this.http.get(`/v1/storages/web/${id}/popular-pages${qs}`);
8522
+ }
8523
+ /**
8524
+ * 페이지 전환 플로우(Sankey) 조회 — JWT/cb_sk_ 인증 필요.
8525
+ */
8526
+ async getNavigationFlow(storageWebId, options) {
8527
+ const id = this.requireServerSideStorageId(storageWebId, "getNavigationFlow");
8528
+ const qs = buildAnalyticsQuery(options);
8529
+ return this.http.get(`/v1/storages/web/${id}/navigation/flow${qs}`);
8530
+ }
8531
+ /**
8532
+ * 방문자 목록 조회 — JWT/cb_sk_ 인증 필요.
8533
+ */
8534
+ async getVisitors(storageWebId, options) {
8535
+ const id = this.requireServerSideStorageId(storageWebId, "getVisitors");
8536
+ const params = new URLSearchParams();
8537
+ if (options?.limit !== void 0) params.set("limit", String(options.limit));
8538
+ if (options?.offset !== void 0) params.set("offset", String(options.offset));
8539
+ if (options?.sort_by) params.set("sort_by", options.sort_by);
8540
+ const qs = params.toString() ? `?${params.toString()}` : "";
8541
+ return this.http.get(`/v1/storages/web/${id}/visitors${qs}`);
8542
+ }
8543
+ /**
8544
+ * 조회 메서드 공통 가드 — Public Key 인증 SDK 인스턴스에서는 명확한 에러를 던진다.
8545
+ * 백엔드 라우트는 cb_pk_ 를 거부하므로 호출 자체를 막는 것이 디버깅에 유리.
8546
+ */
8547
+ requireServerSideStorageId(storageWebId, methodName) {
8548
+ if (this.http.hasPublicKey()) {
8549
+ throw new Error(
8550
+ `cb.analytics.${methodName}() \uB294 \uCF58\uC194 JWT \uB610\uB294 User Secret Key(cb_sk_) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800 SDK \uC758 Public Key(cb_pk_) \uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.`
8551
+ );
8552
+ }
8553
+ const id = storageWebId ?? this.storageWebId;
8554
+ if (!id) {
8555
+ throw new Error(`cb.analytics.${methodName}() \uD638\uCD9C \uC2DC storageWebId \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (init \uB610\uB294 \uC778\uC790\uB85C \uC804\uB2EC).`);
8556
+ }
8557
+ return id;
8558
+ }
8444
8559
  /**
8445
8560
  * 세션 매니저 접근 (고급 사용자용).
8446
8561
  *