@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.
Files changed (37) hide show
  1. package/README.md +33 -1
  2. package/build/assets/prod/lwr-error-shim.js +1 -1
  3. package/build/assets/prod/lwr-loader-shim-legacy.bundle.js +530 -25
  4. package/build/assets/prod/lwr-loader-shim-legacy.bundle.min.js +3 -3
  5. package/build/assets/prod/lwr-loader-shim-legacy.js +317 -14
  6. package/build/assets/prod/lwr-loader-shim.bundle.js +123 -12
  7. package/build/assets/prod/lwr-loader-shim.bundle.min.js +3 -3
  8. package/build/assets/prod/lwr-loader-shim.js +105 -8
  9. package/build/cjs/modules/lwr/loader/validateLoadSpecifier.cjs +4 -1
  10. package/build/cjs/modules/lwr/loaderLegacy/importMap/importMap.cjs +1 -1
  11. package/build/cjs/modules/lwr/loaderLegacy/importMap/importMapResolver.cjs +12 -0
  12. package/build/cjs/modules/lwr/loaderLegacy/importMap/utils.cjs +13 -1
  13. package/build/cjs/modules/lwr/loaderLegacy/utils/validation.cjs +93 -0
  14. package/build/modules/lwr/esmLoader/esmLoader.js +1 -1
  15. package/build/modules/lwr/loader/loader.js +18 -4
  16. package/build/modules/lwr/loader/validateLoadSpecifier.d.ts +2 -1
  17. package/build/modules/lwr/loader/validateLoadSpecifier.js +11 -2
  18. package/build/modules/lwr/loaderLegacy/importMap/importMap.js +3 -2
  19. package/build/modules/lwr/loaderLegacy/importMap/importMapResolver.d.ts +1 -0
  20. package/build/modules/lwr/loaderLegacy/importMap/importMapResolver.js +13 -0
  21. package/build/modules/lwr/loaderLegacy/importMap/utils.d.ts +12 -1
  22. package/build/modules/lwr/loaderLegacy/importMap/utils.js +24 -2
  23. package/build/modules/lwr/loaderLegacy/loaderLegacy.d.ts +11 -1
  24. package/build/modules/lwr/loaderLegacy/loaderLegacy.js +213 -11
  25. package/build/modules/lwr/loaderLegacy/utils/validation.d.ts +50 -0
  26. package/build/modules/lwr/loaderLegacy/utils/validation.js +114 -0
  27. package/build/shim/defineCacheResolver.d.ts +10 -0
  28. package/build/shim/defineCacheResolver.js +78 -0
  29. package/build/shim/loader.d.ts +5 -1
  30. package/build/shim/loader.js +14 -3
  31. package/build/shim/shim.js +3 -2
  32. package/build/shim-legacy/loaderLegacy.d.ts +7 -2
  33. package/build/shim-legacy/loaderLegacy.js +15 -4
  34. package/build/shim-legacy/shimLegacy.d.ts +14 -0
  35. package/build/shim-legacy/shimLegacy.js +53 -4
  36. package/build/types.d.ts +22 -0
  37. package/package.json +6 -6
@@ -0,0 +1,93 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, {get: all[name], enumerable: true});
11
+ };
12
+ var __exportStar = (target, module2, desc) => {
13
+ if (module2 && typeof module2 === "object" || typeof module2 === "function") {
14
+ for (let key of __getOwnPropNames(module2))
15
+ if (!__hasOwnProp.call(target, key) && key !== "default")
16
+ __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
17
+ }
18
+ return target;
19
+ };
20
+ var __toModule = (module2) => {
21
+ return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2);
22
+ };
23
+
24
+ // packages/@lwrjs/loader/src/modules/lwr/loaderLegacy/utils/validation.ts
25
+ __markAsModule(exports);
26
+ __export(exports, {
27
+ isProtectedSpecifier: () => isProtectedSpecifier,
28
+ validateAndConvertImportMapUpdate: () => validateAndConvertImportMapUpdate,
29
+ validateMappingUri: () => validateMappingUri,
30
+ validateSpecifier: () => validateSpecifier
31
+ });
32
+ var import_dom = __toModule(require("../utils/dom.cjs"));
33
+ var PROTECTED_PATTERNS = [
34
+ /^lwc$/i,
35
+ /^lwc\/v\//i,
36
+ /^@lwc\//i,
37
+ /^lightningmobileruntime\//i
38
+ ];
39
+ function isProtectedSpecifier(specifier) {
40
+ return PROTECTED_PATTERNS.some((pattern) => pattern.test(specifier));
41
+ }
42
+ function validateMappingUri(uri, specifier) {
43
+ const lowerUri = uri.toLowerCase();
44
+ if (lowerUri.startsWith("blob:")) {
45
+ throw new Error(`Cannot map ${specifier} to blob: URL`);
46
+ }
47
+ if (lowerUri.startsWith("data:")) {
48
+ throw new Error(`Cannot map ${specifier} to data: URL`);
49
+ }
50
+ }
51
+ function validateSpecifier(specifier) {
52
+ if (typeof specifier !== "string" || specifier.length === 0) {
53
+ throw new Error("Specifier must be a non-empty string");
54
+ }
55
+ if (isProtectedSpecifier(specifier)) {
56
+ throw new Error(`Cannot remap protected module: ${specifier}`);
57
+ }
58
+ }
59
+ function validateAndConvertImportMapUpdate(update) {
60
+ if (!update || typeof update !== "object") {
61
+ throw new Error("LWR.importMap() requires an object argument");
62
+ }
63
+ const entries = Object.entries(update);
64
+ if (entries.length === 0) {
65
+ if (import_dom.hasConsole) {
66
+ console.warn("LWR.importMap() called with empty update object");
67
+ }
68
+ return null;
69
+ }
70
+ const convertedImports = {};
71
+ for (const [moduleScriptURL, moduleNames] of entries) {
72
+ if (!Array.isArray(moduleNames)) {
73
+ throw new Error("moduleNames must be an array");
74
+ }
75
+ if (!moduleScriptURL || typeof moduleScriptURL !== "string") {
76
+ throw new Error("moduleScriptURL must be a string");
77
+ }
78
+ validateMappingUri(moduleScriptURL, moduleScriptURL);
79
+ for (const moduleName of moduleNames) {
80
+ validateSpecifier(moduleName);
81
+ if (moduleName in convertedImports) {
82
+ if (import_dom.hasConsole) {
83
+ console.warn(`LWR.importMap(): duplicate module "${moduleName}" \u2014 already mapped to "${convertedImports[moduleName]}", ignoring mapping to "${moduleScriptURL}"`);
84
+ }
85
+ } else {
86
+ convertedImports[moduleName] = moduleScriptURL;
87
+ }
88
+ }
89
+ }
90
+ return {
91
+ imports: convertedImports
92
+ };
93
+ }
@@ -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 ESM Module Loader v0.22.9 */
7
+ /* LWR ESM Module Loader v0.22.11 */
8
8
  function _optionalChain$1(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; }
9
9
 
10
10
 
@@ -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 Module Loader v0.22.9 */
7
+ /* LWR Module Loader v0.22.11 */
8
8
  const templateRegex = /\{([0-9]+)\}/g;
9
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
10
  function templateString(template, args) {
@@ -158,7 +158,7 @@ const NO_BLOB_IMPORT = Object.freeze({
158
158
  level: 0,
159
159
  message: 'Cannot import a blob URL',
160
160
  });
161
- Object.freeze({
161
+ const NO_IMPORT_TRANSPORT = Object.freeze({
162
162
  code: 3025,
163
163
  level: 0,
164
164
  message: 'Cannot dynamically import "transport" with importer "{0}"',
@@ -1609,9 +1609,10 @@ function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else {
1609
1609
 
1610
1610
 
1611
1611
 
1612
+
1612
1613
  /**
1613
1614
  * Validates that the given module id is not one of the forbidden dynamic import specifiers.
1614
- * Throws LoaderError for: lwc, the LWR loader, and blob URLs.
1615
+ * Throws LoaderError for: lwc, the LWR loader, transport/webruntime/transport, and blob URLs.
1615
1616
  *
1616
1617
  * @param id - Module identifier or URL
1617
1618
  * @param importer - Versioned specifier of the module importer (for error reporting)
@@ -1624,7 +1625,7 @@ function validateLoadSpecifier(
1624
1625
  loaderSpecifier,
1625
1626
  errors,
1626
1627
  ) {
1627
- const { LoaderError, NO_IMPORT_LWC, NO_IMPORT_LOADER, NO_BLOB_IMPORT } = errors;
1628
+ const { LoaderError, NO_IMPORT_LWC, NO_IMPORT_LOADER, NO_IMPORT_TRANSPORT, NO_BLOB_IMPORT } = errors;
1628
1629
 
1629
1630
  // Throw an error if the specifier is "lwc" or a versioned lwc specifier
1630
1631
  // Dynamic import of LWC APIs is not allowed
@@ -1640,6 +1641,18 @@ function validateLoadSpecifier(
1640
1641
  throw new LoaderError(NO_IMPORT_LOADER, [_nullishCoalesce(importer, () => ( 'unknown'))]);
1641
1642
  }
1642
1643
 
1644
+ // Throw an error if the specifier is "transport" or "webruntime/transport" (or versioned)
1645
+ // Dynamic import of transport exposes unsandboxed fetch, bypassing LWS
1646
+ // Reference: Hackforce report HF-3393
1647
+ if (
1648
+ id === 'transport' ||
1649
+ id.startsWith('transport/v/') ||
1650
+ id === 'webruntime/transport' ||
1651
+ id.startsWith('webruntime/transport/v/')
1652
+ ) {
1653
+ throw new LoaderError(NO_IMPORT_TRANSPORT, [_nullishCoalesce(importer, () => ( 'unknown'))]);
1654
+ }
1655
+
1643
1656
  // Throw an error if the specifier is a blob URL (case-insensitive check)
1644
1657
  if (id.toLowerCase().startsWith('blob:')) {
1645
1658
  throw new LoaderError(NO_BLOB_IMPORT);
@@ -1751,6 +1764,7 @@ class Loader {
1751
1764
  LoaderError,
1752
1765
  NO_IMPORT_LWC,
1753
1766
  NO_IMPORT_LOADER,
1767
+ NO_IMPORT_TRANSPORT,
1754
1768
  NO_BLOB_IMPORT,
1755
1769
  });
1756
1770
  return this.registry.load(id, importer);
@@ -8,11 +8,12 @@ export interface ValidateLoadSpecifierErrors {
8
8
  LoaderError: new (errorInfo: LoadSpecifierErrorInfo, errorArgs?: string[]) => Error;
9
9
  NO_IMPORT_LWC: LoadSpecifierErrorInfo;
10
10
  NO_IMPORT_LOADER: LoadSpecifierErrorInfo;
11
+ NO_IMPORT_TRANSPORT: LoadSpecifierErrorInfo;
11
12
  NO_BLOB_IMPORT: LoadSpecifierErrorInfo;
12
13
  }
13
14
  /**
14
15
  * Validates that the given module id is not one of the forbidden dynamic import specifiers.
15
- * Throws LoaderError for: lwc, the LWR loader, and blob URLs.
16
+ * Throws LoaderError for: lwc, the LWR loader, transport/webruntime/transport, and blob URLs.
16
17
  *
17
18
  * @param id - Module identifier or URL
18
19
  * @param importer - Versioned specifier of the module importer (for error reporting)
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Validates that the given module id is not one of the forbidden dynamic import specifiers.
3
- * Throws LoaderError for: lwc, the LWR loader, and blob URLs.
3
+ * Throws LoaderError for: lwc, the LWR loader, transport/webruntime/transport, and blob URLs.
4
4
  *
5
5
  * @param id - Module identifier or URL
6
6
  * @param importer - Versioned specifier of the module importer (for error reporting)
@@ -8,7 +8,7 @@
8
8
  * @param errors - Error class and message constants from the calling loader
9
9
  */
10
10
  export function validateLoadSpecifier(id, importer, loaderSpecifier, errors) {
11
- const { LoaderError, NO_IMPORT_LWC, NO_IMPORT_LOADER, NO_BLOB_IMPORT } = errors;
11
+ const { LoaderError, NO_IMPORT_LWC, NO_IMPORT_LOADER, NO_IMPORT_TRANSPORT, NO_BLOB_IMPORT } = errors;
12
12
  // Throw an error if the specifier is "lwc" or a versioned lwc specifier
13
13
  // Dynamic import of LWC APIs is not allowed
14
14
  // Block both "lwc" and versioned forms like "lwc/v/8_20_1" to prevent bypass attempts
@@ -21,6 +21,15 @@ export function validateLoadSpecifier(id, importer, loaderSpecifier, errors) {
21
21
  if (id === loaderSpecifier || id.startsWith(`${loaderSpecifier}/v/`)) {
22
22
  throw new LoaderError(NO_IMPORT_LOADER, [importer ?? 'unknown']);
23
23
  }
24
+ // Throw an error if the specifier is "transport" or "webruntime/transport" (or versioned)
25
+ // Dynamic import of transport exposes unsandboxed fetch, bypassing LWS
26
+ // Reference: Hackforce report HF-3393
27
+ if (id === 'transport' ||
28
+ id.startsWith('transport/v/') ||
29
+ id === 'webruntime/transport' ||
30
+ id.startsWith('webruntime/transport/v/')) {
31
+ throw new LoaderError(NO_IMPORT_TRANSPORT, [importer ?? 'unknown']);
32
+ }
24
33
  // Throw an error if the specifier is a blob URL (case-insensitive check)
25
34
  if (id.toLowerCase().startsWith('blob:')) {
26
35
  throw new LoaderError(NO_BLOB_IMPORT);
@@ -4,7 +4,7 @@
4
4
  * This implementation is adapted from https://github.com/systemjs/systemjs/blob/master/src/features/import-map.js
5
5
  */
6
6
  import { resolveUrl, resolveIfNotPlainOrUrl, isUrl } from '../utils/url.js';
7
- import { getMatch, targetWarning } from './utils.js';
7
+ import { getMatch, targetWarning, mergeImportMapEntry } from './utils.js';
8
8
  // Resolves an import map package entry
9
9
  function applyPackages(id, packages, defaultUri) {
10
10
  const pkgName = getMatch(id, packages);
@@ -79,7 +79,8 @@ function resolveAndComposePackages(packages, outPackages, baseUrl, parentMap, pa
79
79
  targetWarning(p, rhs, 'bare specifier did not resolve');
80
80
  }
81
81
  else {
82
- outPackages[resolvedLhs] = mapped.uri;
82
+ // Merge into cache with write-once protection
83
+ mergeImportMapEntry(resolvedLhs, mapped.uri, outPackages);
83
84
  }
84
85
  }
85
86
  }
@@ -4,5 +4,6 @@ export declare class ImportMapResolver implements ImportResolver {
4
4
  importMap: ImportMap;
5
5
  constructor(importMap: ImportMap);
6
6
  resolve(resolvedOrPlain: string, parentUrl: string): ImportDefinition | undefined;
7
+ addImportMapEntries(importMap: ImportMap): void;
7
8
  }
8
9
  //# sourceMappingURL=importMapResolver.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import { resolveImportMapEntry } from './importMap.js';
2
+ import { mergeImportMapEntry } from './utils.js';
2
3
  /* spec based import map resolver */
3
4
  export class ImportMapResolver {
4
5
  constructor(importMap) {
@@ -7,5 +8,17 @@ export class ImportMapResolver {
7
8
  resolve(resolvedOrPlain, parentUrl) {
8
9
  return resolveImportMapEntry(this.importMap, resolvedOrPlain, parentUrl);
9
10
  }
11
+ addImportMapEntries(importMap) {
12
+ if (importMap.imports) {
13
+ const current = this.importMap;
14
+ if (!current.imports) {
15
+ current.imports = {};
16
+ }
17
+ // Merge into cache with write-once protection
18
+ for (const specifier in importMap.imports) {
19
+ mergeImportMapEntry(specifier, importMap.imports[specifier], current.imports);
20
+ }
21
+ }
22
+ }
10
23
  }
11
24
  //# sourceMappingURL=importMapResolver.js.map
@@ -1,4 +1,15 @@
1
1
  declare function getMatch(path: string, matchObj: Record<string, unknown>): string | undefined;
2
2
  declare function targetWarning(match: string, target: string, msg: string): void;
3
- export { getMatch, targetWarning };
3
+ /**
4
+ * Import map entries are write-once: once a specifier is mapped, it cannot be overridden.
5
+ * This ensures deterministic module resolution and prevents runtime mutation of previously
6
+ * resolved dependencies. Conflicting mappings are ignored with a warning.
7
+ *
8
+ * Merges a single import map entry into a target imports object.
9
+ * - If the specifier doesn't exist, it is added.
10
+ * - If it exists with the same value, it is silently skipped.
11
+ * - If it exists with a different value, a warning is logged and the entry is skipped.
12
+ */
13
+ declare function mergeImportMapEntry(specifier: string, uri: string, target: Record<string, string>): void;
14
+ export { getMatch, targetWarning, mergeImportMapEntry };
4
15
  //# sourceMappingURL=utils.d.ts.map
@@ -11,7 +11,8 @@ function getMatch(path, matchObj) {
11
11
  if (segment in matchObj) {
12
12
  return segment;
13
13
  }
14
- } while (path.length > 1 && (sepIndex = path.lastIndexOf('/', sepIndex - 1)) !== -1);
14
+ sepIndex = path.lastIndexOf('/', sepIndex - 1);
15
+ } while (sepIndex > 0);
15
16
  }
16
17
  function targetWarning(match, target, msg) {
17
18
  if (hasConsole) {
@@ -19,5 +20,26 @@ function targetWarning(match, target, msg) {
19
20
  console.warn('Package target ' + msg + ", resolving target '" + target + "' for " + match);
20
21
  }
21
22
  }
22
- export { getMatch, targetWarning };
23
+ /**
24
+ * Import map entries are write-once: once a specifier is mapped, it cannot be overridden.
25
+ * This ensures deterministic module resolution and prevents runtime mutation of previously
26
+ * resolved dependencies. Conflicting mappings are ignored with a warning.
27
+ *
28
+ * Merges a single import map entry into a target imports object.
29
+ * - If the specifier doesn't exist, it is added.
30
+ * - If it exists with the same value, it is silently skipped.
31
+ * - If it exists with a different value, a warning is logged and the entry is skipped.
32
+ */
33
+ function mergeImportMapEntry(specifier, uri, target) {
34
+ const existing = target[specifier];
35
+ if (existing !== undefined) {
36
+ if (existing !== uri) {
37
+ targetWarning(specifier, uri, `already mapped to "${existing}", ignoring conflicting mapping`);
38
+ }
39
+ }
40
+ else {
41
+ target[specifier] = uri;
42
+ }
43
+ }
44
+ export { getMatch, targetWarning, mergeImportMapEntry };
23
45
  //# sourceMappingURL=utils.js.map
@@ -1,13 +1,14 @@
1
1
  import { Module, ModuleDefinitionSignatures } from './moduleRegistry/moduleRegistry.js';
2
2
  import { ImportMap } from './importMap/importMap.js';
3
3
  import type { ServiceAPI } from '@lwrjs/types';
4
- import type { LoaderConfig } from '../../../types.js';
4
+ import type { LoaderConfig, ImportMapUpdate } from '../../../types.js';
5
5
  /**
6
6
  * The LWR loader is inspired and borrows from the algorithms and native browser principles of https://github.com/systemjs/systemjs
7
7
  */
8
8
  export declare class Loader {
9
9
  private registry;
10
10
  private baseUrl;
11
+ private importMapResolver?;
11
12
  readonly services: Readonly<Pick<ServiceAPI, 'addLoaderPlugin' | 'handleStaleModule' | 'appMetadata'>>;
12
13
  constructor(config?: LoaderConfig);
13
14
  /**
@@ -54,6 +55,15 @@ export declare class Loader {
54
55
  * @param modules - list of module identifiers
55
56
  */
56
57
  registerExternalModules(modules: string[]): void;
58
+ /**
59
+ * Apply import map updates at runtime.
60
+ * Enables adding new import mappings dynamically.
61
+ *
62
+ * @param updates - Import Map Update object containing:
63
+ - [moduleScriptURL] - Script URL containing LWR define statements
64
+ - string[] - array of module names which are included in the given script
65
+ */
66
+ importMap(update: ImportMapUpdate): void;
57
67
  getModuleWarnings(isAppMounted?: boolean): Record<string, string[]>;
58
68
  }
59
69
  //# sourceMappingURL=loaderLegacy.d.ts.map