@datlv-trustshop/shopify-inapp-components 0.3.4 → 0.3.5
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/components/MarketingCampaignCard.d.ts.map +1 -1
- package/dist/components/MarketingCampaignCard.js +12 -113
- package/dist/components/MarketingCampaignCard.js.map +1 -1
- package/dist/core/SDKManager.d.ts.map +1 -1
- package/dist/core/SDKManager.js +7 -15
- package/dist/core/SDKManager.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useMarketingCampaign.d.ts +27 -0
- package/dist/hooks/useMarketingCampaign.d.ts.map +1 -0
- package/dist/hooks/useMarketingCampaign.js +103 -0
- package/dist/hooks/useMarketingCampaign.js.map +1 -0
- package/dist/utils/campaignCache.d.ts +40 -0
- package/dist/utils/campaignCache.d.ts.map +1 -0
- package/dist/utils/campaignCache.js +119 -0
- package/dist/utils/campaignCache.js.map +1 -0
- package/package.json +1 -1
- package/dist/core/global-manager.d.ts +0 -91
- package/dist/core/global-manager.d.ts.map +0 -1
- package/dist/core/global-manager.js +0 -347
- package/dist/core/global-manager.js.map +0 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from "react";
|
|
2
|
+
import { useSDK } from "../core/SDKManager";
|
|
3
|
+
import SDKManager from "../core/SDKManager";
|
|
4
|
+
/**
|
|
5
|
+
* Hook to fetch and manage marketing campaign card data
|
|
6
|
+
* Uses SDK-managed campaigns data
|
|
7
|
+
*/
|
|
8
|
+
export function useMarketingCampaign(options = {}) {
|
|
9
|
+
const { shopInfo, locale, translations, config } = options;
|
|
10
|
+
const sdk = useSDK({
|
|
11
|
+
shopInfo,
|
|
12
|
+
locale: locale,
|
|
13
|
+
translations,
|
|
14
|
+
config,
|
|
15
|
+
});
|
|
16
|
+
const [isDismissed, setIsDismissed] = useState(false);
|
|
17
|
+
const campaignsData = SDKManager.getCampaignsData();
|
|
18
|
+
const campaignData = useMemo(() => {
|
|
19
|
+
if (!campaignsData || !Array.isArray(campaignsData)) {
|
|
20
|
+
// console.log("[useMarketingCampaign] No campaigns data available");
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// console.log("[useMarketingCampaign] Processing campaigns:", campaignsData);
|
|
24
|
+
const marketingCampaigns = campaignsData.filter((campaign) => campaign.type === "marketing-card");
|
|
25
|
+
if (marketingCampaigns.length > 0) {
|
|
26
|
+
return marketingCampaigns[0];
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}, [campaignsData]);
|
|
30
|
+
// Check if campaign is dismissed
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!campaignData)
|
|
33
|
+
return;
|
|
34
|
+
const dismissKey = `trustshop_marketing_campaign_dismissed_${campaignData.key || campaignData.id}`;
|
|
35
|
+
try {
|
|
36
|
+
const dismissedData = localStorage.getItem(dismissKey);
|
|
37
|
+
if (dismissedData) {
|
|
38
|
+
const parsed = JSON.parse(dismissedData);
|
|
39
|
+
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
40
|
+
if (parsed.timestamp > sevenDaysAgo) {
|
|
41
|
+
setIsDismissed(true);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Clear expired dismiss data
|
|
45
|
+
localStorage.removeItem(dismissKey);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error("Failed to check dismiss state:", error);
|
|
51
|
+
}
|
|
52
|
+
}, [campaignData]);
|
|
53
|
+
// Dismiss campaign handler
|
|
54
|
+
const dismissCampaign = () => {
|
|
55
|
+
if (!campaignData)
|
|
56
|
+
return;
|
|
57
|
+
const dismissKey = `trustshop_marketing_campaign_dismissed_${campaignData.key || campaignData.id}`;
|
|
58
|
+
const dismissData = {
|
|
59
|
+
timestamp: Date.now(),
|
|
60
|
+
dismissed: true,
|
|
61
|
+
campaignId: campaignData.id,
|
|
62
|
+
campaignKey: campaignData.key,
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
localStorage.setItem(dismissKey, JSON.stringify(dismissData));
|
|
66
|
+
setIsDismissed(true);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error("Failed to save dismiss state:", error);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
// Reset dismissed state
|
|
73
|
+
const resetDismissed = () => {
|
|
74
|
+
if (!campaignData)
|
|
75
|
+
return;
|
|
76
|
+
const dismissKey = `trustshop_marketing_campaign_dismissed_${campaignData.key || campaignData.id}`;
|
|
77
|
+
try {
|
|
78
|
+
localStorage.removeItem(dismissKey);
|
|
79
|
+
setIsDismissed(false);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error("Failed to clear dismiss state:", error);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const handleCampaignsRefresh = (_event) => {
|
|
87
|
+
// console.log("Marketing campaigns refreshed via SDK");
|
|
88
|
+
};
|
|
89
|
+
window.addEventListener("trustshop-campaigns-refresh", handleCampaignsRefresh);
|
|
90
|
+
return () => {
|
|
91
|
+
window.removeEventListener("trustshop-campaigns-refresh", handleCampaignsRefresh);
|
|
92
|
+
};
|
|
93
|
+
}, []);
|
|
94
|
+
return {
|
|
95
|
+
campaignData,
|
|
96
|
+
loading: sdk.campaignsLoading || false,
|
|
97
|
+
error: sdk.campaignsError || null,
|
|
98
|
+
isDismissed,
|
|
99
|
+
dismissCampaign,
|
|
100
|
+
resetDismissed,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=useMarketingCampaign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMarketingCampaign.js","sourceRoot":"","sources":["../../src/hooks/useMarketingCampaign.ts"],"names":[],"mappings":"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","sourcesContent":["import { useState, useEffect, useMemo } from \"react\";\nimport { useSDK } from \"../core/SDKManager\";\nimport SDKManager from \"../core/SDKManager\";\nimport type { MarketingCampaignData } from \"../components/MarketingCampaignCard\";\nimport type { SupportedLocale } from \"../translations/translation-manager\";\n\ninterface UseMarketingCampaignOptions {\n shopInfo?: { id: string; domain?: string };\n shopId?: string;\n locale?: string | SupportedLocale;\n translations?: any;\n config?: any;\n}\n\ninterface UseMarketingCampaignReturn {\n campaignData: MarketingCampaignData | null;\n loading: boolean;\n error: Error | null;\n isDismissed: boolean;\n dismissCampaign: () => void;\n resetDismissed: () => void;\n}\n\n/**\n * Hook to fetch and manage marketing campaign card data\n * Uses SDK-managed campaigns data\n */\nexport function useMarketingCampaign(\n options: UseMarketingCampaignOptions = {},\n): UseMarketingCampaignReturn {\n const { shopInfo, locale, translations, config } = options;\n\n const sdk = useSDK({\n shopInfo,\n locale: locale as SupportedLocale | undefined,\n translations,\n config,\n });\n\n const [isDismissed, setIsDismissed] = useState(false);\n const campaignsData = SDKManager.getCampaignsData();\n\n const campaignData = useMemo(() => {\n if (!campaignsData || !Array.isArray(campaignsData)) {\n // console.log(\"[useMarketingCampaign] No campaigns data available\");\n return null;\n }\n\n // console.log(\"[useMarketingCampaign] Processing campaigns:\", campaignsData);\n\n const marketingCampaigns = campaignsData.filter(\n (campaign: any) => campaign.type === \"marketing-card\",\n );\n\n if (marketingCampaigns.length > 0) {\n return marketingCampaigns[0] as MarketingCampaignData;\n }\n\n return null;\n }, [campaignsData]);\n\n // Check if campaign is dismissed\n useEffect(() => {\n if (!campaignData) return;\n\n const dismissKey = `trustshop_marketing_campaign_dismissed_${campaignData.key || campaignData.id}`;\n try {\n const dismissedData = localStorage.getItem(dismissKey);\n if (dismissedData) {\n const parsed = JSON.parse(dismissedData);\n const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;\n if (parsed.timestamp > sevenDaysAgo) {\n setIsDismissed(true);\n } else {\n // Clear expired dismiss data\n localStorage.removeItem(dismissKey);\n }\n }\n } catch (error) {\n console.error(\"Failed to check dismiss state:\", error);\n }\n }, [campaignData]);\n\n // Dismiss campaign handler\n const dismissCampaign = () => {\n if (!campaignData) return;\n\n const dismissKey = `trustshop_marketing_campaign_dismissed_${campaignData.key || campaignData.id}`;\n const dismissData = {\n timestamp: Date.now(),\n dismissed: true,\n campaignId: campaignData.id,\n campaignKey: campaignData.key,\n };\n\n try {\n localStorage.setItem(dismissKey, JSON.stringify(dismissData));\n setIsDismissed(true);\n } catch (error) {\n console.error(\"Failed to save dismiss state:\", error);\n }\n };\n\n // Reset dismissed state\n const resetDismissed = () => {\n if (!campaignData) return;\n\n const dismissKey = `trustshop_marketing_campaign_dismissed_${campaignData.key || campaignData.id}`;\n try {\n localStorage.removeItem(dismissKey);\n setIsDismissed(false);\n } catch (error) {\n console.error(\"Failed to clear dismiss state:\", error);\n }\n };\n\n useEffect(() => {\n const handleCampaignsRefresh = (_event: any) => {\n // console.log(\"Marketing campaigns refreshed via SDK\");\n };\n\n window.addEventListener(\n \"trustshop-campaigns-refresh\",\n handleCampaignsRefresh,\n );\n\n return () => {\n window.removeEventListener(\n \"trustshop-campaigns-refresh\",\n handleCampaignsRefresh,\n );\n };\n }, []);\n\n return {\n campaignData,\n loading: sdk.campaignsLoading || false,\n error: sdk.campaignsError || null,\n isDismissed,\n dismissCampaign,\n resetDismissed,\n };\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Campaign API Cache Manager
|
|
3
|
+
* Prevents multiple identical API calls and caches responses
|
|
4
|
+
*/
|
|
5
|
+
declare class CampaignCacheManager {
|
|
6
|
+
private cache;
|
|
7
|
+
private readonly CACHE_DURATION;
|
|
8
|
+
private readonly DEDUPE_WINDOW;
|
|
9
|
+
/**
|
|
10
|
+
* Get cache key for campaigns
|
|
11
|
+
*/
|
|
12
|
+
private getCacheKey;
|
|
13
|
+
/**
|
|
14
|
+
* Check if cache entry is still valid
|
|
15
|
+
*/
|
|
16
|
+
private isValidCache;
|
|
17
|
+
/**
|
|
18
|
+
* Fetch campaigns with caching and deduplication
|
|
19
|
+
*/
|
|
20
|
+
fetchCampaigns(shopId: string, locale: string, apiUrl: string, headers?: HeadersInit): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Perform the actual fetch
|
|
23
|
+
*/
|
|
24
|
+
private performFetch;
|
|
25
|
+
/**
|
|
26
|
+
* Clear cache for specific shop or all
|
|
27
|
+
*/
|
|
28
|
+
clearCache(shopId?: string, locale?: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Get current cache size
|
|
31
|
+
*/
|
|
32
|
+
getCacheSize(): number;
|
|
33
|
+
/**
|
|
34
|
+
* Debug: Get all cache keys
|
|
35
|
+
*/
|
|
36
|
+
getCacheKeys(): string[];
|
|
37
|
+
}
|
|
38
|
+
export declare const campaignCache: CampaignCacheManager;
|
|
39
|
+
export default CampaignCacheManager;
|
|
40
|
+
//# sourceMappingURL=campaignCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaignCache.d.ts","sourceRoot":"","sources":["../../src/utils/campaignCache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,cAAM,oBAAoB;IACxB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAO;IAErC;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACG,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,GAAG,CAAC;IAkDf;;OAEG;YACW,YAAY;IAuB1B;;OAEG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAWlD;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,YAAY,IAAI,MAAM,EAAE;CAGzB;AAGD,eAAO,MAAM,aAAa,sBAA6B,CAAC;AAGxD,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Campaign API Cache Manager
|
|
3
|
+
* Prevents multiple identical API calls and caches responses
|
|
4
|
+
*/
|
|
5
|
+
class CampaignCacheManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.cache = new Map();
|
|
8
|
+
this.CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
9
|
+
this.DEDUPE_WINDOW = 100; // 100ms window for deduplication
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get cache key for campaigns
|
|
13
|
+
*/
|
|
14
|
+
getCacheKey(shopId, locale) {
|
|
15
|
+
return `campaigns_${shopId}_${locale}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if cache entry is still valid
|
|
19
|
+
*/
|
|
20
|
+
isValidCache(entry) {
|
|
21
|
+
return Date.now() - entry.timestamp < this.CACHE_DURATION;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch campaigns with caching and deduplication
|
|
25
|
+
*/
|
|
26
|
+
async fetchCampaigns(shopId, locale, apiUrl, headers = {}) {
|
|
27
|
+
const cacheKey = this.getCacheKey(shopId, locale);
|
|
28
|
+
const cached = this.cache.get(cacheKey);
|
|
29
|
+
// Return cached data if valid
|
|
30
|
+
if (cached && this.isValidCache(cached)) {
|
|
31
|
+
// console.log("[CampaignCache] Returning cached data for", cacheKey);
|
|
32
|
+
return cached.data;
|
|
33
|
+
}
|
|
34
|
+
// Check if there's an ongoing request for the same data
|
|
35
|
+
if (cached?.promise) {
|
|
36
|
+
// console.log("[CampaignCache] Reusing ongoing request for", cacheKey);
|
|
37
|
+
return cached.promise;
|
|
38
|
+
}
|
|
39
|
+
// Create new request promise
|
|
40
|
+
const promise = this.performFetch(apiUrl, headers)
|
|
41
|
+
.then((data) => {
|
|
42
|
+
this.cache.set(cacheKey, {
|
|
43
|
+
data,
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
});
|
|
46
|
+
// console.log("[CampaignCache] Cached new data for", cacheKey);
|
|
47
|
+
return data;
|
|
48
|
+
})
|
|
49
|
+
.catch((error) => {
|
|
50
|
+
// Remove failed request from cache
|
|
51
|
+
this.cache.delete(cacheKey);
|
|
52
|
+
throw error;
|
|
53
|
+
});
|
|
54
|
+
// Store the promise temporarily for deduplication
|
|
55
|
+
this.cache.set(cacheKey, {
|
|
56
|
+
data: cached?.data,
|
|
57
|
+
timestamp: cached?.timestamp || 0,
|
|
58
|
+
promise,
|
|
59
|
+
});
|
|
60
|
+
// Clean up promise after deduplication window
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
const entry = this.cache.get(cacheKey);
|
|
63
|
+
if (entry?.promise === promise) {
|
|
64
|
+
delete entry.promise;
|
|
65
|
+
}
|
|
66
|
+
}, this.DEDUPE_WINDOW);
|
|
67
|
+
return promise;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Perform the actual fetch
|
|
71
|
+
*/
|
|
72
|
+
async performFetch(apiUrl, headers) {
|
|
73
|
+
// console.log("[CampaignCache] Fetching from API:", apiUrl);
|
|
74
|
+
const response = await fetch(apiUrl, {
|
|
75
|
+
method: "GET",
|
|
76
|
+
headers: {
|
|
77
|
+
Accept: "application/json",
|
|
78
|
+
"Content-Type": "application/json",
|
|
79
|
+
...headers,
|
|
80
|
+
},
|
|
81
|
+
credentials: "include",
|
|
82
|
+
});
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
throw new Error(`Failed to fetch campaigns: ${response.statusText}`);
|
|
85
|
+
}
|
|
86
|
+
return response.json();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Clear cache for specific shop or all
|
|
90
|
+
*/
|
|
91
|
+
clearCache(shopId, locale) {
|
|
92
|
+
if (shopId && locale) {
|
|
93
|
+
const cacheKey = this.getCacheKey(shopId, locale);
|
|
94
|
+
this.cache.delete(cacheKey);
|
|
95
|
+
// console.log("[CampaignCache] Cleared cache for", cacheKey);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.cache.clear();
|
|
99
|
+
// console.log("[CampaignCache] Cleared all cache");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get current cache size
|
|
104
|
+
*/
|
|
105
|
+
getCacheSize() {
|
|
106
|
+
return this.cache.size;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Debug: Get all cache keys
|
|
110
|
+
*/
|
|
111
|
+
getCacheKeys() {
|
|
112
|
+
return Array.from(this.cache.keys());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Export singleton instance
|
|
116
|
+
export const campaignCache = new CampaignCacheManager();
|
|
117
|
+
// Export for testing
|
|
118
|
+
export default CampaignCacheManager;
|
|
119
|
+
//# sourceMappingURL=campaignCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaignCache.js","sourceRoot":"","sources":["../../src/utils/campaignCache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Campaign API Cache Manager\n * Prevents multiple identical API calls and caches responses\n */\n\ninterface CacheEntry {\n data: any;\n timestamp: number;\n promise?: Promise<any>;\n}\n\nclass CampaignCacheManager {\n private cache = new Map<string, CacheEntry>();\n private readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutes\n private readonly DEDUPE_WINDOW = 100; // 100ms window for deduplication\n\n /**\n * Get cache key for campaigns\n */\n private getCacheKey(shopId: string, locale: string): string {\n return `campaigns_${shopId}_${locale}`;\n }\n\n /**\n * Check if cache entry is still valid\n */\n private isValidCache(entry: CacheEntry): boolean {\n return Date.now() - entry.timestamp < this.CACHE_DURATION;\n }\n\n /**\n * Fetch campaigns with caching and deduplication\n */\n async fetchCampaigns(\n shopId: string,\n locale: string,\n apiUrl: string,\n headers: HeadersInit = {},\n ): Promise<any> {\n const cacheKey = this.getCacheKey(shopId, locale);\n const cached = this.cache.get(cacheKey);\n\n // Return cached data if valid\n if (cached && this.isValidCache(cached)) {\n // console.log(\"[CampaignCache] Returning cached data for\", cacheKey);\n return cached.data;\n }\n\n // Check if there's an ongoing request for the same data\n if (cached?.promise) {\n // console.log(\"[CampaignCache] Reusing ongoing request for\", cacheKey);\n return cached.promise;\n }\n\n // Create new request promise\n const promise = this.performFetch(apiUrl, headers)\n .then((data) => {\n this.cache.set(cacheKey, {\n data,\n timestamp: Date.now(),\n });\n // console.log(\"[CampaignCache] Cached new data for\", cacheKey);\n return data;\n })\n .catch((error) => {\n // Remove failed request from cache\n this.cache.delete(cacheKey);\n throw error;\n });\n\n // Store the promise temporarily for deduplication\n this.cache.set(cacheKey, {\n data: cached?.data,\n timestamp: cached?.timestamp || 0,\n promise,\n });\n\n // Clean up promise after deduplication window\n setTimeout(() => {\n const entry = this.cache.get(cacheKey);\n if (entry?.promise === promise) {\n delete entry.promise;\n }\n }, this.DEDUPE_WINDOW);\n\n return promise;\n }\n\n /**\n * Perform the actual fetch\n */\n private async performFetch(\n apiUrl: string,\n headers: HeadersInit,\n ): Promise<any> {\n // console.log(\"[CampaignCache] Fetching from API:\", apiUrl);\n\n const response = await fetch(apiUrl, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n credentials: \"include\",\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch campaigns: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /**\n * Clear cache for specific shop or all\n */\n clearCache(shopId?: string, locale?: string): void {\n if (shopId && locale) {\n const cacheKey = this.getCacheKey(shopId, locale);\n this.cache.delete(cacheKey);\n // console.log(\"[CampaignCache] Cleared cache for\", cacheKey);\n } else {\n this.cache.clear();\n // console.log(\"[CampaignCache] Cleared all cache\");\n }\n }\n\n /**\n * Get current cache size\n */\n getCacheSize(): number {\n return this.cache.size;\n }\n\n /**\n * Debug: Get all cache keys\n */\n getCacheKeys(): string[] {\n return Array.from(this.cache.keys());\n }\n}\n\n// Export singleton instance\nexport const campaignCache = new CampaignCacheManager();\n\n// Export for testing\nexport default CampaignCacheManager;\n"]}
|
package/package.json
CHANGED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { DashboardEngine } from "./engine";
|
|
2
|
-
import { DashboardConfig, DashboardState } from "../types";
|
|
3
|
-
/**
|
|
4
|
-
* GlobalDashboardManager - Singleton manager for shared SDK state
|
|
5
|
-
*
|
|
6
|
-
* This manager ensures all DashboardProvider instances share:
|
|
7
|
-
* - The same engine instance
|
|
8
|
-
* - The same data/cache
|
|
9
|
-
* - The same locale state
|
|
10
|
-
* - Deduplicated API calls
|
|
11
|
-
*
|
|
12
|
-
* Multiple providers can mount/unmount independently while maintaining
|
|
13
|
-
* a stable, shared runtime.
|
|
14
|
-
*/
|
|
15
|
-
export declare class GlobalDashboardManager {
|
|
16
|
-
private static instance;
|
|
17
|
-
private engine;
|
|
18
|
-
private providers;
|
|
19
|
-
private stateListeners;
|
|
20
|
-
private globalState;
|
|
21
|
-
private initPromise;
|
|
22
|
-
private lastConfig;
|
|
23
|
-
private pendingLocaleRefresh;
|
|
24
|
-
private constructor();
|
|
25
|
-
static getInstance(): GlobalDashboardManager;
|
|
26
|
-
/**
|
|
27
|
-
* Register a provider instance
|
|
28
|
-
*/
|
|
29
|
-
registerProvider(id: string, config?: DashboardConfig, autoRefreshOnLocaleChange?: boolean): void;
|
|
30
|
-
/**
|
|
31
|
-
* Unregister a provider instance
|
|
32
|
-
*/
|
|
33
|
-
unregisterProvider(id: string): void;
|
|
34
|
-
/**
|
|
35
|
-
* Get or create the shared engine instance
|
|
36
|
-
*/
|
|
37
|
-
private getOrCreateEngine;
|
|
38
|
-
/**
|
|
39
|
-
* Update the global configuration
|
|
40
|
-
*/
|
|
41
|
-
private updateGlobalConfig;
|
|
42
|
-
/**
|
|
43
|
-
* Check if two configs are equal
|
|
44
|
-
*/
|
|
45
|
-
private configEquals;
|
|
46
|
-
/**
|
|
47
|
-
* Initialize the shared engine
|
|
48
|
-
*/
|
|
49
|
-
init(config?: DashboardConfig): Promise<void>;
|
|
50
|
-
/**
|
|
51
|
-
* Refresh the dashboard data
|
|
52
|
-
*/
|
|
53
|
-
refresh(): Promise<void>;
|
|
54
|
-
/**
|
|
55
|
-
* Update locale globally
|
|
56
|
-
*/
|
|
57
|
-
setLocale(locale: string): Promise<void>;
|
|
58
|
-
/**
|
|
59
|
-
* Get the current locale
|
|
60
|
-
*/
|
|
61
|
-
getCurrentLocale(): string;
|
|
62
|
-
/**
|
|
63
|
-
* Subscribe to global state changes
|
|
64
|
-
*/
|
|
65
|
-
subscribe(id: string, callback: (state: DashboardState) => void): () => void;
|
|
66
|
-
/**
|
|
67
|
-
* Update and broadcast global state
|
|
68
|
-
*/
|
|
69
|
-
private updateGlobalState;
|
|
70
|
-
/**
|
|
71
|
-
* Get the current global state
|
|
72
|
-
*/
|
|
73
|
-
getState(): DashboardState;
|
|
74
|
-
/**
|
|
75
|
-
* Get the shared engine instance (if created)
|
|
76
|
-
*/
|
|
77
|
-
getEngine(): DashboardEngine | null;
|
|
78
|
-
/**
|
|
79
|
-
* Check if manager is initialized
|
|
80
|
-
*/
|
|
81
|
-
isInitialized(): boolean;
|
|
82
|
-
/**
|
|
83
|
-
* Get provider count
|
|
84
|
-
*/
|
|
85
|
-
getProviderCount(): number;
|
|
86
|
-
/**
|
|
87
|
-
* Reset the global manager (for testing)
|
|
88
|
-
*/
|
|
89
|
-
static reset(): void;
|
|
90
|
-
}
|
|
91
|
-
//# sourceMappingURL=global-manager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-manager.d.ts","sourceRoot":"","sources":["../../src/core/global-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAc3D;;;;;;;;;;;GAWG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAuC;IAC9D,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,SAAS,CAAwC;IACzD,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,WAAW,CAKjB;IACF,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,oBAAoB,CAAuB;IAEnD,OAAO;WAIO,WAAW,IAAI,sBAAsB;IAOnD;;OAEG;IACI,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,EAAE,yBAAyB,GAAE,OAAe,GAAG,IAAI;IA8B/G;;OAEG;IACI,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAW3C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsBzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAwC1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACU,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAoD1D;;OAEG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgCrC;;OAEG;IACU,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCrD;;OAEG;IACI,gBAAgB,IAAI,MAAM;IAOjC;;OAEG;IACI,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI;IAanF;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACI,QAAQ,IAAI,cAAc;IAIjC;;OAEG;IACI,SAAS,IAAI,eAAe,GAAG,IAAI;IAI1C;;OAEG;IACI,aAAa,IAAI,OAAO;IAI/B;;OAEG;IACI,gBAAgB,IAAI,MAAM;IAIjC;;OAEG;WACW,KAAK,IAAI,IAAI;CAuB5B"}
|