@lwrjs/loader 0.22.12 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,11 +44,7 @@ export class ImportMetadataResolver {
44
44
  getBaseUrl() {
45
45
  return this.config.baseUrl || '';
46
46
  }
47
- registerImportMappings(newImportMetadata, rootSpecifiers) {
48
- if (!rootSpecifiers || rootSpecifiers.length === 0) {
49
- const imports = newImportMetadata ? JSON.stringify(newImportMetadata) : 'undefined';
50
- throw new LoaderError(BAD_IMPORT_METADATA, [imports, rootSpecifiers ? '[]' : 'undefined']);
51
- }
47
+ registerImportMappings(newImportMetadata, rootSpecifiers = []) {
52
48
  if (!newImportMetadata) {
53
49
  throw new LoaderError(BAD_IMPORT_METADATA, ['undefined', JSON.stringify(rootSpecifiers)]);
54
50
  }
@@ -58,6 +54,8 @@ export class ImportMetadataResolver {
58
54
  JSON.stringify(rootSpecifiers),
59
55
  ]);
60
56
  }
57
+ // An empty rootSpecifiers list is valid for non-bootstrap registrations;
58
+ // any specifier not in the list receives isRoot: false.
61
59
  const index = newImportMetadata.index || {};
62
60
  for (const [uri, specifiers] of Object.entries(newImportMetadata.imports)) {
63
61
  specifiers.forEach((specifier) => {
@@ -80,6 +78,15 @@ export class ImportMetadataResolver {
80
78
  });
81
79
  }
82
80
  }
81
+ /**
82
+ * Returns the raw URI stored for a specifier, or `undefined` if not yet mapped.
83
+ * Used by the runtime `importMap()` path to detect cross-call duplicates while
84
+ * allowing truly idempotent re-registrations (same specifier, same URI) to pass
85
+ * through silently.
86
+ */
87
+ getMappingUri(specifier) {
88
+ return this.importURICache.get(specifier)?.uri;
89
+ }
83
90
  // Get URL from the local cache or return undefiend
84
91
  getURI(specifier) {
85
92
  return this.importURICache && this.importURICache.has(specifier)
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Validation for LWR.importMap() on the fingerprints loader (URL → specifiers `ImportMetadata`).
3
+ *
4
+ * Aligned with `ImportMap` / `ImportMetadata` from @lwrjs/types.
5
+ */
6
+ import type { ImportMapUpdate, ImportMetadata } from '@lwrjs/types';
7
+ /**
8
+ * Validates an ImportMapUpdate and wraps it as ImportMetadata for the fingerprints loader.
9
+ *
10
+ * Does not apply legacy-style protected-specifier blocking; system modules are expected to be
11
+ * defined at bootstrap, and `importMap` write-once semantics handle redundant runtime entries.
12
+ *
13
+ * Duplicate specifiers in the same update are de-duplicated silently (first occurrence wins).
14
+ * An empty object is a silent no-op (returns null).
15
+ */
16
+ export declare function validateAndWrapAsImportMetadata(update: ImportMapUpdate): ImportMetadata | null;
17
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Validation for LWR.importMap() on the fingerprints loader (URL → specifiers `ImportMetadata`).
3
+ *
4
+ * Aligned with `ImportMap` / `ImportMetadata` from @lwrjs/types.
5
+ */
6
+ /**
7
+ * Ensures the update object key is a safe, loader-supported script URL (not a bare specifier).
8
+ * Covers dangerous schemes and URL-key shape in one place.
9
+ */
10
+ function assertValidImportMapUrlKey(moduleScriptURL) {
11
+ const lowerUri = moduleScriptURL.toLowerCase();
12
+ if (lowerUri.startsWith('blob:')) {
13
+ throw new Error(`Cannot map ${moduleScriptURL} to blob: URL`);
14
+ }
15
+ if (lowerUri.startsWith('data:')) {
16
+ throw new Error(`Cannot map ${moduleScriptURL} to data: URL`);
17
+ }
18
+ if (!/^[./]|^https?:/.test(moduleScriptURL)) {
19
+ throw new Error(`LWR.importMap(): "${moduleScriptURL}" is not a valid URL key. Keys must be URL paths (e.g. "/path/to/module.js"), not module specifiers. Transitive specifier aliasing is not supported by this loader.`);
20
+ }
21
+ }
22
+ /**
23
+ * Validates an ImportMapUpdate and wraps it as ImportMetadata for the fingerprints loader.
24
+ *
25
+ * Does not apply legacy-style protected-specifier blocking; system modules are expected to be
26
+ * defined at bootstrap, and `importMap` write-once semantics handle redundant runtime entries.
27
+ *
28
+ * Duplicate specifiers in the same update are de-duplicated silently (first occurrence wins).
29
+ * An empty object is a silent no-op (returns null).
30
+ */
31
+ export function validateAndWrapAsImportMetadata(update) {
32
+ if (!update || typeof update !== 'object') {
33
+ throw new Error('LWR.importMap() requires an object argument');
34
+ }
35
+ const entries = Object.entries(update);
36
+ if (entries.length === 0) {
37
+ return null;
38
+ }
39
+ const seenSpecifiers = new Set();
40
+ const validatedImports = {};
41
+ for (const [moduleScriptURL, moduleNames] of entries) {
42
+ if (!moduleScriptURL || typeof moduleScriptURL !== 'string') {
43
+ throw new Error('moduleScriptURL must be a string');
44
+ }
45
+ if (!Array.isArray(moduleNames)) {
46
+ throw new Error('moduleNames must be an array');
47
+ }
48
+ assertValidImportMapUrlKey(moduleScriptURL);
49
+ const validatedNames = [];
50
+ for (const moduleName of moduleNames) {
51
+ if (typeof moduleName !== 'string' || moduleName.length === 0) {
52
+ throw new Error('Specifier must be a non-empty string');
53
+ }
54
+ if (!seenSpecifiers.has(moduleName)) {
55
+ seenSpecifiers.add(moduleName);
56
+ validatedNames.push(moduleName);
57
+ }
58
+ }
59
+ if (validatedNames.length > 0) {
60
+ validatedImports[moduleScriptURL] = validatedNames;
61
+ }
62
+ }
63
+ if (Object.keys(validatedImports).length === 0) {
64
+ return null;
65
+ }
66
+ return { imports: validatedImports };
67
+ }
68
+ //# sourceMappingURL=validation.js.map
@@ -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 v0.22.12 */
7
+ /* LWR Legacy Module Loader v0.23.0 */
8
8
  const templateRegex = /\{([0-9]+)\}/g;
9
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
10
  function templateString(template, args) {
@@ -25,7 +25,7 @@ export default class LoaderShim {
25
25
  this.errorHandler = this.config.onError;
26
26
  // Set up onAppInit handler
27
27
  this.initAppHandler = this.config.onInitApp;
28
- // Set up the temporary LWR.define function and customInit hook
28
+ // Set up the temporary LWR.define function
29
29
  const tempDefine = this.tempDefine.bind(this);
30
30
  global.LWR.define = tempDefine;
31
31
  this.bootReady = this.config.autoBoot;
@@ -171,9 +171,12 @@ export default class LoaderShim {
171
171
  mountApp(loader) {
172
172
  const { bootstrapModule, rootComponent, rootComponents, serverData, endpoints, imports, index } = this.config;
173
173
  const importsObj = imports || {};
174
- // Set global LWR.define to loader.define
174
+ // Set global LWR.define to loader.define and expose the public-only importMap.
175
+ // The wrapper intentionally omits the internal rootSpecifiers parameter so external
176
+ // callers cannot bypass runtime validation by triggering the bootstrap path.
175
177
  this.global.LWR = Object.freeze({
176
178
  define: loader.define.bind(loader),
179
+ importMap: (update) => loader.importMap(update),
177
180
  rootComponent,
178
181
  rootComponents,
179
182
  serverData: serverData || {},
@@ -193,10 +196,7 @@ export default class LoaderShim {
193
196
  const { initDeferDOM } = this.config;
194
197
  // Load the import mappings and application bootstrap module
195
198
  loader
196
- .registerImportMappings({ imports: importsObj, index }, [
197
- bootstrapModule,
198
- rootComponent,
199
- ])
199
+ .importMap({ imports: importsObj, index }, [bootstrapModule, rootComponent])
200
200
  .then(() => {
201
201
  // eslint-disable-next-line lwr/no-unguarded-apis
202
202
  if (typeof window === 'undefined' || typeof document === 'undefined') {
package/build/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { ImportMetadata, AppMetadata, ServerData, ClientBootstrapConfig } from '@lwrjs/types';
2
- import type { ImportMap } from './modules/lwr/loaderLegacy/importMap/importMap.js';
1
+ import type { ImportMap, ImportMapUpdate, ImportMetadata, AppMetadata, ServerData, ClientBootstrapConfig } from '@lwrjs/types';
3
2
  import type { ProfilerAPI, LogDispatcher } from 'lwr/profiler';
4
3
  export type GlobalThis = {
5
4
  LWR: Partial<ClientBootstrapConfig> & {
@@ -31,7 +30,7 @@ export interface ResolvedLoader {
31
30
  export type LoaderClass = {
32
31
  new (config?: LoaderConfig): LoaderAPI;
33
32
  };
34
- export type ImportMapUpdate = Record<string, string[]>;
33
+ export type { ImportMapUpdate };
35
34
  export type LwrImportMapUpdateMethod = (update: ImportMapUpdate) => void;
36
35
  export interface BaseLoaderAPI {
37
36
  define: LoaderDefine;
@@ -72,6 +71,8 @@ export type Endpoints = {
72
71
  * Fingerprints loader extensions
73
72
  */
74
73
  export interface FingerprintsLoaderAPI extends BaseLoaderAPI {
74
+ importMap(update: ImportMapUpdate | ImportMetadata, rootSpecifiers?: string[]): Promise<void>;
75
+ /** @deprecated Use `importMap(metadata, rootSpecifiers)` instead. */
75
76
  registerImportMappings(importMetadata: ImportMetadata, rootSpecifiers: string[]): Promise<void>;
76
77
  }
77
78
  export interface FingerprintsLoaderConfig extends LoaderConfig {
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "version": "0.22.12",
8
+ "version": "0.23.0",
9
9
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
10
10
  "repository": {
11
11
  "type": "git",
@@ -61,16 +61,16 @@
61
61
  },
62
62
  "devDependencies": {
63
63
  "@locker/trusted-types": "0.26.4",
64
- "@lwrjs/diagnostics": "0.22.12",
65
- "@lwrjs/types": "0.22.12",
64
+ "@lwrjs/diagnostics": "0.23.0",
65
+ "@lwrjs/types": "0.23.0",
66
66
  "@rollup/plugin-node-resolve": "^15.2.3",
67
67
  "@rollup/plugin-sucrase": "^5.0.2",
68
68
  "@rollup/plugin-terser": "^0.4.4",
69
69
  "rollup": "^2.80.0"
70
70
  },
71
71
  "dependencies": {
72
- "@lwrjs/client-modules": "0.22.12",
73
- "@lwrjs/shared-utils": "0.22.12"
72
+ "@lwrjs/client-modules": "0.23.0",
73
+ "@lwrjs/shared-utils": "0.23.0"
74
74
  },
75
75
  "lwc": {
76
76
  "modules": [
@@ -90,5 +90,5 @@
90
90
  "volta": {
91
91
  "extends": "../../../package.json"
92
92
  },
93
- "gitHead": "a928ee1f7c37665c49f77336a4daa179bcd61a4f"
93
+ "gitHead": "5de81558aa52fc9593a386d9f6d82532c61cd4c1"
94
94
  }