@vircle/plugin-ecommerce-core 0.1.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/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # @vircle/plugin-ecommerce-core
2
+
3
+ 커머스 플랫폼 플러그인을 위한 기반 클래스와 유틸리티를 제공하는 패키지입니다. 플랫폼별 플러그인(Cafe24, Shopify 등)은 이 패키지를 확장하여 구현합니다.
4
+
5
+ ## 설치
6
+
7
+ ```bash
8
+ pnpm add @vircle/plugin-ecommerce-core
9
+ ```
10
+
11
+ ## 구성 요소
12
+
13
+ ### BaseEcommercePlugin
14
+
15
+ 모든 커머스 플러그인의 추상 기반 클래스입니다. `VirclePlugin` 인터페이스를 구현하며, 다음 기능을 제공합니다:
16
+
17
+ - 네트워크 요청 인터셉션 (fetch/XHR)
18
+ - History API 기반 페이지 네비게이션 추적
19
+ - 이벤트 중복 방지 (deduplication)
20
+ - 플랫폼 메타데이터 자동 첨부
21
+ - `globalProperties` — 모든 이벤트에 자동 병합되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등)
22
+
23
+ ```typescript
24
+ import { BaseEcommercePlugin, EcommerceEvent, type NetworkPattern, type PlatformAdapter } from '@vircle/plugin-ecommerce-core';
25
+
26
+ class MyPlatformPlugin extends BaseEcommercePlugin {
27
+ readonly name = 'my-platform';
28
+ readonly version = '1.0.0';
29
+ readonly description = 'My platform tracking plugin';
30
+
31
+ protected getNetworkPatterns(): NetworkPattern[] {
32
+ return [
33
+ { pattern: '/api/cart/add', method: 'POST', event: EcommerceEvent.PRODUCT_ADDED_TO_CART },
34
+ ];
35
+ }
36
+
37
+ protected createAdapter(): PlatformAdapter {
38
+ return new MyPlatformAdapter();
39
+ }
40
+
41
+ protected handleNetworkRequest(url: string, method: string, body?: any): void {
42
+ // 인터셉트된 네트워크 요청 처리
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### NetworkInterceptor
48
+
49
+ `window.fetch`와 `XMLHttpRequest`를 패치하여 특정 URL 패턴에 매칭되는 네트워크 요청/응답을 인터셉트합니다.
50
+
51
+ ```typescript
52
+ import { NetworkInterceptor, type NetworkPattern } from '@vircle/plugin-ecommerce-core';
53
+
54
+ const patterns: NetworkPattern[] = [
55
+ { pattern: '/api/cart', method: 'POST', event: EcommerceEvent.PRODUCT_ADDED_TO_CART },
56
+ { pattern: /\/api\/order\/\d+/, event: EcommerceEvent.ORDER_COMPLETED },
57
+ ];
58
+
59
+ const interceptor = new NetworkInterceptor(patterns, {
60
+ onRequest: (url, method, body) => { /* 요청 감지 */ },
61
+ onResponse: (url, status, body) => { /* 응답 감지 */ },
62
+ });
63
+
64
+ interceptor.start();
65
+ // ...
66
+ interceptor.stop(); // 원본 함수 복원
67
+ ```
68
+
69
+ ### PlatformAdapter
70
+
71
+ 플랫폼별 데이터 추출 계약을 정의하는 인터페이스입니다:
72
+
73
+ ```typescript
74
+ interface PlatformAdapter {
75
+ readonly name: string;
76
+ initialize(config: EcommercePluginConfig, context: PluginContext): void;
77
+ cleanup(): void;
78
+ detectPageType(): PageType;
79
+ extractProduct(): Product | null;
80
+ extractCartItems(): CartItem[];
81
+ extractOrder(): OrderData | null;
82
+ getPlatformMeta(): Record<string, any>;
83
+ }
84
+ ```
85
+
86
+ ### 이벤트 타입
87
+
88
+ ```typescript
89
+ enum EcommerceEvent {
90
+ PAGE_VIEWED // 페이지 조회
91
+ PRODUCT_VIEWED // 상품 상세 조회
92
+ PRODUCT_ADDED_TO_CART // 장바구니 추가
93
+ PRODUCT_REMOVED_FROM_CART // 장바구니 제거
94
+ CART_VIEWED // 장바구니 조회
95
+ CART_UPDATED // 장바구니 수량 변경
96
+ PRODUCT_ADDED_TO_WISHLIST // 위시리스트 추가
97
+ CHECKOUT_STARTED // 주문서 진입
98
+ EXTERNAL_PAYMENT_INITIATED // 외부 결제 시작
99
+ ORDER_COMPLETED // 주문 완료
100
+ }
101
+ ```
102
+
103
+ ### 데이터 타입
104
+
105
+ | 타입 | 설명 |
106
+ |------|------|
107
+ | `Product` | 상품 정보 (product_id, name, price, sku, ...) |
108
+ | `CartItem` | 장바구니 아이템 (Product + quantity) |
109
+ | `OrderItem` | 주문 아이템 (Product + quantity + item_total) |
110
+ | `OrderData` | 주문 데이터 (order_id, total, items, ...) |
111
+
112
+ ### 설정
113
+
114
+ ```typescript
115
+ interface EcommercePluginConfig {
116
+ trackPageViews?: boolean; // 페이지뷰 자동 추적 (기본: true)
117
+ trackNetworkRequests?: boolean; // 네트워크 인터셉션 (기본: true)
118
+ trackDomInteractions?: boolean; // DOM 인터랙션 추적 (기본: true)
119
+ deduplicationWindow?: number; // 중복 방지 윈도우 ms (기본: 2000)
120
+ debug?: boolean; // 디버그 로그 (기본: false)
121
+ }
122
+ ```
123
+
124
+ ## 새 플랫폼 플러그인 만들기
125
+
126
+ 1. `BaseEcommercePlugin`을 상속
127
+ 2. `PlatformAdapter`를 구현하여 플랫폼별 데이터 추출 로직 작성
128
+ 3. `getNetworkPatterns()`에서 인터셉트할 API URL 패턴 반환
129
+ 4. `handleNetworkRequest()`에서 인터셉트된 요청을 이벤트로 변환
130
+
131
+ 참조 구현: [`@vircle/plugin-cafe24`](../plugin-cafe24)
@@ -0,0 +1,231 @@
1
+ import { PluginContext, VirclePlugin, PluginHooks } from '@vircle/sdk-core-ts';
2
+
3
+ /**
4
+ * E-commerce plugin core types
5
+ */
6
+
7
+ /**
8
+ * Standard e-commerce event names
9
+ */
10
+ declare enum EcommerceEvent {
11
+ PAGE_VIEWED = "page_viewed",
12
+ PRODUCT_VIEWED = "product_viewed",
13
+ PRODUCT_ADDED_TO_CART = "product_added_to_cart",
14
+ PRODUCT_REMOVED_FROM_CART = "product_removed_from_cart",
15
+ CART_VIEWED = "cart_viewed",
16
+ CART_UPDATED = "cart_updated",
17
+ PRODUCT_ADDED_TO_WISHLIST = "product_added_to_wishlist",
18
+ CHECKOUT_STARTED = "checkout_started",
19
+ EXTERNAL_PAYMENT_INITIATED = "external_payment_initiated",
20
+ ORDER_COMPLETED = "order_completed"
21
+ }
22
+ /**
23
+ * Product sub-object schema
24
+ */
25
+ interface Product {
26
+ product_id: string;
27
+ name: string;
28
+ price: number;
29
+ sku?: string;
30
+ category?: string;
31
+ brand?: string;
32
+ variant?: string;
33
+ quantity?: number;
34
+ currency?: string;
35
+ image_url?: string;
36
+ url?: string;
37
+ position?: number;
38
+ }
39
+ /**
40
+ * Cart item sub-object schema
41
+ */
42
+ interface CartItem extends Product {
43
+ quantity: number;
44
+ item_total?: number;
45
+ }
46
+ /**
47
+ * Order item sub-object schema
48
+ */
49
+ interface OrderItem extends Product {
50
+ quantity: number;
51
+ item_total: number;
52
+ discount?: number;
53
+ }
54
+ /**
55
+ * E-commerce plugin configuration
56
+ */
57
+ interface EcommercePluginConfig {
58
+ /** Enable automatic page view tracking */
59
+ trackPageViews?: boolean;
60
+ /** Enable network request interception */
61
+ trackNetworkRequests?: boolean;
62
+ /** Enable DOM-based tracking */
63
+ trackDomInteractions?: boolean;
64
+ /** Custom URL patterns to intercept */
65
+ urlPatterns?: NetworkPattern[];
66
+ /** Event deduplication window in ms */
67
+ deduplicationWindow?: number;
68
+ /** Debug mode */
69
+ debug?: boolean;
70
+ }
71
+ /**
72
+ * Network URL pattern for interception
73
+ */
74
+ interface NetworkPattern {
75
+ /** URL pattern (string match or regex) */
76
+ pattern: string | RegExp;
77
+ /** HTTP method filter */
78
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
79
+ /** Event to emit when matched */
80
+ event: EcommerceEvent;
81
+ }
82
+ /**
83
+ * Platform adapter interface — contract for platform-specific implementations
84
+ */
85
+ interface PlatformAdapter {
86
+ /** Platform name */
87
+ readonly name: string;
88
+ /** Initialize adapter */
89
+ initialize(config: EcommercePluginConfig, context: PluginContext): void;
90
+ /** Cleanup adapter resources */
91
+ cleanup(): void;
92
+ /** Detect current page type */
93
+ detectPageType(): PageType;
94
+ /** Extract product data from current page */
95
+ extractProduct(): Product | null;
96
+ /** Extract cart items from current page */
97
+ extractCartItems(): CartItem[];
98
+ /** Extract order data from current page */
99
+ extractOrder(): OrderData | null;
100
+ /** Get platform-specific metadata */
101
+ getPlatformMeta(): Record<string, any>;
102
+ }
103
+ /**
104
+ * Page types for e-commerce sites
105
+ */
106
+ declare enum PageType {
107
+ HOME = "home",
108
+ PRODUCT_LIST = "product_list",
109
+ PRODUCT_DETAIL = "product_detail",
110
+ CART = "cart",
111
+ CHECKOUT = "checkout",
112
+ ORDER_COMPLETE = "order_complete",
113
+ SEARCH_RESULTS = "search_results",
114
+ MY_PAGE = "my_page",
115
+ OTHER = "other"
116
+ }
117
+ /**
118
+ * Order data structure
119
+ */
120
+ interface OrderData {
121
+ order_id: string;
122
+ total: number;
123
+ subtotal?: number;
124
+ tax?: number;
125
+ shipping?: number;
126
+ discount?: number;
127
+ currency?: string;
128
+ payment_method?: string;
129
+ items: OrderItem[];
130
+ }
131
+ /**
132
+ * Network intercept callback
133
+ */
134
+ interface NetworkInterceptCallback {
135
+ onRequest?(url: string, method: string, body?: any): void;
136
+ onResponse?(url: string, status: number, body?: any): void;
137
+ }
138
+
139
+ /**
140
+ * Network interceptor for capturing fetch/XHR requests
141
+ * Patches window.fetch and XMLHttpRequest to intercept matching API calls
142
+ */
143
+
144
+ declare class NetworkInterceptor {
145
+ private patterns;
146
+ private callback;
147
+ private originalFetch?;
148
+ private originalXHROpen?;
149
+ private originalXHRSend?;
150
+ private isActive;
151
+ constructor(patterns: NetworkPattern[], callback: NetworkInterceptCallback);
152
+ /**
153
+ * Start intercepting network requests
154
+ */
155
+ start(): void;
156
+ /**
157
+ * Stop intercepting and restore original functions
158
+ */
159
+ stop(): void;
160
+ /**
161
+ * Check if a URL matches any registered pattern
162
+ */
163
+ matchUrl(url: string, method?: string): NetworkPattern | undefined;
164
+ private patchFetch;
165
+ private patchXHR;
166
+ private restoreFetch;
167
+ private restoreXHR;
168
+ }
169
+
170
+ /**
171
+ * Base e-commerce plugin that platform-specific plugins extend
172
+ */
173
+
174
+ declare abstract class BaseEcommercePlugin implements VirclePlugin {
175
+ abstract readonly name: string;
176
+ abstract readonly version: string;
177
+ abstract readonly description: string;
178
+ protected context?: PluginContext;
179
+ protected pluginConfig: EcommercePluginConfig;
180
+ protected adapter?: PlatformAdapter;
181
+ protected interceptor?: NetworkInterceptor;
182
+ /** 모든 이벤트에 자동 첨부되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등) */
183
+ protected globalProperties: Record<string, unknown>;
184
+ private recentEvents;
185
+ private dedupeTimers;
186
+ private navigationCleanup?;
187
+ hooks?: PluginHooks;
188
+ /**
189
+ * Get network patterns for this platform
190
+ */
191
+ protected abstract getNetworkPatterns(): NetworkPattern[];
192
+ /**
193
+ * Create the platform adapter
194
+ */
195
+ protected abstract createAdapter(): PlatformAdapter;
196
+ /**
197
+ * Called after base initialization, override for platform-specific setup
198
+ */
199
+ protected onInitialized(): void;
200
+ /**
201
+ * Called before cleanup, override for platform-specific teardown
202
+ */
203
+ protected onCleanup(): void;
204
+ initialize(config: EcommercePluginConfig, context: PluginContext): Promise<void>;
205
+ cleanup(): Promise<void>;
206
+ /**
207
+ * Track an e-commerce event with deduplication
208
+ */
209
+ protected trackEvent(event: EcommerceEvent, properties: Record<string, any>): void;
210
+ /**
211
+ * Handle intercepted network request — override in subclass for specific behavior
212
+ */
213
+ protected handleNetworkRequest(_url: string, _method: string, _body?: any): void;
214
+ /**
215
+ * Handle intercepted network response — override in subclass for specific behavior
216
+ */
217
+ protected handleNetworkResponse(_url: string, _status: number, _body?: any): void;
218
+ /**
219
+ * Handle page navigation
220
+ */
221
+ protected handlePageNavigation(url: string): void;
222
+ private setupNavigationTracking;
223
+ /**
224
+ * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.
225
+ * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.
226
+ */
227
+ private initCampaignParams;
228
+ private getDefaultConfig;
229
+ }
230
+
231
+ export { BaseEcommercePlugin, type CartItem, EcommerceEvent, type EcommercePluginConfig, type NetworkInterceptCallback, NetworkInterceptor, type NetworkPattern, type OrderData, type OrderItem, PageType, type PlatformAdapter, type Product };
@@ -0,0 +1,231 @@
1
+ import { PluginContext, VirclePlugin, PluginHooks } from '@vircle/sdk-core-ts';
2
+
3
+ /**
4
+ * E-commerce plugin core types
5
+ */
6
+
7
+ /**
8
+ * Standard e-commerce event names
9
+ */
10
+ declare enum EcommerceEvent {
11
+ PAGE_VIEWED = "page_viewed",
12
+ PRODUCT_VIEWED = "product_viewed",
13
+ PRODUCT_ADDED_TO_CART = "product_added_to_cart",
14
+ PRODUCT_REMOVED_FROM_CART = "product_removed_from_cart",
15
+ CART_VIEWED = "cart_viewed",
16
+ CART_UPDATED = "cart_updated",
17
+ PRODUCT_ADDED_TO_WISHLIST = "product_added_to_wishlist",
18
+ CHECKOUT_STARTED = "checkout_started",
19
+ EXTERNAL_PAYMENT_INITIATED = "external_payment_initiated",
20
+ ORDER_COMPLETED = "order_completed"
21
+ }
22
+ /**
23
+ * Product sub-object schema
24
+ */
25
+ interface Product {
26
+ product_id: string;
27
+ name: string;
28
+ price: number;
29
+ sku?: string;
30
+ category?: string;
31
+ brand?: string;
32
+ variant?: string;
33
+ quantity?: number;
34
+ currency?: string;
35
+ image_url?: string;
36
+ url?: string;
37
+ position?: number;
38
+ }
39
+ /**
40
+ * Cart item sub-object schema
41
+ */
42
+ interface CartItem extends Product {
43
+ quantity: number;
44
+ item_total?: number;
45
+ }
46
+ /**
47
+ * Order item sub-object schema
48
+ */
49
+ interface OrderItem extends Product {
50
+ quantity: number;
51
+ item_total: number;
52
+ discount?: number;
53
+ }
54
+ /**
55
+ * E-commerce plugin configuration
56
+ */
57
+ interface EcommercePluginConfig {
58
+ /** Enable automatic page view tracking */
59
+ trackPageViews?: boolean;
60
+ /** Enable network request interception */
61
+ trackNetworkRequests?: boolean;
62
+ /** Enable DOM-based tracking */
63
+ trackDomInteractions?: boolean;
64
+ /** Custom URL patterns to intercept */
65
+ urlPatterns?: NetworkPattern[];
66
+ /** Event deduplication window in ms */
67
+ deduplicationWindow?: number;
68
+ /** Debug mode */
69
+ debug?: boolean;
70
+ }
71
+ /**
72
+ * Network URL pattern for interception
73
+ */
74
+ interface NetworkPattern {
75
+ /** URL pattern (string match or regex) */
76
+ pattern: string | RegExp;
77
+ /** HTTP method filter */
78
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
79
+ /** Event to emit when matched */
80
+ event: EcommerceEvent;
81
+ }
82
+ /**
83
+ * Platform adapter interface — contract for platform-specific implementations
84
+ */
85
+ interface PlatformAdapter {
86
+ /** Platform name */
87
+ readonly name: string;
88
+ /** Initialize adapter */
89
+ initialize(config: EcommercePluginConfig, context: PluginContext): void;
90
+ /** Cleanup adapter resources */
91
+ cleanup(): void;
92
+ /** Detect current page type */
93
+ detectPageType(): PageType;
94
+ /** Extract product data from current page */
95
+ extractProduct(): Product | null;
96
+ /** Extract cart items from current page */
97
+ extractCartItems(): CartItem[];
98
+ /** Extract order data from current page */
99
+ extractOrder(): OrderData | null;
100
+ /** Get platform-specific metadata */
101
+ getPlatformMeta(): Record<string, any>;
102
+ }
103
+ /**
104
+ * Page types for e-commerce sites
105
+ */
106
+ declare enum PageType {
107
+ HOME = "home",
108
+ PRODUCT_LIST = "product_list",
109
+ PRODUCT_DETAIL = "product_detail",
110
+ CART = "cart",
111
+ CHECKOUT = "checkout",
112
+ ORDER_COMPLETE = "order_complete",
113
+ SEARCH_RESULTS = "search_results",
114
+ MY_PAGE = "my_page",
115
+ OTHER = "other"
116
+ }
117
+ /**
118
+ * Order data structure
119
+ */
120
+ interface OrderData {
121
+ order_id: string;
122
+ total: number;
123
+ subtotal?: number;
124
+ tax?: number;
125
+ shipping?: number;
126
+ discount?: number;
127
+ currency?: string;
128
+ payment_method?: string;
129
+ items: OrderItem[];
130
+ }
131
+ /**
132
+ * Network intercept callback
133
+ */
134
+ interface NetworkInterceptCallback {
135
+ onRequest?(url: string, method: string, body?: any): void;
136
+ onResponse?(url: string, status: number, body?: any): void;
137
+ }
138
+
139
+ /**
140
+ * Network interceptor for capturing fetch/XHR requests
141
+ * Patches window.fetch and XMLHttpRequest to intercept matching API calls
142
+ */
143
+
144
+ declare class NetworkInterceptor {
145
+ private patterns;
146
+ private callback;
147
+ private originalFetch?;
148
+ private originalXHROpen?;
149
+ private originalXHRSend?;
150
+ private isActive;
151
+ constructor(patterns: NetworkPattern[], callback: NetworkInterceptCallback);
152
+ /**
153
+ * Start intercepting network requests
154
+ */
155
+ start(): void;
156
+ /**
157
+ * Stop intercepting and restore original functions
158
+ */
159
+ stop(): void;
160
+ /**
161
+ * Check if a URL matches any registered pattern
162
+ */
163
+ matchUrl(url: string, method?: string): NetworkPattern | undefined;
164
+ private patchFetch;
165
+ private patchXHR;
166
+ private restoreFetch;
167
+ private restoreXHR;
168
+ }
169
+
170
+ /**
171
+ * Base e-commerce plugin that platform-specific plugins extend
172
+ */
173
+
174
+ declare abstract class BaseEcommercePlugin implements VirclePlugin {
175
+ abstract readonly name: string;
176
+ abstract readonly version: string;
177
+ abstract readonly description: string;
178
+ protected context?: PluginContext;
179
+ protected pluginConfig: EcommercePluginConfig;
180
+ protected adapter?: PlatformAdapter;
181
+ protected interceptor?: NetworkInterceptor;
182
+ /** 모든 이벤트에 자동 첨부되는 글로벌 프로퍼티 (크로스 도메인 어트리뷰션 등) */
183
+ protected globalProperties: Record<string, unknown>;
184
+ private recentEvents;
185
+ private dedupeTimers;
186
+ private navigationCleanup?;
187
+ hooks?: PluginHooks;
188
+ /**
189
+ * Get network patterns for this platform
190
+ */
191
+ protected abstract getNetworkPatterns(): NetworkPattern[];
192
+ /**
193
+ * Create the platform adapter
194
+ */
195
+ protected abstract createAdapter(): PlatformAdapter;
196
+ /**
197
+ * Called after base initialization, override for platform-specific setup
198
+ */
199
+ protected onInitialized(): void;
200
+ /**
201
+ * Called before cleanup, override for platform-specific teardown
202
+ */
203
+ protected onCleanup(): void;
204
+ initialize(config: EcommercePluginConfig, context: PluginContext): Promise<void>;
205
+ cleanup(): Promise<void>;
206
+ /**
207
+ * Track an e-commerce event with deduplication
208
+ */
209
+ protected trackEvent(event: EcommerceEvent, properties: Record<string, any>): void;
210
+ /**
211
+ * Handle intercepted network request — override in subclass for specific behavior
212
+ */
213
+ protected handleNetworkRequest(_url: string, _method: string, _body?: any): void;
214
+ /**
215
+ * Handle intercepted network response — override in subclass for specific behavior
216
+ */
217
+ protected handleNetworkResponse(_url: string, _status: number, _body?: any): void;
218
+ /**
219
+ * Handle page navigation
220
+ */
221
+ protected handlePageNavigation(url: string): void;
222
+ private setupNavigationTracking;
223
+ /**
224
+ * URL의 캠페인 파라미터(UTM + 광고 ID)를 수집하여 globalProperties에 설정.
225
+ * sessionStorage를 사용하여 MPA 페이지 전환 간에도 유지.
226
+ */
227
+ private initCampaignParams;
228
+ private getDefaultConfig;
229
+ }
230
+
231
+ export { BaseEcommercePlugin, type CartItem, EcommerceEvent, type EcommercePluginConfig, type NetworkInterceptCallback, NetworkInterceptor, type NetworkPattern, type OrderData, type OrderItem, PageType, type PlatformAdapter, type Product };