@tagadapay/plugin-sdk 2.8.8 → 2.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/config/environment.d.ts +1 -22
- package/dist/react/config/environment.js +1 -132
- package/dist/react/utils/deviceInfo.d.ts +1 -39
- package/dist/react/utils/deviceInfo.js +1 -163
- package/dist/react/utils/jwtDecoder.d.ts +1 -14
- package/dist/react/utils/jwtDecoder.js +1 -86
- package/dist/react/utils/tokenStorage.d.ts +1 -16
- package/dist/react/utils/tokenStorage.js +1 -53
- package/dist/v2/core/client.d.ts +92 -0
- package/dist/v2/core/client.js +386 -0
- package/dist/v2/core/config/environment.d.ts +22 -0
- package/dist/v2/core/config/environment.js +140 -0
- package/dist/v2/core/pathRemapping.js +61 -3
- package/dist/v2/core/resources/apiClient.d.ts +8 -0
- package/dist/v2/core/resources/apiClient.js +30 -9
- package/dist/v2/core/resources/funnel.d.ts +14 -0
- package/dist/v2/core/resources/payments.d.ts +23 -0
- package/dist/v2/core/types.d.ts +271 -0
- package/dist/v2/core/types.js +4 -0
- package/dist/v2/core/utils/deviceInfo.d.ts +39 -0
- package/dist/v2/core/utils/deviceInfo.js +162 -0
- package/dist/v2/core/utils/eventDispatcher.d.ts +10 -0
- package/dist/v2/core/utils/eventDispatcher.js +24 -0
- package/dist/v2/core/utils/jwtDecoder.d.ts +14 -0
- package/dist/v2/core/utils/jwtDecoder.js +85 -0
- package/dist/v2/core/utils/pluginConfig.js +6 -0
- package/dist/v2/core/utils/tokenStorage.d.ts +19 -0
- package/dist/v2/core/utils/tokenStorage.js +52 -0
- package/dist/v2/react/components/DebugDrawer.js +90 -1
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.d.ts +12 -0
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +54 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +1 -1
- package/dist/v2/react/hooks/useFunnel.js +209 -32
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +26 -18
- package/dist/v2/react/hooks/useISOData.js +4 -2
- package/dist/v2/react/hooks/useOffersQuery.d.ts +24 -29
- package/dist/v2/react/hooks/useOffersQuery.js +164 -204
- package/dist/v2/react/hooks/usePaymentQuery.js +99 -6
- package/dist/v2/react/providers/TagadaProvider.d.ts +8 -21
- package/dist/v2/react/providers/TagadaProvider.js +79 -673
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import axios from 'axios';
|
|
|
6
6
|
export class ApiClient {
|
|
7
7
|
constructor(config) {
|
|
8
8
|
this.currentToken = null;
|
|
9
|
+
this.tokenProvider = null;
|
|
9
10
|
this.axios = axios.create({
|
|
10
11
|
baseURL: config.baseURL,
|
|
11
12
|
timeout: config.timeout || 30000,
|
|
@@ -15,12 +16,28 @@ export class ApiClient {
|
|
|
15
16
|
},
|
|
16
17
|
});
|
|
17
18
|
// Request interceptor for logging and auth
|
|
18
|
-
this.axios.interceptors.request.use((config) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
this.axios.interceptors.request.use(async (config) => {
|
|
20
|
+
// Check if we need to wait for token
|
|
21
|
+
if (!config.skipAuth && !this.currentToken && this.tokenProvider) {
|
|
22
|
+
try {
|
|
23
|
+
console.log('[SDK] Waiting for token...');
|
|
24
|
+
const token = await this.tokenProvider();
|
|
25
|
+
if (token) {
|
|
26
|
+
this.updateToken(token);
|
|
27
|
+
// Ensure header is set on this specific request config
|
|
28
|
+
config.headers['x-cms-token'] = token;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('[SDK] Failed to get token from provider:', error);
|
|
33
|
+
}
|
|
23
34
|
}
|
|
35
|
+
// Ensure token is in headers if we have it (and not skipped)
|
|
36
|
+
if (!config.skipAuth && this.currentToken) {
|
|
37
|
+
config.headers['x-cms-token'] = this.currentToken;
|
|
38
|
+
}
|
|
39
|
+
console.log(`[SDK] Making ${config.method?.toUpperCase()} request to: ${config.baseURL || ''}${config.url}`);
|
|
40
|
+
// console.log('[SDK] Request headers:', config.headers);
|
|
24
41
|
return config;
|
|
25
42
|
}, (error) => {
|
|
26
43
|
console.error('[SDK] Request error:', error);
|
|
@@ -28,14 +45,18 @@ export class ApiClient {
|
|
|
28
45
|
});
|
|
29
46
|
// Response interceptor for logging
|
|
30
47
|
this.axios.interceptors.response.use((response) => {
|
|
31
|
-
console.log('[SDK] Response status:', response.status);
|
|
32
|
-
console.log('[SDK] Response data:', response.data);
|
|
48
|
+
// console.log('[SDK] Response status:', response.status);
|
|
33
49
|
return response;
|
|
34
50
|
}, (error) => {
|
|
35
|
-
console.error('[SDK] Response error:', error);
|
|
51
|
+
console.error('[SDK] Response error:', error.message);
|
|
36
52
|
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
37
53
|
});
|
|
38
54
|
}
|
|
55
|
+
// Set a provider that returns a promise resolving to the token
|
|
56
|
+
// This allows requests to wait until the token is ready
|
|
57
|
+
setTokenProvider(provider) {
|
|
58
|
+
this.tokenProvider = provider;
|
|
59
|
+
}
|
|
39
60
|
// Convenience methods
|
|
40
61
|
async get(url, config) {
|
|
41
62
|
const response = await this.axios.get(url, config);
|
|
@@ -69,7 +90,7 @@ export class ApiClient {
|
|
|
69
90
|
this.currentToken = token;
|
|
70
91
|
if (token) {
|
|
71
92
|
this.setHeader('x-cms-token', token);
|
|
72
|
-
console.log('[SDK] Token updated in ApiClient:', token.substring(0, 8) + '...');
|
|
93
|
+
// console.log('[SDK] Token updated in ApiClient:', token.substring(0, 8) + '...');
|
|
73
94
|
}
|
|
74
95
|
else {
|
|
75
96
|
this.removeHeader('x-cms-token');
|
|
@@ -391,6 +391,14 @@ export interface SimpleFunnelContext<TCustom = {}> {
|
|
|
391
391
|
* Only moves forward, never backward - used for analytics
|
|
392
392
|
*/
|
|
393
393
|
furthestStepId?: string;
|
|
394
|
+
/**
|
|
395
|
+
* ✅ Environment context (staging or production)
|
|
396
|
+
* - Determined at session initialization based on entry URL
|
|
397
|
+
* - Ensures all navigation stays in the same environment
|
|
398
|
+
* - 'staging': Uses funnel.config (alias domains like funnel--store.cdn.tagadapay.com)
|
|
399
|
+
* - 'production': Uses funnel.productionConfig (custom domains)
|
|
400
|
+
*/
|
|
401
|
+
environment?: 'staging' | 'production';
|
|
394
402
|
startedAt: number;
|
|
395
403
|
lastActivityAt: number;
|
|
396
404
|
/**
|
|
@@ -416,6 +424,12 @@ export interface FunnelInitializeRequest {
|
|
|
416
424
|
funnelId?: string;
|
|
417
425
|
entryStepId?: string;
|
|
418
426
|
existingSessionId?: string;
|
|
427
|
+
/**
|
|
428
|
+
* Current URL for session synchronization (browser back/forward handling)
|
|
429
|
+
* If not provided, SDK will automatically use window.location.href
|
|
430
|
+
* @example '/checkout', 'https://store.com/payment'
|
|
431
|
+
*/
|
|
432
|
+
currentUrl?: string;
|
|
419
433
|
}
|
|
420
434
|
export interface FunnelInitializeResponse {
|
|
421
435
|
success: boolean;
|
|
@@ -76,9 +76,32 @@ export interface PaymentOptions {
|
|
|
76
76
|
threedsProvider?: 'basis_theory';
|
|
77
77
|
initiatedBy?: 'customer' | 'merchant';
|
|
78
78
|
source?: 'upsell' | 'checkout' | 'offer' | 'missing_club' | 'forced';
|
|
79
|
+
/** @deprecated Use onPaymentSuccess instead - this will be removed in v3 */
|
|
79
80
|
onSuccess?: (response: PaymentResponse) => void;
|
|
81
|
+
/** @deprecated Use onPaymentFailed instead - this will be removed in v3 */
|
|
80
82
|
onFailure?: (error: string) => void;
|
|
81
83
|
onRequireAction?: (payment: Payment) => void;
|
|
84
|
+
/**
|
|
85
|
+
* Called when payment succeeds (matches FunnelActionType.PAYMENT_SUCCESS)
|
|
86
|
+
* Use this with useFunnel's next() to navigate to the next step
|
|
87
|
+
* This replaces automatic redirects to /post or /thankyou
|
|
88
|
+
*/
|
|
89
|
+
onPaymentSuccess?: (response: PaymentResponse) => void;
|
|
90
|
+
/**
|
|
91
|
+
* Called when payment fails (matches FunnelActionType.PAYMENT_FAILED)
|
|
92
|
+
* Use this to handle retry logic, show errors, or trigger funnel failure flows
|
|
93
|
+
*/
|
|
94
|
+
onPaymentFailed?: (error: {
|
|
95
|
+
code: string;
|
|
96
|
+
message: string;
|
|
97
|
+
payment?: Payment;
|
|
98
|
+
}) => void;
|
|
99
|
+
/**
|
|
100
|
+
* Disable automatic redirects to /post or /thankyou (default: true)
|
|
101
|
+
* Set to false only for legacy implementations without funnel orchestrator
|
|
102
|
+
* The funnel orchestrator handles navigation automatically with useFunnel
|
|
103
|
+
*/
|
|
104
|
+
disableAutoRedirect?: boolean;
|
|
82
105
|
}
|
|
83
106
|
export interface CardPaymentMethod {
|
|
84
107
|
cardNumber: string;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the Tagada Pay SDK Core
|
|
3
|
+
*/
|
|
4
|
+
export type Environment = 'production' | 'development' | 'local';
|
|
5
|
+
export interface ApiConfig {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
endpoints: {
|
|
8
|
+
checkout: {
|
|
9
|
+
sessionInit: string;
|
|
10
|
+
sessionStatus: string;
|
|
11
|
+
};
|
|
12
|
+
customer: {
|
|
13
|
+
profile: string;
|
|
14
|
+
session: string;
|
|
15
|
+
};
|
|
16
|
+
store: {
|
|
17
|
+
config: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface EnvironmentConfig {
|
|
22
|
+
environment: Environment;
|
|
23
|
+
apiConfig: ApiConfig;
|
|
24
|
+
}
|
|
25
|
+
export interface Customer {
|
|
26
|
+
id: string;
|
|
27
|
+
email?: string;
|
|
28
|
+
firstName?: string;
|
|
29
|
+
lastName?: string;
|
|
30
|
+
phone?: string;
|
|
31
|
+
isAuthenticated: boolean;
|
|
32
|
+
role: 'authenticated' | 'anonymous';
|
|
33
|
+
}
|
|
34
|
+
export type SessionRole = 'authenticated' | 'anonymous';
|
|
35
|
+
export interface Session {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
storeId: string;
|
|
38
|
+
accountId: string;
|
|
39
|
+
customerId: string;
|
|
40
|
+
role: SessionRole;
|
|
41
|
+
isValid: boolean;
|
|
42
|
+
isLoading: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface AuthState {
|
|
45
|
+
isAuthenticated: boolean;
|
|
46
|
+
isLoading: boolean;
|
|
47
|
+
customer: Customer | null;
|
|
48
|
+
session: Session | null;
|
|
49
|
+
}
|
|
50
|
+
export interface Locale {
|
|
51
|
+
locale: string;
|
|
52
|
+
language: string;
|
|
53
|
+
region: string;
|
|
54
|
+
messages?: Record<string, string>;
|
|
55
|
+
}
|
|
56
|
+
export interface Currency {
|
|
57
|
+
code: string;
|
|
58
|
+
symbol: string;
|
|
59
|
+
name: string;
|
|
60
|
+
}
|
|
61
|
+
export interface Store {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
domain: string;
|
|
65
|
+
currency: string;
|
|
66
|
+
locale: string;
|
|
67
|
+
presentmentCurrencies: string[];
|
|
68
|
+
chargeCurrencies: string[];
|
|
69
|
+
}
|
|
70
|
+
export interface PickupPoint {
|
|
71
|
+
id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
country: string;
|
|
74
|
+
postal_code: string;
|
|
75
|
+
city: string;
|
|
76
|
+
address: string;
|
|
77
|
+
address2?: string;
|
|
78
|
+
house_number?: string;
|
|
79
|
+
phone?: string;
|
|
80
|
+
email?: string;
|
|
81
|
+
latitude?: number;
|
|
82
|
+
longitude?: number;
|
|
83
|
+
opening_hours?: string;
|
|
84
|
+
extra_info?: string;
|
|
85
|
+
}
|
|
86
|
+
export interface OrderItem {
|
|
87
|
+
id: string;
|
|
88
|
+
productId: string;
|
|
89
|
+
variantId: string;
|
|
90
|
+
quantity: number;
|
|
91
|
+
unitAmount: number;
|
|
92
|
+
amount: number;
|
|
93
|
+
adjustedAmount: number;
|
|
94
|
+
currency: string;
|
|
95
|
+
recurring?: boolean;
|
|
96
|
+
interval?: 'day' | 'week' | 'month' | 'year' | null;
|
|
97
|
+
intervalCount?: number | null;
|
|
98
|
+
orderLineItemProduct?: {
|
|
99
|
+
name: string;
|
|
100
|
+
};
|
|
101
|
+
orderLineItemVariant?: {
|
|
102
|
+
name: string;
|
|
103
|
+
imageUrl: string | null;
|
|
104
|
+
};
|
|
105
|
+
subscriptionSettings?: {
|
|
106
|
+
trial?: boolean;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export interface OrderSummary {
|
|
110
|
+
currency: string;
|
|
111
|
+
totalPromotionAmount: number;
|
|
112
|
+
totalTaxAmount: number;
|
|
113
|
+
shippingCost: number;
|
|
114
|
+
shippingCostIsFree: boolean;
|
|
115
|
+
subtotalAmount: number;
|
|
116
|
+
subtotalAdjustedAmount: number;
|
|
117
|
+
totalAdjustedAmount: number;
|
|
118
|
+
adjustments?: {
|
|
119
|
+
type: string;
|
|
120
|
+
amount: number;
|
|
121
|
+
description: string;
|
|
122
|
+
}[];
|
|
123
|
+
}
|
|
124
|
+
export interface OrderAddress {
|
|
125
|
+
firstName: string;
|
|
126
|
+
lastName: string;
|
|
127
|
+
address1: string;
|
|
128
|
+
address2?: string;
|
|
129
|
+
city: string;
|
|
130
|
+
state: string;
|
|
131
|
+
postal: string;
|
|
132
|
+
country: string;
|
|
133
|
+
phone?: string;
|
|
134
|
+
}
|
|
135
|
+
export interface Order {
|
|
136
|
+
id: string;
|
|
137
|
+
currency: string;
|
|
138
|
+
paidAmount: number;
|
|
139
|
+
status: string;
|
|
140
|
+
createdAt: string;
|
|
141
|
+
metadata?: Record<string, any>;
|
|
142
|
+
items: OrderItem[];
|
|
143
|
+
summaries?: OrderSummary[];
|
|
144
|
+
shippingAddress?: OrderAddress;
|
|
145
|
+
billingAddress?: OrderAddress;
|
|
146
|
+
pickupAddress?: PickupPoint;
|
|
147
|
+
checkoutSession?: {
|
|
148
|
+
returnUrl?: string;
|
|
149
|
+
[key: string]: any;
|
|
150
|
+
};
|
|
151
|
+
relatedOrders?: Order[];
|
|
152
|
+
}
|
|
153
|
+
export interface PaymentSummary {
|
|
154
|
+
id: string;
|
|
155
|
+
status: string;
|
|
156
|
+
amount: number;
|
|
157
|
+
currency: string;
|
|
158
|
+
createdAt: string;
|
|
159
|
+
updatedAt?: string;
|
|
160
|
+
provider?: string;
|
|
161
|
+
metadata?: Record<string, any>;
|
|
162
|
+
}
|
|
163
|
+
export interface PromotionSummary {
|
|
164
|
+
id: string;
|
|
165
|
+
code?: string | null;
|
|
166
|
+
type?: string;
|
|
167
|
+
amount?: number;
|
|
168
|
+
description?: string | null;
|
|
169
|
+
}
|
|
170
|
+
export interface OrderAdjustmentSummary {
|
|
171
|
+
type: string;
|
|
172
|
+
amount: number;
|
|
173
|
+
description: string;
|
|
174
|
+
}
|
|
175
|
+
export interface OrderWithRelations extends Order {
|
|
176
|
+
customer?: Customer;
|
|
177
|
+
store?: Store;
|
|
178
|
+
account?: {
|
|
179
|
+
id: string;
|
|
180
|
+
name?: string;
|
|
181
|
+
} | undefined;
|
|
182
|
+
items: OrderItem[];
|
|
183
|
+
payments?: PaymentSummary[];
|
|
184
|
+
summaries: OrderSummary[];
|
|
185
|
+
checkoutSession?: {
|
|
186
|
+
id?: string;
|
|
187
|
+
returnUrl?: string;
|
|
188
|
+
[key: string]: any;
|
|
189
|
+
};
|
|
190
|
+
promotions?: PromotionSummary[];
|
|
191
|
+
subscriptions?: any[];
|
|
192
|
+
adjustments: OrderAdjustmentSummary[];
|
|
193
|
+
}
|
|
194
|
+
export interface CustomerAddress {
|
|
195
|
+
company?: string;
|
|
196
|
+
firstName: string;
|
|
197
|
+
lastName: string;
|
|
198
|
+
address1: string;
|
|
199
|
+
city: string;
|
|
200
|
+
country: string;
|
|
201
|
+
state: string;
|
|
202
|
+
postal: string;
|
|
203
|
+
phone?: string;
|
|
204
|
+
email?: string;
|
|
205
|
+
}
|
|
206
|
+
export interface CustomerOrderSummary {
|
|
207
|
+
id: string;
|
|
208
|
+
storeId: string;
|
|
209
|
+
accountId: string;
|
|
210
|
+
createdAt: string;
|
|
211
|
+
updatedAt: string;
|
|
212
|
+
status: string;
|
|
213
|
+
cancelledAt: string | null;
|
|
214
|
+
cancelledReason: string | null;
|
|
215
|
+
paidAt: string | null;
|
|
216
|
+
paidAmount: number | null;
|
|
217
|
+
openAt: string | null;
|
|
218
|
+
abandonedAt: string | null;
|
|
219
|
+
currency: string;
|
|
220
|
+
externalCustomerType: string | null;
|
|
221
|
+
externalCustomerId: string | null;
|
|
222
|
+
externalOrderId: string | null;
|
|
223
|
+
billingAddress: CustomerAddress;
|
|
224
|
+
shippingAddress: Omit<CustomerAddress, 'email'>;
|
|
225
|
+
pickupAddress: any | null;
|
|
226
|
+
taxesIncluded: boolean;
|
|
227
|
+
draft: boolean;
|
|
228
|
+
checkoutSessionId: string | null;
|
|
229
|
+
sessionHash: string | null;
|
|
230
|
+
customerId: string;
|
|
231
|
+
createdFrom: string | null;
|
|
232
|
+
paymentInstrumentId: string | null;
|
|
233
|
+
refundedAt: string | null;
|
|
234
|
+
refundedAmount: number | null;
|
|
235
|
+
metadata?: Record<string, any>;
|
|
236
|
+
}
|
|
237
|
+
export interface CustomerInfos {
|
|
238
|
+
customer: {
|
|
239
|
+
id: string;
|
|
240
|
+
email: string | null;
|
|
241
|
+
firstName: string | null;
|
|
242
|
+
lastName: string | null;
|
|
243
|
+
externalCustomerId: string | null;
|
|
244
|
+
lastOrderId: string | null;
|
|
245
|
+
accountId: string;
|
|
246
|
+
storeId: string;
|
|
247
|
+
billingAddress: CustomerAddress | null;
|
|
248
|
+
shippingAddress: Omit<CustomerAddress, 'email'> | null;
|
|
249
|
+
currency: string | null;
|
|
250
|
+
locale: string | null;
|
|
251
|
+
draft: boolean;
|
|
252
|
+
acceptsMarketing: boolean;
|
|
253
|
+
createdAt: string;
|
|
254
|
+
updatedAt: string;
|
|
255
|
+
metadata: Record<string, any>;
|
|
256
|
+
device: any | null;
|
|
257
|
+
orders: CustomerOrderSummary[];
|
|
258
|
+
subscriptions: any[];
|
|
259
|
+
};
|
|
260
|
+
promotionCodes: any[];
|
|
261
|
+
}
|
|
262
|
+
export interface SessionInitResponse {
|
|
263
|
+
store: Store;
|
|
264
|
+
locale: string;
|
|
265
|
+
messages?: Record<string, string>;
|
|
266
|
+
customer?: Customer;
|
|
267
|
+
session?: Session;
|
|
268
|
+
}
|
|
269
|
+
export interface AnonymousTokenResponse {
|
|
270
|
+
token: string;
|
|
271
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface DeviceInfo {
|
|
2
|
+
userAgent: {
|
|
3
|
+
browser: {
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
};
|
|
7
|
+
os: {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
};
|
|
11
|
+
device?: {
|
|
12
|
+
type: string;
|
|
13
|
+
model: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
screenResolution: {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
};
|
|
20
|
+
timeZone: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get browser locale
|
|
24
|
+
*/
|
|
25
|
+
export declare function getBrowserLocale(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Collect all device information
|
|
28
|
+
*/
|
|
29
|
+
export declare function collectDeviceInfo(): DeviceInfo;
|
|
30
|
+
/**
|
|
31
|
+
* Get URL parameters for session initialization
|
|
32
|
+
*/
|
|
33
|
+
export declare function getUrlParams(): {
|
|
34
|
+
locale?: string;
|
|
35
|
+
currency?: string;
|
|
36
|
+
utmSource?: string;
|
|
37
|
+
utmMedium?: string;
|
|
38
|
+
utmCampaign?: string;
|
|
39
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get basic browser information from user agent
|
|
3
|
+
*/
|
|
4
|
+
function getBrowserInfo() {
|
|
5
|
+
const userAgent = navigator.userAgent;
|
|
6
|
+
// Chrome
|
|
7
|
+
if (userAgent.includes('Chrome')) {
|
|
8
|
+
const match = /Chrome\/(\d+)/.exec(userAgent);
|
|
9
|
+
return { name: 'Chrome', version: match ? match[1] : 'unknown' };
|
|
10
|
+
}
|
|
11
|
+
// Firefox
|
|
12
|
+
if (userAgent.includes('Firefox')) {
|
|
13
|
+
const match = /Firefox\/(\d+)/.exec(userAgent);
|
|
14
|
+
return { name: 'Firefox', version: match ? match[1] : 'unknown' };
|
|
15
|
+
}
|
|
16
|
+
// Safari
|
|
17
|
+
if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
|
|
18
|
+
const match = /Version\/(\d+)/.exec(userAgent);
|
|
19
|
+
return { name: 'Safari', version: match ? match[1] : 'unknown' };
|
|
20
|
+
}
|
|
21
|
+
// Edge
|
|
22
|
+
if (userAgent.includes('Edge')) {
|
|
23
|
+
const match = /Edge\/(\d+)/.exec(userAgent);
|
|
24
|
+
return { name: 'Edge', version: match ? match[1] : 'unknown' };
|
|
25
|
+
}
|
|
26
|
+
return { name: 'unknown', version: 'unknown' };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get basic OS information from user agent
|
|
30
|
+
*/
|
|
31
|
+
function getOSInfo() {
|
|
32
|
+
const userAgent = navigator.userAgent;
|
|
33
|
+
// Windows
|
|
34
|
+
if (userAgent.includes('Windows')) {
|
|
35
|
+
if (userAgent.includes('Windows NT 10.0'))
|
|
36
|
+
return { name: 'Windows', version: '10' };
|
|
37
|
+
if (userAgent.includes('Windows NT 6.3'))
|
|
38
|
+
return { name: 'Windows', version: '8.1' };
|
|
39
|
+
if (userAgent.includes('Windows NT 6.2'))
|
|
40
|
+
return { name: 'Windows', version: '8' };
|
|
41
|
+
if (userAgent.includes('Windows NT 6.1'))
|
|
42
|
+
return { name: 'Windows', version: '7' };
|
|
43
|
+
return { name: 'Windows', version: 'unknown' };
|
|
44
|
+
}
|
|
45
|
+
// macOS
|
|
46
|
+
if (userAgent.includes('Mac OS X')) {
|
|
47
|
+
const match = /Mac OS X (\d+[._]\d+)/.exec(userAgent);
|
|
48
|
+
return { name: 'macOS', version: match ? match[1].replace('_', '.') : 'unknown' };
|
|
49
|
+
}
|
|
50
|
+
// iOS
|
|
51
|
+
if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
|
|
52
|
+
const match = /OS (\d+[._]\d+)/.exec(userAgent);
|
|
53
|
+
return { name: 'iOS', version: match ? match[1].replace('_', '.') : 'unknown' };
|
|
54
|
+
}
|
|
55
|
+
// Android
|
|
56
|
+
if (userAgent.includes('Android')) {
|
|
57
|
+
const match = /Android (\d+[.\d]*)/.exec(userAgent);
|
|
58
|
+
return { name: 'Android', version: match ? match[1] : 'unknown' };
|
|
59
|
+
}
|
|
60
|
+
// Linux
|
|
61
|
+
if (userAgent.includes('Linux')) {
|
|
62
|
+
return { name: 'Linux', version: 'unknown' };
|
|
63
|
+
}
|
|
64
|
+
return { name: 'unknown', version: 'unknown' };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get device information
|
|
68
|
+
*/
|
|
69
|
+
function getDeviceInfo() {
|
|
70
|
+
const userAgent = navigator.userAgent;
|
|
71
|
+
// Mobile devices
|
|
72
|
+
if (userAgent.includes('iPhone')) {
|
|
73
|
+
return { type: 'mobile', model: 'iPhone' };
|
|
74
|
+
}
|
|
75
|
+
if (userAgent.includes('iPad')) {
|
|
76
|
+
return { type: 'tablet', model: 'iPad' };
|
|
77
|
+
}
|
|
78
|
+
if (userAgent.includes('Android')) {
|
|
79
|
+
if (userAgent.includes('Mobile')) {
|
|
80
|
+
return { type: 'mobile', model: 'Android' };
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return { type: 'tablet', model: 'Android' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Desktop (no specific device info)
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get screen resolution
|
|
91
|
+
*/
|
|
92
|
+
function getScreenResolution() {
|
|
93
|
+
return {
|
|
94
|
+
width: window.screen.width,
|
|
95
|
+
height: window.screen.height,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get timezone
|
|
100
|
+
*/
|
|
101
|
+
function getTimeZone() {
|
|
102
|
+
try {
|
|
103
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error('Failed to get timezone:', error);
|
|
107
|
+
return 'UTC';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get browser locale
|
|
112
|
+
*/
|
|
113
|
+
export function getBrowserLocale() {
|
|
114
|
+
try {
|
|
115
|
+
return navigator.language || 'en-US';
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error('Failed to get browser locale:', error);
|
|
119
|
+
return 'en-US';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Collect all device information
|
|
124
|
+
*/
|
|
125
|
+
export function collectDeviceInfo() {
|
|
126
|
+
if (typeof window === 'undefined') {
|
|
127
|
+
// Server-side fallback
|
|
128
|
+
return {
|
|
129
|
+
userAgent: {
|
|
130
|
+
browser: { name: 'unknown', version: 'unknown' },
|
|
131
|
+
os: { name: 'unknown', version: 'unknown' },
|
|
132
|
+
},
|
|
133
|
+
screenResolution: { width: 0, height: 0 },
|
|
134
|
+
timeZone: 'UTC',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
userAgent: {
|
|
139
|
+
browser: getBrowserInfo(),
|
|
140
|
+
os: getOSInfo(),
|
|
141
|
+
device: getDeviceInfo(),
|
|
142
|
+
},
|
|
143
|
+
screenResolution: getScreenResolution(),
|
|
144
|
+
timeZone: getTimeZone(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get URL parameters for session initialization
|
|
149
|
+
*/
|
|
150
|
+
export function getUrlParams() {
|
|
151
|
+
if (typeof window === 'undefined') {
|
|
152
|
+
return {};
|
|
153
|
+
}
|
|
154
|
+
const params = new URLSearchParams(window.location.search);
|
|
155
|
+
return {
|
|
156
|
+
locale: params.get('locale') || undefined,
|
|
157
|
+
currency: params.get('currency') || undefined,
|
|
158
|
+
utmSource: params.get('utm_source') || undefined,
|
|
159
|
+
utmMedium: params.get('utm_medium') || undefined,
|
|
160
|
+
utmCampaign: params.get('utm_campaign') || undefined,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple event dispatcher for handling state changes in the SDK
|
|
3
|
+
*/
|
|
4
|
+
export type Listener<T> = (data: T) => void;
|
|
5
|
+
export declare class EventDispatcher<T> {
|
|
6
|
+
private listeners;
|
|
7
|
+
subscribe(listener: Listener<T>): () => void;
|
|
8
|
+
notify(data: T): void;
|
|
9
|
+
clear(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class EventDispatcher {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.listeners = new Set();
|
|
4
|
+
}
|
|
5
|
+
subscribe(listener) {
|
|
6
|
+
this.listeners.add(listener);
|
|
7
|
+
return () => {
|
|
8
|
+
this.listeners.delete(listener);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
notify(data) {
|
|
12
|
+
this.listeners.forEach((listener) => {
|
|
13
|
+
try {
|
|
14
|
+
listener(data);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
console.error('Error in event listener:', error);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
clear() {
|
|
22
|
+
this.listeners.clear();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Session } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Decode a JWT token to extract session information
|
|
4
|
+
* This is a simple client-side decoder - do not use for security validation
|
|
5
|
+
*/
|
|
6
|
+
export declare function decodeJWTClient(token: string): Session | null;
|
|
7
|
+
/**
|
|
8
|
+
* Check if a JWT token is expired
|
|
9
|
+
*/
|
|
10
|
+
export declare function isTokenExpired(token: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Get token expiration time
|
|
13
|
+
*/
|
|
14
|
+
export declare function getTokenExpiration(token: string): Date | null;
|