@vircle/plugin-ecommerce-core 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -19,6 +19,7 @@ pnpm add @vircle/plugin-ecommerce-core
19
19
  - 이벤트 중복 방지 (deduplication)
20
20
  - 플랫폼 메타데이터 자동 첨부
21
21
  - `globalProperties` — 모든 이벤트에 자동 병합되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등)
22
+ - UTM / 캠페인 파라미터 자동 수집 (`utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`, `gclid`, `fbclid`, `msclkid`, `ttclid`)
22
23
 
23
24
  ```typescript
24
25
  import { BaseEcommercePlugin, EcommerceEvent, type NetworkPattern, type PlatformAdapter } from '@vircle/plugin-ecommerce-core';
package/dist/index.d.mts CHANGED
@@ -217,14 +217,11 @@ declare abstract class BaseEcommercePlugin implements VirclePlugin {
217
217
  protected handleNetworkResponse(_url: string, _status: number, _body?: any): void;
218
218
  /**
219
219
  * Handle page navigation
220
+ * PAGE_VIEWED는 trackPageViews 설정으로 제어 (SDK page_view와 중복 방지)
221
+ * 서브클래스의 page-specific 이벤트(product_viewed 등)는 영향 없음
220
222
  */
221
223
  protected handlePageNavigation(url: string): void;
222
224
  private setupNavigationTracking;
223
- /**
224
- * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.
225
- * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.
226
- */
227
- private initCampaignParams;
228
225
  private getDefaultConfig;
229
226
  }
230
227
 
package/dist/index.d.ts CHANGED
@@ -217,14 +217,11 @@ declare abstract class BaseEcommercePlugin implements VirclePlugin {
217
217
  protected handleNetworkResponse(_url: string, _status: number, _body?: any): void;
218
218
  /**
219
219
  * Handle page navigation
220
+ * PAGE_VIEWED는 trackPageViews 설정으로 제어 (SDK page_view와 중복 방지)
221
+ * 서브클래스의 page-specific 이벤트(product_viewed 등)는 영향 없음
220
222
  */
221
223
  protected handlePageNavigation(url: string): void;
222
224
  private setupNavigationTracking;
223
- /**
224
- * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.
225
- * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.
226
- */
227
- private initCampaignParams;
228
225
  private getDefaultConfig;
229
226
  }
230
227
 
package/dist/index.js CHANGED
@@ -191,18 +191,6 @@ var PageType = /* @__PURE__ */ ((PageType2) => {
191
191
 
192
192
  // src/BaseEcommercePlugin.ts
193
193
  var DEFAULT_DEDUPLICATION_WINDOW = 2e3;
194
- var CAMPAIGN_PARAMS = [
195
- "utm_source",
196
- "utm_medium",
197
- "utm_campaign",
198
- "utm_term",
199
- "utm_content",
200
- "gclid",
201
- "fbclid",
202
- "msclkid",
203
- "ttclid"
204
- ];
205
- var CAMPAIGN_STORAGE_PREFIX = "__vircle_campaign_";
206
194
  var BaseEcommercePlugin = class {
207
195
  constructor() {
208
196
  this.pluginConfig = {};
@@ -238,10 +226,11 @@ var BaseEcommercePlugin = class {
238
226
  });
239
227
  this.interceptor.start();
240
228
  }
241
- if (this.pluginConfig.trackPageViews !== false) {
242
- this.setupNavigationTracking();
229
+ const sdkConfig = context.getConfig();
230
+ if (sdkConfig?.trackPageViews && this.pluginConfig.trackPageViews === void 0) {
231
+ this.pluginConfig.trackPageViews = false;
243
232
  }
244
- this.initCampaignParams();
233
+ this.setupNavigationTracking();
245
234
  this.onInitialized();
246
235
  this.context.logger.info("Plugin initialized");
247
236
  }
@@ -305,15 +294,19 @@ var BaseEcommercePlugin = class {
305
294
  }
306
295
  /**
307
296
  * Handle page navigation
297
+ * PAGE_VIEWED는 trackPageViews 설정으로 제어 (SDK page_view와 중복 방지)
298
+ * 서브클래스의 page-specific 이벤트(product_viewed 등)는 영향 없음
308
299
  */
309
300
  handlePageNavigation(url) {
310
301
  if (!this.adapter || !this.context) return;
311
- const pageType = this.adapter.detectPageType();
312
- this.trackEvent("page_viewed" /* PAGE_VIEWED */, {
313
- url,
314
- page_type: pageType,
315
- title: typeof document !== "undefined" ? document.title : void 0
316
- });
302
+ if (this.pluginConfig.trackPageViews !== false) {
303
+ const pageType = this.adapter.detectPageType();
304
+ this.trackEvent("page_viewed" /* PAGE_VIEWED */, {
305
+ url,
306
+ page_type: pageType,
307
+ title: typeof document !== "undefined" ? document.title : void 0
308
+ });
309
+ }
317
310
  }
318
311
  setupNavigationTracking() {
319
312
  if (typeof window === "undefined") return;
@@ -346,47 +339,6 @@ var BaseEcommercePlugin = class {
346
339
  };
347
340
  handleNavigation();
348
341
  }
349
- /**
350
- * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.
351
- * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.
352
- */
353
- initCampaignParams() {
354
- if (typeof window === "undefined") return;
355
- const params = new URLSearchParams(window.location.search);
356
- let hasUrlCampaign = false;
357
- for (const param of CAMPAIGN_PARAMS) {
358
- if (params.get(param)) {
359
- hasUrlCampaign = true;
360
- break;
361
- }
362
- }
363
- if (hasUrlCampaign) {
364
- for (const param of CAMPAIGN_PARAMS) {
365
- try {
366
- sessionStorage.removeItem(CAMPAIGN_STORAGE_PREFIX + param);
367
- } catch {
368
- }
369
- }
370
- }
371
- for (const param of CAMPAIGN_PARAMS) {
372
- const value = params.get(param);
373
- if (value) {
374
- try {
375
- sessionStorage.setItem(CAMPAIGN_STORAGE_PREFIX + param, value);
376
- } catch {
377
- }
378
- }
379
- }
380
- for (const param of CAMPAIGN_PARAMS) {
381
- try {
382
- const stored = sessionStorage.getItem(CAMPAIGN_STORAGE_PREFIX + param);
383
- if (stored) {
384
- this.globalProperties[param] = stored;
385
- }
386
- } catch {
387
- }
388
- }
389
- }
390
342
  getDefaultConfig() {
391
343
  return {
392
344
  trackPageViews: true,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/NetworkInterceptor.ts","../src/types.ts","../src/BaseEcommercePlugin.ts"],"names":["EcommerceEvent","PageType"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Network interceptor for capturing fetch/XHR requests\n * Patches window.fetch and XMLHttpRequest to intercept matching API calls\n */\n\nimport type { NetworkPattern, NetworkInterceptCallback } from './types';\n\nexport class NetworkInterceptor {\n private patterns: NetworkPattern[] = [];\n private callback: NetworkInterceptCallback;\n private originalFetch?: typeof window.fetch;\n private originalXHROpen?: typeof XMLHttpRequest.prototype.open;\n private originalXHRSend?: typeof XMLHttpRequest.prototype.send;\n private isActive = false;\n\n constructor(patterns: NetworkPattern[], callback: NetworkInterceptCallback) {\n this.patterns = patterns;\n this.callback = callback;\n }\n\n /**\n * Start intercepting network requests\n */\n start(): void {\n if (this.isActive) return;\n if (typeof window === 'undefined') return;\n\n this.patchFetch();\n this.patchXHR();\n this.isActive = true;\n }\n\n /**\n * Stop intercepting and restore original functions\n */\n stop(): void {\n if (!this.isActive) return;\n\n this.restoreFetch();\n this.restoreXHR();\n this.isActive = false;\n }\n\n /**\n * Check if a URL matches any registered pattern\n */\n matchUrl(url: string, method?: string): NetworkPattern | undefined {\n return this.patterns.find((p) => {\n const urlMatch =\n p.pattern instanceof RegExp ? p.pattern.test(url) : url.includes(p.pattern);\n const methodMatch = !p.method || p.method === method?.toUpperCase();\n return urlMatch && methodMatch;\n });\n }\n\n private patchFetch(): void {\n if (typeof window.fetch !== 'function') return;\n\n this.originalFetch = window.fetch;\n const self = this;\n\n window.fetch = function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = init?.method || 'GET';\n const pattern = self.matchUrl(url, method);\n\n if (pattern && self.callback.onRequest) {\n try {\n let body: any;\n try {\n if (init?.body) {\n body =\n typeof init.body === 'string'\n ? init.body\n : init.body instanceof FormData\n ? Object.fromEntries((init.body as any).entries())\n : init.body;\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, body);\n } catch {\n // Never let interceptor errors break application fetch calls\n }\n }\n\n return self.originalFetch!.call(window, input, init).then((response) => {\n if (pattern && self.callback.onResponse) {\n try {\n // Content-Type 체크: JSON 응답만 파싱 (대용량 바이너리 응답 OOM 방지)\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n } else {\n const cloned = response.clone();\n // text() 먼저 호출 후 JSON.parse 시도 (clone body는 1회만 소비 가능)\n cloned\n .text()\n .then((text) => {\n try {\n const json = JSON.parse(text);\n self.callback.onResponse!(url, response.status, json);\n } catch {\n try {\n self.callback.onResponse!(url, response.status, text);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n })\n .catch(() => {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n });\n }\n } catch {\n // Ignore clone/response handling errors to avoid breaking the app\n }\n }\n return response;\n });\n };\n }\n\n private patchXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n this.originalXHROpen = XMLHttpRequest.prototype.open;\n this.originalXHRSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n ...args: any[]\n ) {\n (this as any).__vircle_url = typeof url === 'string' ? url : url.toString();\n (this as any).__vircle_method = method;\n return self.originalXHROpen!.apply(this, [method, url, ...args] as any);\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const url = (this as any).__vircle_url as string;\n const method = (this as any).__vircle_method as string;\n const pattern = url ? self.matchUrl(url, method) : undefined;\n\n if (pattern && self.callback.onRequest) {\n try {\n let parsedBody: any;\n try {\n if (typeof body === 'string') {\n parsedBody = body;\n } else if (body instanceof FormData) {\n parsedBody = Object.fromEntries((body as any).entries());\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, parsedBody);\n } catch {\n // Never let interceptor errors break application XHR calls\n }\n }\n\n if (pattern && self.callback.onResponse) {\n this.addEventListener('load', function () {\n try {\n // Content-Type 체크: JSON/text 응답만 파싱\n const contentType = this.getResponseHeader('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n self.callback.onResponse!(url, this.status, undefined);\n return;\n }\n const responseBody =\n this.responseType === '' || this.responseType === 'text'\n ? JSON.parse(this.responseText)\n : this.response;\n self.callback.onResponse!(url, this.status, responseBody);\n } catch {\n try {\n self.callback.onResponse!(url, this.status, this.responseText);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n });\n }\n\n return self.originalXHRSend!.call(this, body);\n };\n }\n\n private restoreFetch(): void {\n if (this.originalFetch && typeof window !== 'undefined') {\n window.fetch = this.originalFetch;\n this.originalFetch = undefined;\n }\n }\n\n private restoreXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n if (this.originalXHROpen) {\n XMLHttpRequest.prototype.open = this.originalXHROpen;\n this.originalXHROpen = undefined;\n }\n if (this.originalXHRSend) {\n XMLHttpRequest.prototype.send = this.originalXHRSend;\n this.originalXHRSend = undefined;\n }\n }\n}\n","/**\n * E-commerce plugin core types\n */\n\nimport type { PluginContext } from '@vircle/sdk-core-ts';\n\n/**\n * Standard e-commerce event names\n */\nexport enum EcommerceEvent {\n PAGE_VIEWED = 'page_viewed',\n PRODUCT_VIEWED = 'product_viewed',\n PRODUCT_ADDED_TO_CART = 'product_added_to_cart',\n PRODUCT_REMOVED_FROM_CART = 'product_removed_from_cart',\n CART_VIEWED = 'cart_viewed',\n CART_UPDATED = 'cart_updated',\n PRODUCT_ADDED_TO_WISHLIST = 'product_added_to_wishlist',\n CHECKOUT_STARTED = 'checkout_started',\n EXTERNAL_PAYMENT_INITIATED = 'external_payment_initiated',\n ORDER_COMPLETED = 'order_completed',\n}\n\n/**\n * Product sub-object schema\n */\nexport interface Product {\n product_id: string;\n name: string;\n price: number;\n sku?: string;\n category?: string;\n brand?: string;\n variant?: string;\n quantity?: number;\n currency?: string;\n image_url?: string;\n url?: string;\n position?: number;\n}\n\n/**\n * Cart item sub-object schema\n */\nexport interface CartItem extends Product {\n quantity: number;\n item_total?: number;\n}\n\n/**\n * Order item sub-object schema\n */\nexport interface OrderItem extends Product {\n quantity: number;\n item_total: number;\n discount?: number;\n}\n\n/**\n * E-commerce plugin configuration\n */\nexport interface EcommercePluginConfig {\n /** Enable automatic page view tracking */\n trackPageViews?: boolean;\n /** Enable network request interception */\n trackNetworkRequests?: boolean;\n /** Enable DOM-based tracking */\n trackDomInteractions?: boolean;\n /** Custom URL patterns to intercept */\n urlPatterns?: NetworkPattern[];\n /** Event deduplication window in ms */\n deduplicationWindow?: number;\n /** Debug mode */\n debug?: boolean;\n}\n\n/**\n * Network URL pattern for interception\n */\nexport interface NetworkPattern {\n /** URL pattern (string match or regex) */\n pattern: string | RegExp;\n /** HTTP method filter */\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n /** Event to emit when matched */\n event: EcommerceEvent;\n}\n\n/**\n * Platform adapter interface — contract for platform-specific implementations\n */\nexport interface PlatformAdapter {\n /** Platform name */\n readonly name: string;\n\n /** Initialize adapter */\n initialize(config: EcommercePluginConfig, context: PluginContext): void;\n\n /** Cleanup adapter resources */\n cleanup(): void;\n\n /** Detect current page type */\n detectPageType(): PageType;\n\n /** Extract product data from current page */\n extractProduct(): Product | null;\n\n /** Extract cart items from current page */\n extractCartItems(): CartItem[];\n\n /** Extract order data from current page */\n extractOrder(): OrderData | null;\n\n /** Get platform-specific metadata */\n getPlatformMeta(): Record<string, any>;\n}\n\n/**\n * Page types for e-commerce sites\n */\nexport enum PageType {\n HOME = 'home',\n PRODUCT_LIST = 'product_list',\n PRODUCT_DETAIL = 'product_detail',\n CART = 'cart',\n CHECKOUT = 'checkout',\n ORDER_COMPLETE = 'order_complete',\n SEARCH_RESULTS = 'search_results',\n MY_PAGE = 'my_page',\n OTHER = 'other',\n}\n\n/**\n * Order data structure\n */\nexport interface OrderData {\n order_id: string;\n total: number;\n subtotal?: number;\n tax?: number;\n shipping?: number;\n discount?: number;\n currency?: string;\n payment_method?: string;\n items: OrderItem[];\n}\n\n/**\n * Network intercept callback\n */\nexport interface NetworkInterceptCallback {\n onRequest?(url: string, method: string, body?: any): void;\n onResponse?(url: string, status: number, body?: any): void;\n}\n","/**\n * Base e-commerce plugin that platform-specific plugins extend\n */\n\nimport type { VirclePlugin, PluginContext, PluginHooks } from '@vircle/sdk-core-ts';\nimport { NetworkInterceptor } from './NetworkInterceptor';\nimport { EcommerceEvent } from './types';\nimport type {\n EcommercePluginConfig,\n PlatformAdapter,\n NetworkPattern,\n} from './types';\n\nconst DEFAULT_DEDUPLICATION_WINDOW = 2000; // 2 seconds\n\nconst CAMPAIGN_PARAMS = [\n 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'gclid', 'fbclid', 'msclkid', 'ttclid',\n];\nconst CAMPAIGN_STORAGE_PREFIX = '__vircle_campaign_';\n\nexport abstract class BaseEcommercePlugin implements VirclePlugin {\n abstract readonly name: string;\n abstract readonly version: string;\n abstract readonly description: string;\n\n protected context?: PluginContext;\n protected pluginConfig: EcommercePluginConfig = {};\n protected adapter?: PlatformAdapter;\n protected interceptor?: NetworkInterceptor;\n /** 모든 이벤트에 자동 첨부되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등) */\n protected globalProperties: Record<string, unknown> = {};\n private recentEvents = new Set<string>();\n private dedupeTimers = new Map<string, ReturnType<typeof setTimeout>>();\n private navigationCleanup?: () => void;\n\n hooks?: PluginHooks;\n\n /**\n * Get network patterns for this platform\n */\n protected abstract getNetworkPatterns(): NetworkPattern[];\n\n /**\n * Create the platform adapter\n */\n protected abstract createAdapter(): PlatformAdapter;\n\n /**\n * Called after base initialization, override for platform-specific setup\n */\n protected onInitialized(): void {\n // Override in subclass\n }\n\n /**\n * Called before cleanup, override for platform-specific teardown\n */\n protected onCleanup(): void {\n // Override in subclass\n }\n\n async initialize(config: EcommercePluginConfig, context: PluginContext): Promise<void> {\n this.pluginConfig = { ...this.getDefaultConfig(), ...config };\n this.context = context;\n\n // Create platform adapter\n this.adapter = this.createAdapter();\n this.adapter.initialize(this.pluginConfig, context);\n\n // Setup network interceptor\n if (this.pluginConfig.trackNetworkRequests !== false) {\n const patterns = this.getNetworkPatterns();\n this.interceptor = new NetworkInterceptor(patterns, {\n onRequest: (url, method, body) => {\n this.handleNetworkRequest(url, method, body);\n },\n onResponse: (url, status, body) => {\n this.handleNetworkResponse(url, status, body);\n },\n });\n this.interceptor.start();\n }\n\n // Setup navigation tracking\n if (this.pluginConfig.trackPageViews !== false) {\n this.setupNavigationTracking();\n }\n\n this.initCampaignParams();\n this.onInitialized();\n\n this.context.logger.info('Plugin initialized');\n }\n\n async cleanup(): Promise<void> {\n this.onCleanup();\n\n if (this.interceptor) {\n this.interceptor.stop();\n this.interceptor = undefined;\n }\n\n if (this.navigationCleanup) {\n this.navigationCleanup();\n this.navigationCleanup = undefined;\n }\n\n if (this.adapter) {\n this.adapter.cleanup();\n this.adapter = undefined;\n }\n\n this.recentEvents.clear();\n for (const timer of this.dedupeTimers.values()) {\n clearTimeout(timer);\n }\n this.dedupeTimers.clear();\n this.context?.logger.info('Plugin cleaned up');\n this.context = undefined;\n }\n\n /**\n * Track an e-commerce event with deduplication\n */\n protected trackEvent(event: EcommerceEvent, properties: Record<string, any>): void {\n if (!this.context) return;\n\n // Deduplication: skip if same event fired recently (key by event + primary identifier)\n const identifier = properties.product_id\n || properties.order_id\n || properties.url\n || (Array.isArray(properties.items) ? `items:${properties.items.length}` : '');\n const dedupeKey = `${event}:${identifier}`;\n const dedupeWindow = this.pluginConfig.deduplicationWindow || DEFAULT_DEDUPLICATION_WINDOW;\n\n if (this.recentEvents.has(dedupeKey)) {\n this.context.logger.debug(`Deduplicating event: ${event}`);\n return;\n }\n\n this.recentEvents.add(dedupeKey);\n // 자동 만료: deduplicationWindow 후 자동 삭제\n const timer = setTimeout(() => {\n this.recentEvents.delete(dedupeKey);\n this.dedupeTimers.delete(dedupeKey);\n }, dedupeWindow);\n this.dedupeTimers.set(dedupeKey, timer);\n\n // Add global properties and platform metadata\n const meta = this.adapter?.getPlatformMeta() || {};\n const enrichedProperties = {\n ...this.globalProperties,\n ...properties,\n platform: meta,\n };\n\n this.context.track(event, enrichedProperties);\n }\n\n /**\n * Handle intercepted network request — override in subclass for specific behavior\n */\n protected handleNetworkRequest(_url: string, _method: string, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle intercepted network response — override in subclass for specific behavior\n */\n protected handleNetworkResponse(_url: string, _status: number, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle page navigation\n */\n protected handlePageNavigation(url: string): void {\n if (!this.adapter || !this.context) return;\n\n const pageType = this.adapter.detectPageType();\n\n this.trackEvent(EcommerceEvent.PAGE_VIEWED, {\n url,\n page_type: pageType,\n title: typeof document !== 'undefined' ? document.title : undefined,\n });\n }\n\n private setupNavigationTracking(): void {\n if (typeof window === 'undefined') return;\n\n const handleNavigation = () => {\n this.handlePageNavigation(window.location.href);\n };\n\n // Listen for History API changes\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = function (...args) {\n const result = originalPushState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n history.replaceState = function (...args) {\n const result = originalReplaceState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n window.addEventListener('popstate', handleNavigation);\n\n this.navigationCleanup = () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', handleNavigation);\n };\n\n // Track initial page view\n handleNavigation();\n }\n\n /**\n * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.\n * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.\n */\n private initCampaignParams(): void {\n if (typeof window === 'undefined') return;\n\n const params = new URLSearchParams(window.location.search);\n\n // URL에 캠페인 파라미터가 하나라도 있으면 새 캠페인 진입 — 기존 값 클리어 후 새로 저장\n let hasUrlCampaign = false;\n for (const param of CAMPAIGN_PARAMS) {\n if (params.get(param)) {\n hasUrlCampaign = true;\n break;\n }\n }\n if (hasUrlCampaign) {\n for (const param of CAMPAIGN_PARAMS) {\n try { sessionStorage.removeItem(CAMPAIGN_STORAGE_PREFIX + param); } catch {}\n }\n }\n\n // URL에 캠페인 파라미터가 있으면 sessionStorage에 저장\n for (const param of CAMPAIGN_PARAMS) {\n const value = params.get(param);\n if (value) {\n try {\n sessionStorage.setItem(CAMPAIGN_STORAGE_PREFIX + param, value);\n } catch {}\n }\n }\n\n // sessionStorage에서 복원하여 globalProperties에 설정\n for (const param of CAMPAIGN_PARAMS) {\n try {\n const stored = sessionStorage.getItem(CAMPAIGN_STORAGE_PREFIX + param);\n if (stored) {\n this.globalProperties[param] = stored;\n }\n } catch {}\n }\n }\n\n private getDefaultConfig(): EcommercePluginConfig {\n return {\n trackPageViews: true,\n trackNetworkRequests: true,\n trackDomInteractions: true,\n deduplicationWindow: DEFAULT_DEDUPLICATION_WINDOW,\n debug: false,\n };\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/NetworkInterceptor.ts","../src/types.ts","../src/BaseEcommercePlugin.ts"],"names":["EcommerceEvent","PageType"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Network interceptor for capturing fetch/XHR requests\n * Patches window.fetch and XMLHttpRequest to intercept matching API calls\n */\n\nimport type { NetworkPattern, NetworkInterceptCallback } from './types';\n\nexport class NetworkInterceptor {\n private patterns: NetworkPattern[] = [];\n private callback: NetworkInterceptCallback;\n private originalFetch?: typeof window.fetch;\n private originalXHROpen?: typeof XMLHttpRequest.prototype.open;\n private originalXHRSend?: typeof XMLHttpRequest.prototype.send;\n private isActive = false;\n\n constructor(patterns: NetworkPattern[], callback: NetworkInterceptCallback) {\n this.patterns = patterns;\n this.callback = callback;\n }\n\n /**\n * Start intercepting network requests\n */\n start(): void {\n if (this.isActive) return;\n if (typeof window === 'undefined') return;\n\n this.patchFetch();\n this.patchXHR();\n this.isActive = true;\n }\n\n /**\n * Stop intercepting and restore original functions\n */\n stop(): void {\n if (!this.isActive) return;\n\n this.restoreFetch();\n this.restoreXHR();\n this.isActive = false;\n }\n\n /**\n * Check if a URL matches any registered pattern\n */\n matchUrl(url: string, method?: string): NetworkPattern | undefined {\n return this.patterns.find((p) => {\n const urlMatch =\n p.pattern instanceof RegExp ? p.pattern.test(url) : url.includes(p.pattern);\n const methodMatch = !p.method || p.method === method?.toUpperCase();\n return urlMatch && methodMatch;\n });\n }\n\n private patchFetch(): void {\n if (typeof window.fetch !== 'function') return;\n\n this.originalFetch = window.fetch;\n const self = this;\n\n window.fetch = function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = init?.method || 'GET';\n const pattern = self.matchUrl(url, method);\n\n if (pattern && self.callback.onRequest) {\n try {\n let body: any;\n try {\n if (init?.body) {\n body =\n typeof init.body === 'string'\n ? init.body\n : init.body instanceof FormData\n ? Object.fromEntries((init.body as any).entries())\n : init.body;\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, body);\n } catch {\n // Never let interceptor errors break application fetch calls\n }\n }\n\n return self.originalFetch!.call(window, input, init).then((response) => {\n if (pattern && self.callback.onResponse) {\n try {\n // Content-Type 체크: JSON 응답만 파싱 (대용량 바이너리 응답 OOM 방지)\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n } else {\n const cloned = response.clone();\n // text() 먼저 호출 후 JSON.parse 시도 (clone body는 1회만 소비 가능)\n cloned\n .text()\n .then((text) => {\n try {\n const json = JSON.parse(text);\n self.callback.onResponse!(url, response.status, json);\n } catch {\n try {\n self.callback.onResponse!(url, response.status, text);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n })\n .catch(() => {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n });\n }\n } catch {\n // Ignore clone/response handling errors to avoid breaking the app\n }\n }\n return response;\n });\n };\n }\n\n private patchXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n this.originalXHROpen = XMLHttpRequest.prototype.open;\n this.originalXHRSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n ...args: any[]\n ) {\n (this as any).__vircle_url = typeof url === 'string' ? url : url.toString();\n (this as any).__vircle_method = method;\n return self.originalXHROpen!.apply(this, [method, url, ...args] as any);\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const url = (this as any).__vircle_url as string;\n const method = (this as any).__vircle_method as string;\n const pattern = url ? self.matchUrl(url, method) : undefined;\n\n if (pattern && self.callback.onRequest) {\n try {\n let parsedBody: any;\n try {\n if (typeof body === 'string') {\n parsedBody = body;\n } else if (body instanceof FormData) {\n parsedBody = Object.fromEntries((body as any).entries());\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, parsedBody);\n } catch {\n // Never let interceptor errors break application XHR calls\n }\n }\n\n if (pattern && self.callback.onResponse) {\n this.addEventListener('load', function () {\n try {\n // Content-Type 체크: JSON/text 응답만 파싱\n const contentType = this.getResponseHeader('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n self.callback.onResponse!(url, this.status, undefined);\n return;\n }\n const responseBody =\n this.responseType === '' || this.responseType === 'text'\n ? JSON.parse(this.responseText)\n : this.response;\n self.callback.onResponse!(url, this.status, responseBody);\n } catch {\n try {\n self.callback.onResponse!(url, this.status, this.responseText);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n });\n }\n\n return self.originalXHRSend!.call(this, body);\n };\n }\n\n private restoreFetch(): void {\n if (this.originalFetch && typeof window !== 'undefined') {\n window.fetch = this.originalFetch;\n this.originalFetch = undefined;\n }\n }\n\n private restoreXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n if (this.originalXHROpen) {\n XMLHttpRequest.prototype.open = this.originalXHROpen;\n this.originalXHROpen = undefined;\n }\n if (this.originalXHRSend) {\n XMLHttpRequest.prototype.send = this.originalXHRSend;\n this.originalXHRSend = undefined;\n }\n }\n}\n","/**\n * E-commerce plugin core types\n */\n\nimport type { PluginContext } from '@vircle/sdk-core-ts';\n\n/**\n * Standard e-commerce event names\n */\nexport enum EcommerceEvent {\n PAGE_VIEWED = 'page_viewed',\n PRODUCT_VIEWED = 'product_viewed',\n PRODUCT_ADDED_TO_CART = 'product_added_to_cart',\n PRODUCT_REMOVED_FROM_CART = 'product_removed_from_cart',\n CART_VIEWED = 'cart_viewed',\n CART_UPDATED = 'cart_updated',\n PRODUCT_ADDED_TO_WISHLIST = 'product_added_to_wishlist',\n CHECKOUT_STARTED = 'checkout_started',\n EXTERNAL_PAYMENT_INITIATED = 'external_payment_initiated',\n ORDER_COMPLETED = 'order_completed',\n}\n\n/**\n * Product sub-object schema\n */\nexport interface Product {\n product_id: string;\n name: string;\n price: number;\n sku?: string;\n category?: string;\n brand?: string;\n variant?: string;\n quantity?: number;\n currency?: string;\n image_url?: string;\n url?: string;\n position?: number;\n}\n\n/**\n * Cart item sub-object schema\n */\nexport interface CartItem extends Product {\n quantity: number;\n item_total?: number;\n}\n\n/**\n * Order item sub-object schema\n */\nexport interface OrderItem extends Product {\n quantity: number;\n item_total: number;\n discount?: number;\n}\n\n/**\n * E-commerce plugin configuration\n */\nexport interface EcommercePluginConfig {\n /** Enable automatic page view tracking */\n trackPageViews?: boolean;\n /** Enable network request interception */\n trackNetworkRequests?: boolean;\n /** Enable DOM-based tracking */\n trackDomInteractions?: boolean;\n /** Custom URL patterns to intercept */\n urlPatterns?: NetworkPattern[];\n /** Event deduplication window in ms */\n deduplicationWindow?: number;\n /** Debug mode */\n debug?: boolean;\n}\n\n/**\n * Network URL pattern for interception\n */\nexport interface NetworkPattern {\n /** URL pattern (string match or regex) */\n pattern: string | RegExp;\n /** HTTP method filter */\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n /** Event to emit when matched */\n event: EcommerceEvent;\n}\n\n/**\n * Platform adapter interface — contract for platform-specific implementations\n */\nexport interface PlatformAdapter {\n /** Platform name */\n readonly name: string;\n\n /** Initialize adapter */\n initialize(config: EcommercePluginConfig, context: PluginContext): void;\n\n /** Cleanup adapter resources */\n cleanup(): void;\n\n /** Detect current page type */\n detectPageType(): PageType;\n\n /** Extract product data from current page */\n extractProduct(): Product | null;\n\n /** Extract cart items from current page */\n extractCartItems(): CartItem[];\n\n /** Extract order data from current page */\n extractOrder(): OrderData | null;\n\n /** Get platform-specific metadata */\n getPlatformMeta(): Record<string, any>;\n}\n\n/**\n * Page types for e-commerce sites\n */\nexport enum PageType {\n HOME = 'home',\n PRODUCT_LIST = 'product_list',\n PRODUCT_DETAIL = 'product_detail',\n CART = 'cart',\n CHECKOUT = 'checkout',\n ORDER_COMPLETE = 'order_complete',\n SEARCH_RESULTS = 'search_results',\n MY_PAGE = 'my_page',\n OTHER = 'other',\n}\n\n/**\n * Order data structure\n */\nexport interface OrderData {\n order_id: string;\n total: number;\n subtotal?: number;\n tax?: number;\n shipping?: number;\n discount?: number;\n currency?: string;\n payment_method?: string;\n items: OrderItem[];\n}\n\n/**\n * Network intercept callback\n */\nexport interface NetworkInterceptCallback {\n onRequest?(url: string, method: string, body?: any): void;\n onResponse?(url: string, status: number, body?: any): void;\n}\n","/**\n * Base e-commerce plugin that platform-specific plugins extend\n */\n\nimport type { VirclePlugin, PluginContext, PluginHooks } from '@vircle/sdk-core-ts';\nimport { NetworkInterceptor } from './NetworkInterceptor';\nimport { EcommerceEvent } from './types';\nimport type {\n EcommercePluginConfig,\n PlatformAdapter,\n NetworkPattern,\n} from './types';\n\nconst DEFAULT_DEDUPLICATION_WINDOW = 2000; // 2 seconds\n\nexport abstract class BaseEcommercePlugin implements VirclePlugin {\n abstract readonly name: string;\n abstract readonly version: string;\n abstract readonly description: string;\n\n protected context?: PluginContext;\n protected pluginConfig: EcommercePluginConfig = {};\n protected adapter?: PlatformAdapter;\n protected interceptor?: NetworkInterceptor;\n /** 모든 이벤트에 자동 첨부되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등) */\n protected globalProperties: Record<string, unknown> = {};\n private recentEvents = new Set<string>();\n private dedupeTimers = new Map<string, ReturnType<typeof setTimeout>>();\n private navigationCleanup?: () => void;\n\n hooks?: PluginHooks;\n\n /**\n * Get network patterns for this platform\n */\n protected abstract getNetworkPatterns(): NetworkPattern[];\n\n /**\n * Create the platform adapter\n */\n protected abstract createAdapter(): PlatformAdapter;\n\n /**\n * Called after base initialization, override for platform-specific setup\n */\n protected onInitialized(): void {\n // Override in subclass\n }\n\n /**\n * Called before cleanup, override for platform-specific teardown\n */\n protected onCleanup(): void {\n // Override in subclass\n }\n\n async initialize(config: EcommercePluginConfig, context: PluginContext): Promise<void> {\n this.pluginConfig = { ...this.getDefaultConfig(), ...config };\n this.context = context;\n\n // Create platform adapter\n this.adapter = this.createAdapter();\n this.adapter.initialize(this.pluginConfig, context);\n\n // Setup network interceptor\n if (this.pluginConfig.trackNetworkRequests !== false) {\n const patterns = this.getNetworkPatterns();\n this.interceptor = new NetworkInterceptor(patterns, {\n onRequest: (url, method, body) => {\n this.handleNetworkRequest(url, method, body);\n },\n onResponse: (url, status, body) => {\n this.handleNetworkResponse(url, status, body);\n },\n });\n this.interceptor.start();\n }\n\n // SDK가 이미 page_view를 추적 중이면 플러그인의 PAGE_VIEWED 자동 비활성화\n const sdkConfig = context.getConfig();\n if ((sdkConfig as any)?.trackPageViews && this.pluginConfig.trackPageViews === undefined) {\n this.pluginConfig.trackPageViews = false;\n }\n\n // 네비게이션 추적은 항상 활성화 (서브클래스의 page-specific 이벤트에 필요)\n this.setupNavigationTracking();\n\n this.onInitialized();\n\n this.context.logger.info('Plugin initialized');\n }\n\n async cleanup(): Promise<void> {\n this.onCleanup();\n\n if (this.interceptor) {\n this.interceptor.stop();\n this.interceptor = undefined;\n }\n\n if (this.navigationCleanup) {\n this.navigationCleanup();\n this.navigationCleanup = undefined;\n }\n\n if (this.adapter) {\n this.adapter.cleanup();\n this.adapter = undefined;\n }\n\n this.recentEvents.clear();\n for (const timer of this.dedupeTimers.values()) {\n clearTimeout(timer);\n }\n this.dedupeTimers.clear();\n this.context?.logger.info('Plugin cleaned up');\n this.context = undefined;\n }\n\n /**\n * Track an e-commerce event with deduplication\n */\n protected trackEvent(event: EcommerceEvent, properties: Record<string, any>): void {\n if (!this.context) return;\n\n // Deduplication: skip if same event fired recently (key by event + primary identifier)\n const identifier = properties.product_id\n || properties.order_id\n || properties.url\n || (Array.isArray(properties.items) ? `items:${properties.items.length}` : '');\n const dedupeKey = `${event}:${identifier}`;\n const dedupeWindow = this.pluginConfig.deduplicationWindow || DEFAULT_DEDUPLICATION_WINDOW;\n\n if (this.recentEvents.has(dedupeKey)) {\n this.context.logger.debug(`Deduplicating event: ${event}`);\n return;\n }\n\n this.recentEvents.add(dedupeKey);\n // 자동 만료: deduplicationWindow 후 자동 삭제\n const timer = setTimeout(() => {\n this.recentEvents.delete(dedupeKey);\n this.dedupeTimers.delete(dedupeKey);\n }, dedupeWindow);\n this.dedupeTimers.set(dedupeKey, timer);\n\n // Add global properties and platform metadata\n const meta = this.adapter?.getPlatformMeta() || {};\n const enrichedProperties = {\n ...this.globalProperties,\n ...properties,\n platform: meta,\n };\n\n this.context.track(event, enrichedProperties);\n }\n\n /**\n * Handle intercepted network request — override in subclass for specific behavior\n */\n protected handleNetworkRequest(_url: string, _method: string, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle intercepted network response — override in subclass for specific behavior\n */\n protected handleNetworkResponse(_url: string, _status: number, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle page navigation\n * PAGE_VIEWED는 trackPageViews 설정으로 제어 (SDK page_view와 중복 방지)\n * 서브클래스의 page-specific 이벤트(product_viewed 등)는 영향 없음\n */\n protected handlePageNavigation(url: string): void {\n if (!this.adapter || !this.context) return;\n\n if (this.pluginConfig.trackPageViews !== false) {\n const pageType = this.adapter.detectPageType();\n\n this.trackEvent(EcommerceEvent.PAGE_VIEWED, {\n url,\n page_type: pageType,\n title: typeof document !== 'undefined' ? document.title : undefined,\n });\n }\n }\n\n private setupNavigationTracking(): void {\n if (typeof window === 'undefined') return;\n\n const handleNavigation = () => {\n this.handlePageNavigation(window.location.href);\n };\n\n // History API 래핑 (SDK-Web과 이중 래핑되어도 안전 — 체인으로 양쪽 핸들러 모두 동작)\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = function (...args) {\n const result = originalPushState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n history.replaceState = function (...args) {\n const result = originalReplaceState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n window.addEventListener('popstate', handleNavigation);\n\n this.navigationCleanup = () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', handleNavigation);\n };\n\n // 초기 페이지 처리\n handleNavigation();\n }\n\n private getDefaultConfig(): EcommercePluginConfig {\n return {\n trackPageViews: true,\n trackNetworkRequests: true,\n trackDomInteractions: true,\n deduplicationWindow: DEFAULT_DEDUPLICATION_WINDOW,\n debug: false,\n };\n }\n}\n"]}
package/dist/index.mjs CHANGED
@@ -189,18 +189,6 @@ var PageType = /* @__PURE__ */ ((PageType2) => {
189
189
 
190
190
  // src/BaseEcommercePlugin.ts
191
191
  var DEFAULT_DEDUPLICATION_WINDOW = 2e3;
192
- var CAMPAIGN_PARAMS = [
193
- "utm_source",
194
- "utm_medium",
195
- "utm_campaign",
196
- "utm_term",
197
- "utm_content",
198
- "gclid",
199
- "fbclid",
200
- "msclkid",
201
- "ttclid"
202
- ];
203
- var CAMPAIGN_STORAGE_PREFIX = "__vircle_campaign_";
204
192
  var BaseEcommercePlugin = class {
205
193
  constructor() {
206
194
  this.pluginConfig = {};
@@ -236,10 +224,11 @@ var BaseEcommercePlugin = class {
236
224
  });
237
225
  this.interceptor.start();
238
226
  }
239
- if (this.pluginConfig.trackPageViews !== false) {
240
- this.setupNavigationTracking();
227
+ const sdkConfig = context.getConfig();
228
+ if (sdkConfig?.trackPageViews && this.pluginConfig.trackPageViews === void 0) {
229
+ this.pluginConfig.trackPageViews = false;
241
230
  }
242
- this.initCampaignParams();
231
+ this.setupNavigationTracking();
243
232
  this.onInitialized();
244
233
  this.context.logger.info("Plugin initialized");
245
234
  }
@@ -303,15 +292,19 @@ var BaseEcommercePlugin = class {
303
292
  }
304
293
  /**
305
294
  * Handle page navigation
295
+ * PAGE_VIEWED는 trackPageViews 설정으로 제어 (SDK page_view와 중복 방지)
296
+ * 서브클래스의 page-specific 이벤트(product_viewed 등)는 영향 없음
306
297
  */
307
298
  handlePageNavigation(url) {
308
299
  if (!this.adapter || !this.context) return;
309
- const pageType = this.adapter.detectPageType();
310
- this.trackEvent("page_viewed" /* PAGE_VIEWED */, {
311
- url,
312
- page_type: pageType,
313
- title: typeof document !== "undefined" ? document.title : void 0
314
- });
300
+ if (this.pluginConfig.trackPageViews !== false) {
301
+ const pageType = this.adapter.detectPageType();
302
+ this.trackEvent("page_viewed" /* PAGE_VIEWED */, {
303
+ url,
304
+ page_type: pageType,
305
+ title: typeof document !== "undefined" ? document.title : void 0
306
+ });
307
+ }
315
308
  }
316
309
  setupNavigationTracking() {
317
310
  if (typeof window === "undefined") return;
@@ -344,47 +337,6 @@ var BaseEcommercePlugin = class {
344
337
  };
345
338
  handleNavigation();
346
339
  }
347
- /**
348
- * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.
349
- * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.
350
- */
351
- initCampaignParams() {
352
- if (typeof window === "undefined") return;
353
- const params = new URLSearchParams(window.location.search);
354
- let hasUrlCampaign = false;
355
- for (const param of CAMPAIGN_PARAMS) {
356
- if (params.get(param)) {
357
- hasUrlCampaign = true;
358
- break;
359
- }
360
- }
361
- if (hasUrlCampaign) {
362
- for (const param of CAMPAIGN_PARAMS) {
363
- try {
364
- sessionStorage.removeItem(CAMPAIGN_STORAGE_PREFIX + param);
365
- } catch {
366
- }
367
- }
368
- }
369
- for (const param of CAMPAIGN_PARAMS) {
370
- const value = params.get(param);
371
- if (value) {
372
- try {
373
- sessionStorage.setItem(CAMPAIGN_STORAGE_PREFIX + param, value);
374
- } catch {
375
- }
376
- }
377
- }
378
- for (const param of CAMPAIGN_PARAMS) {
379
- try {
380
- const stored = sessionStorage.getItem(CAMPAIGN_STORAGE_PREFIX + param);
381
- if (stored) {
382
- this.globalProperties[param] = stored;
383
- }
384
- } catch {
385
- }
386
- }
387
- }
388
340
  getDefaultConfig() {
389
341
  return {
390
342
  trackPageViews: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/NetworkInterceptor.ts","../src/types.ts","../src/BaseEcommercePlugin.ts"],"names":["EcommerceEvent","PageType"],"mappings":";AAOO,IAAM,qBAAN,MAAyB;AAAA,EAQ5B,WAAA,CAAY,UAA4B,QAAA,EAAoC;AAP5E,IAAA,IAAA,CAAQ,WAA6B,EAAC;AAKtC,IAAA,IAAA,CAAQ,QAAA,GAAW,KAAA;AAGf,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAChB,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAc;AACV,IAAA,IAAI,KAAK,QAAA,EAAU;AACnB,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEnC,IAAA,IAAA,CAAK,UAAA,EAAW;AAChB,IAAA,IAAA,CAAK,QAAA,EAAS;AACd,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA,GAAa;AACT,IAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AAEpB,IAAA,IAAA,CAAK,YAAA,EAAa;AAClB,IAAA,IAAA,CAAK,UAAA,EAAW;AAChB,IAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,CAAS,KAAa,MAAA,EAA6C;AAC/D,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM;AAC7B,MAAA,MAAM,QAAA,GACF,CAAA,CAAE,OAAA,YAAmB,MAAA,GAAS,CAAA,CAAE,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA,CAAI,QAAA,CAAS,CAAA,CAAE,OAAO,CAAA;AAC9E,MAAA,MAAM,cAAc,CAAC,CAAA,CAAE,UAAU,CAAA,CAAE,MAAA,KAAW,QAAQ,WAAA,EAAY;AAClE,MAAA,OAAO,QAAA,IAAY,WAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACL;AAAA,EAEQ,UAAA,GAAmB;AACvB,IAAA,IAAI,OAAO,MAAA,CAAO,KAAA,KAAU,UAAA,EAAY;AAExC,IAAA,IAAA,CAAK,gBAAgB,MAAA,CAAO,KAAA;AAC5B,IAAA,MAAM,IAAA,GAAO,IAAA;AAEb,IAAA,MAAA,CAAO,KAAA,GAAQ,SAAU,KAAA,EAA0B,IAAA,EAAuC;AACtF,MAAA,MAAM,GAAA,GAAM,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,iBAAiB,GAAA,GAAM,KAAA,CAAM,QAAA,EAAS,GAAI,KAAA,CAAM,GAAA;AAChG,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,KAAA;AAC/B,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,GAAA,EAAK,MAAM,CAAA;AAEzC,MAAA,IAAI,OAAA,IAAW,IAAA,CAAK,QAAA,CAAS,SAAA,EAAW;AACpC,QAAA,IAAI;AACA,UAAA,IAAI,IAAA;AACJ,UAAA,IAAI;AACA,YAAA,IAAI,MAAM,IAAA,EAAM;AACZ,cAAA,IAAA,GACI,OAAO,IAAA,CAAK,IAAA,KAAS,QAAA,GACf,IAAA,CAAK,OACL,IAAA,CAAK,IAAA,YAAgB,QAAA,GACnB,MAAA,CAAO,YAAa,IAAA,CAAK,IAAA,CAAa,OAAA,EAAS,IAC/C,IAAA,CAAK,IAAA;AAAA,YACrB;AAAA,UACJ,CAAA,CAAA,MAAQ;AAAA,UAER;AACA,UAAA,IAAA,CAAK,QAAA,CAAS,SAAA,CAAU,GAAA,EAAK,MAAA,EAAQ,IAAI,CAAA;AAAA,QAC7C,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACJ;AAEA,MAAA,OAAO,IAAA,CAAK,cAAe,IAAA,CAAK,MAAA,EAAQ,OAAO,IAAI,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,KAAa;AACpE,QAAA,IAAI,OAAA,IAAW,IAAA,CAAK,QAAA,CAAS,UAAA,EAAY;AACrC,UAAA,IAAI;AAEA,YAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA;AAC5D,YAAA,IAAI,CAAC,YAAY,QAAA,CAAS,kBAAkB,KAAK,CAAC,WAAA,CAAY,QAAA,CAAS,OAAO,CAAA,EAAG;AAC7E,cAAA,IAAI;AACA,gBAAA,IAAA,CAAK,QAAA,CAAS,UAAA,CAAY,GAAA,EAAK,QAAA,CAAS,QAAQ,KAAA,CAAS,CAAA;AAAA,cAC7D,CAAA,CAAA,MAAQ;AAAA,cAER;AAAA,YACJ,CAAA,MAAO;AACH,cAAA,MAAM,MAAA,GAAS,SAAS,KAAA,EAAM;AAE9B,cAAA,MAAA,CACK,IAAA,EAAK,CACL,IAAA,CAAK,CAAC,IAAA,KAAS;AACZ,gBAAA,IAAI;AACA,kBAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC5B,kBAAA,IAAA,CAAK,QAAA,CAAS,UAAA,CAAY,GAAA,EAAK,QAAA,CAAS,QAAQ,IAAI,CAAA;AAAA,gBACxD,CAAA,CAAA,MAAQ;AACJ,kBAAA,IAAI;AACA,oBAAA,IAAA,CAAK,QAAA,CAAS,UAAA,CAAY,GAAA,EAAK,QAAA,CAAS,QAAQ,IAAI,CAAA;AAAA,kBACxD,CAAA,CAAA,MAAQ;AAAA,kBAER;AAAA,gBACJ;AAAA,cACJ,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACT,gBAAA,IAAI;AACA,kBAAA,IAAA,CAAK,QAAA,CAAS,UAAA,CAAY,GAAA,EAAK,QAAA,CAAS,QAAQ,KAAA,CAAS,CAAA;AAAA,gBAC7D,CAAA,CAAA,MAAQ;AAAA,gBAER;AAAA,cACJ,CAAC,CAAA;AAAA,YACT;AAAA,UACJ,CAAA,CAAA,MAAQ;AAAA,UAER;AAAA,QACJ;AACA,QAAA,OAAO,QAAA;AAAA,MACX,CAAC,CAAA;AAAA,IACL,CAAA;AAAA,EACJ;AAAA,EAEQ,QAAA,GAAiB;AACrB,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AAE3C,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAe,SAAA,CAAU,IAAA;AAChD,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAe,SAAA,CAAU,IAAA;AAChD,IAAA,MAAM,IAAA,GAAO,IAAA;AAEb,IAAA,cAAA,CAAe,SAAA,CAAU,IAAA,GAAO,SAC5B,MAAA,EACA,QACG,IAAA,EACL;AACE,MAAC,KAAa,YAAA,GAAe,OAAO,QAAQ,QAAA,GAAW,GAAA,GAAM,IAAI,QAAA,EAAS;AAC1E,MAAC,KAAa,eAAA,GAAkB,MAAA;AAChC,MAAA,OAAO,IAAA,CAAK,gBAAiB,KAAA,CAAM,IAAA,EAAM,CAAC,MAAA,EAAQ,GAAA,EAAK,GAAG,IAAI,CAAQ,CAAA;AAAA,IAC1E,CAAA;AAEA,IAAA,cAAA,CAAe,SAAA,CAAU,IAAA,GAAO,SAAU,IAAA,EAAiD;AACvF,MAAA,MAAM,MAAO,IAAA,CAAa,YAAA;AAC1B,MAAA,MAAM,SAAU,IAAA,CAAa,eAAA;AAC7B,MAAA,MAAM,UAAU,GAAA,GAAM,IAAA,CAAK,QAAA,CAAS,GAAA,EAAK,MAAM,CAAA,GAAI,MAAA;AAEnD,MAAA,IAAI,OAAA,IAAW,IAAA,CAAK,QAAA,CAAS,SAAA,EAAW;AACpC,QAAA,IAAI;AACA,UAAA,IAAI,UAAA;AACJ,UAAA,IAAI;AACA,YAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC1B,cAAA,UAAA,GAAa,IAAA;AAAA,YACjB,CAAA,MAAA,IAAW,gBAAgB,QAAA,EAAU;AACjC,cAAA,UAAA,GAAa,MAAA,CAAO,WAAA,CAAa,IAAA,CAAa,OAAA,EAAS,CAAA;AAAA,YAC3D;AAAA,UACJ,CAAA,CAAA,MAAQ;AAAA,UAER;AACA,UAAA,IAAA,CAAK,QAAA,CAAS,SAAA,CAAU,GAAA,EAAK,MAAA,EAAQ,UAAU,CAAA;AAAA,QACnD,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACJ;AAEA,MAAA,IAAI,OAAA,IAAW,IAAA,CAAK,QAAA,CAAS,UAAA,EAAY;AACrC,QAAA,IAAA,CAAK,gBAAA,CAAiB,QAAQ,WAAY;AACtC,UAAA,IAAI;AAEA,YAAA,MAAM,WAAA,GAAc,IAAA,CAAK,iBAAA,CAAkB,cAAc,CAAA,IAAK,EAAA;AAC9D,YAAA,IAAI,CAAC,YAAY,QAAA,CAAS,kBAAkB,KAAK,CAAC,WAAA,CAAY,QAAA,CAAS,OAAO,CAAA,EAAG;AAC7E,cAAA,IAAA,CAAK,QAAA,CAAS,UAAA,CAAY,GAAA,EAAK,IAAA,CAAK,QAAQ,KAAA,CAAS,CAAA;AACrD,cAAA;AAAA,YACJ;AACA,YAAA,MAAM,YAAA,GACF,IAAA,CAAK,YAAA,KAAiB,EAAA,IAAM,IAAA,CAAK,YAAA,KAAiB,MAAA,GAC5C,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,YAAY,CAAA,GAC5B,IAAA,CAAK,QAAA;AACf,YAAA,IAAA,CAAK,QAAA,CAAS,UAAA,CAAY,GAAA,EAAK,IAAA,CAAK,QAAQ,YAAY,CAAA;AAAA,UAC5D,CAAA,CAAA,MAAQ;AACJ,YAAA,IAAI;AACA,cAAA,IAAA,CAAK,SAAS,UAAA,CAAY,GAAA,EAAK,IAAA,CAAK,MAAA,EAAQ,KAAK,YAAY,CAAA;AAAA,YACjE,CAAA,CAAA,MAAQ;AAAA,YAER;AAAA,UACJ;AAAA,QACJ,CAAC,CAAA;AAAA,MACL;AAEA,MAAA,OAAO,IAAA,CAAK,eAAA,CAAiB,IAAA,CAAK,IAAA,EAAM,IAAI,CAAA;AAAA,IAChD,CAAA;AAAA,EACJ;AAAA,EAEQ,YAAA,GAAqB;AACzB,IAAA,IAAI,IAAA,CAAK,aAAA,IAAiB,OAAO,MAAA,KAAW,WAAA,EAAa;AACrD,MAAA,MAAA,CAAO,QAAQ,IAAA,CAAK,aAAA;AACpB,MAAA,IAAA,CAAK,aAAA,GAAgB,MAAA;AAAA,IACzB;AAAA,EACJ;AAAA,EAEQ,UAAA,GAAmB;AACvB,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AAE3C,IAAA,IAAI,KAAK,eAAA,EAAiB;AACtB,MAAA,cAAA,CAAe,SAAA,CAAU,OAAO,IAAA,CAAK,eAAA;AACrC,MAAA,IAAA,CAAK,eAAA,GAAkB,MAAA;AAAA,IAC3B;AACA,IAAA,IAAI,KAAK,eAAA,EAAiB;AACtB,MAAA,cAAA,CAAe,SAAA,CAAU,OAAO,IAAA,CAAK,eAAA;AACrC,MAAA,IAAA,CAAK,eAAA,GAAkB,MAAA;AAAA,IAC3B;AAAA,EACJ;AACJ;;;AClNO,IAAK,cAAA,qBAAAA,eAAAA,KAAL;AACH,EAAAA,gBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,gBAAA,gBAAA,CAAA,GAAiB,gBAAA;AACjB,EAAAA,gBAAA,uBAAA,CAAA,GAAwB,uBAAA;AACxB,EAAAA,gBAAA,2BAAA,CAAA,GAA4B,2BAAA;AAC5B,EAAAA,gBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,gBAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,gBAAA,2BAAA,CAAA,GAA4B,2BAAA;AAC5B,EAAAA,gBAAA,kBAAA,CAAA,GAAmB,kBAAA;AACnB,EAAAA,gBAAA,4BAAA,CAAA,GAA6B,4BAAA;AAC7B,EAAAA,gBAAA,iBAAA,CAAA,GAAkB,iBAAA;AAVV,EAAA,OAAAA,eAAAA;AAAA,CAAA,EAAA,cAAA,IAAA,EAAA;AA8GL,IAAK,QAAA,qBAAAC,SAAAA,KAAL;AACH,EAAAA,UAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,UAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,UAAA,gBAAA,CAAA,GAAiB,gBAAA;AACjB,EAAAA,UAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,UAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,UAAA,gBAAA,CAAA,GAAiB,gBAAA;AACjB,EAAAA,UAAA,gBAAA,CAAA,GAAiB,gBAAA;AACjB,EAAAA,UAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,UAAA,OAAA,CAAA,GAAQ,OAAA;AATA,EAAA,OAAAA,SAAAA;AAAA,CAAA,EAAA,QAAA,IAAA,EAAA;;;AC1GZ,IAAM,4BAAA,GAA+B,GAAA;AAErC,IAAM,eAAA,GAAkB;AAAA,EACpB,YAAA;AAAA,EAAc,YAAA;AAAA,EAAc,cAAA;AAAA,EAAgB,UAAA;AAAA,EAAY,aAAA;AAAA,EACxD,OAAA;AAAA,EAAS,QAAA;AAAA,EAAU,SAAA;AAAA,EAAW;AAClC,CAAA;AACA,IAAM,uBAAA,GAA0B,oBAAA;AAEzB,IAAe,sBAAf,MAA2D;AAAA,EAA3D,WAAA,GAAA;AAMH,IAAA,IAAA,CAAU,eAAsC,EAAC;AAIjD;AAAA,IAAA,IAAA,CAAU,mBAA4C,EAAC;AACvD,IAAA,IAAA,CAAQ,YAAA,uBAAmB,GAAA,EAAY;AACvC,IAAA,IAAA,CAAQ,YAAA,uBAAmB,GAAA,EAA2C;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAkB5D,aAAA,GAAsB;AAAA,EAEhC;AAAA;AAAA;AAAA;AAAA,EAKU,SAAA,GAAkB;AAAA,EAE5B;AAAA,EAEA,MAAM,UAAA,CAAW,MAAA,EAA+B,OAAA,EAAuC;AACnF,IAAA,IAAA,CAAK,eAAe,EAAE,GAAG,KAAK,gBAAA,EAAiB,EAAG,GAAG,MAAA,EAAO;AAC5D,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAGf,IAAA,IAAA,CAAK,OAAA,GAAU,KAAK,aAAA,EAAc;AAClC,IAAA,IAAA,CAAK,OAAA,CAAQ,UAAA,CAAW,IAAA,CAAK,YAAA,EAAc,OAAO,CAAA;AAGlD,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,oBAAA,KAAyB,KAAA,EAAO;AAClD,MAAA,MAAM,QAAA,GAAW,KAAK,kBAAA,EAAmB;AACzC,MAAA,IAAA,CAAK,WAAA,GAAc,IAAI,kBAAA,CAAmB,QAAA,EAAU;AAAA,QAChD,SAAA,EAAW,CAAC,GAAA,EAAK,MAAA,EAAQ,IAAA,KAAS;AAC9B,UAAA,IAAA,CAAK,oBAAA,CAAqB,GAAA,EAAK,MAAA,EAAQ,IAAI,CAAA;AAAA,QAC/C,CAAA;AAAA,QACA,UAAA,EAAY,CAAC,GAAA,EAAK,MAAA,EAAQ,IAAA,KAAS;AAC/B,UAAA,IAAA,CAAK,qBAAA,CAAsB,GAAA,EAAK,MAAA,EAAQ,IAAI,CAAA;AAAA,QAChD;AAAA,OACH,CAAA;AACD,MAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AAAA,IAC3B;AAGA,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,cAAA,KAAmB,KAAA,EAAO;AAC5C,MAAA,IAAA,CAAK,uBAAA,EAAwB;AAAA,IACjC;AAEA,IAAA,IAAA,CAAK,kBAAA,EAAmB;AACxB,IAAA,IAAA,CAAK,aAAA,EAAc;AAEnB,IAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,IAAA,CAAK,oBAAoB,CAAA;AAAA,EACjD;AAAA,EAEA,MAAM,OAAA,GAAyB;AAC3B,IAAA,IAAA,CAAK,SAAA,EAAU;AAEf,IAAA,IAAI,KAAK,WAAA,EAAa;AAClB,MAAA,IAAA,CAAK,YAAY,IAAA,EAAK;AACtB,MAAA,IAAA,CAAK,WAAA,GAAc,MAAA;AAAA,IACvB;AAEA,IAAA,IAAI,KAAK,iBAAA,EAAmB;AACxB,MAAA,IAAA,CAAK,iBAAA,EAAkB;AACvB,MAAA,IAAA,CAAK,iBAAA,GAAoB,MAAA;AAAA,IAC7B;AAEA,IAAA,IAAI,KAAK,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,QAAQ,OAAA,EAAQ;AACrB,MAAA,IAAA,CAAK,OAAA,GAAU,MAAA;AAAA,IACnB;AAEA,IAAA,IAAA,CAAK,aAAa,KAAA,EAAM;AACxB,IAAA,KAAA,MAAW,KAAA,IAAS,IAAA,CAAK,YAAA,CAAa,MAAA,EAAO,EAAG;AAC5C,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACtB;AACA,IAAA,IAAA,CAAK,aAAa,KAAA,EAAM;AACxB,IAAA,IAAA,CAAK,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,mBAAmB,CAAA;AAC7C,IAAA,IAAA,CAAK,OAAA,GAAU,MAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKU,UAAA,CAAW,OAAuB,UAAA,EAAuC;AAC/E,IAAA,IAAI,CAAC,KAAK,OAAA,EAAS;AAGnB,IAAA,MAAM,aAAa,UAAA,CAAW,UAAA,IACvB,UAAA,CAAW,QAAA,IACX,WAAW,GAAA,KACV,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,GAAI,CAAA,MAAA,EAAS,UAAA,CAAW,KAAA,CAAM,MAAM,CAAA,CAAA,GAAK,EAAA,CAAA;AAC/E,IAAA,MAAM,SAAA,GAAY,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA;AACxC,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,YAAA,CAAa,mBAAA,IAAuB,4BAAA;AAE9D,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA,EAAG;AAClC,MAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,qBAAA,EAAwB,KAAK,CAAA,CAAE,CAAA;AACzD,MAAA;AAAA,IACJ;AAEA,IAAA,IAAA,CAAK,YAAA,CAAa,IAAI,SAAS,CAAA;AAE/B,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC3B,MAAA,IAAA,CAAK,YAAA,CAAa,OAAO,SAAS,CAAA;AAClC,MAAA,IAAA,CAAK,YAAA,CAAa,OAAO,SAAS,CAAA;AAAA,IACtC,GAAG,YAAY,CAAA;AACf,IAAA,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,SAAA,EAAW,KAAK,CAAA;AAGtC,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,EAAS,eAAA,MAAqB,EAAC;AACjD,IAAA,MAAM,kBAAA,GAAqB;AAAA,MACvB,GAAG,IAAA,CAAK,gBAAA;AAAA,MACR,GAAG,UAAA;AAAA,MACH,QAAA,EAAU;AAAA,KACd;AAEA,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,kBAAkB,CAAA;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKU,oBAAA,CAAqB,IAAA,EAAc,OAAA,EAAiB,KAAA,EAAmB;AAAA,EAEjF;AAAA;AAAA;AAAA;AAAA,EAKU,qBAAA,CAAsB,IAAA,EAAc,OAAA,EAAiB,KAAA,EAAmB;AAAA,EAElF;AAAA;AAAA;AAAA;AAAA,EAKU,qBAAqB,GAAA,EAAmB;AAC9C,IAAA,IAAI,CAAC,IAAA,CAAK,OAAA,IAAW,CAAC,KAAK,OAAA,EAAS;AAEpC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,cAAA,EAAe;AAE7C,IAAA,IAAA,CAAK,UAAA,CAAA,aAAA,oBAAuC;AAAA,MACxC,GAAA;AAAA,MACA,SAAA,EAAW,QAAA;AAAA,MACX,KAAA,EAAO,OAAO,QAAA,KAAa,WAAA,GAAc,SAAS,KAAA,GAAQ;AAAA,KAC7D,CAAA;AAAA,EACL;AAAA,EAEQ,uBAAA,GAAgC;AACpC,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEnC,IAAA,MAAM,mBAAmB,MAAM;AAC3B,MAAA,IAAA,CAAK,oBAAA,CAAqB,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA;AAAA,IAClD,CAAA;AAGA,IAAA,MAAM,oBAAoB,OAAA,CAAQ,SAAA;AAClC,IAAA,MAAM,uBAAuB,OAAA,CAAQ,YAAA;AAErC,IAAA,OAAA,CAAQ,SAAA,GAAY,YAAa,IAAA,EAAM;AACnC,MAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,KAAA,CAAM,IAAA,EAAM,IAAI,CAAA;AACjD,MAAA,IAAI;AAAE,QAAA,gBAAA,EAAiB;AAAA,MAAG,SAAS,CAAA,EAAG;AAAA,MAAqC;AAC3E,MAAA,OAAO,MAAA;AAAA,IACX,CAAA;AAEA,IAAA,OAAA,CAAQ,YAAA,GAAe,YAAa,IAAA,EAAM;AACtC,MAAA,MAAM,MAAA,GAAS,oBAAA,CAAqB,KAAA,CAAM,IAAA,EAAM,IAAI,CAAA;AACpD,MAAA,IAAI;AAAE,QAAA,gBAAA,EAAiB;AAAA,MAAG,SAAS,CAAA,EAAG;AAAA,MAAqC;AAC3E,MAAA,OAAO,MAAA;AAAA,IACX,CAAA;AAEA,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,gBAAgB,CAAA;AAEpD,IAAA,IAAA,CAAK,oBAAoB,MAAM;AAC3B,MAAA,OAAA,CAAQ,SAAA,GAAY,iBAAA;AACpB,MAAA,OAAA,CAAQ,YAAA,GAAe,oBAAA;AACvB,MAAA,MAAA,CAAO,mBAAA,CAAoB,YAAY,gBAAgB,CAAA;AAAA,IAC3D,CAAA;AAGA,IAAA,gBAAA,EAAiB;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAA,GAA2B;AAC/B,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEnC,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,MAAA,CAAO,SAAS,MAAM,CAAA;AAGzD,IAAA,IAAI,cAAA,GAAiB,KAAA;AACrB,IAAA,KAAA,MAAW,SAAS,eAAA,EAAiB;AACjC,MAAA,IAAI,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA,EAAG;AACnB,QAAA,cAAA,GAAiB,IAAA;AACjB,QAAA;AAAA,MACJ;AAAA,IACJ;AACA,IAAA,IAAI,cAAA,EAAgB;AAChB,MAAA,KAAA,MAAW,SAAS,eAAA,EAAiB;AACjC,QAAA,IAAI;AAAE,UAAA,cAAA,CAAe,UAAA,CAAW,0BAA0B,KAAK,CAAA;AAAA,QAAG,CAAA,CAAA,MAAQ;AAAA,QAAC;AAAA,MAC/E;AAAA,IACJ;AAGA,IAAA,KAAA,MAAW,SAAS,eAAA,EAAiB;AACjC,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AAC9B,MAAA,IAAI,KAAA,EAAO;AACP,QAAA,IAAI;AACA,UAAA,cAAA,CAAe,OAAA,CAAQ,uBAAA,GAA0B,KAAA,EAAO,KAAK,CAAA;AAAA,QACjE,CAAA,CAAA,MAAQ;AAAA,QAAC;AAAA,MACb;AAAA,IACJ;AAGA,IAAA,KAAA,MAAW,SAAS,eAAA,EAAiB;AACjC,MAAA,IAAI;AACA,QAAA,MAAM,MAAA,GAAS,cAAA,CAAe,OAAA,CAAQ,uBAAA,GAA0B,KAAK,CAAA;AACrE,QAAA,IAAI,MAAA,EAAQ;AACR,UAAA,IAAA,CAAK,gBAAA,CAAiB,KAAK,CAAA,GAAI,MAAA;AAAA,QACnC;AAAA,MACJ,CAAA,CAAA,MAAQ;AAAA,MAAC;AAAA,IACb;AAAA,EACJ;AAAA,EAEQ,gBAAA,GAA0C;AAC9C,IAAA,OAAO;AAAA,MACH,cAAA,EAAgB,IAAA;AAAA,MAChB,oBAAA,EAAsB,IAAA;AAAA,MACtB,oBAAA,EAAsB,IAAA;AAAA,MACtB,mBAAA,EAAqB,4BAAA;AAAA,MACrB,KAAA,EAAO;AAAA,KACX;AAAA,EACJ;AACJ","file":"index.mjs","sourcesContent":["/**\n * Network interceptor for capturing fetch/XHR requests\n * Patches window.fetch and XMLHttpRequest to intercept matching API calls\n */\n\nimport type { NetworkPattern, NetworkInterceptCallback } from './types';\n\nexport class NetworkInterceptor {\n private patterns: NetworkPattern[] = [];\n private callback: NetworkInterceptCallback;\n private originalFetch?: typeof window.fetch;\n private originalXHROpen?: typeof XMLHttpRequest.prototype.open;\n private originalXHRSend?: typeof XMLHttpRequest.prototype.send;\n private isActive = false;\n\n constructor(patterns: NetworkPattern[], callback: NetworkInterceptCallback) {\n this.patterns = patterns;\n this.callback = callback;\n }\n\n /**\n * Start intercepting network requests\n */\n start(): void {\n if (this.isActive) return;\n if (typeof window === 'undefined') return;\n\n this.patchFetch();\n this.patchXHR();\n this.isActive = true;\n }\n\n /**\n * Stop intercepting and restore original functions\n */\n stop(): void {\n if (!this.isActive) return;\n\n this.restoreFetch();\n this.restoreXHR();\n this.isActive = false;\n }\n\n /**\n * Check if a URL matches any registered pattern\n */\n matchUrl(url: string, method?: string): NetworkPattern | undefined {\n return this.patterns.find((p) => {\n const urlMatch =\n p.pattern instanceof RegExp ? p.pattern.test(url) : url.includes(p.pattern);\n const methodMatch = !p.method || p.method === method?.toUpperCase();\n return urlMatch && methodMatch;\n });\n }\n\n private patchFetch(): void {\n if (typeof window.fetch !== 'function') return;\n\n this.originalFetch = window.fetch;\n const self = this;\n\n window.fetch = function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = init?.method || 'GET';\n const pattern = self.matchUrl(url, method);\n\n if (pattern && self.callback.onRequest) {\n try {\n let body: any;\n try {\n if (init?.body) {\n body =\n typeof init.body === 'string'\n ? init.body\n : init.body instanceof FormData\n ? Object.fromEntries((init.body as any).entries())\n : init.body;\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, body);\n } catch {\n // Never let interceptor errors break application fetch calls\n }\n }\n\n return self.originalFetch!.call(window, input, init).then((response) => {\n if (pattern && self.callback.onResponse) {\n try {\n // Content-Type 체크: JSON 응답만 파싱 (대용량 바이너리 응답 OOM 방지)\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n } else {\n const cloned = response.clone();\n // text() 먼저 호출 후 JSON.parse 시도 (clone body는 1회만 소비 가능)\n cloned\n .text()\n .then((text) => {\n try {\n const json = JSON.parse(text);\n self.callback.onResponse!(url, response.status, json);\n } catch {\n try {\n self.callback.onResponse!(url, response.status, text);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n })\n .catch(() => {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n });\n }\n } catch {\n // Ignore clone/response handling errors to avoid breaking the app\n }\n }\n return response;\n });\n };\n }\n\n private patchXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n this.originalXHROpen = XMLHttpRequest.prototype.open;\n this.originalXHRSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n ...args: any[]\n ) {\n (this as any).__vircle_url = typeof url === 'string' ? url : url.toString();\n (this as any).__vircle_method = method;\n return self.originalXHROpen!.apply(this, [method, url, ...args] as any);\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const url = (this as any).__vircle_url as string;\n const method = (this as any).__vircle_method as string;\n const pattern = url ? self.matchUrl(url, method) : undefined;\n\n if (pattern && self.callback.onRequest) {\n try {\n let parsedBody: any;\n try {\n if (typeof body === 'string') {\n parsedBody = body;\n } else if (body instanceof FormData) {\n parsedBody = Object.fromEntries((body as any).entries());\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, parsedBody);\n } catch {\n // Never let interceptor errors break application XHR calls\n }\n }\n\n if (pattern && self.callback.onResponse) {\n this.addEventListener('load', function () {\n try {\n // Content-Type 체크: JSON/text 응답만 파싱\n const contentType = this.getResponseHeader('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n self.callback.onResponse!(url, this.status, undefined);\n return;\n }\n const responseBody =\n this.responseType === '' || this.responseType === 'text'\n ? JSON.parse(this.responseText)\n : this.response;\n self.callback.onResponse!(url, this.status, responseBody);\n } catch {\n try {\n self.callback.onResponse!(url, this.status, this.responseText);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n });\n }\n\n return self.originalXHRSend!.call(this, body);\n };\n }\n\n private restoreFetch(): void {\n if (this.originalFetch && typeof window !== 'undefined') {\n window.fetch = this.originalFetch;\n this.originalFetch = undefined;\n }\n }\n\n private restoreXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n if (this.originalXHROpen) {\n XMLHttpRequest.prototype.open = this.originalXHROpen;\n this.originalXHROpen = undefined;\n }\n if (this.originalXHRSend) {\n XMLHttpRequest.prototype.send = this.originalXHRSend;\n this.originalXHRSend = undefined;\n }\n }\n}\n","/**\n * E-commerce plugin core types\n */\n\nimport type { PluginContext } from '@vircle/sdk-core-ts';\n\n/**\n * Standard e-commerce event names\n */\nexport enum EcommerceEvent {\n PAGE_VIEWED = 'page_viewed',\n PRODUCT_VIEWED = 'product_viewed',\n PRODUCT_ADDED_TO_CART = 'product_added_to_cart',\n PRODUCT_REMOVED_FROM_CART = 'product_removed_from_cart',\n CART_VIEWED = 'cart_viewed',\n CART_UPDATED = 'cart_updated',\n PRODUCT_ADDED_TO_WISHLIST = 'product_added_to_wishlist',\n CHECKOUT_STARTED = 'checkout_started',\n EXTERNAL_PAYMENT_INITIATED = 'external_payment_initiated',\n ORDER_COMPLETED = 'order_completed',\n}\n\n/**\n * Product sub-object schema\n */\nexport interface Product {\n product_id: string;\n name: string;\n price: number;\n sku?: string;\n category?: string;\n brand?: string;\n variant?: string;\n quantity?: number;\n currency?: string;\n image_url?: string;\n url?: string;\n position?: number;\n}\n\n/**\n * Cart item sub-object schema\n */\nexport interface CartItem extends Product {\n quantity: number;\n item_total?: number;\n}\n\n/**\n * Order item sub-object schema\n */\nexport interface OrderItem extends Product {\n quantity: number;\n item_total: number;\n discount?: number;\n}\n\n/**\n * E-commerce plugin configuration\n */\nexport interface EcommercePluginConfig {\n /** Enable automatic page view tracking */\n trackPageViews?: boolean;\n /** Enable network request interception */\n trackNetworkRequests?: boolean;\n /** Enable DOM-based tracking */\n trackDomInteractions?: boolean;\n /** Custom URL patterns to intercept */\n urlPatterns?: NetworkPattern[];\n /** Event deduplication window in ms */\n deduplicationWindow?: number;\n /** Debug mode */\n debug?: boolean;\n}\n\n/**\n * Network URL pattern for interception\n */\nexport interface NetworkPattern {\n /** URL pattern (string match or regex) */\n pattern: string | RegExp;\n /** HTTP method filter */\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n /** Event to emit when matched */\n event: EcommerceEvent;\n}\n\n/**\n * Platform adapter interface — contract for platform-specific implementations\n */\nexport interface PlatformAdapter {\n /** Platform name */\n readonly name: string;\n\n /** Initialize adapter */\n initialize(config: EcommercePluginConfig, context: PluginContext): void;\n\n /** Cleanup adapter resources */\n cleanup(): void;\n\n /** Detect current page type */\n detectPageType(): PageType;\n\n /** Extract product data from current page */\n extractProduct(): Product | null;\n\n /** Extract cart items from current page */\n extractCartItems(): CartItem[];\n\n /** Extract order data from current page */\n extractOrder(): OrderData | null;\n\n /** Get platform-specific metadata */\n getPlatformMeta(): Record<string, any>;\n}\n\n/**\n * Page types for e-commerce sites\n */\nexport enum PageType {\n HOME = 'home',\n PRODUCT_LIST = 'product_list',\n PRODUCT_DETAIL = 'product_detail',\n CART = 'cart',\n CHECKOUT = 'checkout',\n ORDER_COMPLETE = 'order_complete',\n SEARCH_RESULTS = 'search_results',\n MY_PAGE = 'my_page',\n OTHER = 'other',\n}\n\n/**\n * Order data structure\n */\nexport interface OrderData {\n order_id: string;\n total: number;\n subtotal?: number;\n tax?: number;\n shipping?: number;\n discount?: number;\n currency?: string;\n payment_method?: string;\n items: OrderItem[];\n}\n\n/**\n * Network intercept callback\n */\nexport interface NetworkInterceptCallback {\n onRequest?(url: string, method: string, body?: any): void;\n onResponse?(url: string, status: number, body?: any): void;\n}\n","/**\n * Base e-commerce plugin that platform-specific plugins extend\n */\n\nimport type { VirclePlugin, PluginContext, PluginHooks } from '@vircle/sdk-core-ts';\nimport { NetworkInterceptor } from './NetworkInterceptor';\nimport { EcommerceEvent } from './types';\nimport type {\n EcommercePluginConfig,\n PlatformAdapter,\n NetworkPattern,\n} from './types';\n\nconst DEFAULT_DEDUPLICATION_WINDOW = 2000; // 2 seconds\n\nconst CAMPAIGN_PARAMS = [\n 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'gclid', 'fbclid', 'msclkid', 'ttclid',\n];\nconst CAMPAIGN_STORAGE_PREFIX = '__vircle_campaign_';\n\nexport abstract class BaseEcommercePlugin implements VirclePlugin {\n abstract readonly name: string;\n abstract readonly version: string;\n abstract readonly description: string;\n\n protected context?: PluginContext;\n protected pluginConfig: EcommercePluginConfig = {};\n protected adapter?: PlatformAdapter;\n protected interceptor?: NetworkInterceptor;\n /** 모든 이벤트에 자동 첨부되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등) */\n protected globalProperties: Record<string, unknown> = {};\n private recentEvents = new Set<string>();\n private dedupeTimers = new Map<string, ReturnType<typeof setTimeout>>();\n private navigationCleanup?: () => void;\n\n hooks?: PluginHooks;\n\n /**\n * Get network patterns for this platform\n */\n protected abstract getNetworkPatterns(): NetworkPattern[];\n\n /**\n * Create the platform adapter\n */\n protected abstract createAdapter(): PlatformAdapter;\n\n /**\n * Called after base initialization, override for platform-specific setup\n */\n protected onInitialized(): void {\n // Override in subclass\n }\n\n /**\n * Called before cleanup, override for platform-specific teardown\n */\n protected onCleanup(): void {\n // Override in subclass\n }\n\n async initialize(config: EcommercePluginConfig, context: PluginContext): Promise<void> {\n this.pluginConfig = { ...this.getDefaultConfig(), ...config };\n this.context = context;\n\n // Create platform adapter\n this.adapter = this.createAdapter();\n this.adapter.initialize(this.pluginConfig, context);\n\n // Setup network interceptor\n if (this.pluginConfig.trackNetworkRequests !== false) {\n const patterns = this.getNetworkPatterns();\n this.interceptor = new NetworkInterceptor(patterns, {\n onRequest: (url, method, body) => {\n this.handleNetworkRequest(url, method, body);\n },\n onResponse: (url, status, body) => {\n this.handleNetworkResponse(url, status, body);\n },\n });\n this.interceptor.start();\n }\n\n // Setup navigation tracking\n if (this.pluginConfig.trackPageViews !== false) {\n this.setupNavigationTracking();\n }\n\n this.initCampaignParams();\n this.onInitialized();\n\n this.context.logger.info('Plugin initialized');\n }\n\n async cleanup(): Promise<void> {\n this.onCleanup();\n\n if (this.interceptor) {\n this.interceptor.stop();\n this.interceptor = undefined;\n }\n\n if (this.navigationCleanup) {\n this.navigationCleanup();\n this.navigationCleanup = undefined;\n }\n\n if (this.adapter) {\n this.adapter.cleanup();\n this.adapter = undefined;\n }\n\n this.recentEvents.clear();\n for (const timer of this.dedupeTimers.values()) {\n clearTimeout(timer);\n }\n this.dedupeTimers.clear();\n this.context?.logger.info('Plugin cleaned up');\n this.context = undefined;\n }\n\n /**\n * Track an e-commerce event with deduplication\n */\n protected trackEvent(event: EcommerceEvent, properties: Record<string, any>): void {\n if (!this.context) return;\n\n // Deduplication: skip if same event fired recently (key by event + primary identifier)\n const identifier = properties.product_id\n || properties.order_id\n || properties.url\n || (Array.isArray(properties.items) ? `items:${properties.items.length}` : '');\n const dedupeKey = `${event}:${identifier}`;\n const dedupeWindow = this.pluginConfig.deduplicationWindow || DEFAULT_DEDUPLICATION_WINDOW;\n\n if (this.recentEvents.has(dedupeKey)) {\n this.context.logger.debug(`Deduplicating event: ${event}`);\n return;\n }\n\n this.recentEvents.add(dedupeKey);\n // 자동 만료: deduplicationWindow 후 자동 삭제\n const timer = setTimeout(() => {\n this.recentEvents.delete(dedupeKey);\n this.dedupeTimers.delete(dedupeKey);\n }, dedupeWindow);\n this.dedupeTimers.set(dedupeKey, timer);\n\n // Add global properties and platform metadata\n const meta = this.adapter?.getPlatformMeta() || {};\n const enrichedProperties = {\n ...this.globalProperties,\n ...properties,\n platform: meta,\n };\n\n this.context.track(event, enrichedProperties);\n }\n\n /**\n * Handle intercepted network request — override in subclass for specific behavior\n */\n protected handleNetworkRequest(_url: string, _method: string, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle intercepted network response — override in subclass for specific behavior\n */\n protected handleNetworkResponse(_url: string, _status: number, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle page navigation\n */\n protected handlePageNavigation(url: string): void {\n if (!this.adapter || !this.context) return;\n\n const pageType = this.adapter.detectPageType();\n\n this.trackEvent(EcommerceEvent.PAGE_VIEWED, {\n url,\n page_type: pageType,\n title: typeof document !== 'undefined' ? document.title : undefined,\n });\n }\n\n private setupNavigationTracking(): void {\n if (typeof window === 'undefined') return;\n\n const handleNavigation = () => {\n this.handlePageNavigation(window.location.href);\n };\n\n // Listen for History API changes\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = function (...args) {\n const result = originalPushState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n history.replaceState = function (...args) {\n const result = originalReplaceState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n window.addEventListener('popstate', handleNavigation);\n\n this.navigationCleanup = () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', handleNavigation);\n };\n\n // Track initial page view\n handleNavigation();\n }\n\n /**\n * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.\n * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.\n */\n private initCampaignParams(): void {\n if (typeof window === 'undefined') return;\n\n const params = new URLSearchParams(window.location.search);\n\n // URL에 캠페인 파라미터가 하나라도 있으면 새 캠페인 진입 — 기존 값 클리어 후 새로 저장\n let hasUrlCampaign = false;\n for (const param of CAMPAIGN_PARAMS) {\n if (params.get(param)) {\n hasUrlCampaign = true;\n break;\n }\n }\n if (hasUrlCampaign) {\n for (const param of CAMPAIGN_PARAMS) {\n try { sessionStorage.removeItem(CAMPAIGN_STORAGE_PREFIX + param); } catch {}\n }\n }\n\n // URL에 캠페인 파라미터가 있으면 sessionStorage에 저장\n for (const param of CAMPAIGN_PARAMS) {\n const value = params.get(param);\n if (value) {\n try {\n sessionStorage.setItem(CAMPAIGN_STORAGE_PREFIX + param, value);\n } catch {}\n }\n }\n\n // sessionStorage에서 복원하여 globalProperties에 설정\n for (const param of CAMPAIGN_PARAMS) {\n try {\n const stored = sessionStorage.getItem(CAMPAIGN_STORAGE_PREFIX + param);\n if (stored) {\n this.globalProperties[param] = stored;\n }\n } catch {}\n }\n }\n\n private getDefaultConfig(): EcommercePluginConfig {\n return {\n trackPageViews: true,\n trackNetworkRequests: true,\n trackDomInteractions: true,\n deduplicationWindow: DEFAULT_DEDUPLICATION_WINDOW,\n debug: false,\n };\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/NetworkInterceptor.ts","../src/types.ts","../src/BaseEcommercePlugin.ts"],"names":["EcommerceEvent","PageType"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Network interceptor for capturing fetch/XHR requests\n * Patches window.fetch and XMLHttpRequest to intercept matching API calls\n */\n\nimport type { NetworkPattern, NetworkInterceptCallback } from './types';\n\nexport class NetworkInterceptor {\n private patterns: NetworkPattern[] = [];\n private callback: NetworkInterceptCallback;\n private originalFetch?: typeof window.fetch;\n private originalXHROpen?: typeof XMLHttpRequest.prototype.open;\n private originalXHRSend?: typeof XMLHttpRequest.prototype.send;\n private isActive = false;\n\n constructor(patterns: NetworkPattern[], callback: NetworkInterceptCallback) {\n this.patterns = patterns;\n this.callback = callback;\n }\n\n /**\n * Start intercepting network requests\n */\n start(): void {\n if (this.isActive) return;\n if (typeof window === 'undefined') return;\n\n this.patchFetch();\n this.patchXHR();\n this.isActive = true;\n }\n\n /**\n * Stop intercepting and restore original functions\n */\n stop(): void {\n if (!this.isActive) return;\n\n this.restoreFetch();\n this.restoreXHR();\n this.isActive = false;\n }\n\n /**\n * Check if a URL matches any registered pattern\n */\n matchUrl(url: string, method?: string): NetworkPattern | undefined {\n return this.patterns.find((p) => {\n const urlMatch =\n p.pattern instanceof RegExp ? p.pattern.test(url) : url.includes(p.pattern);\n const methodMatch = !p.method || p.method === method?.toUpperCase();\n return urlMatch && methodMatch;\n });\n }\n\n private patchFetch(): void {\n if (typeof window.fetch !== 'function') return;\n\n this.originalFetch = window.fetch;\n const self = this;\n\n window.fetch = function (input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = init?.method || 'GET';\n const pattern = self.matchUrl(url, method);\n\n if (pattern && self.callback.onRequest) {\n try {\n let body: any;\n try {\n if (init?.body) {\n body =\n typeof init.body === 'string'\n ? init.body\n : init.body instanceof FormData\n ? Object.fromEntries((init.body as any).entries())\n : init.body;\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, body);\n } catch {\n // Never let interceptor errors break application fetch calls\n }\n }\n\n return self.originalFetch!.call(window, input, init).then((response) => {\n if (pattern && self.callback.onResponse) {\n try {\n // Content-Type 체크: JSON 응답만 파싱 (대용량 바이너리 응답 OOM 방지)\n const contentType = response.headers.get('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n } else {\n const cloned = response.clone();\n // text() 먼저 호출 후 JSON.parse 시도 (clone body는 1회만 소비 가능)\n cloned\n .text()\n .then((text) => {\n try {\n const json = JSON.parse(text);\n self.callback.onResponse!(url, response.status, json);\n } catch {\n try {\n self.callback.onResponse!(url, response.status, text);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n })\n .catch(() => {\n try {\n self.callback.onResponse!(url, response.status, undefined);\n } catch {\n // Ignore onResponse callback errors\n }\n });\n }\n } catch {\n // Ignore clone/response handling errors to avoid breaking the app\n }\n }\n return response;\n });\n };\n }\n\n private patchXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n this.originalXHROpen = XMLHttpRequest.prototype.open;\n this.originalXHRSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n ...args: any[]\n ) {\n (this as any).__vircle_url = typeof url === 'string' ? url : url.toString();\n (this as any).__vircle_method = method;\n return self.originalXHROpen!.apply(this, [method, url, ...args] as any);\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const url = (this as any).__vircle_url as string;\n const method = (this as any).__vircle_method as string;\n const pattern = url ? self.matchUrl(url, method) : undefined;\n\n if (pattern && self.callback.onRequest) {\n try {\n let parsedBody: any;\n try {\n if (typeof body === 'string') {\n parsedBody = body;\n } else if (body instanceof FormData) {\n parsedBody = Object.fromEntries((body as any).entries());\n }\n } catch {\n // Ignore body parsing errors\n }\n self.callback.onRequest(url, method, parsedBody);\n } catch {\n // Never let interceptor errors break application XHR calls\n }\n }\n\n if (pattern && self.callback.onResponse) {\n this.addEventListener('load', function () {\n try {\n // Content-Type 체크: JSON/text 응답만 파싱\n const contentType = this.getResponseHeader('content-type') || '';\n if (!contentType.includes('application/json') && !contentType.includes('text/')) {\n self.callback.onResponse!(url, this.status, undefined);\n return;\n }\n const responseBody =\n this.responseType === '' || this.responseType === 'text'\n ? JSON.parse(this.responseText)\n : this.response;\n self.callback.onResponse!(url, this.status, responseBody);\n } catch {\n try {\n self.callback.onResponse!(url, this.status, this.responseText);\n } catch {\n // Ignore onResponse callback errors\n }\n }\n });\n }\n\n return self.originalXHRSend!.call(this, body);\n };\n }\n\n private restoreFetch(): void {\n if (this.originalFetch && typeof window !== 'undefined') {\n window.fetch = this.originalFetch;\n this.originalFetch = undefined;\n }\n }\n\n private restoreXHR(): void {\n if (typeof XMLHttpRequest === 'undefined') return;\n\n if (this.originalXHROpen) {\n XMLHttpRequest.prototype.open = this.originalXHROpen;\n this.originalXHROpen = undefined;\n }\n if (this.originalXHRSend) {\n XMLHttpRequest.prototype.send = this.originalXHRSend;\n this.originalXHRSend = undefined;\n }\n }\n}\n","/**\n * E-commerce plugin core types\n */\n\nimport type { PluginContext } from '@vircle/sdk-core-ts';\n\n/**\n * Standard e-commerce event names\n */\nexport enum EcommerceEvent {\n PAGE_VIEWED = 'page_viewed',\n PRODUCT_VIEWED = 'product_viewed',\n PRODUCT_ADDED_TO_CART = 'product_added_to_cart',\n PRODUCT_REMOVED_FROM_CART = 'product_removed_from_cart',\n CART_VIEWED = 'cart_viewed',\n CART_UPDATED = 'cart_updated',\n PRODUCT_ADDED_TO_WISHLIST = 'product_added_to_wishlist',\n CHECKOUT_STARTED = 'checkout_started',\n EXTERNAL_PAYMENT_INITIATED = 'external_payment_initiated',\n ORDER_COMPLETED = 'order_completed',\n}\n\n/**\n * Product sub-object schema\n */\nexport interface Product {\n product_id: string;\n name: string;\n price: number;\n sku?: string;\n category?: string;\n brand?: string;\n variant?: string;\n quantity?: number;\n currency?: string;\n image_url?: string;\n url?: string;\n position?: number;\n}\n\n/**\n * Cart item sub-object schema\n */\nexport interface CartItem extends Product {\n quantity: number;\n item_total?: number;\n}\n\n/**\n * Order item sub-object schema\n */\nexport interface OrderItem extends Product {\n quantity: number;\n item_total: number;\n discount?: number;\n}\n\n/**\n * E-commerce plugin configuration\n */\nexport interface EcommercePluginConfig {\n /** Enable automatic page view tracking */\n trackPageViews?: boolean;\n /** Enable network request interception */\n trackNetworkRequests?: boolean;\n /** Enable DOM-based tracking */\n trackDomInteractions?: boolean;\n /** Custom URL patterns to intercept */\n urlPatterns?: NetworkPattern[];\n /** Event deduplication window in ms */\n deduplicationWindow?: number;\n /** Debug mode */\n debug?: boolean;\n}\n\n/**\n * Network URL pattern for interception\n */\nexport interface NetworkPattern {\n /** URL pattern (string match or regex) */\n pattern: string | RegExp;\n /** HTTP method filter */\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n /** Event to emit when matched */\n event: EcommerceEvent;\n}\n\n/**\n * Platform adapter interface — contract for platform-specific implementations\n */\nexport interface PlatformAdapter {\n /** Platform name */\n readonly name: string;\n\n /** Initialize adapter */\n initialize(config: EcommercePluginConfig, context: PluginContext): void;\n\n /** Cleanup adapter resources */\n cleanup(): void;\n\n /** Detect current page type */\n detectPageType(): PageType;\n\n /** Extract product data from current page */\n extractProduct(): Product | null;\n\n /** Extract cart items from current page */\n extractCartItems(): CartItem[];\n\n /** Extract order data from current page */\n extractOrder(): OrderData | null;\n\n /** Get platform-specific metadata */\n getPlatformMeta(): Record<string, any>;\n}\n\n/**\n * Page types for e-commerce sites\n */\nexport enum PageType {\n HOME = 'home',\n PRODUCT_LIST = 'product_list',\n PRODUCT_DETAIL = 'product_detail',\n CART = 'cart',\n CHECKOUT = 'checkout',\n ORDER_COMPLETE = 'order_complete',\n SEARCH_RESULTS = 'search_results',\n MY_PAGE = 'my_page',\n OTHER = 'other',\n}\n\n/**\n * Order data structure\n */\nexport interface OrderData {\n order_id: string;\n total: number;\n subtotal?: number;\n tax?: number;\n shipping?: number;\n discount?: number;\n currency?: string;\n payment_method?: string;\n items: OrderItem[];\n}\n\n/**\n * Network intercept callback\n */\nexport interface NetworkInterceptCallback {\n onRequest?(url: string, method: string, body?: any): void;\n onResponse?(url: string, status: number, body?: any): void;\n}\n","/**\n * Base e-commerce plugin that platform-specific plugins extend\n */\n\nimport type { VirclePlugin, PluginContext, PluginHooks } from '@vircle/sdk-core-ts';\nimport { NetworkInterceptor } from './NetworkInterceptor';\nimport { EcommerceEvent } from './types';\nimport type {\n EcommercePluginConfig,\n PlatformAdapter,\n NetworkPattern,\n} from './types';\n\nconst DEFAULT_DEDUPLICATION_WINDOW = 2000; // 2 seconds\n\nexport abstract class BaseEcommercePlugin implements VirclePlugin {\n abstract readonly name: string;\n abstract readonly version: string;\n abstract readonly description: string;\n\n protected context?: PluginContext;\n protected pluginConfig: EcommercePluginConfig = {};\n protected adapter?: PlatformAdapter;\n protected interceptor?: NetworkInterceptor;\n /** 모든 이벤트에 자동 첨부되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등) */\n protected globalProperties: Record<string, unknown> = {};\n private recentEvents = new Set<string>();\n private dedupeTimers = new Map<string, ReturnType<typeof setTimeout>>();\n private navigationCleanup?: () => void;\n\n hooks?: PluginHooks;\n\n /**\n * Get network patterns for this platform\n */\n protected abstract getNetworkPatterns(): NetworkPattern[];\n\n /**\n * Create the platform adapter\n */\n protected abstract createAdapter(): PlatformAdapter;\n\n /**\n * Called after base initialization, override for platform-specific setup\n */\n protected onInitialized(): void {\n // Override in subclass\n }\n\n /**\n * Called before cleanup, override for platform-specific teardown\n */\n protected onCleanup(): void {\n // Override in subclass\n }\n\n async initialize(config: EcommercePluginConfig, context: PluginContext): Promise<void> {\n this.pluginConfig = { ...this.getDefaultConfig(), ...config };\n this.context = context;\n\n // Create platform adapter\n this.adapter = this.createAdapter();\n this.adapter.initialize(this.pluginConfig, context);\n\n // Setup network interceptor\n if (this.pluginConfig.trackNetworkRequests !== false) {\n const patterns = this.getNetworkPatterns();\n this.interceptor = new NetworkInterceptor(patterns, {\n onRequest: (url, method, body) => {\n this.handleNetworkRequest(url, method, body);\n },\n onResponse: (url, status, body) => {\n this.handleNetworkResponse(url, status, body);\n },\n });\n this.interceptor.start();\n }\n\n // SDK가 이미 page_view를 추적 중이면 플러그인의 PAGE_VIEWED 자동 비활성화\n const sdkConfig = context.getConfig();\n if ((sdkConfig as any)?.trackPageViews && this.pluginConfig.trackPageViews === undefined) {\n this.pluginConfig.trackPageViews = false;\n }\n\n // 네비게이션 추적은 항상 활성화 (서브클래스의 page-specific 이벤트에 필요)\n this.setupNavigationTracking();\n\n this.onInitialized();\n\n this.context.logger.info('Plugin initialized');\n }\n\n async cleanup(): Promise<void> {\n this.onCleanup();\n\n if (this.interceptor) {\n this.interceptor.stop();\n this.interceptor = undefined;\n }\n\n if (this.navigationCleanup) {\n this.navigationCleanup();\n this.navigationCleanup = undefined;\n }\n\n if (this.adapter) {\n this.adapter.cleanup();\n this.adapter = undefined;\n }\n\n this.recentEvents.clear();\n for (const timer of this.dedupeTimers.values()) {\n clearTimeout(timer);\n }\n this.dedupeTimers.clear();\n this.context?.logger.info('Plugin cleaned up');\n this.context = undefined;\n }\n\n /**\n * Track an e-commerce event with deduplication\n */\n protected trackEvent(event: EcommerceEvent, properties: Record<string, any>): void {\n if (!this.context) return;\n\n // Deduplication: skip if same event fired recently (key by event + primary identifier)\n const identifier = properties.product_id\n || properties.order_id\n || properties.url\n || (Array.isArray(properties.items) ? `items:${properties.items.length}` : '');\n const dedupeKey = `${event}:${identifier}`;\n const dedupeWindow = this.pluginConfig.deduplicationWindow || DEFAULT_DEDUPLICATION_WINDOW;\n\n if (this.recentEvents.has(dedupeKey)) {\n this.context.logger.debug(`Deduplicating event: ${event}`);\n return;\n }\n\n this.recentEvents.add(dedupeKey);\n // 자동 만료: deduplicationWindow 후 자동 삭제\n const timer = setTimeout(() => {\n this.recentEvents.delete(dedupeKey);\n this.dedupeTimers.delete(dedupeKey);\n }, dedupeWindow);\n this.dedupeTimers.set(dedupeKey, timer);\n\n // Add global properties and platform metadata\n const meta = this.adapter?.getPlatformMeta() || {};\n const enrichedProperties = {\n ...this.globalProperties,\n ...properties,\n platform: meta,\n };\n\n this.context.track(event, enrichedProperties);\n }\n\n /**\n * Handle intercepted network request — override in subclass for specific behavior\n */\n protected handleNetworkRequest(_url: string, _method: string, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle intercepted network response — override in subclass for specific behavior\n */\n protected handleNetworkResponse(_url: string, _status: number, _body?: any): void {\n // Default: no-op, subclasses implement specific logic\n }\n\n /**\n * Handle page navigation\n * PAGE_VIEWED는 trackPageViews 설정으로 제어 (SDK page_view와 중복 방지)\n * 서브클래스의 page-specific 이벤트(product_viewed 등)는 영향 없음\n */\n protected handlePageNavigation(url: string): void {\n if (!this.adapter || !this.context) return;\n\n if (this.pluginConfig.trackPageViews !== false) {\n const pageType = this.adapter.detectPageType();\n\n this.trackEvent(EcommerceEvent.PAGE_VIEWED, {\n url,\n page_type: pageType,\n title: typeof document !== 'undefined' ? document.title : undefined,\n });\n }\n }\n\n private setupNavigationTracking(): void {\n if (typeof window === 'undefined') return;\n\n const handleNavigation = () => {\n this.handlePageNavigation(window.location.href);\n };\n\n // History API 래핑 (SDK-Web과 이중 래핑되어도 안전 — 체인으로 양쪽 핸들러 모두 동작)\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = function (...args) {\n const result = originalPushState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n history.replaceState = function (...args) {\n const result = originalReplaceState.apply(this, args);\n try { handleNavigation(); } catch (_) { /* SDK 오류가 호스트 앱 라우팅에 영향 주지 않도록 */ }\n return result;\n };\n\n window.addEventListener('popstate', handleNavigation);\n\n this.navigationCleanup = () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', handleNavigation);\n };\n\n // 초기 페이지 처리\n handleNavigation();\n }\n\n private getDefaultConfig(): EcommercePluginConfig {\n return {\n trackPageViews: true,\n trackNetworkRequests: true,\n trackDomInteractions: true,\n deduplicationWindow: DEFAULT_DEDUPLICATION_WINDOW,\n debug: false,\n };\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vircle/plugin-ecommerce-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Vircle SDK E-commerce Plugin Core - Base classes and utilities for commerce tracking plugins",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -27,7 +27,7 @@
27
27
  "registry": "https://registry.npmjs.org/"
28
28
  },
29
29
  "dependencies": {
30
- "@vircle/sdk-core-ts": "1.1.1"
30
+ "@vircle/sdk-core-ts": "1.1.3"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^20.0.0",