@tagadapay/plugin-sdk 2.7.14 → 2.7.18
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/v2/core/__tests__/pathRemapping.test.d.ts +11 -0
- package/dist/v2/core/__tests__/pathRemapping.test.js +776 -0
- package/dist/v2/core/pathRemapping.d.ts +92 -0
- package/dist/v2/core/pathRemapping.js +295 -21
- package/dist/v2/core/resources/funnel.d.ts +29 -1
- package/dist/v2/core/resources/funnel.js +6 -2
- package/dist/v2/core/utils/env.d.ts +5 -0
- package/dist/v2/core/utils/env.js +20 -0
- package/dist/v2/core/utils/order.d.ts +1 -0
- package/dist/v2/core/utils/pluginConfig.d.ts +1 -4
- package/dist/v2/core/utils/pluginConfig.js +14 -24
- package/dist/v2/index.d.ts +1 -1
- package/dist/v2/index.js +1 -1
- package/dist/v2/react/hooks/useApiClient.d.ts +19 -0
- package/dist/v2/react/hooks/useApiClient.js +22 -0
- package/dist/v2/react/hooks/useCredits.js +0 -1
- package/dist/v2/react/hooks/useFunnel.js +31 -4
- package/dist/v2/react/hooks/useRemappableParams.d.ts +21 -0
- package/dist/v2/react/hooks/useRemappableParams.js +104 -0
- package/dist/v2/react/index.d.ts +1 -0
- package/dist/v2/react/index.js +1 -0
- package/dist/v2/react/providers/TagadaProvider.js +15 -2
- package/dist/v2/react/utils/sessionWaiter.d.ts +26 -0
- package/dist/v2/react/utils/sessionWaiter.js +72 -0
- package/package.json +8 -2
|
@@ -23,12 +23,26 @@ export interface PathInfo {
|
|
|
23
23
|
/** URL hash without the # symbol */
|
|
24
24
|
hash: string;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Result of route matching with extracted parameters
|
|
28
|
+
*/
|
|
29
|
+
export interface RouteMatchResult {
|
|
30
|
+
/** Whether the route matched */
|
|
31
|
+
matched: boolean;
|
|
32
|
+
/** Extracted URL parameters (empty object if no match or no params) */
|
|
33
|
+
params: Record<string, string>;
|
|
34
|
+
}
|
|
26
35
|
/**
|
|
27
36
|
* Get the internal path that the plugin expects
|
|
28
37
|
*
|
|
29
38
|
* Reads from the meta tag injected by TagadaPay middleware.
|
|
30
39
|
* Results are cached for performance.
|
|
31
40
|
*
|
|
41
|
+
* **Development/Testing Mode (localhost only):**
|
|
42
|
+
* You can simulate path remapping in two ways:
|
|
43
|
+
* 1. Query parameter: `?__remap=/internal-path`
|
|
44
|
+
* 2. localStorage with explicit external → internal mapping
|
|
45
|
+
*
|
|
32
46
|
* @returns The internal path from meta tag, or null if no remapping is active
|
|
33
47
|
* @throws {Error} If called in a non-browser environment (SSR)
|
|
34
48
|
*
|
|
@@ -39,6 +53,40 @@ export interface PathInfo {
|
|
|
39
53
|
* console.log('Remapped from external to:', internalPath);
|
|
40
54
|
* }
|
|
41
55
|
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example Simulating remapping in localhost - RECOMMENDED FORMAT
|
|
58
|
+
* ```typescript
|
|
59
|
+
* // NEW: Explicit mapping (clear which path remaps to which)
|
|
60
|
+
* localStorage.setItem('tagadapay-remap', JSON.stringify({
|
|
61
|
+
* externalPath: '/myremap/:param',
|
|
62
|
+
* internalPath: '/hello-with-param/:myparam'
|
|
63
|
+
* }));
|
|
64
|
+
* // Then navigate to: http://localhost:5173/myremap/test123
|
|
65
|
+
* // Result: Matches external pattern, uses internal path /hello-with-param/:myparam
|
|
66
|
+
*
|
|
67
|
+
* // Option 2: Query parameter (for quick tests)
|
|
68
|
+
* // Navigate to: http://localhost:5173/custom-page?__remap=/hello
|
|
69
|
+
*
|
|
70
|
+
* // Clear simulation
|
|
71
|
+
* localStorage.removeItem('tagadapay-remap');
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example More Examples
|
|
75
|
+
* ```typescript
|
|
76
|
+
* // Remap /products/:id to /hello-with-param/:myparam
|
|
77
|
+
* localStorage.setItem('tagadapay-remap', JSON.stringify({
|
|
78
|
+
* externalPath: '/products/:id',
|
|
79
|
+
* internalPath: '/hello-with-param/:myparam'
|
|
80
|
+
* }));
|
|
81
|
+
* // Visit: http://localhost:5173/products/abc123
|
|
82
|
+
*
|
|
83
|
+
* // Remap /custom-hello to /hello
|
|
84
|
+
* localStorage.setItem('tagadapay-remap', JSON.stringify({
|
|
85
|
+
* externalPath: '/custom-hello',
|
|
86
|
+
* internalPath: '/hello'
|
|
87
|
+
* }));
|
|
88
|
+
* // Visit: http://localhost:5173/custom-hello
|
|
89
|
+
* ```
|
|
42
90
|
*/
|
|
43
91
|
export declare function getInternalPath(): string | null;
|
|
44
92
|
/**
|
|
@@ -72,6 +120,50 @@ export declare function isPathRemapped(): boolean;
|
|
|
72
120
|
* ```
|
|
73
121
|
*/
|
|
74
122
|
export declare function getPathInfo(): PathInfo;
|
|
123
|
+
/**
|
|
124
|
+
* Match a route and extract URL parameters
|
|
125
|
+
*
|
|
126
|
+
* Handles path remapping automatically and extracts parameters from the URL.
|
|
127
|
+
* This is the preferred method when you need both match status and parameters.
|
|
128
|
+
*
|
|
129
|
+
* @param internalPath - The plugin's defined path (must start with /)
|
|
130
|
+
* @returns Object with `matched` boolean and `params` object
|
|
131
|
+
* @throws {Error} If internalPath is invalid
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* // In a catch-all route handler
|
|
136
|
+
* const result = matchRoute('/hello-with-param/:myparam');
|
|
137
|
+
* if (result.matched) {
|
|
138
|
+
* console.log('Matched! Param:', result.params.myparam);
|
|
139
|
+
* return <HelloPage myparam={result.params.myparam} />;
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function matchRoute(internalPath: string): RouteMatchResult;
|
|
144
|
+
/**
|
|
145
|
+
* Check if the current URL should match a given internal path
|
|
146
|
+
*
|
|
147
|
+
* Handles path remapping automatically. Use this for catch-all routing
|
|
148
|
+
* implementations where you only need to check if a path matches.
|
|
149
|
+
*
|
|
150
|
+
* For extracting parameters, use `matchRoute()` instead.
|
|
151
|
+
*
|
|
152
|
+
* @param internalPath - The plugin's defined path (must start with /)
|
|
153
|
+
* @returns true if current URL should render this path's component
|
|
154
|
+
* @throws {Error} If internalPath is invalid
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* // In a catch-all route handler
|
|
159
|
+
* if (shouldMatchRoute('/hello')) {
|
|
160
|
+
* return <HelloPage />;
|
|
161
|
+
* }
|
|
162
|
+
* if (shouldMatchRoute('/about')) {
|
|
163
|
+
* return <AboutPage />;
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
75
167
|
export declare function shouldMatchRoute(internalPath: string): boolean;
|
|
76
168
|
/**
|
|
77
169
|
* Get the current matched path
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
|
+
import { match } from 'path-to-regexp';
|
|
9
10
|
// ============================================================================
|
|
10
11
|
// Private Cache
|
|
11
12
|
// ============================================================================
|
|
@@ -56,6 +57,11 @@ if (isBrowser) {
|
|
|
56
57
|
* Reads from the meta tag injected by TagadaPay middleware.
|
|
57
58
|
* Results are cached for performance.
|
|
58
59
|
*
|
|
60
|
+
* **Development/Testing Mode (localhost only):**
|
|
61
|
+
* You can simulate path remapping in two ways:
|
|
62
|
+
* 1. Query parameter: `?__remap=/internal-path`
|
|
63
|
+
* 2. localStorage with explicit external → internal mapping
|
|
64
|
+
*
|
|
59
65
|
* @returns The internal path from meta tag, or null if no remapping is active
|
|
60
66
|
* @throws {Error} If called in a non-browser environment (SSR)
|
|
61
67
|
*
|
|
@@ -66,6 +72,40 @@ if (isBrowser) {
|
|
|
66
72
|
* console.log('Remapped from external to:', internalPath);
|
|
67
73
|
* }
|
|
68
74
|
* ```
|
|
75
|
+
*
|
|
76
|
+
* @example Simulating remapping in localhost - RECOMMENDED FORMAT
|
|
77
|
+
* ```typescript
|
|
78
|
+
* // NEW: Explicit mapping (clear which path remaps to which)
|
|
79
|
+
* localStorage.setItem('tagadapay-remap', JSON.stringify({
|
|
80
|
+
* externalPath: '/myremap/:param',
|
|
81
|
+
* internalPath: '/hello-with-param/:myparam'
|
|
82
|
+
* }));
|
|
83
|
+
* // Then navigate to: http://localhost:5173/myremap/test123
|
|
84
|
+
* // Result: Matches external pattern, uses internal path /hello-with-param/:myparam
|
|
85
|
+
*
|
|
86
|
+
* // Option 2: Query parameter (for quick tests)
|
|
87
|
+
* // Navigate to: http://localhost:5173/custom-page?__remap=/hello
|
|
88
|
+
*
|
|
89
|
+
* // Clear simulation
|
|
90
|
+
* localStorage.removeItem('tagadapay-remap');
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @example More Examples
|
|
94
|
+
* ```typescript
|
|
95
|
+
* // Remap /products/:id to /hello-with-param/:myparam
|
|
96
|
+
* localStorage.setItem('tagadapay-remap', JSON.stringify({
|
|
97
|
+
* externalPath: '/products/:id',
|
|
98
|
+
* internalPath: '/hello-with-param/:myparam'
|
|
99
|
+
* }));
|
|
100
|
+
* // Visit: http://localhost:5173/products/abc123
|
|
101
|
+
*
|
|
102
|
+
* // Remap /custom-hello to /hello
|
|
103
|
+
* localStorage.setItem('tagadapay-remap', JSON.stringify({
|
|
104
|
+
* externalPath: '/custom-hello',
|
|
105
|
+
* internalPath: '/hello'
|
|
106
|
+
* }));
|
|
107
|
+
* // Visit: http://localhost:5173/custom-hello
|
|
108
|
+
* ```
|
|
69
109
|
*/
|
|
70
110
|
export function getInternalPath() {
|
|
71
111
|
// SSR check
|
|
@@ -77,12 +117,111 @@ export function getInternalPath() {
|
|
|
77
117
|
}
|
|
78
118
|
// Return cached value if available
|
|
79
119
|
if (internalPathCache !== undefined) {
|
|
120
|
+
console.log('[TagadaPay SDK] 📦 Returning cached internal path:', internalPathCache);
|
|
80
121
|
return internalPathCache;
|
|
81
122
|
}
|
|
123
|
+
console.log('[TagadaPay SDK] 🔍 getInternalPath() called for:', window.location.pathname);
|
|
82
124
|
try {
|
|
83
|
-
//
|
|
125
|
+
// Development/Testing: Check for simulation via query parameter or localStorage
|
|
126
|
+
// Only works on localhost for security
|
|
127
|
+
const isLocalhost = window.location.hostname === 'localhost' ||
|
|
128
|
+
window.location.hostname === '127.0.0.1' ||
|
|
129
|
+
window.location.hostname.endsWith('.localhost');
|
|
130
|
+
console.log('[TagadaPay SDK] 🏠 isLocalhost:', isLocalhost);
|
|
131
|
+
if (isLocalhost) {
|
|
132
|
+
// Priority 1: Query parameter ?__remap=/internal-path
|
|
133
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
134
|
+
const queryRemap = urlParams.get('__remap');
|
|
135
|
+
console.log('[TagadaPay SDK] 🔍 Query parameter __remap:', queryRemap);
|
|
136
|
+
if (queryRemap) {
|
|
137
|
+
internalPathCache = queryRemap;
|
|
138
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
139
|
+
console.log(`[TagadaPay SDK] 🔧 Simulating path remap via query: ${window.location.pathname} → ${queryRemap}`);
|
|
140
|
+
}
|
|
141
|
+
return queryRemap;
|
|
142
|
+
}
|
|
143
|
+
// Priority 2: localStorage tagadapay-remap
|
|
144
|
+
// Format: JSON object with external -> internal mapping
|
|
145
|
+
// Example: {"externalPath": "/myremap/:param", "internalPath": "/hello-with-param/:myparam"}
|
|
146
|
+
try {
|
|
147
|
+
const storageRemap = localStorage.getItem('tagadapay-remap');
|
|
148
|
+
console.log('[TagadaPay SDK] 🔍 localStorage tagadapay-remap:', storageRemap);
|
|
149
|
+
if (storageRemap) {
|
|
150
|
+
try {
|
|
151
|
+
// Try parsing as JSON first (new explicit format)
|
|
152
|
+
const remapConfig = JSON.parse(storageRemap);
|
|
153
|
+
console.log('[TagadaPay SDK] 📝 Parsed remap config:', remapConfig);
|
|
154
|
+
if (remapConfig.externalPath && remapConfig.internalPath) {
|
|
155
|
+
const currentPath = window.location.pathname;
|
|
156
|
+
console.log('[TagadaPay SDK] 🔍 Checking if', currentPath, 'matches pattern', remapConfig.externalPath);
|
|
157
|
+
// Check if current path matches the external path pattern
|
|
158
|
+
const matchFn = match(remapConfig.externalPath, {
|
|
159
|
+
decode: decodeURIComponent,
|
|
160
|
+
end: true
|
|
161
|
+
});
|
|
162
|
+
const result = matchFn(currentPath);
|
|
163
|
+
console.log('[TagadaPay SDK] 🎯 Match result:', result);
|
|
164
|
+
if (result !== false) {
|
|
165
|
+
// Current path matches the external pattern, return internal path
|
|
166
|
+
internalPathCache = remapConfig.internalPath;
|
|
167
|
+
console.log(`[TagadaPay SDK] ✅ Path remap: ${remapConfig.externalPath} → ${remapConfig.internalPath}`);
|
|
168
|
+
console.log(`[TagadaPay SDK] ✅ Current URL ${currentPath} matches external pattern`);
|
|
169
|
+
console.log('[TagadaPay SDK] 📦 Caching and returning:', remapConfig.internalPath);
|
|
170
|
+
return remapConfig.internalPath;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// Current path doesn't match the external pattern, no remap
|
|
174
|
+
console.log(`[TagadaPay SDK] ❌ Current URL ${currentPath} does NOT match external pattern ${remapConfig.externalPath}`);
|
|
175
|
+
internalPathCache = null;
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.log('[TagadaPay SDK] ⚠️ Parsed config missing externalPath or internalPath');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (jsonError) {
|
|
184
|
+
// Not JSON, treat as legacy format (just internal path)
|
|
185
|
+
console.log('[TagadaPay SDK] ⚠️ Failed to parse as JSON, using legacy format:', jsonError);
|
|
186
|
+
internalPathCache = storageRemap;
|
|
187
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
188
|
+
console.log(`[TagadaPay SDK] 🔧 Simulating path remap (legacy): ${window.location.pathname} → ${storageRemap}`);
|
|
189
|
+
}
|
|
190
|
+
return storageRemap;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
console.log('[TagadaPay SDK] 📭 No localStorage tagadapay-remap found');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
console.log('[TagadaPay SDK] ❌ localStorage error:', e);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Production: Try to get internal path from full remap config first (new way)
|
|
202
|
+
const fullRemapMetaTag = document.querySelector('meta[name="tagadapay-path-remap"]');
|
|
203
|
+
if (fullRemapMetaTag) {
|
|
204
|
+
try {
|
|
205
|
+
const content = fullRemapMetaTag.getAttribute('content');
|
|
206
|
+
if (content) {
|
|
207
|
+
const decodedContent = decodeURIComponent(content);
|
|
208
|
+
const parsed = JSON.parse(decodedContent);
|
|
209
|
+
if (parsed.internal) {
|
|
210
|
+
console.log('[TagadaPay SDK] ✅ Got internal path from full remap config:', parsed.internal);
|
|
211
|
+
internalPathCache = parsed.internal;
|
|
212
|
+
return parsed.internal;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
console.error('[TagadaPay SDK] Error parsing full remap config:', e);
|
|
218
|
+
// Fall through to try legacy meta tag
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Fallback: Try legacy meta tag (old way, for backward compatibility)
|
|
84
222
|
const metaTag = document.querySelector('meta[name="x-plugin-internal-path"]');
|
|
85
223
|
if (!metaTag) {
|
|
224
|
+
console.log('[TagadaPay SDK] ℹ️ No path remap meta tags found');
|
|
86
225
|
internalPathCache = null;
|
|
87
226
|
return null;
|
|
88
227
|
}
|
|
@@ -104,6 +243,7 @@ export function getInternalPath() {
|
|
|
104
243
|
internalPathCache = null;
|
|
105
244
|
return null;
|
|
106
245
|
}
|
|
246
|
+
console.log('[TagadaPay SDK] ✅ Got internal path from legacy meta tag:', trimmedContent);
|
|
107
247
|
// Cache and return
|
|
108
248
|
internalPathCache = trimmedContent;
|
|
109
249
|
return trimmedContent;
|
|
@@ -207,36 +347,75 @@ export function getPathInfo() {
|
|
|
207
347
|
/**
|
|
208
348
|
* Helper function to match a path against a pattern using path-to-regexp
|
|
209
349
|
* @param pathname - The actual path to test
|
|
210
|
-
* @param pattern - The pattern to match against (can include :params)
|
|
211
|
-
* @returns
|
|
350
|
+
* @param pattern - The pattern to match against (can include :params, wildcards, etc.)
|
|
351
|
+
* @returns Object with matched status and extracted params
|
|
212
352
|
*/
|
|
213
353
|
function matchesPathPattern(pathname, pattern) {
|
|
214
354
|
try {
|
|
215
355
|
// Exact match (no pattern)
|
|
216
356
|
if (pathname === pattern) {
|
|
217
|
-
return true;
|
|
357
|
+
return { matched: true, params: {} };
|
|
218
358
|
}
|
|
219
|
-
// Check if pattern contains
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
359
|
+
// Check if pattern contains special characters that require path-to-regexp
|
|
360
|
+
// :param, *wildcard, {optional}, etc.
|
|
361
|
+
const hasPatternSyntax = pattern.includes(':') || pattern.includes('*') || pattern.includes('{');
|
|
362
|
+
if (!hasPatternSyntax) {
|
|
363
|
+
// No pattern syntax - use exact match only
|
|
364
|
+
// For paths without parameters, we should match exactly, not with prefix
|
|
365
|
+
return { matched: pathname === pattern, params: {} };
|
|
223
366
|
}
|
|
224
|
-
// Use path-to-regexp for
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
367
|
+
// Use path-to-regexp for patterns with parameters, wildcards, or optional segments
|
|
368
|
+
const matchFn = match(pattern, {
|
|
369
|
+
decode: decodeURIComponent,
|
|
370
|
+
end: true
|
|
371
|
+
});
|
|
228
372
|
const result = matchFn(pathname);
|
|
229
|
-
|
|
373
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
374
|
+
console.log(`[TagadaPay SDK] Pattern match: "${pathname}" against "${pattern}" =`, result !== false);
|
|
375
|
+
}
|
|
376
|
+
if (result === false) {
|
|
377
|
+
return { matched: false, params: {} };
|
|
378
|
+
}
|
|
379
|
+
// Extract params from result
|
|
380
|
+
const params = {};
|
|
381
|
+
if (typeof result === 'object' && result.params) {
|
|
382
|
+
Object.keys(result.params).forEach(key => {
|
|
383
|
+
const value = result.params[key];
|
|
384
|
+
// Convert to string and handle arrays (for splat params)
|
|
385
|
+
params[key] = Array.isArray(value) ? value.join('/') : String(value);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return { matched: true, params };
|
|
230
389
|
}
|
|
231
390
|
catch (error) {
|
|
232
391
|
// Fallback to exact match if path-to-regexp fails
|
|
233
392
|
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
234
393
|
console.warn(`[TagadaPay SDK] Pattern matching failed for "${pattern}":`, error);
|
|
235
394
|
}
|
|
236
|
-
return pathname === pattern
|
|
395
|
+
return { matched: pathname === pattern, params: {} };
|
|
237
396
|
}
|
|
238
397
|
}
|
|
239
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Match a route and extract URL parameters
|
|
400
|
+
*
|
|
401
|
+
* Handles path remapping automatically and extracts parameters from the URL.
|
|
402
|
+
* This is the preferred method when you need both match status and parameters.
|
|
403
|
+
*
|
|
404
|
+
* @param internalPath - The plugin's defined path (must start with /)
|
|
405
|
+
* @returns Object with `matched` boolean and `params` object
|
|
406
|
+
* @throws {Error} If internalPath is invalid
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```typescript
|
|
410
|
+
* // In a catch-all route handler
|
|
411
|
+
* const result = matchRoute('/hello-with-param/:myparam');
|
|
412
|
+
* if (result.matched) {
|
|
413
|
+
* console.log('Matched! Param:', result.params.myparam);
|
|
414
|
+
* return <HelloPage myparam={result.params.myparam} />;
|
|
415
|
+
* }
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
418
|
+
export function matchRoute(internalPath) {
|
|
240
419
|
// Validate input
|
|
241
420
|
if (typeof internalPath !== 'string') {
|
|
242
421
|
throw new Error(`[TagadaPay SDK] internalPath must be a string, got ${typeof internalPath}`);
|
|
@@ -249,25 +428,120 @@ export function shouldMatchRoute(internalPath) {
|
|
|
249
428
|
}
|
|
250
429
|
// SSR check
|
|
251
430
|
if (!isBrowser) {
|
|
252
|
-
return false;
|
|
431
|
+
return { matched: false, params: {} };
|
|
253
432
|
}
|
|
254
433
|
try {
|
|
255
434
|
const currentPath = window.location.pathname;
|
|
256
435
|
const remappedInternalPath = getInternalPath();
|
|
257
|
-
// If remapped,
|
|
436
|
+
// If remapped, need to extract params from external URL using external pattern
|
|
258
437
|
if (remappedInternalPath) {
|
|
259
|
-
|
|
438
|
+
const internalMatch = matchesPathPattern(remappedInternalPath, internalPath);
|
|
439
|
+
if (!internalMatch.matched) {
|
|
440
|
+
return { matched: false, params: {} };
|
|
441
|
+
}
|
|
442
|
+
// Get the external pattern from remap config
|
|
443
|
+
let externalPattern = null;
|
|
444
|
+
// Check localStorage for remap config (development/testing)
|
|
445
|
+
if (typeof localStorage !== 'undefined') {
|
|
446
|
+
try {
|
|
447
|
+
const remapData = localStorage.getItem('tagadapay-remap');
|
|
448
|
+
if (remapData) {
|
|
449
|
+
const parsed = JSON.parse(remapData);
|
|
450
|
+
if (parsed.externalPath && parsed.internalPath === remappedInternalPath) {
|
|
451
|
+
externalPattern = parsed.externalPath;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch (e) {
|
|
456
|
+
// Ignore parsing errors
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Check meta tag for production remap config
|
|
460
|
+
if (!externalPattern && typeof document !== 'undefined') {
|
|
461
|
+
console.log('[TagadaPay SDK] 🔍 Checking for production path remap meta tag...');
|
|
462
|
+
const metaTag = document.querySelector('meta[name="tagadapay-path-remap"]');
|
|
463
|
+
if (metaTag) {
|
|
464
|
+
try {
|
|
465
|
+
const content = metaTag.getAttribute('content');
|
|
466
|
+
console.log('[TagadaPay SDK] 📄 Found meta tag content (encoded):', content?.substring(0, 100) + '...');
|
|
467
|
+
if (content) {
|
|
468
|
+
// Decode URL-encoded content before parsing
|
|
469
|
+
const decodedContent = decodeURIComponent(content);
|
|
470
|
+
console.log('[TagadaPay SDK] 📄 Decoded content:', decodedContent);
|
|
471
|
+
const parsed = JSON.parse(decodedContent);
|
|
472
|
+
console.log('[TagadaPay SDK] 📦 Parsed remap config:', parsed);
|
|
473
|
+
console.log('[TagadaPay SDK] 🔍 Checking if internal path matches:', {
|
|
474
|
+
parsedInternal: parsed.internal,
|
|
475
|
+
remappedInternalPath
|
|
476
|
+
});
|
|
477
|
+
if (parsed.external && parsed.internal === remappedInternalPath) {
|
|
478
|
+
externalPattern = parsed.external;
|
|
479
|
+
console.log('[TagadaPay SDK] ✅ Using external pattern from meta tag:', externalPattern);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
console.log('[TagadaPay SDK] ⚠️ Internal path mismatch - not using meta tag config');
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch (e) {
|
|
487
|
+
// Ignore parsing errors
|
|
488
|
+
console.error('[TagadaPay SDK] ❌ Error parsing path remap meta tag:', e);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
console.log('[TagadaPay SDK] ❌ No tagadapay-path-remap meta tag found');
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Extract params from current URL using external pattern
|
|
496
|
+
if (externalPattern) {
|
|
497
|
+
const externalMatch = matchesPathPattern(currentPath, externalPattern);
|
|
498
|
+
if (externalMatch.matched) {
|
|
499
|
+
// Since we enforce matching parameter names in CRM,
|
|
500
|
+
// the params extracted from external pattern will have the correct names
|
|
501
|
+
return { matched: true, params: externalMatch.params };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// Fallback: try to extract from current URL using internal pattern
|
|
505
|
+
// (in case external pattern not found or doesn't match)
|
|
506
|
+
const currentMatch = matchesPathPattern(currentPath, internalPath);
|
|
507
|
+
return { matched: true, params: currentMatch.params };
|
|
260
508
|
}
|
|
261
|
-
// No remapping -
|
|
509
|
+
// No remapping - match and extract from current path
|
|
262
510
|
return matchesPathPattern(currentPath, internalPath);
|
|
263
511
|
}
|
|
264
512
|
catch (error) {
|
|
265
513
|
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
266
|
-
console.error('[TagadaPay SDK] Error in
|
|
514
|
+
console.error('[TagadaPay SDK] Error in matchRoute:', error);
|
|
267
515
|
}
|
|
268
|
-
return false;
|
|
516
|
+
return { matched: false, params: {} };
|
|
269
517
|
}
|
|
270
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Check if the current URL should match a given internal path
|
|
521
|
+
*
|
|
522
|
+
* Handles path remapping automatically. Use this for catch-all routing
|
|
523
|
+
* implementations where you only need to check if a path matches.
|
|
524
|
+
*
|
|
525
|
+
* For extracting parameters, use `matchRoute()` instead.
|
|
526
|
+
*
|
|
527
|
+
* @param internalPath - The plugin's defined path (must start with /)
|
|
528
|
+
* @returns true if current URL should render this path's component
|
|
529
|
+
* @throws {Error} If internalPath is invalid
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* ```typescript
|
|
533
|
+
* // In a catch-all route handler
|
|
534
|
+
* if (shouldMatchRoute('/hello')) {
|
|
535
|
+
* return <HelloPage />;
|
|
536
|
+
* }
|
|
537
|
+
* if (shouldMatchRoute('/about')) {
|
|
538
|
+
* return <AboutPage />;
|
|
539
|
+
* }
|
|
540
|
+
* ```
|
|
541
|
+
*/
|
|
542
|
+
export function shouldMatchRoute(internalPath) {
|
|
543
|
+
return matchRoute(internalPath).matched;
|
|
544
|
+
}
|
|
271
545
|
/**
|
|
272
546
|
* Get the current matched path
|
|
273
547
|
*
|
|
@@ -6,6 +6,12 @@ export interface FunnelEvent {
|
|
|
6
6
|
type: string;
|
|
7
7
|
data?: any;
|
|
8
8
|
timestamp?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Current URL for session synchronization (browser back/forward handling)
|
|
11
|
+
* If not provided, SDK will automatically use window.location.href
|
|
12
|
+
* @example '/checkout', 'https://store.com/payment'
|
|
13
|
+
*/
|
|
14
|
+
currentUrl?: string;
|
|
9
15
|
}
|
|
10
16
|
export interface FunnelNavigationAction {
|
|
11
17
|
type: 'redirect' | 'replace' | 'push' | 'external' | 'none';
|
|
@@ -30,6 +36,11 @@ export interface SimpleFunnelContext {
|
|
|
30
36
|
funnelId: string;
|
|
31
37
|
currentStepId: string;
|
|
32
38
|
previousStepId?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Furthest step reached in the funnel (for progress tracking in cyclic funnels)
|
|
41
|
+
* Only moves forward, never backward - used for analytics
|
|
42
|
+
*/
|
|
43
|
+
furthestStepId?: string;
|
|
33
44
|
startedAt: number;
|
|
34
45
|
lastActivityAt: number;
|
|
35
46
|
metadata?: Record<string, any>;
|
|
@@ -54,12 +65,27 @@ export interface FunnelNavigateRequest {
|
|
|
54
65
|
sessionId: string;
|
|
55
66
|
event: FunnelEvent;
|
|
56
67
|
contextUpdates?: Partial<SimpleFunnelContext>;
|
|
68
|
+
/**
|
|
69
|
+
* Current URL for URL-to-Step mapping (browser back/forward sync)
|
|
70
|
+
* Automatically extracted from event.currentUrl or window.location.href
|
|
71
|
+
*/
|
|
72
|
+
currentUrl?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Funnel ID for session recovery when session expires
|
|
75
|
+
* If session is not found and funnelId is provided, a new session will be created
|
|
76
|
+
*/
|
|
77
|
+
funnelId?: string;
|
|
57
78
|
}
|
|
58
79
|
export interface FunnelNavigateResponse {
|
|
59
80
|
success: boolean;
|
|
60
81
|
result?: {
|
|
61
82
|
stepId: string;
|
|
62
83
|
url?: string;
|
|
84
|
+
/**
|
|
85
|
+
* New session ID if session was recovered (expired/removed)
|
|
86
|
+
* SDK should update its sessionId if this is present
|
|
87
|
+
*/
|
|
88
|
+
sessionId?: string;
|
|
63
89
|
tracking?: {
|
|
64
90
|
from: string;
|
|
65
91
|
to: string;
|
|
@@ -100,8 +126,10 @@ export declare class FunnelResource {
|
|
|
100
126
|
}>;
|
|
101
127
|
/**
|
|
102
128
|
* Get funnel session by ID
|
|
129
|
+
* @param sessionId - The session ID to fetch
|
|
130
|
+
* @param currentUrl - Optional current URL for session synchronization on page load
|
|
103
131
|
*/
|
|
104
|
-
getSession(sessionId: string): Promise<{
|
|
132
|
+
getSession(sessionId: string, currentUrl?: string): Promise<{
|
|
105
133
|
success: boolean;
|
|
106
134
|
context?: SimpleFunnelContext;
|
|
107
135
|
error?: string;
|
|
@@ -31,8 +31,12 @@ export class FunnelResource {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Get funnel session by ID
|
|
34
|
+
* @param sessionId - The session ID to fetch
|
|
35
|
+
* @param currentUrl - Optional current URL for session synchronization on page load
|
|
34
36
|
*/
|
|
35
|
-
async getSession(sessionId) {
|
|
36
|
-
|
|
37
|
+
async getSession(sessionId, currentUrl) {
|
|
38
|
+
const params = currentUrl ? new URLSearchParams({ currentUrl }) : undefined;
|
|
39
|
+
const url = `/api/v1/funnel/session/${sessionId}${params ? `?${params}` : ''}`;
|
|
40
|
+
return this.apiClient.get(url);
|
|
37
41
|
}
|
|
38
42
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const DEFAULT_PREFIXES = ['', 'VITE_', 'REACT_APP_', 'NEXT_PUBLIC_'];
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a value from multiple possible environment sources (Node, Vite, CRA, Next, window)
|
|
4
|
+
* using a list of supported prefixes.
|
|
5
|
+
*/
|
|
6
|
+
export function resolveEnvValue(key, prefixes = DEFAULT_PREFIXES) {
|
|
7
|
+
for (const prefix of prefixes) {
|
|
8
|
+
const envKey = prefix ? `${prefix}${key}` : key;
|
|
9
|
+
if (typeof process !== 'undefined' && process?.env?.[envKey]) {
|
|
10
|
+
return process.env[envKey];
|
|
11
|
+
}
|
|
12
|
+
if (typeof import.meta !== 'undefined' && import.meta?.env?.[envKey]) {
|
|
13
|
+
return import.meta.env[envKey];
|
|
14
|
+
}
|
|
15
|
+
if (typeof window !== 'undefined' && window?.__TAGADA_ENV__?.[envKey]) {
|
|
16
|
+
return window.__TAGADA_ENV__[envKey];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
@@ -32,15 +32,12 @@ export declare const loadPluginConfig: (configVariant?: string, rawConfig?: RawP
|
|
|
32
32
|
* Returns the config object that can be passed to TagadaProvider
|
|
33
33
|
*/
|
|
34
34
|
export declare function loadLocalConfig(configName?: string, defaultConfig?: any): Promise<Record<string, unknown> | null>;
|
|
35
|
-
export interface CreateRawPluginConfigOptions {
|
|
36
|
-
config?: any;
|
|
37
|
-
}
|
|
38
35
|
/**
|
|
39
36
|
* Creates a RawPluginConfig object from provided options
|
|
40
37
|
* @param options - Configuration options including storeId, accountId, basePath, configName, or a direct config object
|
|
41
38
|
* @returns A RawPluginConfig object or undefined if required fields are missing
|
|
42
39
|
*/
|
|
43
|
-
export declare function createRawPluginConfig(
|
|
40
|
+
export declare function createRawPluginConfig(): Promise<RawPluginConfig | undefined>;
|
|
44
41
|
export declare class PluginConfigUtils {
|
|
45
42
|
/**
|
|
46
43
|
* Get plugin configuration from various sources
|