@datlv-trustshop/shopify-inapp-components 0.3.4 → 0.3.6
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/FloatingCard.d.ts +2 -0
- package/dist/components/FloatingCard.d.ts.map +1 -1
- package/dist/components/FloatingCard.js.map +1 -1
- package/dist/components/MarketingCampaignCard.d.ts +2 -0
- package/dist/components/MarketingCampaignCard.d.ts.map +1 -1
- package/dist/components/MarketingCampaignCard.js +13 -113
- package/dist/components/MarketingCampaignCard.js.map +1 -1
- package/dist/core/SDKManager.d.ts.map +1 -1
- package/dist/core/SDKManager.js +11 -19
- 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/useFloatingCards.d.ts.map +1 -1
- package/dist/hooks/useFloatingCards.js +36 -14
- package/dist/hooks/useFloatingCards.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 +122 -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,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"}
|
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
import { DashboardEngine } from "./engine";
|
|
2
|
-
/**
|
|
3
|
-
* GlobalDashboardManager - Singleton manager for shared SDK state
|
|
4
|
-
*
|
|
5
|
-
* This manager ensures all DashboardProvider instances share:
|
|
6
|
-
* - The same engine instance
|
|
7
|
-
* - The same data/cache
|
|
8
|
-
* - The same locale state
|
|
9
|
-
* - Deduplicated API calls
|
|
10
|
-
*
|
|
11
|
-
* Multiple providers can mount/unmount independently while maintaining
|
|
12
|
-
* a stable, shared runtime.
|
|
13
|
-
*/
|
|
14
|
-
export class GlobalDashboardManager {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.engine = null;
|
|
17
|
-
this.providers = new Map();
|
|
18
|
-
this.stateListeners = new Set();
|
|
19
|
-
this.globalState = {
|
|
20
|
-
data: null,
|
|
21
|
-
loading: false,
|
|
22
|
-
error: null,
|
|
23
|
-
lastFetch: null,
|
|
24
|
-
};
|
|
25
|
-
this.initPromise = null;
|
|
26
|
-
this.lastConfig = null;
|
|
27
|
-
this.pendingLocaleRefresh = null;
|
|
28
|
-
// Private constructor for singleton
|
|
29
|
-
}
|
|
30
|
-
static getInstance() {
|
|
31
|
-
if (!GlobalDashboardManager.instance) {
|
|
32
|
-
GlobalDashboardManager.instance = new GlobalDashboardManager();
|
|
33
|
-
}
|
|
34
|
-
return GlobalDashboardManager.instance;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Register a provider instance
|
|
38
|
-
*/
|
|
39
|
-
registerProvider(id, config, autoRefreshOnLocaleChange = false) {
|
|
40
|
-
this.providers.set(id, {
|
|
41
|
-
id,
|
|
42
|
-
mounted: true,
|
|
43
|
-
config,
|
|
44
|
-
autoRefreshOnLocaleChange
|
|
45
|
-
});
|
|
46
|
-
// Update config if provided and different
|
|
47
|
-
if (config && !this.configEquals(this.lastConfig, config)) {
|
|
48
|
-
this.updateGlobalConfig(config);
|
|
49
|
-
}
|
|
50
|
-
// Check if we need to initialize due to pending locale refresh
|
|
51
|
-
// This handles the case where all providers have autoInit=false but want locale refresh
|
|
52
|
-
if (this.pendingLocaleRefresh && autoRefreshOnLocaleChange) {
|
|
53
|
-
const locale = this.pendingLocaleRefresh;
|
|
54
|
-
this.pendingLocaleRefresh = null;
|
|
55
|
-
// Force initialization with the new locale if not initialized
|
|
56
|
-
if (!this.engine || !this.engine.isInitialized()) {
|
|
57
|
-
if (this.lastConfig) {
|
|
58
|
-
this.init({ ...this.lastConfig, locale }).catch(console.error);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
this.engine.setLocale(locale).catch(console.error);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Unregister a provider instance
|
|
68
|
-
*/
|
|
69
|
-
unregisterProvider(id) {
|
|
70
|
-
this.providers.delete(id);
|
|
71
|
-
// Remove associated listeners
|
|
72
|
-
this.stateListeners = new Set(Array.from(this.stateListeners).filter(l => !l.id.startsWith(id)));
|
|
73
|
-
// Don't destroy engine even if no providers - maintain stable runtime
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Get or create the shared engine instance
|
|
77
|
-
*/
|
|
78
|
-
getOrCreateEngine(config) {
|
|
79
|
-
if (!this.engine) {
|
|
80
|
-
if (!config && !this.lastConfig) {
|
|
81
|
-
throw new Error("GlobalDashboardManager requires config on first use");
|
|
82
|
-
}
|
|
83
|
-
const finalConfig = config || this.lastConfig;
|
|
84
|
-
this.engine = DashboardEngine.getInstance(finalConfig);
|
|
85
|
-
this.lastConfig = finalConfig;
|
|
86
|
-
// Subscribe to engine changes
|
|
87
|
-
this.engine.subscribe((data) => {
|
|
88
|
-
this.updateGlobalState({
|
|
89
|
-
data,
|
|
90
|
-
loading: false,
|
|
91
|
-
error: null,
|
|
92
|
-
lastFetch: data ? new Date() : this.globalState.lastFetch,
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return this.engine;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Update the global configuration
|
|
100
|
-
*/
|
|
101
|
-
updateGlobalConfig(config) {
|
|
102
|
-
const previousLocale = this.lastConfig?.locale;
|
|
103
|
-
this.lastConfig = config;
|
|
104
|
-
if (this.engine) {
|
|
105
|
-
// Update existing engine config
|
|
106
|
-
this.engine.updateConfig(config);
|
|
107
|
-
// If locale changed, we need to refresh data
|
|
108
|
-
if (config.locale && config.locale !== previousLocale) {
|
|
109
|
-
// Check if any provider wants auto-refresh on locale change
|
|
110
|
-
const shouldRefresh = Array.from(this.providers.values()).some(p => p.autoRefreshOnLocaleChange);
|
|
111
|
-
if (this.engine.isInitialized()) {
|
|
112
|
-
// Engine is initialized, refresh immediately if any provider wants it
|
|
113
|
-
if (shouldRefresh) {
|
|
114
|
-
this.engine.setLocale(config.locale).catch(console.error);
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
this.engine.updateLocale(config.locale);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
// Engine not initialized yet
|
|
122
|
-
this.engine.updateLocale(config.locale);
|
|
123
|
-
// If any provider wants refresh, force initialization NOW
|
|
124
|
-
// This is critical for autoInit=false providers
|
|
125
|
-
if (shouldRefresh) {
|
|
126
|
-
this.init(config).catch(console.error);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
// Mark for later refresh when a provider initializes
|
|
130
|
-
this.pendingLocaleRefresh = config.locale;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
else if (config.locale && config.locale !== previousLocale) {
|
|
136
|
-
// No engine yet, but locale changed - mark for refresh when engine is created
|
|
137
|
-
this.pendingLocaleRefresh = config.locale;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Check if two configs are equal
|
|
142
|
-
*/
|
|
143
|
-
configEquals(a, b) {
|
|
144
|
-
if (!a || !b)
|
|
145
|
-
return a === b;
|
|
146
|
-
return (a.apiUrl === b.apiUrl &&
|
|
147
|
-
a.locale === b.locale &&
|
|
148
|
-
a.cacheTime === b.cacheTime &&
|
|
149
|
-
a.retryAttempts === b.retryAttempts &&
|
|
150
|
-
a.retryDelay === b.retryDelay);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Initialize the shared engine
|
|
154
|
-
*/
|
|
155
|
-
async init(config) {
|
|
156
|
-
// If we have a pending locale refresh and config has a different locale,
|
|
157
|
-
// use the pending locale instead
|
|
158
|
-
if (this.pendingLocaleRefresh && config) {
|
|
159
|
-
config = { ...config, locale: this.pendingLocaleRefresh };
|
|
160
|
-
}
|
|
161
|
-
if (this.initPromise && !config) {
|
|
162
|
-
return this.initPromise;
|
|
163
|
-
}
|
|
164
|
-
if (config) {
|
|
165
|
-
this.updateGlobalConfig(config);
|
|
166
|
-
}
|
|
167
|
-
this.updateGlobalState({ ...this.globalState, loading: true, error: null });
|
|
168
|
-
this.initPromise = (async () => {
|
|
169
|
-
try {
|
|
170
|
-
const engine = this.getOrCreateEngine(config);
|
|
171
|
-
await engine.init();
|
|
172
|
-
const data = engine.getData();
|
|
173
|
-
// Clear pending locale refresh since we just initialized with it
|
|
174
|
-
if (this.pendingLocaleRefresh) {
|
|
175
|
-
this.pendingLocaleRefresh = null;
|
|
176
|
-
}
|
|
177
|
-
this.updateGlobalState({
|
|
178
|
-
data,
|
|
179
|
-
loading: false,
|
|
180
|
-
error: null,
|
|
181
|
-
lastFetch: new Date(),
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
const err = error instanceof Error
|
|
186
|
-
? error
|
|
187
|
-
: new Error("Failed to initialize dashboard");
|
|
188
|
-
this.updateGlobalState({
|
|
189
|
-
...this.globalState,
|
|
190
|
-
loading: false,
|
|
191
|
-
error: err,
|
|
192
|
-
});
|
|
193
|
-
throw err;
|
|
194
|
-
}
|
|
195
|
-
})();
|
|
196
|
-
return this.initPromise;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Refresh the dashboard data
|
|
200
|
-
*/
|
|
201
|
-
async refresh() {
|
|
202
|
-
if (!this.engine) {
|
|
203
|
-
return this.init();
|
|
204
|
-
}
|
|
205
|
-
this.updateGlobalState({ ...this.globalState, loading: true, error: null });
|
|
206
|
-
try {
|
|
207
|
-
await this.engine.refresh();
|
|
208
|
-
const data = this.engine.getData();
|
|
209
|
-
this.updateGlobalState({
|
|
210
|
-
data,
|
|
211
|
-
loading: false,
|
|
212
|
-
error: null,
|
|
213
|
-
lastFetch: new Date(),
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
catch (error) {
|
|
217
|
-
const err = error instanceof Error
|
|
218
|
-
? error
|
|
219
|
-
: new Error("Failed to refresh dashboard");
|
|
220
|
-
this.updateGlobalState({
|
|
221
|
-
...this.globalState,
|
|
222
|
-
loading: false,
|
|
223
|
-
error: err,
|
|
224
|
-
});
|
|
225
|
-
throw err;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Update locale globally
|
|
230
|
-
*/
|
|
231
|
-
async setLocale(locale) {
|
|
232
|
-
// Update the last config
|
|
233
|
-
if (this.lastConfig) {
|
|
234
|
-
this.lastConfig = { ...this.lastConfig, locale };
|
|
235
|
-
}
|
|
236
|
-
// Check if any provider wants auto-refresh
|
|
237
|
-
const shouldRefresh = Array.from(this.providers.values()).some(p => p.autoRefreshOnLocaleChange);
|
|
238
|
-
if (!this.engine) {
|
|
239
|
-
// No engine yet, but if any provider wants refresh, initialize NOW
|
|
240
|
-
if (shouldRefresh && this.lastConfig) {
|
|
241
|
-
await this.init({ ...this.lastConfig, locale });
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
// Mark locale for refresh when engine is created
|
|
245
|
-
this.pendingLocaleRefresh = locale;
|
|
246
|
-
}
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
// Engine exists, update locale
|
|
250
|
-
if (this.engine.isInitialized()) {
|
|
251
|
-
await this.engine.setLocale(locale);
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
// Engine exists but not initialized
|
|
255
|
-
this.engine.updateLocale(locale);
|
|
256
|
-
// If any provider wants refresh, initialize now
|
|
257
|
-
if (shouldRefresh) {
|
|
258
|
-
await this.init();
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Get the current locale
|
|
264
|
-
*/
|
|
265
|
-
getCurrentLocale() {
|
|
266
|
-
if (this.engine) {
|
|
267
|
-
return this.engine.getCurrentLocale();
|
|
268
|
-
}
|
|
269
|
-
return this.lastConfig?.locale || "en";
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Subscribe to global state changes
|
|
273
|
-
*/
|
|
274
|
-
subscribe(id, callback) {
|
|
275
|
-
const listener = { id, callback };
|
|
276
|
-
this.stateListeners.add(listener);
|
|
277
|
-
// Immediately call with current state
|
|
278
|
-
callback(this.globalState);
|
|
279
|
-
// Return unsubscribe function
|
|
280
|
-
return () => {
|
|
281
|
-
this.stateListeners.delete(listener);
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Update and broadcast global state
|
|
286
|
-
*/
|
|
287
|
-
updateGlobalState(state) {
|
|
288
|
-
this.globalState = state;
|
|
289
|
-
// Notify all listeners
|
|
290
|
-
this.stateListeners.forEach(listener => {
|
|
291
|
-
try {
|
|
292
|
-
listener.callback(state);
|
|
293
|
-
}
|
|
294
|
-
catch (error) {
|
|
295
|
-
console.error(`Error in state listener ${listener.id}:`, error);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Get the current global state
|
|
301
|
-
*/
|
|
302
|
-
getState() {
|
|
303
|
-
return this.globalState;
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Get the shared engine instance (if created)
|
|
307
|
-
*/
|
|
308
|
-
getEngine() {
|
|
309
|
-
return this.engine;
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Check if manager is initialized
|
|
313
|
-
*/
|
|
314
|
-
isInitialized() {
|
|
315
|
-
return this.engine?.isInitialized() || false;
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Get provider count
|
|
319
|
-
*/
|
|
320
|
-
getProviderCount() {
|
|
321
|
-
return this.providers.size;
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Reset the global manager (for testing)
|
|
325
|
-
*/
|
|
326
|
-
static reset() {
|
|
327
|
-
if (GlobalDashboardManager.instance) {
|
|
328
|
-
const manager = GlobalDashboardManager.instance;
|
|
329
|
-
// Clear listeners
|
|
330
|
-
manager.stateListeners.clear();
|
|
331
|
-
manager.providers.clear();
|
|
332
|
-
// Reset state
|
|
333
|
-
manager.globalState = {
|
|
334
|
-
data: null,
|
|
335
|
-
loading: false,
|
|
336
|
-
error: null,
|
|
337
|
-
lastFetch: null,
|
|
338
|
-
};
|
|
339
|
-
manager.initPromise = null;
|
|
340
|
-
manager.lastConfig = null;
|
|
341
|
-
manager.engine = null;
|
|
342
|
-
}
|
|
343
|
-
GlobalDashboardManager.instance = null;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
GlobalDashboardManager.instance = null;
|
|
347
|
-
//# sourceMappingURL=global-manager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-manager.js","sourceRoot":"","sources":["../../src/core/global-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { DashboardEngine } from \"./engine\";\nimport { DashboardConfig, DashboardState } from \"../types\";\n\ninterface ProviderInfo {\n id: string;\n mounted: boolean;\n config?: DashboardConfig;\n autoRefreshOnLocaleChange?: boolean;\n}\n\ninterface GlobalStateListener {\n id: string;\n callback: (state: DashboardState) => void;\n}\n\n/**\n * GlobalDashboardManager - Singleton manager for shared SDK state\n * \n * This manager ensures all DashboardProvider instances share:\n * - The same engine instance\n * - The same data/cache\n * - The same locale state\n * - Deduplicated API calls\n * \n * Multiple providers can mount/unmount independently while maintaining\n * a stable, shared runtime.\n */\nexport class GlobalDashboardManager {\n private static instance: GlobalDashboardManager | null = null;\n private engine: DashboardEngine | null = null;\n private providers: Map<string, ProviderInfo> = new Map();\n private stateListeners: Set<GlobalStateListener> = new Set();\n private globalState: DashboardState = {\n data: null,\n loading: false,\n error: null,\n lastFetch: null,\n };\n private initPromise: Promise<void> | null = null;\n private lastConfig: DashboardConfig | null = null;\n private pendingLocaleRefresh: string | null = null;\n\n private constructor() {\n // Private constructor for singleton\n }\n\n public static getInstance(): GlobalDashboardManager {\n if (!GlobalDashboardManager.instance) {\n GlobalDashboardManager.instance = new GlobalDashboardManager();\n }\n return GlobalDashboardManager.instance;\n }\n\n /**\n * Register a provider instance\n */\n public registerProvider(id: string, config?: DashboardConfig, autoRefreshOnLocaleChange: boolean = false): void {\n this.providers.set(id, { \n id, \n mounted: true, \n config,\n autoRefreshOnLocaleChange\n });\n\n // Update config if provided and different\n if (config && !this.configEquals(this.lastConfig, config)) {\n this.updateGlobalConfig(config);\n }\n\n // Check if we need to initialize due to pending locale refresh\n // This handles the case where all providers have autoInit=false but want locale refresh\n if (this.pendingLocaleRefresh && autoRefreshOnLocaleChange) {\n const locale = this.pendingLocaleRefresh;\n this.pendingLocaleRefresh = null;\n \n // Force initialization with the new locale if not initialized\n if (!this.engine || !this.engine.isInitialized()) {\n if (this.lastConfig) {\n this.init({ ...this.lastConfig, locale }).catch(console.error);\n }\n } else {\n this.engine.setLocale(locale).catch(console.error);\n }\n }\n }\n\n /**\n * Unregister a provider instance\n */\n public unregisterProvider(id: string): void {\n this.providers.delete(id);\n \n // Remove associated listeners\n this.stateListeners = new Set(\n Array.from(this.stateListeners).filter(l => !l.id.startsWith(id))\n );\n\n // Don't destroy engine even if no providers - maintain stable runtime\n }\n\n /**\n * Get or create the shared engine instance\n */\n private getOrCreateEngine(config?: DashboardConfig): DashboardEngine {\n if (!this.engine) {\n if (!config && !this.lastConfig) {\n throw new Error(\"GlobalDashboardManager requires config on first use\");\n }\n const finalConfig = config || this.lastConfig!;\n this.engine = DashboardEngine.getInstance(finalConfig);\n this.lastConfig = finalConfig;\n \n // Subscribe to engine changes\n this.engine.subscribe((data) => {\n this.updateGlobalState({\n data,\n loading: false,\n error: null,\n lastFetch: data ? new Date() : this.globalState.lastFetch,\n });\n });\n }\n return this.engine;\n }\n\n /**\n * Update the global configuration\n */\n private updateGlobalConfig(config: DashboardConfig): void {\n const previousLocale = this.lastConfig?.locale;\n this.lastConfig = config;\n \n if (this.engine) {\n // Update existing engine config\n this.engine.updateConfig(config);\n \n // If locale changed, we need to refresh data\n if (config.locale && config.locale !== previousLocale) {\n // Check if any provider wants auto-refresh on locale change\n const shouldRefresh = Array.from(this.providers.values()).some(p => p.autoRefreshOnLocaleChange);\n \n if (this.engine.isInitialized()) {\n // Engine is initialized, refresh immediately if any provider wants it\n if (shouldRefresh) {\n this.engine.setLocale(config.locale).catch(console.error);\n } else {\n this.engine.updateLocale(config.locale);\n }\n } else {\n // Engine not initialized yet\n this.engine.updateLocale(config.locale);\n \n // If any provider wants refresh, force initialization NOW\n // This is critical for autoInit=false providers\n if (shouldRefresh) {\n this.init(config).catch(console.error);\n } else {\n // Mark for later refresh when a provider initializes\n this.pendingLocaleRefresh = config.locale;\n }\n }\n }\n } else if (config.locale && config.locale !== previousLocale) {\n // No engine yet, but locale changed - mark for refresh when engine is created\n this.pendingLocaleRefresh = config.locale;\n }\n }\n\n /**\n * Check if two configs are equal\n */\n private configEquals(a: DashboardConfig | null, b: DashboardConfig | null): boolean {\n if (!a || !b) return a === b;\n return (\n a.apiUrl === b.apiUrl &&\n a.locale === b.locale &&\n a.cacheTime === b.cacheTime &&\n a.retryAttempts === b.retryAttempts &&\n a.retryDelay === b.retryDelay\n );\n }\n\n /**\n * Initialize the shared engine\n */\n public async init(config?: DashboardConfig): Promise<void> {\n // If we have a pending locale refresh and config has a different locale,\n // use the pending locale instead\n if (this.pendingLocaleRefresh && config) {\n config = { ...config, locale: this.pendingLocaleRefresh };\n }\n \n if (this.initPromise && !config) {\n return this.initPromise;\n }\n\n if (config) {\n this.updateGlobalConfig(config);\n }\n\n this.updateGlobalState({ ...this.globalState, loading: true, error: null });\n\n this.initPromise = (async () => {\n try {\n const engine = this.getOrCreateEngine(config);\n await engine.init();\n const data = engine.getData();\n \n // Clear pending locale refresh since we just initialized with it\n if (this.pendingLocaleRefresh) {\n this.pendingLocaleRefresh = null;\n }\n \n this.updateGlobalState({\n data,\n loading: false,\n error: null,\n lastFetch: new Date(),\n });\n } catch (error) {\n const err = error instanceof Error \n ? error \n : new Error(\"Failed to initialize dashboard\");\n \n this.updateGlobalState({\n ...this.globalState,\n loading: false,\n error: err,\n });\n \n throw err;\n }\n })();\n\n return this.initPromise;\n }\n\n /**\n * Refresh the dashboard data\n */\n public async refresh(): Promise<void> {\n if (!this.engine) {\n return this.init();\n }\n\n this.updateGlobalState({ ...this.globalState, loading: true, error: null });\n\n try {\n await this.engine.refresh();\n const data = this.engine.getData();\n \n this.updateGlobalState({\n data,\n loading: false,\n error: null,\n lastFetch: new Date(),\n });\n } catch (error) {\n const err = error instanceof Error \n ? error \n : new Error(\"Failed to refresh dashboard\");\n \n this.updateGlobalState({\n ...this.globalState,\n loading: false,\n error: err,\n });\n \n throw err;\n }\n }\n\n /**\n * Update locale globally\n */\n public async setLocale(locale: string): Promise<void> {\n // Update the last config\n if (this.lastConfig) {\n this.lastConfig = { ...this.lastConfig, locale };\n }\n \n // Check if any provider wants auto-refresh\n const shouldRefresh = Array.from(this.providers.values()).some(p => p.autoRefreshOnLocaleChange);\n \n if (!this.engine) {\n // No engine yet, but if any provider wants refresh, initialize NOW\n if (shouldRefresh && this.lastConfig) {\n await this.init({ ...this.lastConfig, locale });\n } else {\n // Mark locale for refresh when engine is created\n this.pendingLocaleRefresh = locale;\n }\n return;\n }\n\n // Engine exists, update locale\n if (this.engine.isInitialized()) {\n await this.engine.setLocale(locale);\n } else {\n // Engine exists but not initialized\n this.engine.updateLocale(locale);\n \n // If any provider wants refresh, initialize now\n if (shouldRefresh) {\n await this.init();\n }\n }\n }\n\n /**\n * Get the current locale\n */\n public getCurrentLocale(): string {\n if (this.engine) {\n return this.engine.getCurrentLocale();\n }\n return this.lastConfig?.locale || \"en\";\n }\n\n /**\n * Subscribe to global state changes\n */\n public subscribe(id: string, callback: (state: DashboardState) => void): () => void {\n const listener = { id, callback };\n this.stateListeners.add(listener);\n \n // Immediately call with current state\n callback(this.globalState);\n \n // Return unsubscribe function\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n /**\n * Update and broadcast global state\n */\n private updateGlobalState(state: DashboardState): void {\n this.globalState = state;\n \n // Notify all listeners\n this.stateListeners.forEach(listener => {\n try {\n listener.callback(state);\n } catch (error) {\n console.error(`Error in state listener ${listener.id}:`, error);\n }\n });\n }\n\n /**\n * Get the current global state\n */\n public getState(): DashboardState {\n return this.globalState;\n }\n\n /**\n * Get the shared engine instance (if created)\n */\n public getEngine(): DashboardEngine | null {\n return this.engine;\n }\n\n /**\n * Check if manager is initialized\n */\n public isInitialized(): boolean {\n return this.engine?.isInitialized() || false;\n }\n\n /**\n * Get provider count\n */\n public getProviderCount(): number {\n return this.providers.size;\n }\n\n /**\n * Reset the global manager (for testing)\n */\n public static reset(): void {\n if (GlobalDashboardManager.instance) {\n const manager = GlobalDashboardManager.instance;\n \n // Clear listeners\n manager.stateListeners.clear();\n manager.providers.clear();\n \n // Reset state\n manager.globalState = {\n data: null,\n loading: false,\n error: null,\n lastFetch: null,\n };\n \n manager.initPromise = null;\n manager.lastConfig = null;\n manager.engine = null;\n }\n \n GlobalDashboardManager.instance = null;\n }\n}"]}
|