@lwrjs/loader 0.22.9 → 0.22.11
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/README.md +33 -1
- package/build/assets/prod/lwr-error-shim.js +1 -1
- package/build/assets/prod/lwr-loader-shim-legacy.bundle.js +530 -25
- package/build/assets/prod/lwr-loader-shim-legacy.bundle.min.js +3 -3
- package/build/assets/prod/lwr-loader-shim-legacy.js +317 -14
- package/build/assets/prod/lwr-loader-shim.bundle.js +123 -12
- package/build/assets/prod/lwr-loader-shim.bundle.min.js +3 -3
- package/build/assets/prod/lwr-loader-shim.js +105 -8
- package/build/cjs/modules/lwr/loader/validateLoadSpecifier.cjs +4 -1
- package/build/cjs/modules/lwr/loaderLegacy/importMap/importMap.cjs +1 -1
- package/build/cjs/modules/lwr/loaderLegacy/importMap/importMapResolver.cjs +12 -0
- package/build/cjs/modules/lwr/loaderLegacy/importMap/utils.cjs +13 -1
- package/build/cjs/modules/lwr/loaderLegacy/utils/validation.cjs +93 -0
- package/build/modules/lwr/esmLoader/esmLoader.js +1 -1
- package/build/modules/lwr/loader/loader.js +18 -4
- package/build/modules/lwr/loader/validateLoadSpecifier.d.ts +2 -1
- package/build/modules/lwr/loader/validateLoadSpecifier.js +11 -2
- package/build/modules/lwr/loaderLegacy/importMap/importMap.js +3 -2
- package/build/modules/lwr/loaderLegacy/importMap/importMapResolver.d.ts +1 -0
- package/build/modules/lwr/loaderLegacy/importMap/importMapResolver.js +13 -0
- package/build/modules/lwr/loaderLegacy/importMap/utils.d.ts +12 -1
- package/build/modules/lwr/loaderLegacy/importMap/utils.js +24 -2
- package/build/modules/lwr/loaderLegacy/loaderLegacy.d.ts +11 -1
- package/build/modules/lwr/loaderLegacy/loaderLegacy.js +213 -11
- package/build/modules/lwr/loaderLegacy/utils/validation.d.ts +50 -0
- package/build/modules/lwr/loaderLegacy/utils/validation.js +114 -0
- package/build/shim/defineCacheResolver.d.ts +10 -0
- package/build/shim/defineCacheResolver.js +78 -0
- package/build/shim/loader.d.ts +5 -1
- package/build/shim/loader.js +14 -3
- package/build/shim/shim.js +3 -2
- package/build/shim-legacy/loaderLegacy.d.ts +7 -2
- package/build/shim-legacy/loaderLegacy.js +15 -4
- package/build/shim-legacy/shimLegacy.d.ts +14 -0
- package/build/shim-legacy/shimLegacy.js +53 -4
- package/build/types.d.ts +22 -0
- package/package.json +6 -6
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
6
6
|
*/
|
|
7
|
-
/* LWR Legacy Module Loader Shim v0.22.
|
|
7
|
+
/* LWR Legacy Module Loader Shim v0.22.11 */
|
|
8
8
|
(function () {
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
@@ -105,26 +105,121 @@
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Parse AMD define args. Supports define(id, deps, factory).
|
|
110
|
+
*/
|
|
111
|
+
function parseDefine(def) {
|
|
112
|
+
const [, depsOrFactory, factory] = def;
|
|
113
|
+
if (Array.isArray(depsOrFactory) && typeof factory === 'function') {
|
|
114
|
+
return { deps: depsOrFactory, factory };
|
|
115
|
+
}
|
|
116
|
+
if (typeof depsOrFactory === 'function') {
|
|
117
|
+
return { deps: [], factory: depsOrFactory };
|
|
118
|
+
}
|
|
119
|
+
throw new Error('Invalid module definition');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Resolve a loader dependency: 'exports' returns the bag; otherwise look up in cache and instantiate.
|
|
124
|
+
* Loader deps are assumed to be leaf modules (no deps of their own)—no recursive resolution.
|
|
125
|
+
*/
|
|
126
|
+
function resolveDep(dep, exportsObj, cache) {
|
|
127
|
+
if (dep === 'exports') return exportsObj;
|
|
128
|
+
const mod = cache[dep];
|
|
129
|
+
if (!mod) {
|
|
130
|
+
throw new Error(`Dependency "${dep}" not found in defineCache for loader`);
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
return instantiateLeafModule(mod);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
136
|
+
throw new Error(`Loader dependency "${dep}" has invalid definition: ${msg}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Instantiate a leaf module (loader dep). Assumes the module has no dependencies of its own.
|
|
142
|
+
* Supports only deps: [] or ['exports']; any other dep list throws.
|
|
143
|
+
*/
|
|
144
|
+
function instantiateLeafModule(def) {
|
|
145
|
+
const { deps, factory } = parseDefine(def);
|
|
146
|
+
if (deps.length > 1 || (deps.length === 1 && deps[0] !== 'exports')) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Loader dependencies must have no deps or only ['exports']; got [${deps.join(', ')}]`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const exports = {};
|
|
152
|
+
const out = factory(exports);
|
|
153
|
+
return out !== undefined ? out : exports;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Normalize loader definition to canonical (exports, ...deps). Injects 'exports' if missing.
|
|
158
|
+
*/
|
|
159
|
+
function normalizeLoaderDefinition(def) {
|
|
160
|
+
const { deps, factory } = parseDefine(def);
|
|
161
|
+
if (deps.includes('exports')) {
|
|
162
|
+
return { deps, factory };
|
|
163
|
+
}
|
|
164
|
+
const isEmptyDeps = deps.length === 0;
|
|
165
|
+
const normalizedFactory = (exports, ...rest) => {
|
|
166
|
+
const result = isEmptyDeps ? factory(exports) : factory(...rest);
|
|
167
|
+
if (result !== undefined && typeof result === 'object') {
|
|
168
|
+
Object.assign(exports, result);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
return { deps: ['exports', ...deps], factory: normalizedFactory };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Resolve the loader's dependencies from defineCache. Throws if definition is invalid or deps cannot be resolved.
|
|
176
|
+
*/
|
|
177
|
+
function resolveLoaderDepsFromDefineCache(
|
|
178
|
+
loaderSpecifier,
|
|
179
|
+
definition,
|
|
180
|
+
defineCache,
|
|
181
|
+
) {
|
|
182
|
+
try {
|
|
183
|
+
const { deps, factory } = normalizeLoaderDefinition(definition);
|
|
184
|
+
const exportsObj = {};
|
|
185
|
+
const args = deps.map((dep) => resolveDep(dep, exportsObj, defineCache)) ;
|
|
186
|
+
return { factory: factory , args, exportsObj };
|
|
187
|
+
} catch (e) {
|
|
188
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
189
|
+
throw new Error(`Expected loader with specifier "${loaderSpecifier}" to be a module. ${msg}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Create a loader from a definition. Original API preserved.
|
|
195
|
+
* Optional defineCache for resolving loader deps.
|
|
196
|
+
* definition[3] (signatures: ownHash, hashes) is passed to loader.define for cache invalidation.
|
|
197
|
+
*/
|
|
108
198
|
function createLoader(
|
|
109
199
|
name,
|
|
110
200
|
definition,
|
|
111
201
|
config,
|
|
112
202
|
externalModules,
|
|
203
|
+
defineCache,
|
|
113
204
|
) {
|
|
114
205
|
if (!definition || typeof definition[2] !== 'function') {
|
|
115
206
|
throw new Error(`Expected loader with specifier "${name}" to be a module`);
|
|
116
207
|
}
|
|
117
208
|
|
|
118
|
-
// Create a Loader instance
|
|
119
209
|
const exports = {};
|
|
120
|
-
|
|
210
|
+
if (defineCache) {
|
|
211
|
+
const { factory, args, exportsObj } = resolveLoaderDepsFromDefineCache(name, definition, defineCache);
|
|
212
|
+
factory(...args);
|
|
213
|
+
Object.assign(exports, exportsObj);
|
|
214
|
+
} else {
|
|
215
|
+
definition[2].call(null, exports);
|
|
216
|
+
}
|
|
121
217
|
const { Loader } = exports;
|
|
122
218
|
if (!Loader) {
|
|
123
219
|
throw new Error('Expected Loader class to be defined');
|
|
124
220
|
}
|
|
125
221
|
const loader = new Loader(config);
|
|
126
222
|
|
|
127
|
-
// register externally loaded modules
|
|
128
223
|
if (externalModules && externalModules.length) {
|
|
129
224
|
loader.registerExternalModules(externalModules);
|
|
130
225
|
}
|
|
@@ -181,9 +276,164 @@
|
|
|
181
276
|
}
|
|
182
277
|
}
|
|
183
278
|
|
|
184
|
-
/*
|
|
279
|
+
/* eslint-disable lwr/no-unguarded-apis */
|
|
280
|
+
|
|
281
|
+
const hasConsole$1 = typeof console !== 'undefined';
|
|
282
|
+
|
|
283
|
+
const hasProcess$1 = typeof process !== 'undefined';
|
|
284
|
+
|
|
285
|
+
// eslint-disable-next-line no-undef
|
|
286
|
+
hasProcess$1 && process.env;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* List of protected module patterns that cannot be remapped.
|
|
290
|
+
* Uses case-insensitive matching to prevent bypass attempts (e.g., HF-1027).
|
|
291
|
+
*/
|
|
292
|
+
const PROTECTED_PATTERNS = [
|
|
293
|
+
/^lwc$/i, // Core LWC
|
|
294
|
+
/^lwc\/v\//i, // Versioned LWC variants
|
|
295
|
+
/^@lwc\//i, // LWC scoped packages
|
|
296
|
+
/^lightningmobileruntime\//i, // Lightning mobile runtime
|
|
297
|
+
];
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Checks if a specifier matches any protected module pattern.
|
|
301
|
+
*
|
|
302
|
+
* @param specifier - The module specifier to check
|
|
303
|
+
* @returns true if the specifier is protected, false otherwise
|
|
304
|
+
*/
|
|
305
|
+
function isProtectedSpecifier(specifier) {
|
|
306
|
+
return PROTECTED_PATTERNS.some((pattern) => pattern.test(specifier));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Validates that a URI does not use dangerous URL schemes.
|
|
311
|
+
*
|
|
312
|
+
* Blocks:
|
|
313
|
+
* - blob: URLs (HF-1027 bypass prevention) - case-insensitive
|
|
314
|
+
* - data: URLs (inline code injection) - case-insensitive
|
|
315
|
+
*
|
|
316
|
+
* @param uri - The URI to validate
|
|
317
|
+
* @param specifier - The specifier being mapped (for error messages)
|
|
318
|
+
* @throws Error if the URI uses a dangerous scheme
|
|
319
|
+
*/
|
|
320
|
+
function validateMappingUri(uri, specifier) {
|
|
321
|
+
const lowerUri = uri.toLowerCase();
|
|
322
|
+
|
|
323
|
+
if (lowerUri.startsWith('blob:')) {
|
|
324
|
+
throw new Error(`Cannot map ${specifier} to blob: URL`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (lowerUri.startsWith('data:')) {
|
|
328
|
+
throw new Error(`Cannot map ${specifier} to data: URL`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Validates that a specifier is well-formed and not protected.
|
|
334
|
+
*
|
|
335
|
+
* @param specifier - The module specifier to validate
|
|
336
|
+
* @throws Error if the specifier is invalid or protected
|
|
337
|
+
*/
|
|
338
|
+
function validateSpecifier(specifier) {
|
|
339
|
+
if (typeof specifier !== 'string' || specifier.length === 0) {
|
|
340
|
+
throw new Error('Specifier must be a non-empty string');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (isProtectedSpecifier(specifier)) {
|
|
344
|
+
throw new Error(`Cannot remap protected module: ${specifier}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Validates and converts an ImportMapUpdate to ImportMap format.
|
|
350
|
+
*
|
|
351
|
+
* Performs validation checks and then converts from:
|
|
352
|
+
* { moduleScriptURL: [moduleName1, moduleName2] }
|
|
353
|
+
* To:
|
|
354
|
+
* { imports: { moduleName1: moduleScriptURL, moduleName2: moduleScriptURL } }
|
|
355
|
+
*
|
|
356
|
+
* @param update - The ImportMapUpdate object to validate and convert
|
|
357
|
+
* @returns The converted ImportMap, or null if the update is empty
|
|
358
|
+
* @throws Error if any validation fails
|
|
359
|
+
*/
|
|
360
|
+
function validateAndConvertImportMapUpdate(update) {
|
|
361
|
+
if (!update || typeof update !== 'object') {
|
|
362
|
+
throw new Error('LWR.importMap() requires an object argument');
|
|
363
|
+
}
|
|
185
364
|
|
|
365
|
+
// Check if update is empty
|
|
366
|
+
const entries = Object.entries(update);
|
|
367
|
+
if (entries.length === 0) {
|
|
368
|
+
if (hasConsole$1) {
|
|
369
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
370
|
+
console.warn('LWR.importMap() called with empty update object');
|
|
371
|
+
}
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
186
374
|
|
|
375
|
+
const convertedImports = {};
|
|
376
|
+
|
|
377
|
+
for (const [moduleScriptURL, moduleNames] of entries) {
|
|
378
|
+
if (!Array.isArray(moduleNames)) {
|
|
379
|
+
throw new Error('moduleNames must be an array');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!moduleScriptURL || typeof moduleScriptURL !== 'string') {
|
|
383
|
+
throw new Error('moduleScriptURL must be a string');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
validateMappingUri(moduleScriptURL, moduleScriptURL);
|
|
387
|
+
|
|
388
|
+
for (const moduleName of moduleNames) {
|
|
389
|
+
validateSpecifier(moduleName);
|
|
390
|
+
if (moduleName in convertedImports) {
|
|
391
|
+
if (hasConsole$1) {
|
|
392
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
393
|
+
console.warn(
|
|
394
|
+
`LWR.importMap(): duplicate module "${moduleName}" — already mapped to "${convertedImports[moduleName]}", ignoring mapping to "${moduleScriptURL}"`,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
convertedImports[moduleName] = moduleScriptURL;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
imports: convertedImports,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function targetWarning(match, target, msg) {
|
|
409
|
+
if (hasConsole$1) {
|
|
410
|
+
// eslint-disable-next-line lwr/no-unguarded-apis, no-undef
|
|
411
|
+
console.warn('Package target ' + msg + ", resolving target '" + target + "' for " + match);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Import map entries are write-once: once a specifier is mapped, it cannot be overridden.
|
|
417
|
+
* This ensures deterministic module resolution and prevents runtime mutation of previously
|
|
418
|
+
* resolved dependencies. Conflicting mappings are ignored with a warning.
|
|
419
|
+
*
|
|
420
|
+
* Merges a single import map entry into a target imports object.
|
|
421
|
+
* - If the specifier doesn't exist, it is added.
|
|
422
|
+
* - If it exists with the same value, it is silently skipped.
|
|
423
|
+
* - If it exists with a different value, a warning is logged and the entry is skipped.
|
|
424
|
+
*/
|
|
425
|
+
function mergeImportMapEntry(specifier, uri, target) {
|
|
426
|
+
const existing = target[specifier];
|
|
427
|
+
if (existing !== undefined) {
|
|
428
|
+
if (existing !== uri) {
|
|
429
|
+
targetWarning(specifier, uri, `already mapped to "${existing}", ignoring conflicting mapping`);
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
target[specifier] = uri;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/* global document, process, console */
|
|
187
437
|
|
|
188
438
|
/* eslint-disable lwr/no-unguarded-apis */
|
|
189
439
|
const hasSetTimeout = typeof setTimeout === 'function';
|
|
@@ -198,10 +448,11 @@
|
|
|
198
448
|
|
|
199
449
|
__init() {this.defineCache = {};}
|
|
200
450
|
__init2() {this.orderedDefs = [];}
|
|
451
|
+
__init3() {this.importMapUpdatesCache = {};}
|
|
201
452
|
|
|
202
453
|
// eslint-disable-line no-undef, lwr/no-unguarded-apis
|
|
203
454
|
|
|
204
|
-
constructor(global) {LoaderShim.prototype.__init.call(this);LoaderShim.prototype.__init2.call(this);
|
|
455
|
+
constructor(global) {LoaderShim.prototype.__init.call(this);LoaderShim.prototype.__init2.call(this);LoaderShim.prototype.__init3.call(this);
|
|
205
456
|
// Start watchdog timer
|
|
206
457
|
if (hasSetTimeout) {
|
|
207
458
|
this.watchdogTimerId = this.startWatchdogTimer();
|
|
@@ -210,14 +461,16 @@
|
|
|
210
461
|
// Parse configuration
|
|
211
462
|
this.global = global;
|
|
212
463
|
this.config = global.LWR ;
|
|
213
|
-
this.loaderModule = 'lwr/loaderLegacy/v/
|
|
464
|
+
this.loaderModule = 'lwr/loaderLegacy/v/0_22_11';
|
|
214
465
|
|
|
215
466
|
// Set up error handler
|
|
216
467
|
this.errorHandler = this.config.onError ;
|
|
217
468
|
|
|
218
|
-
// Set up the temporary LWR.define
|
|
469
|
+
// Set up the temporary LWR.define and LWR.importMap functions
|
|
219
470
|
const tempDefine = this.tempDefine.bind(this);
|
|
220
471
|
global.LWR.define = tempDefine;
|
|
472
|
+
const tempImportMapMethod = this.tempImportMap.bind(this);
|
|
473
|
+
global.LWR.importMap = tempImportMapMethod;
|
|
221
474
|
this.bootReady = this.config.autoBoot;
|
|
222
475
|
|
|
223
476
|
try {
|
|
@@ -273,6 +526,53 @@
|
|
|
273
526
|
}
|
|
274
527
|
}
|
|
275
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Create a temporary LWR.importMap() function which captures all
|
|
531
|
+
* import map updates that occur BEFORE the full loader module is available
|
|
532
|
+
*
|
|
533
|
+
* Each import map update is validated, converted to moduleName -> URL mapping,
|
|
534
|
+
* and merged into the importMapUpdatesCache with write-once protection
|
|
535
|
+
*/
|
|
536
|
+
tempImportMap(importMapUpdate) {
|
|
537
|
+
try {
|
|
538
|
+
// Validate and convert the import map update to { imports: { moduleName: moduleScriptURL } }
|
|
539
|
+
const importMap = validateAndConvertImportMapUpdate(importMapUpdate);
|
|
540
|
+
|
|
541
|
+
// Early return if update was empty
|
|
542
|
+
if (!importMap) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Merge into cache with write-once protection
|
|
547
|
+
for (const [moduleName, moduleScriptURL] of Object.entries(importMap.imports)) {
|
|
548
|
+
mergeImportMapEntry(moduleName, moduleScriptURL, this.importMapUpdatesCache);
|
|
549
|
+
}
|
|
550
|
+
} catch (e) {
|
|
551
|
+
this.enterErrorState(e);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Apply all cached import map updates and merge with bootstrap import map
|
|
557
|
+
* Returns merged import map
|
|
558
|
+
*/
|
|
559
|
+
getImportMappingsWithUpdates() {
|
|
560
|
+
// Start with bootstrap import map
|
|
561
|
+
// Cast importMappings from object to ImportMap to access properties
|
|
562
|
+
const bootstrapMappings = this.config.importMappings ;
|
|
563
|
+
|
|
564
|
+
// Merge with write-once protection: bootstrap mappings take precedence
|
|
565
|
+
const mergedImports = { ...(_optionalChain([bootstrapMappings, 'optionalAccess', _ => _.imports]) || {}) };
|
|
566
|
+
for (const [specifier, uri] of Object.entries(this.importMapUpdatesCache)) {
|
|
567
|
+
mergeImportMapEntry(specifier, uri, mergedImports);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return {
|
|
571
|
+
...(bootstrapMappings || {}),
|
|
572
|
+
imports: mergedImports,
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
276
576
|
// Called by the customInit hook via lwr.initializeApp()
|
|
277
577
|
postCustomInit() {
|
|
278
578
|
this.bootReady = true;
|
|
@@ -305,6 +605,7 @@
|
|
|
305
605
|
this.defineCache[this.loaderModule],
|
|
306
606
|
loaderConfig,
|
|
307
607
|
this.config.preloadModules,
|
|
608
|
+
this.defineCache,
|
|
308
609
|
);
|
|
309
610
|
this.mountApp(loader);
|
|
310
611
|
if (
|
|
@@ -360,17 +661,19 @@
|
|
|
360
661
|
const exporter = (exports) => {
|
|
361
662
|
Object.assign(exports, { logOperationStart, logOperationEnd });
|
|
362
663
|
};
|
|
363
|
-
define('lwr/profiler/v/
|
|
664
|
+
define('lwr/profiler/v/0_22_11', ['exports'], exporter, {});
|
|
364
665
|
}
|
|
365
666
|
|
|
366
667
|
// Set up the application globals, import map, root custom element...
|
|
367
668
|
mountApp(loader) {
|
|
368
|
-
const { bootstrapModule, rootComponent,
|
|
369
|
-
|
|
669
|
+
const { bootstrapModule, rootComponent, rootComponents, serverData, endpoints } = this.config;
|
|
670
|
+
|
|
671
|
+
const importMappings = this.getImportMappingsWithUpdates();
|
|
370
672
|
|
|
371
673
|
// Set global LWR.define to loader.define
|
|
372
674
|
this.global.LWR = Object.freeze({
|
|
373
675
|
define: loader.define.bind(loader),
|
|
676
|
+
importMap: loader.importMap.bind(loader),
|
|
374
677
|
rootComponent,
|
|
375
678
|
rootComponents,
|
|
376
679
|
serverData: serverData || {},
|
|
@@ -395,7 +698,7 @@
|
|
|
395
698
|
.registerImportMappings(importMappings)
|
|
396
699
|
.then(() => {
|
|
397
700
|
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
398
|
-
if (typeof window === 'undefined' || typeof document === undefined) {
|
|
701
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
399
702
|
return Promise.resolve();
|
|
400
703
|
}
|
|
401
704
|
if (initDeferDOM) {
|
|
@@ -455,14 +758,14 @@
|
|
|
455
758
|
// The loader module is ALWAYS required
|
|
456
759
|
const GLOBAL = globalThis ;
|
|
457
760
|
GLOBAL.LWR.requiredModules = GLOBAL.LWR.requiredModules || [];
|
|
458
|
-
if (GLOBAL.LWR.requiredModules.indexOf('lwr/loaderLegacy/v/
|
|
459
|
-
GLOBAL.LWR.requiredModules.push('lwr/loaderLegacy/v/
|
|
761
|
+
if (GLOBAL.LWR.requiredModules.indexOf('lwr/loaderLegacy/v/0_22_11') < 0) {
|
|
762
|
+
GLOBAL.LWR.requiredModules.push('lwr/loaderLegacy/v/0_22_11');
|
|
460
763
|
}
|
|
461
764
|
new LoaderShim(GLOBAL);
|
|
462
765
|
|
|
463
766
|
})();
|
|
464
767
|
|
|
465
|
-
LWR.define('lwr/loaderLegacy/v/
|
|
768
|
+
LWR.define('lwr/loaderLegacy/v/0_22_11', ['exports'], (function (exports) { 'use strict';
|
|
466
769
|
|
|
467
770
|
const templateRegex = /\{([0-9]+)\}/g;
|
|
468
771
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -602,7 +905,7 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
602
905
|
level: 0,
|
|
603
906
|
message: 'Cannot dynamically import the LWR loader with importer "{0}"',
|
|
604
907
|
});
|
|
605
|
-
Object.freeze({
|
|
908
|
+
const NO_IMPORT_TRANSPORT = Object.freeze({
|
|
606
909
|
code: 3025,
|
|
607
910
|
level: 0,
|
|
608
911
|
message: 'Cannot dynamically import "transport" with importer "{0}"',
|
|
@@ -1857,9 +2160,10 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
1857
2160
|
|
|
1858
2161
|
|
|
1859
2162
|
|
|
2163
|
+
|
|
1860
2164
|
/**
|
|
1861
2165
|
* Validates that the given module id is not one of the forbidden dynamic import specifiers.
|
|
1862
|
-
* Throws LoaderError for: lwc, the LWR loader, and blob URLs.
|
|
2166
|
+
* Throws LoaderError for: lwc, the LWR loader, transport/webruntime/transport, and blob URLs.
|
|
1863
2167
|
*
|
|
1864
2168
|
* @param id - Module identifier or URL
|
|
1865
2169
|
* @param importer - Versioned specifier of the module importer (for error reporting)
|
|
@@ -1872,7 +2176,7 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
1872
2176
|
loaderSpecifier,
|
|
1873
2177
|
errors,
|
|
1874
2178
|
) {
|
|
1875
|
-
const { LoaderError, NO_IMPORT_LWC, NO_IMPORT_LOADER, NO_BLOB_IMPORT } = errors;
|
|
2179
|
+
const { LoaderError, NO_IMPORT_LWC, NO_IMPORT_LOADER, NO_IMPORT_TRANSPORT, NO_BLOB_IMPORT } = errors;
|
|
1876
2180
|
|
|
1877
2181
|
// Throw an error if the specifier is "lwc" or a versioned lwc specifier
|
|
1878
2182
|
// Dynamic import of LWC APIs is not allowed
|
|
@@ -1888,6 +2192,18 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
1888
2192
|
throw new LoaderError(NO_IMPORT_LOADER, [_nullishCoalesce(importer, () => ( 'unknown'))]);
|
|
1889
2193
|
}
|
|
1890
2194
|
|
|
2195
|
+
// Throw an error if the specifier is "transport" or "webruntime/transport" (or versioned)
|
|
2196
|
+
// Dynamic import of transport exposes unsandboxed fetch, bypassing LWS
|
|
2197
|
+
// Reference: Hackforce report HF-3393
|
|
2198
|
+
if (
|
|
2199
|
+
id === 'transport' ||
|
|
2200
|
+
id.startsWith('transport/v/') ||
|
|
2201
|
+
id === 'webruntime/transport' ||
|
|
2202
|
+
id.startsWith('webruntime/transport/v/')
|
|
2203
|
+
) {
|
|
2204
|
+
throw new LoaderError(NO_IMPORT_TRANSPORT, [_nullishCoalesce(importer, () => ( 'unknown'))]);
|
|
2205
|
+
}
|
|
2206
|
+
|
|
1891
2207
|
// Throw an error if the specifier is a blob URL (case-insensitive check)
|
|
1892
2208
|
if (id.toLowerCase().startsWith('blob:')) {
|
|
1893
2209
|
throw new LoaderError(NO_BLOB_IMPORT);
|
|
@@ -1906,7 +2222,8 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
1906
2222
|
if (segment in matchObj) {
|
|
1907
2223
|
return segment;
|
|
1908
2224
|
}
|
|
1909
|
-
|
|
2225
|
+
sepIndex = path.lastIndexOf('/', sepIndex - 1);
|
|
2226
|
+
} while (sepIndex > 0);
|
|
1910
2227
|
}
|
|
1911
2228
|
function targetWarning(match, target, msg) {
|
|
1912
2229
|
if (hasConsole) {
|
|
@@ -1915,6 +2232,27 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
1915
2232
|
}
|
|
1916
2233
|
}
|
|
1917
2234
|
|
|
2235
|
+
/**
|
|
2236
|
+
* Import map entries are write-once: once a specifier is mapped, it cannot be overridden.
|
|
2237
|
+
* This ensures deterministic module resolution and prevents runtime mutation of previously
|
|
2238
|
+
* resolved dependencies. Conflicting mappings are ignored with a warning.
|
|
2239
|
+
*
|
|
2240
|
+
* Merges a single import map entry into a target imports object.
|
|
2241
|
+
* - If the specifier doesn't exist, it is added.
|
|
2242
|
+
* - If it exists with the same value, it is silently skipped.
|
|
2243
|
+
* - If it exists with a different value, a warning is logged and the entry is skipped.
|
|
2244
|
+
*/
|
|
2245
|
+
function mergeImportMapEntry(specifier, uri, target) {
|
|
2246
|
+
const existing = target[specifier];
|
|
2247
|
+
if (existing !== undefined) {
|
|
2248
|
+
if (existing !== uri) {
|
|
2249
|
+
targetWarning(specifier, uri, `already mapped to "${existing}", ignoring conflicting mapping`);
|
|
2250
|
+
}
|
|
2251
|
+
} else {
|
|
2252
|
+
target[specifier] = uri;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
|
|
1918
2256
|
/**
|
|
1919
2257
|
* Import map support for LWR based on the spec: https://github.com/WICG/import-maps
|
|
1920
2258
|
*
|
|
@@ -2036,7 +2374,8 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
2036
2374
|
if (!mapped) {
|
|
2037
2375
|
targetWarning(p, rhs, 'bare specifier did not resolve');
|
|
2038
2376
|
} else {
|
|
2039
|
-
|
|
2377
|
+
// Merge into cache with write-once protection
|
|
2378
|
+
mergeImportMapEntry(resolvedLhs, mapped.uri, outPackages);
|
|
2040
2379
|
}
|
|
2041
2380
|
}
|
|
2042
2381
|
}
|
|
@@ -2087,6 +2426,20 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
2087
2426
|
resolve(resolvedOrPlain, parentUrl) {
|
|
2088
2427
|
return resolveImportMapEntry(this.importMap, resolvedOrPlain, parentUrl);
|
|
2089
2428
|
}
|
|
2429
|
+
|
|
2430
|
+
addImportMapEntries(importMap) {
|
|
2431
|
+
if (importMap.imports) {
|
|
2432
|
+
const current = this.importMap;
|
|
2433
|
+
if (!current.imports) {
|
|
2434
|
+
current.imports = {};
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// Merge into cache with write-once protection
|
|
2438
|
+
for (const specifier in importMap.imports) {
|
|
2439
|
+
mergeImportMapEntry(specifier, importMap.imports[specifier], current.imports);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2090
2443
|
}
|
|
2091
2444
|
|
|
2092
2445
|
/**
|
|
@@ -2158,15 +2511,134 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
2158
2511
|
return importMapPromise;
|
|
2159
2512
|
}
|
|
2160
2513
|
|
|
2161
|
-
|
|
2514
|
+
/**
|
|
2515
|
+
* List of protected module patterns that cannot be remapped.
|
|
2516
|
+
* Uses case-insensitive matching to prevent bypass attempts (e.g., HF-1027).
|
|
2517
|
+
*/
|
|
2518
|
+
const PROTECTED_PATTERNS = [
|
|
2519
|
+
/^lwc$/i, // Core LWC
|
|
2520
|
+
/^lwc\/v\//i, // Versioned LWC variants
|
|
2521
|
+
/^@lwc\//i, // LWC scoped packages
|
|
2522
|
+
/^lightningmobileruntime\//i, // Lightning mobile runtime
|
|
2523
|
+
];
|
|
2524
|
+
|
|
2525
|
+
/**
|
|
2526
|
+
* Checks if a specifier matches any protected module pattern.
|
|
2527
|
+
*
|
|
2528
|
+
* @param specifier - The module specifier to check
|
|
2529
|
+
* @returns true if the specifier is protected, false otherwise
|
|
2530
|
+
*/
|
|
2531
|
+
function isProtectedSpecifier(specifier) {
|
|
2532
|
+
return PROTECTED_PATTERNS.some((pattern) => pattern.test(specifier));
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
/**
|
|
2536
|
+
* Validates that a URI does not use dangerous URL schemes.
|
|
2537
|
+
*
|
|
2538
|
+
* Blocks:
|
|
2539
|
+
* - blob: URLs (HF-1027 bypass prevention) - case-insensitive
|
|
2540
|
+
* - data: URLs (inline code injection) - case-insensitive
|
|
2541
|
+
*
|
|
2542
|
+
* @param uri - The URI to validate
|
|
2543
|
+
* @param specifier - The specifier being mapped (for error messages)
|
|
2544
|
+
* @throws Error if the URI uses a dangerous scheme
|
|
2545
|
+
*/
|
|
2546
|
+
function validateMappingUri(uri, specifier) {
|
|
2547
|
+
const lowerUri = uri.toLowerCase();
|
|
2548
|
+
|
|
2549
|
+
if (lowerUri.startsWith('blob:')) {
|
|
2550
|
+
throw new Error(`Cannot map ${specifier} to blob: URL`);
|
|
2551
|
+
}
|
|
2162
2552
|
|
|
2553
|
+
if (lowerUri.startsWith('data:')) {
|
|
2554
|
+
throw new Error(`Cannot map ${specifier} to data: URL`);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2163
2557
|
|
|
2558
|
+
/**
|
|
2559
|
+
* Validates that a specifier is well-formed and not protected.
|
|
2560
|
+
*
|
|
2561
|
+
* @param specifier - The module specifier to validate
|
|
2562
|
+
* @throws Error if the specifier is invalid or protected
|
|
2563
|
+
*/
|
|
2564
|
+
function validateSpecifier(specifier) {
|
|
2565
|
+
if (typeof specifier !== 'string' || specifier.length === 0) {
|
|
2566
|
+
throw new Error('Specifier must be a non-empty string');
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
if (isProtectedSpecifier(specifier)) {
|
|
2570
|
+
throw new Error(`Cannot remap protected module: ${specifier}`);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
/**
|
|
2575
|
+
* Validates and converts an ImportMapUpdate to ImportMap format.
|
|
2576
|
+
*
|
|
2577
|
+
* Performs validation checks and then converts from:
|
|
2578
|
+
* { moduleScriptURL: [moduleName1, moduleName2] }
|
|
2579
|
+
* To:
|
|
2580
|
+
* { imports: { moduleName1: moduleScriptURL, moduleName2: moduleScriptURL } }
|
|
2581
|
+
*
|
|
2582
|
+
* @param update - The ImportMapUpdate object to validate and convert
|
|
2583
|
+
* @returns The converted ImportMap, or null if the update is empty
|
|
2584
|
+
* @throws Error if any validation fails
|
|
2585
|
+
*/
|
|
2586
|
+
function validateAndConvertImportMapUpdate(update) {
|
|
2587
|
+
if (!update || typeof update !== 'object') {
|
|
2588
|
+
throw new Error('LWR.importMap() requires an object argument');
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
// Check if update is empty
|
|
2592
|
+
const entries = Object.entries(update);
|
|
2593
|
+
if (entries.length === 0) {
|
|
2594
|
+
if (hasConsole) {
|
|
2595
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
2596
|
+
console.warn('LWR.importMap() called with empty update object');
|
|
2597
|
+
}
|
|
2598
|
+
return null;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
const convertedImports = {};
|
|
2602
|
+
|
|
2603
|
+
for (const [moduleScriptURL, moduleNames] of entries) {
|
|
2604
|
+
if (!Array.isArray(moduleNames)) {
|
|
2605
|
+
throw new Error('moduleNames must be an array');
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
if (!moduleScriptURL || typeof moduleScriptURL !== 'string') {
|
|
2609
|
+
throw new Error('moduleScriptURL must be a string');
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
validateMappingUri(moduleScriptURL, moduleScriptURL);
|
|
2613
|
+
|
|
2614
|
+
for (const moduleName of moduleNames) {
|
|
2615
|
+
validateSpecifier(moduleName);
|
|
2616
|
+
if (moduleName in convertedImports) {
|
|
2617
|
+
if (hasConsole) {
|
|
2618
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
2619
|
+
console.warn(
|
|
2620
|
+
`LWR.importMap(): duplicate module "${moduleName}" — already mapped to "${convertedImports[moduleName]}", ignoring mapping to "${moduleScriptURL}"`,
|
|
2621
|
+
);
|
|
2622
|
+
}
|
|
2623
|
+
} else {
|
|
2624
|
+
convertedImports[moduleName] = moduleScriptURL;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
return {
|
|
2630
|
+
imports: convertedImports,
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2164
2635
|
/**
|
|
2165
2636
|
* The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
|
|
2166
2637
|
*/
|
|
2167
2638
|
class Loader {
|
|
2168
2639
|
|
|
2169
2640
|
|
|
2641
|
+
|
|
2170
2642
|
|
|
2171
2643
|
|
|
2172
2644
|
constructor(config) {
|
|
@@ -2252,6 +2724,7 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
2252
2724
|
LoaderError,
|
|
2253
2725
|
NO_IMPORT_LWC,
|
|
2254
2726
|
NO_IMPORT_LOADER,
|
|
2727
|
+
NO_IMPORT_TRANSPORT,
|
|
2255
2728
|
NO_BLOB_IMPORT,
|
|
2256
2729
|
});
|
|
2257
2730
|
return this.registry.load(id, importer);
|
|
@@ -2294,10 +2767,13 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
2294
2767
|
// import maps spec if we do this after resolving any imports
|
|
2295
2768
|
importMap = resolveAndComposeImportMap(mappings, this.baseUrl, this.parentImportMap);
|
|
2296
2769
|
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2770
|
+
if (importMap) {
|
|
2771
|
+
if (this.importMapResolver || this.parentImportMap) {
|
|
2772
|
+
throw new LoaderError(BAD_IMPORT_MAP);
|
|
2773
|
+
}
|
|
2774
|
+
this.importMapResolver = new ImportMapResolver(importMap);
|
|
2775
|
+
this.registry.setImportResolver(this.importMapResolver);
|
|
2776
|
+
this.parentImportMap = this.importMapResolver.importMap;
|
|
2301
2777
|
}
|
|
2302
2778
|
}
|
|
2303
2779
|
|
|
@@ -2310,6 +2786,35 @@ LWR.define('lwr/loaderLegacy/v/0_22_9', ['exports'], (function (exports) { 'use
|
|
|
2310
2786
|
this.registry.registerExternalModules(modules);
|
|
2311
2787
|
}
|
|
2312
2788
|
|
|
2789
|
+
/**
|
|
2790
|
+
* Apply import map updates at runtime.
|
|
2791
|
+
* Enables adding new import mappings dynamically.
|
|
2792
|
+
*
|
|
2793
|
+
* @param updates - Import Map Update object containing:
|
|
2794
|
+
- [moduleScriptURL] - Script URL containing LWR define statements
|
|
2795
|
+
- string[] - array of module names which are included in the given script
|
|
2796
|
+
*/
|
|
2797
|
+
importMap(update) {
|
|
2798
|
+
// Validate and convert the import map update to the ImportMap format (moduleName -> moduleScriptURL)
|
|
2799
|
+
const importMap = validateAndConvertImportMapUpdate(update);
|
|
2800
|
+
|
|
2801
|
+
// Early return if update was empty
|
|
2802
|
+
if (!importMap) {
|
|
2803
|
+
return;
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
if (!this.parentImportMap || !this.importMapResolver) {
|
|
2807
|
+
throw new LoaderError(BAD_IMPORT_MAP);
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
// Merge the new mappings with the base import map - note this goes against
|
|
2811
|
+
// import maps spec if we do this after resolving any imports
|
|
2812
|
+
const resolvedImportMap = resolveAndComposeImportMap(importMap, this.baseUrl, this.parentImportMap);
|
|
2813
|
+
|
|
2814
|
+
this.importMapResolver.addImportMapEntries(resolvedImportMap);
|
|
2815
|
+
this.parentImportMap = this.importMapResolver.importMap;
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2313
2818
|
getModuleWarnings(isAppMounted = false) {
|
|
2314
2819
|
return this.registry.getModuleWarnings(isAppMounted);
|
|
2315
2820
|
}
|