@memberjunction/react-runtime 5.1.0 → 5.3.0
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/.turbo/turbo-build.log +15 -15
- package/CHANGELOG.md +27 -0
- package/dist/324.runtime.umd.js +20 -12
- package/dist/component-manager/component-manager.js.map +1 -1
- package/dist/runtime.umd.js +1 -1
- package/dist/types/library-config.d.ts +1 -0
- package/dist/types/library-config.d.ts.map +1 -1
- package/dist/types/library-config.js.map +1 -1
- package/dist/utilities/core-libraries.d.ts.map +1 -1
- package/dist/utilities/core-libraries.js +13 -0
- package/dist/utilities/core-libraries.js.map +1 -1
- package/dist/utilities/library-loader.d.ts +2 -0
- package/dist/utilities/library-loader.d.ts.map +1 -1
- package/dist/utilities/library-loader.js +52 -8
- package/dist/utilities/library-loader.js.map +1 -1
- package/package.json +6 -6
- package/src/component-manager/component-manager.ts +2 -2
- package/src/types/library-config.ts +4 -1
- package/src/utilities/core-libraries.ts +25 -0
- package/src/utilities/library-loader.ts +92 -18
|
@@ -103,8 +103,8 @@ export class LibraryLoader {
|
|
|
103
103
|
static async loadLibrariesFromConfig(options?: ConfigLoadOptions, debug?: boolean): Promise<LibraryLoadResult> {
|
|
104
104
|
// Always load core runtime libraries first
|
|
105
105
|
const coreLibraries = getCoreRuntimeLibraries(debug);
|
|
106
|
-
const corePromises = coreLibraries.map(lib =>
|
|
107
|
-
this.loadScript(lib.cdnUrl, lib.globalVariable, debug)
|
|
106
|
+
const corePromises = coreLibraries.map(lib =>
|
|
107
|
+
this.loadScript(lib.cdnUrl, lib.globalVariable, debug, lib.fallbackCdnUrls)
|
|
108
108
|
);
|
|
109
109
|
|
|
110
110
|
const coreResults = await Promise.all(corePromises);
|
|
@@ -157,8 +157,8 @@ export class LibraryLoader {
|
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
// Load plugin libraries
|
|
160
|
-
const pluginPromises = pluginLibraries.map(lib =>
|
|
161
|
-
this.loadScript(lib.cdnUrl, lib.globalVariable, debug)
|
|
160
|
+
const pluginPromises = pluginLibraries.map(lib =>
|
|
161
|
+
this.loadScript(lib.cdnUrl, lib.globalVariable, debug, lib.fallbackCdnUrls)
|
|
162
162
|
);
|
|
163
163
|
|
|
164
164
|
const pluginResults = await Promise.all(pluginPromises);
|
|
@@ -219,10 +219,25 @@ export class LibraryLoader {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
|
-
* Load a script from URL
|
|
222
|
+
* Load a script from URL, with optional fallback CDN URLs.
|
|
223
|
+
* Tries the primary URL first, then each fallback in order until one succeeds.
|
|
223
224
|
*/
|
|
224
|
-
private static async loadScript(
|
|
225
|
-
|
|
225
|
+
private static async loadScript(
|
|
226
|
+
url: string,
|
|
227
|
+
globalName: string,
|
|
228
|
+
debug: boolean = false,
|
|
229
|
+
fallbackUrls?: string[]
|
|
230
|
+
): Promise<any> {
|
|
231
|
+
// Check if the global is already available (loaded by any URL)
|
|
232
|
+
const existingGlobal = typeof window !== 'undefined' ? (window as any)[globalName] : undefined;
|
|
233
|
+
if (existingGlobal) {
|
|
234
|
+
if (debug) {
|
|
235
|
+
console.log(`✅ Library '${globalName}' already available globally`);
|
|
236
|
+
}
|
|
237
|
+
return existingGlobal;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check if already loaded from this URL
|
|
226
241
|
const existing = this.loadedResources.get(url);
|
|
227
242
|
if (existing) {
|
|
228
243
|
if (debug) {
|
|
@@ -231,13 +246,71 @@ export class LibraryLoader {
|
|
|
231
246
|
return existing.promise;
|
|
232
247
|
}
|
|
233
248
|
|
|
249
|
+
// Try the primary URL first
|
|
250
|
+
try {
|
|
251
|
+
return await this.loadScriptFromUrl(url, globalName, debug);
|
|
252
|
+
} catch (primaryError) {
|
|
253
|
+
// If no fallbacks, rethrow immediately
|
|
254
|
+
if (!fallbackUrls || fallbackUrls.length === 0) {
|
|
255
|
+
throw primaryError;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Clean up the failed primary entry so fallbacks can try fresh
|
|
259
|
+
this.cleanupFailedScript(url);
|
|
260
|
+
|
|
261
|
+
if (debug) {
|
|
262
|
+
console.warn(`⚠️ Primary CDN failed for '${globalName}', trying fallback CDNs...`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Try each fallback URL in order
|
|
266
|
+
for (let i = 0; i < fallbackUrls.length; i++) {
|
|
267
|
+
const fallbackUrl = fallbackUrls[i];
|
|
268
|
+
try {
|
|
269
|
+
const result = await this.loadScriptFromUrl(fallbackUrl, globalName, debug);
|
|
270
|
+
console.log(`✅ Library '${globalName}' loaded from fallback CDN: ${fallbackUrl}`);
|
|
271
|
+
return result;
|
|
272
|
+
} catch (fallbackError) {
|
|
273
|
+
this.cleanupFailedScript(fallbackUrl);
|
|
274
|
+
if (debug) {
|
|
275
|
+
console.warn(`⚠️ Fallback CDN ${i + 1}/${fallbackUrls.length} failed for '${globalName}'`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// All URLs failed
|
|
281
|
+
const allUrls = [url, ...fallbackUrls];
|
|
282
|
+
throw new Error(
|
|
283
|
+
`Failed to load '${globalName}' from all CDN sources: ${allUrls.join(', ')}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Remove a failed script from the cache and DOM so a fallback URL can be tried.
|
|
290
|
+
*/
|
|
291
|
+
private static cleanupFailedScript(url: string): void {
|
|
292
|
+
const failed = this.loadedResources.get(url);
|
|
293
|
+
if (failed?.element?.parentNode) {
|
|
294
|
+
failed.element.parentNode.removeChild(failed.element);
|
|
295
|
+
}
|
|
296
|
+
this.loadedResources.delete(url);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Load a single script from a specific URL. This is the low-level loader
|
|
301
|
+
* that handles DOM script element creation and global variable detection.
|
|
302
|
+
*/
|
|
303
|
+
private static loadScriptFromUrl(url: string, globalName: string, debug: boolean = false): Promise<any> {
|
|
304
|
+
// Check if already cached for this URL
|
|
305
|
+
const existing = this.loadedResources.get(url);
|
|
306
|
+
if (existing) {
|
|
307
|
+
return existing.promise;
|
|
308
|
+
}
|
|
309
|
+
|
|
234
310
|
const promise = new Promise((resolve, reject) => {
|
|
235
311
|
// Check if global already exists
|
|
236
312
|
const existingGlobal = (window as any)[globalName];
|
|
237
313
|
if (existingGlobal) {
|
|
238
|
-
if (debug) {
|
|
239
|
-
console.log(`✅ Library '${globalName}' already available globally`);
|
|
240
|
-
}
|
|
241
314
|
resolve(existingGlobal);
|
|
242
315
|
return;
|
|
243
316
|
}
|
|
@@ -249,11 +322,12 @@ export class LibraryLoader {
|
|
|
249
322
|
return;
|
|
250
323
|
}
|
|
251
324
|
|
|
252
|
-
// Create new script
|
|
325
|
+
// Create new script — no crossOrigin attribute so the browser uses standard
|
|
326
|
+
// "no-cors" mode, which avoids CORS failures when CDN headers are missing
|
|
327
|
+
// or browser tracking prevention interferes with the response.
|
|
253
328
|
const script = document.createElement('script');
|
|
254
329
|
script.src = url;
|
|
255
330
|
script.async = true;
|
|
256
|
-
script.crossOrigin = 'anonymous';
|
|
257
331
|
|
|
258
332
|
const cleanup = () => {
|
|
259
333
|
script.removeEventListener('load', onLoad);
|
|
@@ -262,7 +336,7 @@ export class LibraryLoader {
|
|
|
262
336
|
|
|
263
337
|
const onLoad = async () => {
|
|
264
338
|
cleanup();
|
|
265
|
-
|
|
339
|
+
|
|
266
340
|
// Use progressive delay if enabled, otherwise use original behavior
|
|
267
341
|
if (LibraryLoader.enableProgressiveDelay) {
|
|
268
342
|
try {
|
|
@@ -281,7 +355,7 @@ export class LibraryLoader {
|
|
|
281
355
|
resolve(global);
|
|
282
356
|
} else {
|
|
283
357
|
// Some libraries may take a moment to initialize
|
|
284
|
-
|
|
358
|
+
resourceManager.setTimeout(
|
|
285
359
|
LIBRARY_LOADER_COMPONENT_ID,
|
|
286
360
|
() => {
|
|
287
361
|
const delayedGlobal = (window as any)[globalName];
|
|
@@ -317,14 +391,14 @@ export class LibraryLoader {
|
|
|
317
391
|
// These are harmless and expected when loading minified libraries from CDNs
|
|
318
392
|
// that reference source maps which aren't available. This doesn't affect functionality.
|
|
319
393
|
document.head.appendChild(script);
|
|
320
|
-
|
|
394
|
+
|
|
321
395
|
// Register the script element for cleanup
|
|
322
396
|
resourceManager.registerDOMElement(LIBRARY_LOADER_COMPONENT_ID, script);
|
|
323
397
|
});
|
|
324
398
|
|
|
325
|
-
this.loadedResources.set(url, {
|
|
326
|
-
element: document.querySelector(`script[src="${url}"]`)!,
|
|
327
|
-
promise
|
|
399
|
+
this.loadedResources.set(url, {
|
|
400
|
+
element: document.querySelector(`script[src="${url}"]`)!,
|
|
401
|
+
promise
|
|
328
402
|
});
|
|
329
403
|
|
|
330
404
|
return promise;
|