@lwrjs/everywhere 0.8.0-alpha.10

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.
@@ -0,0 +1,1344 @@
1
+ /**
2
+ * Copyright (c) 2022, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: MIT
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6
+ */
7
+ // Construct the Client Bootstrap Config object
8
+ function getConfiguration(options) {
9
+ const { format, server, apiVersion, apiPrefix, locale, bundle, debug } = options;
10
+ const thisUrl = new URL(import.meta.url);
11
+ // URLs
12
+ const ORIGIN = server || thisUrl.origin || '';
13
+ const PREFIX = `${apiPrefix}/${apiVersion}`;
14
+ const POSTFIX = `0${locale ? `/l/${locale}` : ''}`; // hardcode compat='0': LWR-S doesn't accept '1' and LWR-JS ignores it
15
+ const BUNDLE_ID = '/bi/3_A,B,O,S,EG,JLMT,C,D,F,H,I,K,N,P,Q,R'; // core strategies
16
+ const ENDPOINT = bundle
17
+ ? `${ORIGIN}${PREFIX}/bundle/${format}/${POSTFIX}${BUNDLE_ID}-0/module/mi/`
18
+ : `${ORIGIN}${PREFIX}/module/${format}/${POSTFIX}/mi/`;
19
+ // Component specifiers and URIs
20
+ const BOOT_MODULE = `lwr/everywhere${format === 'esm' ? 'Esm' : 'Amd'}/v/0_0_1`;
21
+ const BOOT_URI = `${ENDPOINT}${encodeURIComponent(BOOT_MODULE)}/latest${debug ? '?debug' : ''}`;
22
+ // Client Bootstrap Config
23
+ const config = {
24
+ appId: 'lwre',
25
+ autoBoot: true,
26
+ bootstrapModule: BOOT_MODULE,
27
+ baseUrl: ORIGIN,
28
+ imports: { [BOOT_URI]: [BOOT_MODULE] },
29
+ index: { [BOOT_MODULE]: BOOT_URI },
30
+ endpoints: {
31
+ uris: {
32
+ module: ENDPOINT,
33
+ mapping: `${ORIGIN}${PREFIX}/mapping/${format}/${POSTFIX}${bundle ? BUNDLE_ID : ''}/mp/`,
34
+ },
35
+ },
36
+ };
37
+ // Add the debug modifier to the endpoints
38
+ if (debug && config.endpoints) {
39
+ config.endpoints.modifiers = { debug: 'true' };
40
+ }
41
+ return {
42
+ options: { NODE_ENV: debug ? 'development' : 'production' },
43
+ config, // globalThis.LWR
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Copyright (c) 2021, salesforce.com, inc.
49
+ * All rights reserved.
50
+ * SPDX-License-Identifier: MIT
51
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
52
+ */
53
+ /* LWR Module Loader v0.8.0-alpha.10 */
54
+ const templateRegex = /\{([0-9]+)\}/g;
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ function templateString(template, args) {
57
+ return template.replace(templateRegex, (_, index) => {
58
+ return args[index];
59
+ });
60
+ }
61
+
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ function generateErrorMessage(errorInfo, args) {
64
+ const message = Array.isArray(args) ? templateString(errorInfo.message, args) : errorInfo.message;
65
+ return `LWR${errorInfo.code}: ${message}`;
66
+ }
67
+ class LoaderError extends Error {
68
+ constructor(errorInfo, errorArgs) {
69
+ super();
70
+ this.message = generateErrorMessage(errorInfo, errorArgs);
71
+ }
72
+ }
73
+ function invariant(condition, errorInfo) {
74
+ if (!condition) {
75
+ throw new LoaderError(errorInfo);
76
+ }
77
+ }
78
+ const MISSING_NAME = Object.freeze({
79
+ code: 3000,
80
+ message: 'A module name is required.',
81
+ level: 0,
82
+ });
83
+ const FAIL_INSTANTIATE = Object.freeze({
84
+ code: 3004,
85
+ message: 'Failed to instantiate module: {0}',
86
+ level: 0,
87
+ });
88
+ const NO_AMD_REQUIRE = Object.freeze({
89
+ code: 3005,
90
+ message: 'AMD require not supported.',
91
+ level: 0,
92
+ });
93
+ const FAILED_DEP = Object.freeze({
94
+ code: 3006,
95
+ level: 0,
96
+ message: 'Failed to load dependency: {0}',
97
+ });
98
+ const INVALID_DEPS = Object.freeze({
99
+ code: 3007,
100
+ message: 'Unexpected value received for dependencies argument; expected an array.',
101
+ level: 0,
102
+ });
103
+ const FAIL_LOAD = Object.freeze({
104
+ code: 3008,
105
+ level: 0,
106
+ message: 'Error loading {0}',
107
+ });
108
+ const UNRESOLVED = Object.freeze({
109
+ code: 3009,
110
+ level: 0,
111
+ message: 'Unable to resolve bare specifier: {0}',
112
+ });
113
+ const NO_BASE_URL = Object.freeze({
114
+ code: 3010,
115
+ level: 0,
116
+ message: 'baseUrl not set',
117
+ });
118
+ Object.freeze({
119
+ code: 3011,
120
+ level: 0,
121
+ message: 'Cannot set a loader service multiple times',
122
+ });
123
+ const INVALID_HOOK = Object.freeze({
124
+ code: 3012,
125
+ level: 0,
126
+ message: 'Invalid hook received',
127
+ });
128
+ const INVALID_LOADER_SERVICE_RESPONSE = Object.freeze({
129
+ code: 3013,
130
+ level: 0,
131
+ message: 'Invalid response received from hook',
132
+ });
133
+ const MODULE_LOAD_TIMEOUT = Object.freeze({
134
+ code: 3014,
135
+ level: 0,
136
+ message: 'Error loading {0} - timed out',
137
+ });
138
+ const HTTP_FAIL_LOAD = Object.freeze({
139
+ code: 3015,
140
+ level: 0,
141
+ message: 'Error loading {0}, status code {1}',
142
+ });
143
+ const STALE_HOOK_ERROR = Object.freeze({
144
+ code: 3016,
145
+ level: 0,
146
+ message: 'An error occurred handling module conflict',
147
+ });
148
+ const MODULE_ALREADY_LOADED = Object.freeze({
149
+ code: 3017,
150
+ level: 0,
151
+ message: 'Marking module(s) as externally loaded, but they are already loaded: {0}',
152
+ });
153
+ const FAIL_HOOK_LOAD = Object.freeze({
154
+ code: 3018,
155
+ level: 0,
156
+ message: 'Error loading "{0}" from hook',
157
+ });
158
+ const NO_MAPPING_URL = Object.freeze({
159
+ code: 3019,
160
+ level: 0,
161
+ message: 'Mapping endpoint not set',
162
+ });
163
+ const BAD_IMPORT_METADATA = Object.freeze({
164
+ code: 3020,
165
+ level: 0,
166
+ message: 'Invalid import metadata: {0} {1}',
167
+ });
168
+ /* importMap errors */
169
+ Object.freeze({
170
+ code: 3011,
171
+ level: 0,
172
+ message: 'import map is not valid',
173
+ });
174
+
175
+ /* eslint-disable lwr/no-unguarded-apis */
176
+ const hasDocument = typeof document !== 'undefined';
177
+ const hasSetTimeout = typeof setTimeout === 'function';
178
+ const hasConsole = typeof console !== 'undefined';
179
+ /* eslint-enable lwr/no-unguarded-apis */
180
+
181
+ function getBaseUrl() {
182
+ let baseUrl = undefined;
183
+ if (hasDocument) {
184
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
185
+ const baseEl = document.querySelector('base[href]');
186
+ baseUrl = baseEl && baseEl.href;
187
+ }
188
+ // eslint-disable-next-line lwr/no-unguarded-apis
189
+ if (!baseUrl && typeof location !== 'undefined') {
190
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
191
+ baseUrl = location.href.split('#')[0].split('?')[0];
192
+ const lastSepIndex = baseUrl.lastIndexOf('/');
193
+ if (lastSepIndex !== -1) {
194
+ baseUrl = baseUrl.slice(0, lastSepIndex + 1);
195
+ }
196
+ }
197
+ return baseUrl;
198
+ }
199
+ /**
200
+ * Check if a string is a URL based on Common Internet Scheme Syntax
201
+ * https://www.ietf.org/rfc/rfc1738.txt
202
+ *
203
+ * URL Format:
204
+ * <scheme>:<scheme-specific-part>
205
+ * Common Internet Scheme Syntax:
206
+ * The scheme specific part starts with a double slash('//')
207
+ *
208
+ * A valid URL has a colon that is followed by a double slash.
209
+ *
210
+ * @param url - the url that is being checked
211
+ * @returns boolean
212
+ *
213
+ * @example Valid URLs
214
+ * 'https://salesforce.com'
215
+ * 'http://localhost:3000'
216
+ *
217
+ * @example Invalid URLs
218
+ * 'salesforce.com'
219
+ * 'localhost:3000'
220
+ * '@salesforce/label/type:namespace:name'
221
+ */
222
+ function isUrl(url) {
223
+ return url.indexOf('://') !== -1;
224
+ }
225
+ // Borrowed and adapted from https://github.com/systemjs/systemjs/blob/master/src/common.js
226
+ // Resolves the first path segment relative to the second/parent URL
227
+ // eg: resolveIfNotPlainOrUrl('../test', 'http://www.site.com/one/two') => 'http://www.site.com/test'
228
+ // eg: resolveIfNotPlainOrUrl('./x/y/z', 'https://my.com/segment')).toBe('https://my.com/x/y/z')
229
+ function resolveIfNotPlainOrUrl(relUrl, parentUrl) {
230
+ const backslashRegEx = /\\/g;
231
+ if (relUrl.indexOf('\\') !== -1)
232
+ relUrl = relUrl.replace(backslashRegEx, '/');
233
+ // protocol-relative
234
+ if (relUrl[0] === '/' && relUrl[1] === '/') {
235
+ return parentUrl.slice(0, parentUrl.indexOf(':') + 1) + relUrl;
236
+ }
237
+ // relative-url
238
+ else if ((relUrl[0] === '.' &&
239
+ (relUrl[1] === '/' ||
240
+ (relUrl[1] === '.' && (relUrl[2] === '/' || (relUrl.length === 2 && (relUrl += '/')))) ||
241
+ (relUrl.length === 1 && (relUrl += '/')))) ||
242
+ relUrl[0] === '/') {
243
+ const parentProtocol = parentUrl.slice(0, parentUrl.indexOf(':') + 1);
244
+ let pathname;
245
+ if (parentUrl[parentProtocol.length + 1] === '/') {
246
+ // resolving to a :// so we need to read out the auth and host
247
+ if (parentProtocol !== 'file:') {
248
+ pathname = parentUrl.slice(parentProtocol.length + 2);
249
+ pathname = pathname.slice(pathname.indexOf('/') + 1);
250
+ }
251
+ else {
252
+ pathname = parentUrl.slice(8);
253
+ }
254
+ }
255
+ else {
256
+ // resolving to :/ so pathname is the /... part
257
+ pathname = parentUrl.slice(parentProtocol.length + (parentUrl[parentProtocol.length] === '/' ? 1 : 0));
258
+ }
259
+ if (relUrl[0] === '/')
260
+ return parentUrl.slice(0, parentUrl.length - pathname.length - 1) + relUrl;
261
+ // join together and split for removal of .. and . segments
262
+ // looping the string instead of anything fancy for perf reasons
263
+ // '../../../../../z' resolved to 'x/y' is just 'z'
264
+ const segmented = pathname.slice(0, pathname.lastIndexOf('/') + 1) + relUrl;
265
+ const output = [];
266
+ let segmentIndex = -1;
267
+ for (let i = 0; i < segmented.length; i++) {
268
+ // busy reading a segment - only terminate on '/'
269
+ if (segmentIndex !== -1) {
270
+ if (segmented[i] === '/') {
271
+ output.push(segmented.slice(segmentIndex, i + 1));
272
+ segmentIndex = -1;
273
+ }
274
+ }
275
+ // new segment - check if it is relative
276
+ else if (segmented[i] === '.') {
277
+ // ../ segment
278
+ if (segmented[i + 1] === '.' && (segmented[i + 2] === '/' || i + 2 === segmented.length)) {
279
+ output.pop();
280
+ i += 2;
281
+ }
282
+ // ./ segment
283
+ else if (segmented[i + 1] === '/' || i + 1 === segmented.length) {
284
+ i += 1;
285
+ }
286
+ else {
287
+ // the start of a new segment as below
288
+ segmentIndex = i;
289
+ }
290
+ }
291
+ // it is the start of a new segment
292
+ else {
293
+ segmentIndex = i;
294
+ }
295
+ }
296
+ // finish reading out the last segment
297
+ if (segmentIndex !== -1)
298
+ output.push(segmented.slice(segmentIndex));
299
+ return parentUrl.slice(0, parentUrl.length - pathname.length) + output.join('');
300
+ }
301
+ }
302
+ function resolveUrl(relUrl, parentUrl) {
303
+ const resolvedUrl = resolveIfNotPlainOrUrl(relUrl, parentUrl) ||
304
+ (isUrl(relUrl) ? relUrl : resolveIfNotPlainOrUrl('./' + relUrl, parentUrl));
305
+ return resolvedUrl;
306
+ }
307
+
308
+ function createScript(url) {
309
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
310
+ const script = document.createElement('script');
311
+ script.async = true;
312
+ script.crossOrigin = 'anonymous';
313
+ script.src = url;
314
+ return script;
315
+ }
316
+ let lastWindowError$1, lastWindowErrorUrl;
317
+ function loadModuleDef(url) {
318
+ return new Promise(function (resolve, reject) {
319
+ if (hasDocument) {
320
+ /* eslint-disable lwr/no-unguarded-apis, no-undef */
321
+ const script = createScript(url);
322
+ script.addEventListener('error', () => {
323
+ reject(new LoaderError(FAIL_LOAD, [url]));
324
+ });
325
+ script.addEventListener('load', () => {
326
+ document.head.removeChild(script);
327
+ if (lastWindowErrorUrl === url) {
328
+ reject(lastWindowError$1);
329
+ }
330
+ else {
331
+ resolve();
332
+ }
333
+ });
334
+ document.head.appendChild(script);
335
+ /* eslint-enable lwr/no-unguarded-apis, no-undef */
336
+ }
337
+ });
338
+ }
339
+ if (hasDocument) {
340
+ // When a script is executed, runtime errors are on the global/window scope which are NOT caught by the script's onerror handler.
341
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
342
+ window.addEventListener('error', (evt) => {
343
+ lastWindowErrorUrl = evt.filename;
344
+ lastWindowError$1 = evt.error;
345
+ });
346
+ }
347
+
348
+ // Bootstrap / shim
349
+
350
+ const LOADER_PREFIX = 'lwr.loader.';
351
+ const MODULE_DEFINE = `${LOADER_PREFIX}module.define`;
352
+ const MODULE_FETCH = `${LOADER_PREFIX}module.fetch`;
353
+ const MODULE_ERROR = `${LOADER_PREFIX}module.error`;
354
+
355
+ const MAPPINGS_FETCH = `${LOADER_PREFIX}mappings.fetch`;
356
+ const MAPPINGS_ERROR = `${LOADER_PREFIX}mappings.error`;
357
+
358
+ /* spec based import map resolver */
359
+ class ImportMetadataResolver {
360
+ constructor(config, invalidationCallback) {
361
+ // Default to empty mappings
362
+ this.importURICache = new Map();
363
+ this.pendingURICache = new Map();
364
+ this.loadMappingHooks = [];
365
+ this.config = config;
366
+ this.invalidationCallback = invalidationCallback;
367
+ }
368
+ addLoadMappingHook(hook) {
369
+ this.loadMappingHooks.push(hook);
370
+ }
371
+ getMappingEndpoint() {
372
+ return this.config.endpoints && this.config.endpoints.uris
373
+ ? this.config.endpoints.uris.mapping
374
+ : undefined;
375
+ }
376
+ getModifiersAsUrlParams() {
377
+ const modifiers = this.config.endpoints ? this.config.endpoints.modifiers : undefined;
378
+ if (!modifiers) {
379
+ // No modifiers return an empty string to append to the URL
380
+ return '';
381
+ }
382
+ else {
383
+ const qs = Object.keys(modifiers)
384
+ .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(modifiers[key])}`)
385
+ .join('&');
386
+ return `?${qs}`;
387
+ }
388
+ }
389
+ buildMappingUrl(specifier) {
390
+ const mappingEndpoint = this.getMappingEndpoint();
391
+ const specifiers = encodeURIComponent(specifier);
392
+ const modifiers = this.getModifiersAsUrlParams();
393
+ return `${mappingEndpoint}${specifiers}${modifiers}`;
394
+ }
395
+ getBaseUrl() {
396
+ return this.config.baseUrl;
397
+ }
398
+ registerImportMappings(newImportMetadata, rootSpecifiers) {
399
+ if (!rootSpecifiers || rootSpecifiers.length === 0) {
400
+ const imports = newImportMetadata ? JSON.stringify(newImportMetadata) : 'undefined';
401
+ throw new LoaderError(BAD_IMPORT_METADATA, [imports, rootSpecifiers ? '[]' : 'undefined']);
402
+ }
403
+ if (!newImportMetadata) {
404
+ throw new LoaderError(BAD_IMPORT_METADATA, ['undefined', JSON.stringify(rootSpecifiers)]);
405
+ }
406
+ if (!newImportMetadata.imports || Object.keys(newImportMetadata.imports).length === 0) {
407
+ throw new LoaderError(BAD_IMPORT_METADATA, [
408
+ JSON.stringify(newImportMetadata),
409
+ JSON.stringify(rootSpecifiers),
410
+ ]);
411
+ }
412
+ const index = newImportMetadata.index || {};
413
+ for (const [uri, specifiers] of Object.entries(newImportMetadata.imports)) {
414
+ specifiers.forEach((specifier) => {
415
+ const indexValue = index[specifier];
416
+ const existing = this.importURICache.get(specifier);
417
+ if (!existing) {
418
+ this.saveImportURIRecord(specifier, uri, indexValue, rootSpecifiers.includes(specifier));
419
+ }
420
+ else {
421
+ const identity = indexValue || uri;
422
+ const existingIdentity = existing.identity || existing.uri;
423
+ if (existingIdentity !== identity) {
424
+ this.invalidationCallback({
425
+ name: specifier,
426
+ oldUrl: existingIdentity,
427
+ newUrl: identity,
428
+ });
429
+ }
430
+ }
431
+ });
432
+ }
433
+ }
434
+ // Get URL from the local cache or return undefiend
435
+ getURI(specifier) {
436
+ return this.importURICache.has(specifier)
437
+ ? resolveUrl(this.importURICache.get(specifier).uri, this.getBaseUrl())
438
+ : undefined;
439
+ }
440
+ resolveLocal(specifier) {
441
+ const uri = this.getURI(specifier);
442
+ if (uri) {
443
+ return uri;
444
+ }
445
+ else if (isUrl(specifier) || specifier.startsWith('/')) {
446
+ return specifier;
447
+ }
448
+ return undefined;
449
+ }
450
+ /**
451
+ * Resolves a the URI for a specified module. It will return the value in this order:
452
+ *
453
+ * 1) Mapping from local URI cache
454
+ * 2) The URI if a specifier is already an absolute URI
455
+ * 3) Mapping fetched from a registered loader hook
456
+ * 4)
457
+ * @param specifier
458
+ * @returns module URI
459
+ */
460
+ async resolve(specifier) {
461
+ let uri = this.getURI(specifier);
462
+ if (uri) {
463
+ return uri;
464
+ }
465
+ else if (isUrl(specifier) || specifier.startsWith('/')) {
466
+ return specifier;
467
+ }
468
+ else {
469
+ const pending = this.pendingURICache.get(specifier);
470
+ if (pending) {
471
+ return pending;
472
+ }
473
+ this.config.profiler.logOperationStart({ id: MAPPINGS_FETCH, specifier });
474
+ const fetchMappingService = this.hasMappingHooks()
475
+ ? this.evaluateMappingHooks
476
+ : this.fetchNewMappings;
477
+ const promise = fetchMappingService
478
+ .bind(this)(specifier)
479
+ .then((importMetadata) => {
480
+ if (!importMetadata || !importMetadata.imports) {
481
+ throw new LoaderError(UNRESOLVED, [specifier]);
482
+ }
483
+ this.registerImportMappings(importMetadata, [specifier]);
484
+ uri = this.getURI(specifier);
485
+ if (!uri) {
486
+ throw new LoaderError(UNRESOLVED, [specifier]);
487
+ }
488
+ this.config.profiler.logOperationEnd({ id: MAPPINGS_FETCH, specifier });
489
+ return uri;
490
+ })
491
+ .finally(() => {
492
+ this.pendingURICache.delete(specifier);
493
+ });
494
+ this.pendingURICache.set(specifier, promise);
495
+ return promise;
496
+ }
497
+ }
498
+ hasMappingHooks() {
499
+ return this.loadMappingHooks.length > 0;
500
+ }
501
+ /**
502
+ * Evaluates mapping hooks. Returns first match. If all hooks return null call the mapping service.
503
+ * @param specifier Request module identifier
504
+ * @returns Import Metadata from the module root
505
+ */
506
+ async evaluateMappingHooks(specifier) {
507
+ // Check with any registered loadMappingHooks
508
+ const loadMappingHooks = this.loadMappingHooks;
509
+ if (loadMappingHooks.length) {
510
+ const knownModules = Array.from(this.importURICache.keys());
511
+ for (let i = 0; i < loadMappingHooks.length; i++) {
512
+ const loadMappingHook = loadMappingHooks[i];
513
+ // eslint-disable-next-line no-await-in-loop
514
+ const response = await loadMappingHook(specifier, { knownModules });
515
+ // undefined (but not null) is considered an un expected response so we will stop processing hooks here and throw an error
516
+ if (response || response === undefined) {
517
+ return response;
518
+ }
519
+ }
520
+ }
521
+ // If we still do not have a match call the mapping service
522
+ return this.fetchNewMappings(specifier);
523
+ }
524
+ async fetchNewMappings(specifier) {
525
+ if (typeof globalThis.fetch !== 'function') {
526
+ throw new LoaderError(UNRESOLVED, [specifier]);
527
+ }
528
+ // TODO For module invalidation with bundles it is recommended we have to send back all loaded root specified
529
+ // to ensure we detect all conflicts.
530
+ const uri = resolveUrl(this.buildMappingUrl(specifier), this.getBaseUrl());
531
+ return globalThis.fetch(uri).then((res) => {
532
+ if (!res.ok) {
533
+ this.config.profiler.logOperationStart({ id: MAPPINGS_ERROR, specifier });
534
+ throw new LoaderError(UNRESOLVED, [specifier]);
535
+ }
536
+ return res
537
+ .json()
538
+ .then((ret) => {
539
+ return ret;
540
+ })
541
+ .catch((err) => {
542
+ throw new LoaderError(UNRESOLVED, [specifier]);
543
+ });
544
+ });
545
+ }
546
+ saveImportURIRecord(specifier, uri, identity, isRoot) {
547
+ if (!identity || uri === identity) {
548
+ this.importURICache.set(specifier, {
549
+ uri,
550
+ isRoot: isRoot,
551
+ });
552
+ }
553
+ else {
554
+ this.importURICache.set(specifier, {
555
+ uri,
556
+ identity,
557
+ isRoot: isRoot,
558
+ });
559
+ }
560
+ }
561
+ }
562
+
563
+ function reportError(error) {
564
+ // TODO eventually this should be configurable instrumentation to send this somewhere
565
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
566
+ if (hasConsole)
567
+ console.error(error);
568
+ }
569
+
570
+ function evaluateHandleStaleModuleHooks(handleStaleModuleHooks, hookArgs) {
571
+ const { name, oldUrl, newUrl } = hookArgs;
572
+ // keep evaluating hooks if return value is null
573
+ for (let i = 0; i < handleStaleModuleHooks.length; i++) {
574
+ const hook = handleStaleModuleHooks[i];
575
+ try {
576
+ const hookResult = hook({ name, oldUrl, newUrl });
577
+ if (hookResult !== null) {
578
+ break;
579
+ }
580
+ }
581
+ catch (e) {
582
+ reportError(new LoaderError(STALE_HOOK_ERROR));
583
+ }
584
+ }
585
+ }
586
+
587
+ const MODULE_LOAD_TIMEOUT_TIMER = 300000;
588
+
589
+ let lastWindowError;
590
+ if (hasDocument) {
591
+ globalThis.addEventListener('error', (evt) => {
592
+ lastWindowError = evt.error;
593
+ });
594
+ }
595
+ if (!hasSetTimeout && hasConsole) {
596
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
597
+ console.warn('setTimeout API is not available, watchdog timer on load hook will not be set');
598
+ }
599
+ function isCustomResponse(response) {
600
+ return (Object.prototype.hasOwnProperty.call(response, 'data') &&
601
+ !Object.prototype.hasOwnProperty.call(response, 'blob'));
602
+ }
603
+ function isFetchResponse(response) {
604
+ // if it quacks like a duck...
605
+ return typeof response.blob === 'function';
606
+ }
607
+ function isResponseAPromise(response) {
608
+ return !!(response && response.then);
609
+ }
610
+ async function evaluateLoadHookResponse(response, id) {
611
+ return Promise.resolve().then(async () => {
612
+ if (!response.status) {
613
+ throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
614
+ }
615
+ if (response.status !== 200) {
616
+ throw new LoaderError(HTTP_FAIL_LOAD, [id, `${response.status}`]);
617
+ }
618
+ const isResponse = isFetchResponse(response);
619
+ let code;
620
+ if (isCustomResponse(response)) {
621
+ code = response.data;
622
+ }
623
+ else if (isResponse) {
624
+ // handle fetch response
625
+ code = await response.text();
626
+ }
627
+ else {
628
+ throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
629
+ }
630
+ if (!code) {
631
+ throw new LoaderError(FAIL_LOAD, [id]);
632
+ }
633
+ code = `${code}\n//# sourceURL=${id}`; // append sourceURL for debugging
634
+ try {
635
+ // TODO eval source maps for debugging
636
+ eval(code);
637
+ }
638
+ catch (e) {
639
+ throw new LoaderError(FAIL_LOAD, [id]);
640
+ }
641
+ if (lastWindowError) {
642
+ throw new LoaderError(FAIL_LOAD, [id]);
643
+ }
644
+ return true;
645
+ });
646
+ }
647
+ async function evaluateLoadHook(id, hookPromise) {
648
+ if (!hasSetTimeout) {
649
+ return hookPromise;
650
+ }
651
+ return new Promise((resolve, reject) => {
652
+ // wrap the hook in a watchdog timer
653
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
654
+ const timer = setTimeout(() => {
655
+ reject(new LoaderError(MODULE_LOAD_TIMEOUT, [id]));
656
+ }, MODULE_LOAD_TIMEOUT_TIMER);
657
+ hookPromise
658
+ .then((response) => {
659
+ resolve(response);
660
+ })
661
+ .catch(() => {
662
+ reject(new LoaderError(FAIL_HOOK_LOAD, [id]));
663
+ })
664
+ .finally(() => {
665
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
666
+ clearTimeout(timer);
667
+ });
668
+ });
669
+ }
670
+
671
+ /* global console,process */
672
+ class ModuleRegistry {
673
+ constructor(config) {
674
+ // A registry for named AMD defines containing the *metadata* of AMD module
675
+ this.namedDefineRegistry = new Map();
676
+ // The evaluted module registry where the module identifier (name or URL?) is the key
677
+ this.moduleRegistry = new Map();
678
+ this.profiler = config.profiler;
679
+ this.resolver = new ImportMetadataResolver(config, this.importMetadataInvalidationCallback.bind(this));
680
+ }
681
+ async load(id, importer) {
682
+ const resolvedId = await this.resolve(id, importer);
683
+ const moduleRecord = this.getModuleRecord(resolvedId, id);
684
+ if (moduleRecord.evaluated) {
685
+ return moduleRecord.module;
686
+ }
687
+ else {
688
+ if (!moduleRecord.evaluationPromise) {
689
+ moduleRecord.evaluationPromise = this.topLevelEvaluation(moduleRecord);
690
+ }
691
+ return moduleRecord.evaluationPromise;
692
+ }
693
+ }
694
+ async resolve(id, importer) {
695
+ const parentUrl = this.resolver.getBaseUrl(); // only support baseUrl for now
696
+ let resolved;
697
+ let aliasedId = id;
698
+ const resolveHooks = this.resolveHook;
699
+ if (resolveHooks) {
700
+ for (let i = 0; i < resolveHooks.length; i++) {
701
+ const resolveHook = resolveHooks[i];
702
+ const response = resolveHook(aliasedId, { parentUrl });
703
+ let result;
704
+ if (response || response === null) {
705
+ // eslint-disable-next-line no-await-in-loop
706
+ result = isResponseAPromise(response) ? await response : response;
707
+ }
708
+ // if result is not null, attempt resolution
709
+ if (result !== null) {
710
+ if (typeof result === 'string') {
711
+ if (resolveIfNotPlainOrUrl(result, parentUrl)) {
712
+ // string response can't be a URL
713
+ throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
714
+ }
715
+ aliasedId = result; // the next hook will receive the new id
716
+ continue;
717
+ }
718
+ resolved =
719
+ result && result.url && (resolveIfNotPlainOrUrl(result.url, parentUrl) || result.url);
720
+ if (!resolved) {
721
+ throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
722
+ }
723
+ // Don't process any more hooks if we have resolved
724
+ break;
725
+ }
726
+ }
727
+ if (aliasedId !== id) {
728
+ // resolved module id is the aliased module if it has already been defined
729
+ if (!resolved && this.namedDefineRegistry.has(aliasedId)) {
730
+ return aliasedId;
731
+ }
732
+ else {
733
+ id = aliasedId;
734
+ }
735
+ }
736
+ }
737
+ if (!resolved) {
738
+ const resolvedOrPlain = resolveIfNotPlainOrUrl(id, parentUrl) || id;
739
+ // if module registry already has named module the resolved id is the plain id
740
+ if (this.moduleRegistry.has(resolvedOrPlain)) {
741
+ return resolvedOrPlain;
742
+ }
743
+ const resolvedUrl = this.resolver.resolveLocal(resolvedOrPlain);
744
+ if (resolvedUrl) {
745
+ // return the plain id if it is already defined && the resolvedUrl is NOT already in the module registry
746
+ if (this.namedDefineRegistry.has(resolvedOrPlain) &&
747
+ this.namedDefineRegistry.get(resolvedOrPlain).defined) {
748
+ const record = this.moduleRegistry.get(resolvedUrl);
749
+ if (!record || !record.aliases.has(resolvedOrPlain)) {
750
+ return resolvedOrPlain;
751
+ }
752
+ }
753
+ return resolvedUrl;
754
+ }
755
+ if (this.namedDefineRegistry.has(resolvedOrPlain)) {
756
+ return resolvedOrPlain;
757
+ }
758
+ try {
759
+ resolved = await this.resolver.resolve(resolvedOrPlain);
760
+ }
761
+ catch (e) {
762
+ // defer to error handling below for unresolved
763
+ }
764
+ }
765
+ if (!resolved || !isUrl(resolved)) {
766
+ if (this.namedDefineRegistry.has(id)) {
767
+ return id;
768
+ }
769
+ throw new LoaderError(UNRESOLVED, [id]);
770
+ }
771
+ if (importer && isUrl(resolved)) {
772
+ resolved += `?importer=${encodeURIComponent(importer)}`;
773
+ }
774
+ return resolved;
775
+ }
776
+ has(id) {
777
+ return this.moduleRegistry.has(id);
778
+ }
779
+ define(name, dependencies, exporter) {
780
+ const mod = this.namedDefineRegistry.get(name);
781
+ // Don't allow redefining a module.
782
+ if (mod && mod.defined) {
783
+ if (process.env.NODE_ENV !== 'production' && hasConsole) {
784
+ // eslint-disable-next-line lwr/no-unguarded-apis
785
+ console.warn(`Module redefine attempted: ${name}`);
786
+ }
787
+ this.lastDefine = mod;
788
+ return;
789
+ }
790
+ const moduleDef = {
791
+ name,
792
+ dependencies,
793
+ exporter,
794
+ defined: true,
795
+ };
796
+ if (mod && mod.external) {
797
+ // if module is "external", resolve the external promise to notify any dependees
798
+ mod.external.resolveExternal(moduleDef);
799
+ }
800
+ this.profiler.logOperationStart({ id: MODULE_DEFINE, specifier: name });
801
+ this.namedDefineRegistry.set(name, moduleDef);
802
+ this.lastDefine = moduleDef;
803
+ }
804
+ /**
805
+ * Marks modules as "externally" loaded/provided, so that the loader does not attempt to fetch them.
806
+ *
807
+ * @param modules - list of module identifiers
808
+ */
809
+ registerExternalModules(modules) {
810
+ const alreadyRegistered = [];
811
+ modules.map((id) => {
812
+ if (this.namedDefineRegistry.has(id)) {
813
+ alreadyRegistered.push(id);
814
+ }
815
+ else {
816
+ let resolveExternal;
817
+ let timer;
818
+ const moduleDefPromise = new Promise((resolve, reject) => {
819
+ resolveExternal = resolve;
820
+ // watch the external for timeout
821
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
822
+ timer = setTimeout(() => {
823
+ reject(new LoaderError(MODULE_LOAD_TIMEOUT, [id]));
824
+ }, MODULE_LOAD_TIMEOUT_TIMER);
825
+ }).finally(() => {
826
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
827
+ clearTimeout(timer);
828
+ });
829
+ const moduleDef = {
830
+ name: id,
831
+ defined: false,
832
+ external: {
833
+ resolveExternal,
834
+ moduleDefPromise,
835
+ },
836
+ };
837
+ this.namedDefineRegistry.set(id, moduleDef);
838
+ }
839
+ });
840
+ // throw error for modules that were already registered
841
+ if (alreadyRegistered.length) {
842
+ throw new LoaderError(MODULE_ALREADY_LOADED, [alreadyRegistered.join(', ')]);
843
+ }
844
+ }
845
+ getImportMetadataResolver() {
846
+ return this.resolver;
847
+ }
848
+ getModuleRecord(resolvedId, id) {
849
+ let moduleRecord = this.moduleRegistry.get(resolvedId);
850
+ if (moduleRecord) {
851
+ // Make sure the original id is in the alias set
852
+ if (!moduleRecord.aliases.has(id)) {
853
+ moduleRecord.aliases.add(id);
854
+ }
855
+ return moduleRecord;
856
+ }
857
+ // loop thru registry to see if there is a known alias id
858
+ const keys = this.moduleRegistry.keys();
859
+ for (const key of keys) {
860
+ const entry = this.moduleRegistry.get(key);
861
+ if (entry === null || entry === void 0 ? void 0 : entry.aliases.has(id)) {
862
+ return entry;
863
+ }
864
+ }
865
+ const instantiation = this.getModuleDef(resolvedId, id);
866
+ const dependencyRecords = instantiation.then((moduleDef) => {
867
+ const dependencies = moduleDef.dependencies;
868
+ // get dep and filter out exports
869
+ const filtered = dependencies
870
+ .map((dep) => {
871
+ if (dep === 'exports') {
872
+ return;
873
+ }
874
+ invariant(dep !== 'require', NO_AMD_REQUIRE);
875
+ return this.getModuleDependencyRecord.call(this, dep);
876
+ })
877
+ .filter((depRecord) => depRecord !== undefined);
878
+ return Promise.all(filtered);
879
+ });
880
+ moduleRecord = {
881
+ id: resolvedId,
882
+ aliases: new Set([id]),
883
+ module: Object.create(null),
884
+ dependencyRecords,
885
+ instantiation,
886
+ evaluated: false,
887
+ evaluationPromise: null,
888
+ };
889
+ this.moduleRegistry.set(resolvedId, moduleRecord);
890
+ return moduleRecord;
891
+ }
892
+ async getModuleDependencyRecord(dependency) {
893
+ const resolvedDepId = await this.resolve(dependency);
894
+ return this.getModuleRecord(resolvedDepId, dependency);
895
+ }
896
+ // execute the "top-level code" (the code outside of functions) of a module
897
+ async topLevelEvaluation(moduleRecord) {
898
+ await this.instantiateAll(moduleRecord, {});
899
+ return this.evaluateModule(moduleRecord, {});
900
+ }
901
+ // Returns a promise when a module and all of it's dependencies have finished instantiation
902
+ async instantiateAll(moduleRecord, instantiatedMap) {
903
+ if (!instantiatedMap[moduleRecord.id]) {
904
+ instantiatedMap[moduleRecord.id] = true;
905
+ const dependencyModuleRecords = await moduleRecord.dependencyRecords;
906
+ if (dependencyModuleRecords) {
907
+ for (let i = 0; i < dependencyModuleRecords.length; i++) {
908
+ const depRecord = dependencyModuleRecords[i];
909
+ // eslint-disable-next-line no-await-in-loop
910
+ await this.instantiateAll(depRecord, instantiatedMap);
911
+ }
912
+ }
913
+ }
914
+ }
915
+ async evaluateModule(moduleRecord, evaluationMap) {
916
+ const dependencyModuleRecords = await moduleRecord.dependencyRecords;
917
+ if (dependencyModuleRecords.length > 0) {
918
+ evaluationMap[moduleRecord.id] = true;
919
+ // evaluate dependencies first
920
+ await this.evaluateModuleDependencies(dependencyModuleRecords, evaluationMap);
921
+ }
922
+ const { exporter, dependencies } = await moduleRecord.instantiation;
923
+ // The exports object automatically gets filled in by the exporter evaluation
924
+ const exports = {};
925
+ const depsMapped = await Promise.all(dependencies.map(async (dep) => {
926
+ if (dep === 'exports') {
927
+ return exports;
928
+ }
929
+ const resolvedDepId = await this.resolve(dep);
930
+ const moduleRecord = this.moduleRegistry.get(resolvedDepId);
931
+ if (!moduleRecord) {
932
+ throw new LoaderError(FAILED_DEP, [resolvedDepId]);
933
+ }
934
+ const module = moduleRecord.module;
935
+ /**
936
+ * Circular dependencies are handled properly when named exports are used,
937
+ * however, for default exports there is a bug: https://github.com/rollup/rollup/issues/3384
938
+ *
939
+ * The workaround below applies for circular dependencies (!moduleRecord.evaluated)
940
+ */
941
+ if (!moduleRecord.evaluated) {
942
+ return this.getCircularDependencyWrapper(module);
943
+ }
944
+ if (module) {
945
+ return module.__defaultInterop ? module.default : module;
946
+ }
947
+ throw new LoaderError(FAILED_DEP, [resolvedDepId]);
948
+ }));
949
+ // W-10029836 - In the case where we could be instantiating multiple graphs at the same time lets make sure the module have not already been evaluated
950
+ if (moduleRecord.evaluated) {
951
+ return moduleRecord.module;
952
+ }
953
+ // evaluates the module function
954
+ let moduleDefault = exporter(...depsMapped);
955
+ // value is returned from exporter, then we are not using named exports
956
+ if (moduleDefault !== undefined) {
957
+ moduleDefault = { default: moduleDefault };
958
+ // __defaultInterop is ONLY used to support backwards compatibility
959
+ // of importing default exports the "wrong" way (when not using named exports).
960
+ // See https://github.com/salesforce/lwr/pull/816
961
+ Object.defineProperty(moduleDefault, '__defaultInterop', { value: true });
962
+ }
963
+ // if no return value, then we are using the exports object
964
+ else {
965
+ // handle only default export with Rollup forced named exports
966
+ if (this.isNamedExportDefaultOnly(exports)) {
967
+ Object.defineProperty(exports, '__useDefault', { value: true });
968
+ }
969
+ }
970
+ const moduleExports = moduleDefault || exports;
971
+ // update the module record
972
+ // copy over enumerable public methods to module
973
+ for (const key in moduleExports) {
974
+ Object.defineProperty(moduleRecord.module, key, {
975
+ enumerable: true,
976
+ set(value) {
977
+ moduleExports[key] = value;
978
+ },
979
+ get() {
980
+ return moduleExports[key];
981
+ },
982
+ });
983
+ }
984
+ // copy non-enumerable to module
985
+ if (moduleExports.__useDefault) {
986
+ Object.defineProperty(moduleRecord.module, '__useDefault', { value: true });
987
+ }
988
+ if (moduleExports.__defaultInterop) {
989
+ Object.defineProperty(moduleRecord.module, '__defaultInterop', { value: true });
990
+ }
991
+ if (moduleExports.__esModule) {
992
+ Object.defineProperty(moduleRecord.module, '__esModule', { value: true });
993
+ }
994
+ moduleRecord.evaluated = true;
995
+ Object.freeze(moduleRecord.module);
996
+ return moduleRecord.module;
997
+ }
998
+ // Determines if named exports module has only default export
999
+ isNamedExportDefaultOnly(exports) {
1000
+ return (exports !== undefined &&
1001
+ Object.getOwnPropertyNames(exports).length === 2 &&
1002
+ Object.prototype.hasOwnProperty.call(exports, 'default') &&
1003
+ Object.prototype.hasOwnProperty.call(exports, '__esModule'));
1004
+ }
1005
+ // Wrap the dependency in a function that can be called and detected by __circular__ property.
1006
+ // The LWC engine checks for __circular__ to detect circular dependencies.
1007
+ getCircularDependencyWrapper(module) {
1008
+ const tmp = () => {
1009
+ return module.__useDefault || module.__defaultInterop ? module.default : module;
1010
+ };
1011
+ tmp.__circular__ = true;
1012
+ return tmp;
1013
+ }
1014
+ async evaluateModuleDependencies(dependencyModuleRecords, evaluationMap) {
1015
+ for (let i = 0; i < dependencyModuleRecords.length; i++) {
1016
+ const depRecord = dependencyModuleRecords[i];
1017
+ if (!depRecord.evaluated && !evaluationMap[depRecord.id]) {
1018
+ evaluationMap[depRecord.id] = true;
1019
+ // eslint-disable-next-line no-await-in-loop
1020
+ await this.evaluateModule(depRecord, evaluationMap);
1021
+ }
1022
+ }
1023
+ }
1024
+ async getModuleDef(resolvedId, originalId) {
1025
+ // reset lastDefine
1026
+ this.lastDefine = undefined;
1027
+ // the module name can be the resolved ID or the original ID if neither are URL's.
1028
+ const moduleName = !isUrl(resolvedId)
1029
+ ? resolvedId
1030
+ : originalId !== resolvedId
1031
+ ? originalId
1032
+ : undefined;
1033
+ let moduleDef = moduleName && this.namedDefineRegistry.get(moduleName);
1034
+ if (moduleDef && moduleDef.external) {
1035
+ return moduleDef.external.moduleDefPromise;
1036
+ }
1037
+ if (moduleDef && moduleDef.defined) {
1038
+ return moduleDef;
1039
+ }
1040
+ const parentUrl = this.resolver.getBaseUrl(); // only support baseUrl for now
1041
+ const specifier = moduleName || originalId;
1042
+ this.profiler.logOperationStart({ id: MODULE_FETCH, specifier });
1043
+ return Promise.resolve()
1044
+ .then(async () => {
1045
+ const loadHooks = this.loadHook;
1046
+ if (loadHooks) {
1047
+ for (let i = 0; i < loadHooks.length; i++) {
1048
+ const loadHook = loadHooks[i];
1049
+ const response = loadHook(resolvedId, parentUrl);
1050
+ const result = (isResponseAPromise(response)
1051
+ ? // eslint-disable-next-line no-await-in-loop
1052
+ await evaluateLoadHook(resolvedId, response)
1053
+ : response);
1054
+ if (result === undefined) {
1055
+ throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
1056
+ }
1057
+ if (result && result !== null) {
1058
+ return evaluateLoadHookResponse(result, resolvedId);
1059
+ }
1060
+ }
1061
+ }
1062
+ return false;
1063
+ })
1064
+ .then((result) => {
1065
+ if (result !== true && hasDocument) {
1066
+ return loadModuleDef(resolvedId);
1067
+ }
1068
+ })
1069
+ .then(() => {
1070
+ // Attempt to retrieve the module definition by name first
1071
+ moduleDef = moduleName && this.namedDefineRegistry.get(moduleName);
1072
+ // Fallback to the last loader.define call
1073
+ if (!moduleDef) {
1074
+ moduleDef = this.lastDefine;
1075
+ }
1076
+ // This should not happen
1077
+ if (!moduleDef) {
1078
+ throw new LoaderError(FAIL_INSTANTIATE, [resolvedId]);
1079
+ }
1080
+ this.profiler.logOperationEnd({ id: MODULE_FETCH, specifier });
1081
+ return moduleDef;
1082
+ })
1083
+ .catch((e) => {
1084
+ this.profiler.logOperationStart({ id: MODULE_ERROR, specifier });
1085
+ throw e;
1086
+ });
1087
+ }
1088
+ addLoaderPlugin(hooks) {
1089
+ if (typeof hooks !== 'object') {
1090
+ throw new LoaderError(INVALID_HOOK);
1091
+ }
1092
+ const { loadModule: loadHook, resolveModule: resolveHook, loadMapping } = hooks;
1093
+ if (resolveHook) {
1094
+ if (this.resolveHook) {
1095
+ this.resolveHook.push(resolveHook);
1096
+ }
1097
+ else {
1098
+ this.resolveHook = [resolveHook];
1099
+ }
1100
+ }
1101
+ if (loadHook) {
1102
+ if (this.loadHook) {
1103
+ this.loadHook.push(loadHook);
1104
+ }
1105
+ else {
1106
+ this.loadHook = [loadHook];
1107
+ }
1108
+ }
1109
+ if (loadMapping) {
1110
+ this.resolver.addLoadMappingHook(loadMapping);
1111
+ }
1112
+ }
1113
+ importMetadataInvalidationCallback({ name, oldUrl, newUrl }) {
1114
+ const handleStaleModuleHooks = this.handleStaleModuleHook;
1115
+ if (handleStaleModuleHooks) {
1116
+ evaluateHandleStaleModuleHooks(handleStaleModuleHooks, {
1117
+ name,
1118
+ oldUrl,
1119
+ newUrl,
1120
+ });
1121
+ }
1122
+ else {
1123
+ if (hasConsole) {
1124
+ // eslint-disable-next-line lwr/no-unguarded-apis, no-undef
1125
+ console.warn(`stale module detected ${name}, current URL:${oldUrl}, new URL:${newUrl}`);
1126
+ }
1127
+ }
1128
+ }
1129
+ registerHandleStaleModuleHook(handleStaleModule) {
1130
+ if (this.handleStaleModuleHook) {
1131
+ this.handleStaleModuleHook.push(handleStaleModule);
1132
+ }
1133
+ else {
1134
+ this.handleStaleModuleHook = [handleStaleModule];
1135
+ }
1136
+ }
1137
+ }
1138
+
1139
+ /**
1140
+ * The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
1141
+ */
1142
+ class Loader {
1143
+ constructor(config) {
1144
+ let baseUrl = config.baseUrl;
1145
+ const mappingEndpoint = config.endpoints ? config.endpoints.uris.mapping : undefined;
1146
+ let profiler = config.profiler;
1147
+ if (!mappingEndpoint) {
1148
+ throw new LoaderError(NO_MAPPING_URL);
1149
+ }
1150
+ // add a trailing slash, if it does not exist
1151
+ config.endpoints.uris.mapping = mappingEndpoint.replace(/\/?$/, '/');
1152
+ if (baseUrl) {
1153
+ // add a trailing slash, if it does not exist
1154
+ baseUrl = baseUrl.replace(/\/?$/, '/');
1155
+ }
1156
+ if (!baseUrl) {
1157
+ baseUrl = getBaseUrl();
1158
+ }
1159
+ if (!baseUrl) {
1160
+ throw new LoaderError(NO_BASE_URL);
1161
+ }
1162
+ if (!profiler) {
1163
+ // default noop profiler
1164
+ profiler = {
1165
+ logOperationStart: () => {
1166
+ /* noop */
1167
+ },
1168
+ logOperationEnd: () => {
1169
+ /* noop */
1170
+ },
1171
+ };
1172
+ }
1173
+ this.registry = new ModuleRegistry(Object.freeze({ endpoints: config.endpoints, baseUrl, profiler }));
1174
+ // TODO: W-10539691 - temp workaround for LWR-Java -- remove once appId is implemented there
1175
+ if (config.appMetadata && !config.appMetadata.appId) {
1176
+ // Parse the appId from the bootstrapModule
1177
+ // LWR-Java bootstrap module format: @lwr-bootstrap/my/app/v/0_0_1 -- my/app is the appId
1178
+ const match = config.appMetadata.bootstrapModule.match(/@lwr-bootstrap\/(.+)\/v\/.+/);
1179
+ const appId = match && match[1];
1180
+ config.appMetadata.appId = appId;
1181
+ }
1182
+ // TODO: https://github.com/salesforce/lwr/issues/1087
1183
+ this.services = Object.freeze({
1184
+ addLoaderPlugin: this.registry.addLoaderPlugin.bind(this.registry),
1185
+ handleStaleModule: this.registry.registerHandleStaleModuleHook.bind(this.registry),
1186
+ appMetadata: config.appMetadata,
1187
+ });
1188
+ }
1189
+ /**
1190
+ * Defines/registers a single named AMD module definition.
1191
+ *
1192
+ * @param {string} name The module name
1193
+ * @param {string[]} dependencies A list of module dependencies (module imports)
1194
+ * @param {Function} execute The function containing the module code. AKA exporter as it also returns the modules exports when executed
1195
+ * @return {void}
1196
+ */
1197
+ define(name, dependencies, execute) {
1198
+ invariant(typeof name === 'string', MISSING_NAME);
1199
+ let ctor = execute;
1200
+ let deps = dependencies;
1201
+ // Convert no dependencies form `define('name', function(){}, {});` to: `define('name', [], function(){}, {})`
1202
+ if (typeof deps === 'function') {
1203
+ ctor = dependencies;
1204
+ deps = [];
1205
+ }
1206
+ invariant(Array.isArray(deps), INVALID_DEPS);
1207
+ this.registry.define(name, deps, ctor);
1208
+ }
1209
+ /**
1210
+ * Retrieves/loads a module, returning it from the registry if it exists and fetching it if it doesn't.
1211
+ *
1212
+ * @param {string} id - A module identifier or URL
1213
+ * @param {string} importer - The versioned specifier of the module importer
1214
+ * Used when the ID is not versioned (eg: variable dynamic imports)
1215
+ * @return {Promise<Module>}
1216
+ */
1217
+ async load(id, importer) {
1218
+ return this.registry.load(id, importer);
1219
+ }
1220
+ /**
1221
+ * Checks if a Module exists in the registry. Note, returns false even if the ModuleDefinition exists but the Module has not been instantiated yet (executed).
1222
+ *
1223
+ * @param {string} id - A module identifier or URL
1224
+ * @return {boolean}
1225
+ */
1226
+ has(id) {
1227
+ return this.registry.has(id);
1228
+ }
1229
+ /**
1230
+ * Resolves the module identifier or URL. Returns the module identifier if the moduleDefinition exists, or the full resolved URL if a URL is given.
1231
+ *
1232
+ * @param {string} id - A module identifier or URL
1233
+ * @param {string} importer - The versioned specifier of the module importer
1234
+ * Used when the ID is not versioned (eg: variable dynamic imports)
1235
+ * @return {string}
1236
+ */
1237
+ async resolve(id, importer) {
1238
+ return this.registry.resolve(id, importer);
1239
+ }
1240
+ async registerImportMappings(mappings, rootSpecifiers) {
1241
+ this.registry.getImportMetadataResolver().registerImportMappings(mappings, rootSpecifiers);
1242
+ }
1243
+ /**
1244
+ * Marks modules as "externally" loaded/provided (e.g. preloaded), so that the loader does not attempt to load them.
1245
+ *
1246
+ * @param modules - list of module identifiers
1247
+ */
1248
+ registerExternalModules(modules) {
1249
+ this.registry.registerExternalModules(modules);
1250
+ }
1251
+ }
1252
+
1253
+ /** LWR Everywhere module for AMD **/
1254
+ // Process the LWR Client Bootstrap Config
1255
+ const configOptions = { "format": "amd", "apiVersion": "v57.0", "apiPrefix": "/lwr", "bundle": false, "debug": true };
1256
+ const configuration = getConfiguration(configOptions);
1257
+ const { bootstrapModule, endpoints, baseUrl, imports, index } = configuration.config;
1258
+ globalThis.process = { env: { NODE_ENV: configuration.options.NODE_ENV } }; // Required by LWC
1259
+ // Initialize the AMD loader (its code is bundled into the LWR Everywhere module)
1260
+ const loader = new Loader({ endpoints, baseUrl });
1261
+ loader.define('lwr/loader/v/0_0_1', ['exports'], (exports) => {
1262
+ Object.assign(exports, {
1263
+ define: loader.define.bind(loader),
1264
+ load: loader.load.bind(loader),
1265
+ });
1266
+ });
1267
+ await loader.registerImportMappings({ imports, index }, [bootstrapModule]);
1268
+ globalThis.LWR = Object.freeze({ define: loader.define.bind(loader) });
1269
+ // Define preload modules (ie: ABS, vault, etc) BEFORE loading them
1270
+ LWR.define("lwr/everywhereAmd/v/0_0_1", ["exports", "lwr/vault/v/0_0_1"], function (exports, _0_8_0Alpha_10) {
1271
+ async function authFetch(url, accept = "javascript") {
1272
+ const authInfo = _0_8_0Alpha_10.getAuthInfo();
1273
+ if (authInfo) {
1274
+ return fetch(url, {
1275
+ headers: {
1276
+ Accept: `application/${accept}`,
1277
+ Authorization: `Bearer ${authInfo.access_token}`
1278
+ }
1279
+ });
1280
+ }
1281
+ return null;
1282
+ }
1283
+ function everywhereHooks(serviceAPI, mappingEndpoint, coreVersion) {
1284
+ serviceAPI.addLoaderPlugin({
1285
+ loadModule: async (url) => {
1286
+ return authFetch(url);
1287
+ },
1288
+ loadMapping: async (specifier) => {
1289
+ const res = await authFetch(`${mappingEndpoint}${encodeURIComponent(specifier)}`, "json");
1290
+ if (res && res.ok) {
1291
+ return res.json();
1292
+ }
1293
+ return null;
1294
+ },
1295
+ resolveModule: (specifier) => {
1296
+ if (specifier && !/\/v\/[^/]+?$/.test(specifier)) {
1297
+ return `${specifier}/v/${coreVersion}`;
1298
+ }
1299
+ return null;
1300
+ }
1301
+ });
1302
+ }
1303
+ function initAmd(services, mappingEndpoint, coreVersion) {
1304
+ if (mappingEndpoint.match(/\/v5\d\.0\//)) {
1305
+ everywhereHooks(services, mappingEndpoint, coreVersion);
1306
+ }
1307
+ }
1308
+ function authenticate(authInfo) {
1309
+ if (authInfo) {
1310
+ _0_8_0Alpha_10.setAuthInfo(authInfo);
1311
+ }
1312
+ }
1313
+ exports.authenticate = authenticate;
1314
+ exports.initAmd = initAmd;
1315
+ Object.defineProperty(exports, "__esModule", { value: true });
1316
+ });
1317
+ LWR.define("lwr/vault/v/0_0_1", ["exports"], function (exports) {
1318
+ let authInfo;
1319
+ function getAuthInfo() {
1320
+ return authInfo;
1321
+ }
1322
+ function setAuthInfo(ai) {
1323
+ authInfo = ai;
1324
+ }
1325
+ exports.getAuthInfo = getAuthInfo;
1326
+ exports.setAuthInfo = setAuthInfo;
1327
+ Object.defineProperty(exports, "__esModule", { value: true });
1328
+ });
1329
+ // Set up the AMD loader hooks for OAuth
1330
+ const { initAmd, authenticate } = await loader.load(bootstrapModule);
1331
+ initAmd(loader.services, endpoints?.uris.mapping, '0_0_1');
1332
+ // Wrap the createComponent API to allow proper ordering of module definitions:
1333
+ // 1. LWRE module bundle (this): lwr/loader, lwr/everywhereAmd, lwr/vault
1334
+ // 2. lwr/everywhere (deps: lwc, synthetic shadow, lwr/host, etc)
1335
+ // 3. Salesforce hosted UI content (i.e. embedded components and deps)
1336
+ async function createComponent(specifier, nodeId, properties = {}, config) {
1337
+ // Do not load the "lwr/everywhere" API module ahead of time
1338
+ // This import MUST be done AFTER the loader hooks are in place via authenticate() so
1339
+ // modules [statically] imported from core have the appropriate Authorization headers
1340
+ const { createComponent: cc } = await loader.load(bootstrapModule.replace('Amd', ''));
1341
+ return cc(specifier, nodeId, properties, config);
1342
+ }
1343
+
1344
+ export { authenticate, createComponent };