@tagadapay/plugin-sdk 2.7.0 → 2.7.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/dist/react/hooks/usePluginConfig.js +15 -7
- package/dist/v2/core/index.d.ts +1 -0
- package/dist/v2/core/index.js +2 -0
- package/dist/v2/core/pathRemapping.d.ts +156 -0
- package/dist/v2/core/pathRemapping.js +440 -0
- package/dist/v2/core/resources/credits.d.ts +42 -0
- package/dist/v2/core/resources/credits.js +21 -0
- package/dist/v2/core/resources/index.d.ts +1 -0
- package/dist/v2/core/resources/index.js +1 -0
- package/dist/v2/core/utils/pluginConfig.d.ts +14 -0
- package/dist/v2/core/utils/pluginConfig.js +96 -29
- package/dist/v2/index.d.ts +1 -0
- package/dist/v2/index.js +2 -0
- package/dist/v2/react/hooks/useCredits.d.ts +67 -0
- package/dist/v2/react/hooks/useCredits.js +80 -0
- package/dist/v2/react/hooks/useTranslation.d.ts +40 -3
- package/dist/v2/react/hooks/useTranslation.js +127 -15
- package/dist/v2/react/index.d.ts +2 -0
- package/dist/v2/react/index.js +1 -0
- package/package.json +2 -1
|
@@ -105,15 +105,16 @@ const loadLocalDevConfig = async (configVariant = 'default') => {
|
|
|
105
105
|
}
|
|
106
106
|
};
|
|
107
107
|
/**
|
|
108
|
-
* Load production config from
|
|
108
|
+
* Load production config from meta tags
|
|
109
|
+
* Meta tags are injected by the plugin middleware during HTML serving
|
|
110
|
+
* This avoids making an additional HEAD request (~300ms savings)
|
|
109
111
|
*/
|
|
110
112
|
const loadProductionConfig = async () => {
|
|
111
113
|
try {
|
|
112
|
-
//
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const basePath = response.headers.get('X-Plugin-Base-Path') || '/';
|
|
114
|
+
// Read store/account info from meta tags (injected by middleware)
|
|
115
|
+
const storeId = document.querySelector('meta[name="x-plugin-store-id"]')?.getAttribute('content') || undefined;
|
|
116
|
+
const accountId = document.querySelector('meta[name="x-plugin-account-id"]')?.getAttribute('content') || undefined;
|
|
117
|
+
const basePath = document.querySelector('meta[name="x-plugin-base-path"]')?.getAttribute('content') || '/';
|
|
117
118
|
// Get deployment config from meta tags
|
|
118
119
|
let config = {};
|
|
119
120
|
try {
|
|
@@ -127,9 +128,16 @@ const loadProductionConfig = async () => {
|
|
|
127
128
|
catch {
|
|
128
129
|
// Deployment config is optional
|
|
129
130
|
}
|
|
131
|
+
console.log('🚀 Plugin config loaded from meta tags (no HEAD request needed):', {
|
|
132
|
+
storeId,
|
|
133
|
+
accountId,
|
|
134
|
+
basePath,
|
|
135
|
+
configKeys: Object.keys(config)
|
|
136
|
+
});
|
|
130
137
|
return { storeId, accountId, basePath, config };
|
|
131
138
|
}
|
|
132
|
-
catch {
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.warn('⚠️ Failed to load plugin config from meta tags:', error);
|
|
133
141
|
return { basePath: '/', config: {} };
|
|
134
142
|
}
|
|
135
143
|
};
|
package/dist/v2/core/index.d.ts
CHANGED
package/dist/v2/core/index.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
export * from './utils';
|
|
7
7
|
// Export resources (axios-based API clients)
|
|
8
8
|
export * from './resources';
|
|
9
|
+
// Export path remapping helpers (framework-agnostic)
|
|
10
|
+
export * from './pathRemapping';
|
|
9
11
|
// Export legacy files that are still needed
|
|
10
12
|
export * from './googleAutocomplete';
|
|
11
13
|
export * from './isoData';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Remapping Helpers - Production Ready
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic utility for handling path remapping from TagadaPay routing
|
|
5
|
+
* Includes error handling, SSR compatibility, performance optimizations, and edge case handling
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Path information including both external and internal paths
|
|
11
|
+
*/
|
|
12
|
+
export interface PathInfo {
|
|
13
|
+
/** The path shown in the browser URL bar (external/remapped path) */
|
|
14
|
+
externalPath: string;
|
|
15
|
+
/** The path that the plugin expects (internal/original path) */
|
|
16
|
+
internalPath: string | null;
|
|
17
|
+
/** Whether path remapping is active */
|
|
18
|
+
isRemapped: boolean;
|
|
19
|
+
/** The full URL including protocol, host, query, and hash */
|
|
20
|
+
fullUrl: string;
|
|
21
|
+
/** Query parameters from the URL */
|
|
22
|
+
query: URLSearchParams;
|
|
23
|
+
/** URL hash without the # symbol */
|
|
24
|
+
hash: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get the internal path that the plugin expects
|
|
28
|
+
*
|
|
29
|
+
* Reads from the meta tag injected by TagadaPay middleware.
|
|
30
|
+
* Results are cached for performance.
|
|
31
|
+
*
|
|
32
|
+
* @returns The internal path from meta tag, or null if no remapping is active
|
|
33
|
+
* @throws {Error} If called in a non-browser environment (SSR)
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const internalPath = getInternalPath();
|
|
38
|
+
* if (internalPath) {
|
|
39
|
+
* console.log('Remapped from external to:', internalPath);
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function getInternalPath(): string | null;
|
|
44
|
+
/**
|
|
45
|
+
* Check if path remapping is currently active
|
|
46
|
+
*
|
|
47
|
+
* @returns true if a remapped path is configured, false otherwise
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* if (isPathRemapped()) {
|
|
52
|
+
* console.log('User is viewing a remapped URL');
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function isPathRemapped(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get comprehensive path mapping information
|
|
59
|
+
*
|
|
60
|
+
* Returns both the external (browser) path and internal (plugin) path,
|
|
61
|
+
* along with query parameters and hash.
|
|
62
|
+
*
|
|
63
|
+
* @returns {PathInfo} Complete path information object
|
|
64
|
+
* @throws {Error} If called in a non-browser environment (SSR)
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const pathInfo = getPathInfo();
|
|
69
|
+
* console.log('Browser shows:', pathInfo.externalPath);
|
|
70
|
+
* console.log('Plugin expects:', pathInfo.internalPath);
|
|
71
|
+
* console.log('Query params:', pathInfo.query.get('id'));
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function getPathInfo(): PathInfo;
|
|
75
|
+
export declare function shouldMatchRoute(internalPath: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Get the current matched path
|
|
78
|
+
*
|
|
79
|
+
* Returns the actual path in the browser if it matches the given internal path.
|
|
80
|
+
* Useful when you need the actual URL path for constructing links or API calls.
|
|
81
|
+
*
|
|
82
|
+
* @param internalPath - The plugin's defined path
|
|
83
|
+
* @returns The current browser path if it matches, null otherwise
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const matchedPath = getMatchedPath('/hello');
|
|
88
|
+
* if (matchedPath) {
|
|
89
|
+
* console.log('Matched at:', matchedPath); // Could be /hello or /hello2
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function getMatchedPath(internalPath: string): string | null;
|
|
94
|
+
/**
|
|
95
|
+
* Manually clear the internal path cache
|
|
96
|
+
*
|
|
97
|
+
* Useful for testing or when you know the meta tag has changed.
|
|
98
|
+
* Normally you don't need to call this as it's handled automatically.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // After dynamically updating the meta tag
|
|
103
|
+
* document.querySelector('meta[name="x-plugin-internal-path"]')
|
|
104
|
+
* ?.setAttribute('content', '/new-path');
|
|
105
|
+
* clearInternalPathCache();
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function clearInternalPathCache(): void;
|
|
109
|
+
/**
|
|
110
|
+
* Check if running in a browser environment
|
|
111
|
+
*
|
|
112
|
+
* Useful for conditional logic in universal/isomorphic code.
|
|
113
|
+
*
|
|
114
|
+
* @returns true if in browser, false if in SSR/Node environment
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* if (isBrowserEnvironment()) {
|
|
119
|
+
* // Safe to use window/document
|
|
120
|
+
* const path = getInternalPath();
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare function isBrowserEnvironment(): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Get debug information about the current routing state
|
|
127
|
+
*
|
|
128
|
+
* Returns detailed information for debugging path remapping issues.
|
|
129
|
+
* Only available in development mode.
|
|
130
|
+
*
|
|
131
|
+
* @returns Debug information object
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* const debug = getDebugInfo();
|
|
136
|
+
* console.table(debug);
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export declare function getDebugInfo(): Record<string, any>;
|
|
140
|
+
/**
|
|
141
|
+
* Cleanup function to remove event listeners
|
|
142
|
+
*
|
|
143
|
+
* Call this when unmounting the plugin or cleaning up resources.
|
|
144
|
+
* Important for preventing memory leaks in SPAs.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* // In React
|
|
149
|
+
* useEffect(() => {
|
|
150
|
+
* return () => {
|
|
151
|
+
* cleanupPathRemapping();
|
|
152
|
+
* };
|
|
153
|
+
* }, []);
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare function cleanupPathRemapping(): void;
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Remapping Helpers - Production Ready
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic utility for handling path remapping from TagadaPay routing
|
|
5
|
+
* Includes error handling, SSR compatibility, performance optimizations, and edge case handling
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Private Cache
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Cache for internal path lookup to avoid repeated DOM queries
|
|
14
|
+
* Cleared on navigation events
|
|
15
|
+
*/
|
|
16
|
+
let internalPathCache = undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Flag to check if we're in a browser environment
|
|
19
|
+
* Prevents errors during SSR
|
|
20
|
+
*/
|
|
21
|
+
const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
22
|
+
/**
|
|
23
|
+
* Store original history methods for cleanup
|
|
24
|
+
*/
|
|
25
|
+
const originalPushState = isBrowser ? history.pushState.bind(history) : null;
|
|
26
|
+
const originalReplaceState = isBrowser ? history.replaceState.bind(history) : null;
|
|
27
|
+
/**
|
|
28
|
+
* Clear the internal path cache
|
|
29
|
+
* Called automatically on navigation events
|
|
30
|
+
*/
|
|
31
|
+
function clearCache() {
|
|
32
|
+
internalPathCache = undefined;
|
|
33
|
+
}
|
|
34
|
+
// Setup cache invalidation on navigation (only in browser)
|
|
35
|
+
if (isBrowser) {
|
|
36
|
+
// Clear cache on history navigation
|
|
37
|
+
window.addEventListener('popstate', clearCache);
|
|
38
|
+
// Clear cache on hash change
|
|
39
|
+
window.addEventListener('hashchange', clearCache);
|
|
40
|
+
// Intercept pushState and replaceState to clear cache
|
|
41
|
+
history.pushState = function (...args) {
|
|
42
|
+
clearCache();
|
|
43
|
+
return originalPushState.apply(this, args);
|
|
44
|
+
};
|
|
45
|
+
history.replaceState = function (...args) {
|
|
46
|
+
clearCache();
|
|
47
|
+
return originalReplaceState.apply(this, args);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Core Functions
|
|
52
|
+
// ============================================================================
|
|
53
|
+
/**
|
|
54
|
+
* Get the internal path that the plugin expects
|
|
55
|
+
*
|
|
56
|
+
* Reads from the meta tag injected by TagadaPay middleware.
|
|
57
|
+
* Results are cached for performance.
|
|
58
|
+
*
|
|
59
|
+
* @returns The internal path from meta tag, or null if no remapping is active
|
|
60
|
+
* @throws {Error} If called in a non-browser environment (SSR)
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const internalPath = getInternalPath();
|
|
65
|
+
* if (internalPath) {
|
|
66
|
+
* console.log('Remapped from external to:', internalPath);
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function getInternalPath() {
|
|
71
|
+
// SSR check
|
|
72
|
+
if (!isBrowser) {
|
|
73
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
74
|
+
console.warn('[TagadaPay SDK] getInternalPath() called in SSR context');
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
// Return cached value if available
|
|
79
|
+
if (internalPathCache !== undefined) {
|
|
80
|
+
return internalPathCache;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
// Query the meta tag
|
|
84
|
+
const metaTag = document.querySelector('meta[name="x-plugin-internal-path"]');
|
|
85
|
+
if (!metaTag) {
|
|
86
|
+
internalPathCache = null;
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
// Get and validate the content
|
|
90
|
+
const content = metaTag.getAttribute('content');
|
|
91
|
+
if (!content || content.trim() === '') {
|
|
92
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
93
|
+
console.warn('[TagadaPay SDK] Meta tag found but content is empty');
|
|
94
|
+
}
|
|
95
|
+
internalPathCache = null;
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
// Validate path format
|
|
99
|
+
const trimmedContent = content.trim();
|
|
100
|
+
if (!trimmedContent.startsWith('/')) {
|
|
101
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
102
|
+
console.warn(`[TagadaPay SDK] Invalid internal path format: "${trimmedContent}" (must start with /)`);
|
|
103
|
+
}
|
|
104
|
+
internalPathCache = null;
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
// Cache and return
|
|
108
|
+
internalPathCache = trimmedContent;
|
|
109
|
+
return trimmedContent;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
// Log error in development
|
|
113
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
114
|
+
console.error('[TagadaPay SDK] Error reading internal path:', error);
|
|
115
|
+
}
|
|
116
|
+
internalPathCache = null;
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if path remapping is currently active
|
|
122
|
+
*
|
|
123
|
+
* @returns true if a remapped path is configured, false otherwise
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* if (isPathRemapped()) {
|
|
128
|
+
* console.log('User is viewing a remapped URL');
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function isPathRemapped() {
|
|
133
|
+
return getInternalPath() !== null;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get comprehensive path mapping information
|
|
137
|
+
*
|
|
138
|
+
* Returns both the external (browser) path and internal (plugin) path,
|
|
139
|
+
* along with query parameters and hash.
|
|
140
|
+
*
|
|
141
|
+
* @returns {PathInfo} Complete path information object
|
|
142
|
+
* @throws {Error} If called in a non-browser environment (SSR)
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const pathInfo = getPathInfo();
|
|
147
|
+
* console.log('Browser shows:', pathInfo.externalPath);
|
|
148
|
+
* console.log('Plugin expects:', pathInfo.internalPath);
|
|
149
|
+
* console.log('Query params:', pathInfo.query.get('id'));
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export function getPathInfo() {
|
|
153
|
+
// SSR check
|
|
154
|
+
if (!isBrowser) {
|
|
155
|
+
throw new Error('[TagadaPay SDK] getPathInfo() cannot be called in SSR context');
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const externalPath = window.location.pathname;
|
|
159
|
+
const internalPath = getInternalPath();
|
|
160
|
+
const query = new URLSearchParams(window.location.search);
|
|
161
|
+
const hash = window.location.hash.replace(/^#/, '');
|
|
162
|
+
return {
|
|
163
|
+
externalPath,
|
|
164
|
+
internalPath,
|
|
165
|
+
isRemapped: internalPath !== null,
|
|
166
|
+
fullUrl: window.location.href,
|
|
167
|
+
query,
|
|
168
|
+
hash,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
// Fallback to safe defaults
|
|
173
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
174
|
+
console.error('[TagadaPay SDK] Error getting path info:', error);
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
externalPath: '/',
|
|
178
|
+
internalPath: null,
|
|
179
|
+
isRemapped: false,
|
|
180
|
+
fullUrl: '',
|
|
181
|
+
query: new URLSearchParams(),
|
|
182
|
+
hash: '',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if the current URL should match a given internal path
|
|
188
|
+
*
|
|
189
|
+
* Handles path remapping automatically. Use this for catch-all routing
|
|
190
|
+
* implementations where you need to check multiple paths.
|
|
191
|
+
*
|
|
192
|
+
* @param internalPath - The plugin's defined path (must start with /)
|
|
193
|
+
* @returns true if current URL should render this path's component
|
|
194
|
+
* @throws {Error} If internalPath is invalid
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* // In a catch-all route handler
|
|
199
|
+
* if (shouldMatchRoute('/hello')) {
|
|
200
|
+
* return <HelloPage />;
|
|
201
|
+
* }
|
|
202
|
+
* if (shouldMatchRoute('/about')) {
|
|
203
|
+
* return <AboutPage />;
|
|
204
|
+
* }
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
/**
|
|
208
|
+
* Helper function to match a path against a pattern using path-to-regexp
|
|
209
|
+
* @param pathname - The actual path to test
|
|
210
|
+
* @param pattern - The pattern to match against (can include :params)
|
|
211
|
+
* @returns true if the path matches the pattern
|
|
212
|
+
*/
|
|
213
|
+
function matchesPathPattern(pathname, pattern) {
|
|
214
|
+
try {
|
|
215
|
+
// Exact match (no pattern)
|
|
216
|
+
if (pathname === pattern) {
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
// Check if pattern contains parameters (e.g., :id, :name)
|
|
220
|
+
if (!pattern.includes(':')) {
|
|
221
|
+
// No parameters - use simple prefix matching
|
|
222
|
+
return pathname.startsWith(pattern);
|
|
223
|
+
}
|
|
224
|
+
// Use path-to-regexp for parameterized patterns
|
|
225
|
+
// Import dynamically to avoid bundle size issues
|
|
226
|
+
const { match } = require('path-to-regexp');
|
|
227
|
+
const matchFn = match(pattern, { decode: decodeURIComponent, end: false });
|
|
228
|
+
const result = matchFn(pathname);
|
|
229
|
+
return result !== false;
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
// Fallback to exact match if path-to-regexp fails
|
|
233
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
234
|
+
console.warn(`[TagadaPay SDK] Pattern matching failed for "${pattern}":`, error);
|
|
235
|
+
}
|
|
236
|
+
return pathname === pattern || pathname.startsWith(pattern);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
export function shouldMatchRoute(internalPath) {
|
|
240
|
+
// Validate input
|
|
241
|
+
if (typeof internalPath !== 'string') {
|
|
242
|
+
throw new Error(`[TagadaPay SDK] internalPath must be a string, got ${typeof internalPath}`);
|
|
243
|
+
}
|
|
244
|
+
if (internalPath.trim() === '') {
|
|
245
|
+
throw new Error('[TagadaPay SDK] internalPath cannot be empty');
|
|
246
|
+
}
|
|
247
|
+
if (!internalPath.startsWith('/')) {
|
|
248
|
+
throw new Error(`[TagadaPay SDK] internalPath must start with /, got "${internalPath}"`);
|
|
249
|
+
}
|
|
250
|
+
// SSR check
|
|
251
|
+
if (!isBrowser) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const currentPath = window.location.pathname;
|
|
256
|
+
const remappedInternalPath = getInternalPath();
|
|
257
|
+
// If remapped, check if the remapped internal path matches the pattern
|
|
258
|
+
if (remappedInternalPath) {
|
|
259
|
+
return matchesPathPattern(remappedInternalPath, internalPath);
|
|
260
|
+
}
|
|
261
|
+
// No remapping - check if current path matches the pattern
|
|
262
|
+
return matchesPathPattern(currentPath, internalPath);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
266
|
+
console.error('[TagadaPay SDK] Error in shouldMatchRoute:', error);
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the current matched path
|
|
273
|
+
*
|
|
274
|
+
* Returns the actual path in the browser if it matches the given internal path.
|
|
275
|
+
* Useful when you need the actual URL path for constructing links or API calls.
|
|
276
|
+
*
|
|
277
|
+
* @param internalPath - The plugin's defined path
|
|
278
|
+
* @returns The current browser path if it matches, null otherwise
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* const matchedPath = getMatchedPath('/hello');
|
|
283
|
+
* if (matchedPath) {
|
|
284
|
+
* console.log('Matched at:', matchedPath); // Could be /hello or /hello2
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
export function getMatchedPath(internalPath) {
|
|
289
|
+
// Validate input
|
|
290
|
+
if (typeof internalPath !== 'string' || !internalPath.startsWith('/')) {
|
|
291
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
292
|
+
console.warn(`[TagadaPay SDK] Invalid internalPath: "${internalPath}"`);
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
// SSR check
|
|
297
|
+
if (!isBrowser) {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
const currentPath = window.location.pathname;
|
|
302
|
+
const remappedInternalPath = getInternalPath();
|
|
303
|
+
// If current path matches internal path
|
|
304
|
+
if (currentPath === internalPath ||
|
|
305
|
+
(remappedInternalPath === internalPath && currentPath !== internalPath)) {
|
|
306
|
+
return currentPath;
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
312
|
+
console.error('[TagadaPay SDK] Error in getMatchedPath:', error);
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Utility Functions
|
|
319
|
+
// ============================================================================
|
|
320
|
+
/**
|
|
321
|
+
* Manually clear the internal path cache
|
|
322
|
+
*
|
|
323
|
+
* Useful for testing or when you know the meta tag has changed.
|
|
324
|
+
* Normally you don't need to call this as it's handled automatically.
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* // After dynamically updating the meta tag
|
|
329
|
+
* document.querySelector('meta[name="x-plugin-internal-path"]')
|
|
330
|
+
* ?.setAttribute('content', '/new-path');
|
|
331
|
+
* clearInternalPathCache();
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
export function clearInternalPathCache() {
|
|
335
|
+
clearCache();
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Check if running in a browser environment
|
|
339
|
+
*
|
|
340
|
+
* Useful for conditional logic in universal/isomorphic code.
|
|
341
|
+
*
|
|
342
|
+
* @returns true if in browser, false if in SSR/Node environment
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```typescript
|
|
346
|
+
* if (isBrowserEnvironment()) {
|
|
347
|
+
* // Safe to use window/document
|
|
348
|
+
* const path = getInternalPath();
|
|
349
|
+
* }
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
export function isBrowserEnvironment() {
|
|
353
|
+
return isBrowser;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Get debug information about the current routing state
|
|
357
|
+
*
|
|
358
|
+
* Returns detailed information for debugging path remapping issues.
|
|
359
|
+
* Only available in development mode.
|
|
360
|
+
*
|
|
361
|
+
* @returns Debug information object
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const debug = getDebugInfo();
|
|
366
|
+
* console.table(debug);
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
export function getDebugInfo() {
|
|
370
|
+
if (!isBrowser) {
|
|
371
|
+
return {
|
|
372
|
+
environment: 'SSR/Node',
|
|
373
|
+
error: 'Not in browser environment',
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
const pathInfo = getPathInfo();
|
|
378
|
+
const metaTag = document.querySelector('meta[name="x-plugin-internal-path"]');
|
|
379
|
+
return {
|
|
380
|
+
environment: 'Browser',
|
|
381
|
+
externalPath: pathInfo.externalPath,
|
|
382
|
+
internalPath: pathInfo.internalPath,
|
|
383
|
+
isRemapped: pathInfo.isRemapped,
|
|
384
|
+
fullUrl: pathInfo.fullUrl,
|
|
385
|
+
query: Object.fromEntries(pathInfo.query.entries()),
|
|
386
|
+
hash: pathInfo.hash,
|
|
387
|
+
metaTagExists: !!metaTag,
|
|
388
|
+
metaTagContent: metaTag?.getAttribute('content') || null,
|
|
389
|
+
cacheHit: internalPathCache !== undefined,
|
|
390
|
+
cachedValue: internalPathCache,
|
|
391
|
+
timestamp: new Date().toISOString(),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
return {
|
|
396
|
+
environment: 'Browser',
|
|
397
|
+
error: String(error),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// ============================================================================
|
|
402
|
+
// Cleanup
|
|
403
|
+
// ============================================================================
|
|
404
|
+
/**
|
|
405
|
+
* Cleanup function to remove event listeners
|
|
406
|
+
*
|
|
407
|
+
* Call this when unmounting the plugin or cleaning up resources.
|
|
408
|
+
* Important for preventing memory leaks in SPAs.
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```typescript
|
|
412
|
+
* // In React
|
|
413
|
+
* useEffect(() => {
|
|
414
|
+
* return () => {
|
|
415
|
+
* cleanupPathRemapping();
|
|
416
|
+
* };
|
|
417
|
+
* }, []);
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
420
|
+
export function cleanupPathRemapping() {
|
|
421
|
+
if (!isBrowser)
|
|
422
|
+
return;
|
|
423
|
+
try {
|
|
424
|
+
window.removeEventListener('popstate', clearCache);
|
|
425
|
+
window.removeEventListener('hashchange', clearCache);
|
|
426
|
+
// Restore original history methods
|
|
427
|
+
if (originalPushState) {
|
|
428
|
+
history.pushState = originalPushState;
|
|
429
|
+
}
|
|
430
|
+
if (originalReplaceState) {
|
|
431
|
+
history.replaceState = originalReplaceState;
|
|
432
|
+
}
|
|
433
|
+
clearCache();
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
437
|
+
console.error('[TagadaPay SDK] Error during cleanup:', error);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credits Resource Client
|
|
3
|
+
* Handles credits-related API operations
|
|
4
|
+
*/
|
|
5
|
+
import { ApiClient } from './apiClient';
|
|
6
|
+
export interface CreditTransaction {
|
|
7
|
+
id: string;
|
|
8
|
+
amount: number;
|
|
9
|
+
type: 'earn' | 'spend' | 'adjustment';
|
|
10
|
+
description: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
sourceId: string | null;
|
|
13
|
+
sourceType: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface CustomerCredits {
|
|
16
|
+
balance: number;
|
|
17
|
+
lifetimeEarned: number;
|
|
18
|
+
lifetimeSpent: number;
|
|
19
|
+
transactions: CreditTransaction[];
|
|
20
|
+
}
|
|
21
|
+
export interface RedeemProductResponse {
|
|
22
|
+
success: boolean;
|
|
23
|
+
orderId: string;
|
|
24
|
+
creditsSpent: number;
|
|
25
|
+
remainingCredits: number;
|
|
26
|
+
}
|
|
27
|
+
export declare class CreditsResource {
|
|
28
|
+
private apiClient;
|
|
29
|
+
constructor(apiClient: ApiClient);
|
|
30
|
+
/**
|
|
31
|
+
* Get customer credit balance and transaction history
|
|
32
|
+
*/
|
|
33
|
+
getCustomerCredits(customerId: string, storeId: string): Promise<CustomerCredits>;
|
|
34
|
+
/**
|
|
35
|
+
* Redeem a product using credits
|
|
36
|
+
*/
|
|
37
|
+
redeemProduct(data: {
|
|
38
|
+
productId?: string;
|
|
39
|
+
variantId: string;
|
|
40
|
+
quantity?: number;
|
|
41
|
+
}): Promise<RedeemProductResponse>;
|
|
42
|
+
}
|