@lwrjs/view-registry 0.17.2-alpha.0 → 0.17.2-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.
@@ -32,6 +32,7 @@ var import_instrumentation = __toModule(require("@lwrjs/instrumentation"));
32
32
  var import_utils = __toModule(require("./utils.cjs"));
33
33
  var import_link_lwr_resources = __toModule(require("./linkers/link-lwr-resources.cjs"));
34
34
  var import_lru_cache = __toModule(require("lru-cache"));
35
+ var import_crypto = __toModule(require("crypto"));
35
36
  var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
36
37
  var import_view_handler = __toModule(require("./view-handler.cjs"));
37
38
  var LwrViewRegistry = class {
@@ -40,6 +41,7 @@ var LwrViewRegistry = class {
40
41
  this.compiledViews = new Map();
41
42
  this.immutableAssets = new Map();
42
43
  this.pendingViewDefinitions = new import_shared_utils.InflightTasks();
44
+ this.metadataCache = new Map();
43
45
  this.name = "lwr-view-registry";
44
46
  this.resourceRegistry = context.resourceRegistry;
45
47
  this.runtimeEnvironment = context.runtimeEnvironment;
@@ -187,13 +189,14 @@ var LwrViewRegistry = class {
187
189
  const updatableViewParams = {...viewParams};
188
190
  (0, import_utils.generateViewNonce)(updatableViewParams);
189
191
  const pendingViewDefCacheKey = viewDefCacheKey + viewParamKey;
190
- const viewDefinition = await this.pendingViewDefinitions.execute(pendingViewDefCacheKey, () => (0, import_instrumentation.getTracer)().trace({
192
+ const pendingViewDefCacheKeyHashed = (0, import_crypto.createHash)("md5").update(pendingViewDefCacheKey).digest("hex");
193
+ const viewDefinition = await this.pendingViewDefinitions.execute(pendingViewDefCacheKeyHashed, () => (0, import_instrumentation.getTracer)().trace({
191
194
  name: import_instrumentation.ViewSpan.RenderView,
192
195
  attributes: {
193
196
  view: view.id,
194
197
  ssr: view.bootstrap?.ssr === true
195
198
  }
196
- }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, pendingViewDefCacheKey, renderOptions)));
199
+ }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, pendingViewDefCacheKeyHashed, renderOptions)));
197
200
  viewDefinition.nonce = (0, import_utils.getViewNonce)(updatableViewParams);
198
201
  const route = this.globalConfig.routes.find((r) => r.id === view.id);
199
202
  const maxViewCacheTtl = (0, import_shared_utils.getFeatureFlags)().MAX_VIEW_CACHE_TTL;
@@ -208,18 +211,18 @@ var LwrViewRegistry = class {
208
211
  }
209
212
  return viewDefinition;
210
213
  } catch (err) {
211
- if (err instanceof import_diagnostics.DiagnosticsError) {
214
+ if (err instanceof import_diagnostics.LwrError) {
212
215
  throw err;
213
216
  }
214
217
  import_diagnostics.logger.error(`Failed to get view definition "${view.id}"`);
215
218
  import_diagnostics.logger.error(err);
216
219
  const message = err instanceof Error ? err.message : String(err);
217
- throw (0, import_diagnostics.createSingleDiagnosticError)({description: import_diagnostics.descriptions.SERVER.UNEXPECTED_ERROR(message)}, import_diagnostics.LwrServerError);
220
+ throw new import_diagnostics.LwrServerError(import_diagnostics.descriptions.SERVER.UNEXPECTED_ERROR(message));
218
221
  }
219
222
  }
220
223
  async renderView(view, viewParams, runtimeEnvironment, runtimeParams, viewCacheKey, renderOptions) {
221
224
  const {id, contentTemplate, rootComponent, layoutTemplate} = view;
222
- const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
225
+ const lwrResourcesId = `__LWR_RESOURCES__${viewCacheKey}`;
223
226
  const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, runtimeEnvironment);
224
227
  let normalizedRenderOptions = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderedContent.options, renderOptions);
225
228
  const layout = layoutTemplate || renderedContent.compiledView.layoutTemplate;
@@ -243,6 +246,10 @@ var LwrViewRegistry = class {
243
246
  lwr_resources: lwrResourcesId
244
247
  }, runtimeParams, runtimeEnvironment);
245
248
  normalizedRenderOptions = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderedLayout.options, normalizedRenderOptions);
249
+ const warnings = [
250
+ ...renderedContent.metadata.serverDebug?.warnings || [],
251
+ ...renderedLayout.metadata.serverDebug?.warnings || []
252
+ ];
246
253
  const renderedViewDef = await this.link({
247
254
  ...renderedLayout,
248
255
  compiledView: {
@@ -262,14 +269,10 @@ var LwrViewRegistry = class {
262
269
  ...renderedContent.metadata.serverData,
263
270
  ...renderedLayout.metadata.serverData
264
271
  },
265
- serverDebug: {
266
- ...renderedContent.metadata.serverDebug,
267
- ...renderedLayout.metadata.serverDebug
268
- },
272
+ serverDebug: {warnings},
269
273
  serverBundles: renderedContent.metadata.serverBundles
270
274
  },
271
- cache: renderedContent.cache,
272
- status: renderedContent.status
275
+ cache: renderedContent.cache
273
276
  }, {
274
277
  view: {...view, layoutTemplate: layoutTemplatePath},
275
278
  viewParams,
@@ -290,7 +293,8 @@ var LwrViewRegistry = class {
290
293
  ...runtimeParams,
291
294
  ...globalContext,
292
295
  ...compiledView.properties,
293
- ...viewParams
296
+ ...viewParams,
297
+ runtimeParams: {...runtimeParams}
294
298
  }, runtimeEnvironment);
295
299
  const normalizedResult = (0, import_utils.normalizeRenderedResult)(result);
296
300
  return {
@@ -325,7 +329,17 @@ var LwrViewRegistry = class {
325
329
  ssr: view.bootstrap?.ssr === true
326
330
  }
327
331
  }, () => {
328
- const linkedMetadata2 = skipMetadataCollection ? renderedViewMetadata : (0, import_shared_utils.extractMetadataFromHtml)(renderedViewContent, renderedViewMetadata, this.globalConfig);
332
+ let linkedMetadata2 = renderedViewMetadata;
333
+ if (!skipMetadataCollection) {
334
+ const contentHash = (0, import_crypto.createHash)("md5").update(renderedViewContent).digest("hex");
335
+ const cacheKey = `${view.id}:${contentHash}`;
336
+ if (this.metadataCache.has(cacheKey)) {
337
+ linkedMetadata2 = this.metadataCache.get(cacheKey);
338
+ } else {
339
+ linkedMetadata2 = (0, import_shared_utils.extractMetadataFromHtml)(renderedViewContent, renderedViewMetadata, this.globalConfig);
340
+ this.metadataCache.set(cacheKey, linkedMetadata2);
341
+ }
342
+ }
329
343
  const stringBuilder2 = (0, import_shared_utils.createStringBuilder)(renderedViewContent);
330
344
  return {linkedMetadata: linkedMetadata2, stringBuilder: stringBuilder2};
331
345
  });
@@ -336,7 +350,6 @@ var LwrViewRegistry = class {
336
350
  importer: importer || renderedView.compiledView.filePath
337
351
  };
338
352
  let pageTtl = renderedView.cache.ttl;
339
- let pageStatus = renderedView.status;
340
353
  for (const viewTransformer of this.viewTransformers) {
341
354
  const linkResults = await (0, import_instrumentation.getTracer)().trace({
342
355
  name: import_instrumentation.ViewSpan.Transform,
@@ -346,9 +359,6 @@ var LwrViewRegistry = class {
346
359
  }, () => viewTransformer.link?.(stringBuilder, mergedViewContext, linkedMetadata));
347
360
  const ttl = linkResults && linkResults.cache?.ttl;
348
361
  pageTtl = (0, import_shared_utils.shortestTtl)(ttl || void 0, pageTtl);
349
- if (!pageStatus && linkResults) {
350
- pageStatus = linkResults.status;
351
- }
352
362
  }
353
363
  const linkedAssetContent = stringBuilder.toString();
354
364
  if (linkedAssetContent.includes(lwrResourcesId)) {
@@ -370,7 +380,8 @@ var LwrViewRegistry = class {
370
380
  return res;
371
381
  res.push({
372
382
  url: asset.src,
373
- relative: (0, import_shared_utils.isRelative)(asset.src)
383
+ relative: (0, import_shared_utils.isRelative)(asset.src),
384
+ integrity: asset.integrity
374
385
  });
375
386
  return res;
376
387
  }, []));
@@ -383,8 +394,7 @@ var LwrViewRegistry = class {
383
394
  ...viewRecord,
384
395
  serverBundles: linkedMetadata.serverBundles
385
396
  },
386
- cache: {ttl: pageTtl},
387
- status: pageStatus
397
+ cache: {ttl: pageTtl}
388
398
  };
389
399
  }
390
400
  return {
@@ -395,8 +405,7 @@ var LwrViewRegistry = class {
395
405
  moduleResources: [],
396
406
  serverBundles: linkedMetadata.serverBundles
397
407
  },
398
- cache: {ttl: pageTtl},
399
- status: pageStatus
408
+ cache: {ttl: pageTtl}
400
409
  };
401
410
  }
402
411
  };
@@ -251,7 +251,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
251
251
  }
252
252
  }
253
253
  }));
254
- if (viewContainsLiveElements || serverDebug?.message) {
254
+ if (viewContainsLiveElements || serverDebug?.warnings?.length) {
255
255
  if ((0, import_shared_utils.isLocalDev)()) {
256
256
  const localDevSpecifier = "lwr_local_dev/bootstrap";
257
257
  rootComponents.push(localDevSpecifier);
@@ -278,7 +278,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
278
278
  serverData,
279
279
  ...isAMD && {requiredModules: requiredAmdModules},
280
280
  ...isAMD && {preloadModules: viewPreloads.specifiers}
281
- }, runtimeEnvironment, runtimeParams, serverDebug?.message));
281
+ }, runtimeEnvironment, runtimeParams, serverDebug?.warnings));
282
282
  }
283
283
  if (!isAMD && hmrEnabled) {
284
284
  configResources.unshift((0, import_utils2.getViewHmrConfigurationResource)(view, viewMetadata));
@@ -31,9 +31,7 @@ var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
31
31
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
32
32
  function setPreloadModulesMeta(specifier, uri, integrity, groups, preloads) {
33
33
  if (!uri) {
34
- throw (0, import_diagnostics.createSingleDiagnosticError)({
35
- description: import_diagnostics.descriptions.UNRESOLVABLE.PRELOAD_MODULE(specifier)
36
- }, import_diagnostics.LwrUnresolvableError);
34
+ throw new import_diagnostics.LwrInvalidError(import_diagnostics.descriptions.INVALID.PRELOAD_MODULE(specifier));
37
35
  }
38
36
  const [removedVersion, version] = specifier.split("/v/");
39
37
  const normalizedSpecifier = version === import_shared_utils.VERSION_NOT_PROVIDED ? removedVersion : specifier;
@@ -29,9 +29,10 @@ __export(exports, {
29
29
  getViewBootstrapConfigurationResource: () => getViewBootstrapConfigurationResource,
30
30
  getViewHmrConfigurationResource: () => getViewHmrConfigurationResource
31
31
  });
32
+ var import_diagnostics = __toModule(require("@lwrjs/diagnostics"));
32
33
  var import_shared_utils = __toModule(require("@lwrjs/shared-utils"));
33
34
  var CONTENT_TYPE = "application/javascript";
34
- function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironment, runtimeParams, debugMessage) {
35
+ function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironment, runtimeParams, warnings) {
35
36
  const {compat, debug, hmrEnabled, apiVersion, format} = runtimeEnvironment;
36
37
  const isESM = format === "esm";
37
38
  const defaultUrl = (0, import_shared_utils.getModuleUriPrefix)(runtimeEnvironment, runtimeParams);
@@ -53,6 +54,9 @@ function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironm
53
54
  SSR: false,
54
55
  ...(0, import_shared_utils.buildEnvironmentContext)(runtimeParams)
55
56
  };
57
+ let warnMessages = `console.group('Server-side rendering warnings:');`;
58
+ warnings?.forEach((warning) => warnMessages += `console.warn('${(0, import_diagnostics.stringifyError)(warning)}');`);
59
+ warnMessages += "console.groupEnd();";
56
60
  const configString = [
57
61
  "/* This script is generated */",
58
62
  "/* Client Bootstrap configuration */",
@@ -64,7 +68,7 @@ function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironm
64
68
  `globalThis.LWR = {...globalThis.LWR, env: ${JSON.stringify(lwrEnv)}};`,
65
69
  `globalThis.process={...globalThis.process,env:{...globalThis.process?.env,...${JSON.stringify(nodeEnv)}}};`,
66
70
  `globalThis.lwcRuntimeFlags = { ENABLE_MIXED_SHADOW_MODE: ${viewInfo.mixedMode}, ENABLE_WIRE_SYNC_EMIT: ${viewInfo.ssr} };`,
67
- debug && debugMessage && `console.error(${JSON.stringify(debugMessage)});`
71
+ warnings?.length && warnMessages
68
72
  ].filter(Boolean).join("\n");
69
73
  if (viewInfo.configAsSrc) {
70
74
  const viewUrl = viewInfo.url || "/";
@@ -234,7 +234,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
234
234
  importMetadata = await (0, import_shared_utils.toImportMetadata)(graph, importMetadata, moduleRegistry, runtimeEnvironment, runtimeParams);
235
235
  }
236
236
  }
237
- if (viewContainsLiveElements || serverDebug?.message) {
237
+ if (viewContainsLiveElements || serverDebug?.warnings?.length) {
238
238
  configResources.unshift((0, import_utils2.getViewBootstrapConfigurationResource)({
239
239
  id: view.id,
240
240
  url: viewParams?.page?.url,
@@ -252,7 +252,7 @@ async function getHtmlResources(view, viewParams, resourceContext) {
252
252
  serverData,
253
253
  ...isAMD && {requiredModules: requiredAmdModules},
254
254
  ...isAMD && {preloadModules: viewPreloads.specifiers}
255
- }, runtimeEnvironment, runtimeParams, serverDebug?.message));
255
+ }, runtimeEnvironment, runtimeParams, serverDebug?.warnings));
256
256
  }
257
257
  if (!isAMD && hmrEnabled) {
258
258
  configResources.unshift((0, import_utils2.getViewHmrConfigurationResource)(view, viewMetadata));
@@ -112,8 +112,7 @@ function normalizeRenderedResult({
112
112
  renderedView,
113
113
  metadata,
114
114
  options,
115
- cache,
116
- status
115
+ cache
117
116
  }) {
118
117
  return {
119
118
  renderedView,
@@ -127,16 +126,16 @@ function normalizeRenderedResult({
127
126
  options: {
128
127
  skipMetadataCollection: options ? options.skipMetadataCollection : false
129
128
  },
130
- cache: cache || {},
131
- status
129
+ cache: cache || {}
132
130
  };
133
131
  }
134
132
  function reduceSourceAssetReferences(assets) {
135
- return assets.map(({url, tagName, override}) => {
133
+ return assets.map(({url, tagName, override, integrity}) => {
136
134
  return {
137
135
  url,
138
136
  tagName,
139
- override
137
+ override,
138
+ integrity
140
139
  };
141
140
  });
142
141
  }
@@ -314,7 +313,7 @@ function generateLinkHeaders(assets, patterns) {
314
313
  for (const filePattern of matchPatterns) {
315
314
  const regex = new RegExp(filePattern);
316
315
  if (regex.test(path)) {
317
- matched = pattern.attributes;
316
+ matched = pattern.attributes || {};
318
317
  break;
319
318
  }
320
319
  }
@@ -323,6 +322,9 @@ function generateLinkHeaders(assets, patterns) {
323
322
  }
324
323
  if (matched) {
325
324
  assetConfig[path] = matched;
325
+ if (assetRef.integrity) {
326
+ assetConfig[path].integrity = assetRef.integrity;
327
+ }
326
328
  }
327
329
  }
328
330
  return Object.keys(assetConfig).reduce((linkHeader, path) => {
@@ -147,11 +147,11 @@ var LwrViewHandler = class {
147
147
  try {
148
148
  return await routeHandlerFn({...viewRequest, locale, basePath, assetBasePath, uiBasePath}, {route, viewApi, ...paths}, routeHandlerOptions);
149
149
  } catch (err) {
150
- if (err instanceof import_diagnostics.DiagnosticsError) {
150
+ if (err instanceof import_diagnostics.LwrError) {
151
151
  throw err;
152
152
  }
153
153
  const message = err instanceof Error ? err.message : String(err);
154
- throw (0, import_diagnostics.createSingleDiagnosticError)({description: import_diagnostics.descriptions.APPLICATION.ROUTE_HANDLER_ERROR(route.id, message)}, import_diagnostics.LwrApplicationError);
154
+ throw new import_diagnostics.LwrApplicationError(import_diagnostics.descriptions.APPLICATION.ROUTE_HANDLER_ERROR(route.id, message));
155
155
  }
156
156
  });
157
157
  if (response?.locale) {
@@ -47,6 +47,7 @@ export declare class LwrViewRegistry implements ViewRegistry {
47
47
  getViewDefinition(view: View, viewParams: ViewParams, runtimeEnvironment: RuntimeEnvironment, runtimeParams?: RuntimeParams, renderOptions?: RenderOptions): Promise<LinkedViewDefinition>;
48
48
  private renderView;
49
49
  private render;
50
+ private metadataCache;
50
51
  private link;
51
52
  }
52
53
  //# sourceMappingURL=index.d.ts.map
package/build/es/index.js CHANGED
@@ -4,7 +4,8 @@ import { generateViewNonce, getViewNonce, normalizeRenderOptions, normalizeRende
4
4
  import { linkLwrResources } from './linkers/link-lwr-resources.js';
5
5
  // TODO: investigate perf impact W-16056356
6
6
  import { LRUCache } from 'lru-cache';
7
- import { DiagnosticsError, LwrServerError, createSingleDiagnosticError, descriptions, logger, } from '@lwrjs/diagnostics';
7
+ import { createHash } from 'crypto';
8
+ import { LwrError, LwrServerError, descriptions, logger } from '@lwrjs/diagnostics';
8
9
  export { LwrViewHandler } from './view-handler.js';
9
10
  export class LwrViewRegistry {
10
11
  constructor(context, globalConfig) {
@@ -19,6 +20,7 @@ export class LwrViewRegistry {
19
20
  // Pending view definitions are tracked to prevent concurrent resolution of the same view.
20
21
  // Subsequent requests for the same view will await the original promise.
21
22
  this.pendingViewDefinitions = new InflightTasks();
23
+ this.metadataCache = new Map();
22
24
  this.name = 'lwr-view-registry';
23
25
  this.resourceRegistry = context.resourceRegistry;
24
26
  this.runtimeEnvironment = context.runtimeEnvironment;
@@ -194,13 +196,16 @@ export class LwrViewRegistry {
194
196
  const updatableViewParams = { ...viewParams };
195
197
  generateViewNonce(updatableViewParams);
196
198
  const pendingViewDefCacheKey = viewDefCacheKey + viewParamKey;
197
- const viewDefinition = await this.pendingViewDefinitions.execute(pendingViewDefCacheKey, () => getTracer().trace({
199
+ const pendingViewDefCacheKeyHashed = createHash('md5')
200
+ .update(pendingViewDefCacheKey)
201
+ .digest('hex');
202
+ const viewDefinition = await this.pendingViewDefinitions.execute(pendingViewDefCacheKeyHashed, () => getTracer().trace({
198
203
  name: ViewSpan.RenderView,
199
204
  attributes: {
200
205
  view: view.id,
201
206
  ssr: view.bootstrap?.ssr === true,
202
207
  },
203
- }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, pendingViewDefCacheKey, renderOptions)));
208
+ }, () => this.renderView(view, updatableViewParams, runtimeEnvironment, runtimeParams, pendingViewDefCacheKeyHashed, renderOptions)));
204
209
  // Once the view is generated add the nonce to the response so it can be cached and then added to the headers
205
210
  viewDefinition.nonce = getViewNonce(updatableViewParams);
206
211
  const route = this.globalConfig.routes.find((r) => r.id === view.id);
@@ -225,18 +230,18 @@ export class LwrViewRegistry {
225
230
  return viewDefinition;
226
231
  }
227
232
  catch (err) {
228
- if (err instanceof DiagnosticsError) {
233
+ if (err instanceof LwrError) {
229
234
  throw err;
230
235
  }
231
236
  logger.error(`Failed to get view definition "${view.id}"`);
232
237
  logger.error(err);
233
238
  const message = err instanceof Error ? err.message : String(err);
234
- throw createSingleDiagnosticError({ description: descriptions.SERVER.UNEXPECTED_ERROR(message) }, LwrServerError);
239
+ throw new LwrServerError(descriptions.SERVER.UNEXPECTED_ERROR(message));
235
240
  }
236
241
  }
237
242
  async renderView(view, viewParams, runtimeEnvironment, runtimeParams, viewCacheKey, renderOptions) {
238
243
  const { id, contentTemplate, rootComponent, layoutTemplate } = view;
239
- const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
244
+ const lwrResourcesId = `__LWR_RESOURCES__${viewCacheKey}`;
240
245
  const renderedContent = await this.render({ id, contentTemplate, rootComponent }, { ...viewParams, lwr_resources: lwrResourcesId }, runtimeParams, runtimeEnvironment);
241
246
  // normalize the renderOptions provided by the CompiledView content with the request options.
242
247
  let normalizedRenderOptions = normalizeRenderOptions(this.runtimeEnvironment, renderedContent.options, renderOptions);
@@ -264,6 +269,10 @@ export class LwrViewRegistry {
264
269
  lwr_resources: lwrResourcesId,
265
270
  }, runtimeParams, runtimeEnvironment);
266
271
  normalizedRenderOptions = normalizeRenderOptions(this.runtimeEnvironment, renderedLayout.options, normalizedRenderOptions);
272
+ const warnings = [
273
+ ...(renderedContent.metadata.serverDebug?.warnings || []),
274
+ ...(renderedLayout.metadata.serverDebug?.warnings || []),
275
+ ];
267
276
  const renderedViewDef = await this.link({
268
277
  ...renderedLayout,
269
278
  // Rendered Layout view's immutability is a composite of the layouts mutability and the body's mutability
@@ -285,14 +294,10 @@ export class LwrViewRegistry {
285
294
  ...renderedContent.metadata.serverData,
286
295
  ...renderedLayout.metadata.serverData,
287
296
  },
288
- serverDebug: {
289
- ...renderedContent.metadata.serverDebug,
290
- ...renderedLayout.metadata.serverDebug,
291
- },
297
+ serverDebug: { warnings },
292
298
  serverBundles: renderedContent.metadata.serverBundles, // 1st pass of SSR
293
299
  },
294
300
  cache: renderedContent.cache,
295
- status: renderedContent.status,
296
301
  },
297
302
  // Render Content now contains a layout
298
303
  {
@@ -328,6 +333,8 @@ export class LwrViewRegistry {
328
333
  ...globalContext,
329
334
  ...compiledView.properties,
330
335
  ...viewParams,
336
+ // provides access to runtimeParams which are not overwritten by context/props/viewParams
337
+ runtimeParams: { ...runtimeParams },
331
338
  }, runtimeEnvironment);
332
339
  const normalizedResult = normalizeRenderedResult(result);
333
340
  return {
@@ -350,9 +357,19 @@ export class LwrViewRegistry {
350
357
  ssr: view.bootstrap?.ssr === true,
351
358
  },
352
359
  }, () => {
353
- const linkedMetadata = skipMetadataCollection
354
- ? renderedViewMetadata
355
- : extractMetadataFromHtml(renderedViewContent, renderedViewMetadata, this.globalConfig);
360
+ let linkedMetadata = renderedViewMetadata;
361
+ if (!skipMetadataCollection) {
362
+ // Create a unique key based on view ID and content hash
363
+ const contentHash = createHash('md5').update(renderedViewContent).digest('hex');
364
+ const cacheKey = `${view.id}:${contentHash}`;
365
+ if (this.metadataCache.has(cacheKey)) {
366
+ linkedMetadata = this.metadataCache.get(cacheKey);
367
+ }
368
+ else {
369
+ linkedMetadata = extractMetadataFromHtml(renderedViewContent, renderedViewMetadata, this.globalConfig);
370
+ this.metadataCache.set(cacheKey, linkedMetadata);
371
+ }
372
+ }
356
373
  const stringBuilder = createStringBuilder(renderedViewContent);
357
374
  return { linkedMetadata, stringBuilder };
358
375
  });
@@ -364,7 +381,6 @@ export class LwrViewRegistry {
364
381
  };
365
382
  // Note: this is the TTL for the page NOT for the view registry cache
366
383
  let pageTtl = renderedView.cache.ttl;
367
- let pageStatus = renderedView.status;
368
384
  for (const viewTransformer of this.viewTransformers) {
369
385
  // eslint-disable-next-line no-await-in-loop
370
386
  const linkResults = await getTracer().trace({
@@ -376,10 +392,6 @@ export class LwrViewRegistry {
376
392
  // Keep track of the shortest TTL from each view transformer (e.g. lwcSsrViewTransformer)
377
393
  const ttl = linkResults && linkResults.cache?.ttl;
378
394
  pageTtl = shortestTtl(ttl || undefined, pageTtl);
379
- // Set the status info, if it doesn't already exist
380
- if (!pageStatus && linkResults) {
381
- pageStatus = linkResults.status;
382
- }
383
395
  }
384
396
  const linkedAssetContent = stringBuilder.toString();
385
397
  // Link LWR related resources
@@ -406,6 +418,7 @@ export class LwrViewRegistry {
406
418
  res.push({
407
419
  url: asset.src,
408
420
  relative: isRelative(asset.src),
421
+ integrity: asset.integrity,
409
422
  });
410
423
  return res;
411
424
  }, []));
@@ -420,7 +433,6 @@ export class LwrViewRegistry {
420
433
  serverBundles: linkedMetadata.serverBundles,
421
434
  },
422
435
  cache: { ttl: pageTtl },
423
- status: pageStatus,
424
436
  };
425
437
  }
426
438
  return {
@@ -432,7 +444,6 @@ export class LwrViewRegistry {
432
444
  serverBundles: linkedMetadata.serverBundles,
433
445
  },
434
446
  cache: { ttl: pageTtl },
435
- status: pageStatus,
436
447
  };
437
448
  }
438
449
  }
@@ -290,7 +290,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
290
290
  }
291
291
  }
292
292
  }));
293
- if (viewContainsLiveElements || serverDebug?.message) {
293
+ if (viewContainsLiveElements || serverDebug?.warnings?.length) {
294
294
  if (isLocalDev()) {
295
295
  // ADD the client-side bootstrap module and mapping for local-dev
296
296
  const localDevSpecifier = 'lwr_local_dev/bootstrap';
@@ -320,7 +320,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
320
320
  ...(isAMD && { requiredModules: requiredAmdModules }),
321
321
  // in AMD we need to tell the loader what modules we are preloading
322
322
  ...(isAMD && { preloadModules: viewPreloads.specifiers }),
323
- }, runtimeEnvironment, runtimeParams, serverDebug?.message));
323
+ }, runtimeEnvironment, runtimeParams, serverDebug?.warnings));
324
324
  }
325
325
  if (!isAMD && hmrEnabled) {
326
326
  configResources.unshift(getViewHmrConfigurationResource(view, viewMetadata));
@@ -1,4 +1,4 @@
1
- import { LwrUnresolvableError, createSingleDiagnosticError, descriptions, logger } from '@lwrjs/diagnostics';
1
+ import { LwrInvalidError, descriptions, logger } from '@lwrjs/diagnostics';
2
2
  import { explodeSpecifier, getGroupName, getVersionedModuleId, normalizeVersionToUri, VERSION_NOT_PROVIDED, getSpecifier, isGroupie, } from '@lwrjs/shared-utils';
3
3
  /**
4
4
  * keeps track of preloadModules metadata
@@ -6,9 +6,7 @@ import { explodeSpecifier, getGroupName, getVersionedModuleId, normalizeVersionT
6
6
  export function setPreloadModulesMeta(specifier, uri, integrity, groups, preloads) {
7
7
  // Throw a very specific error if we get this back and the uri property is not set
8
8
  if (!uri) {
9
- throw createSingleDiagnosticError({
10
- description: descriptions.UNRESOLVABLE.PRELOAD_MODULE(specifier),
11
- }, LwrUnresolvableError);
9
+ throw new LwrInvalidError(descriptions.INVALID.PRELOAD_MODULE(specifier));
12
10
  }
13
11
  // We need to support version-less preloadModules, including version-less rootComponents.
14
12
  // Removing the "/v/version_not_provided" hack from the preloadModules specifier
@@ -1,5 +1,5 @@
1
1
  import type { ClientBootstrapConfig, RuntimeEnvironment, RuntimeParams, ResourceDefinition, RenderedViewMetadata, CustomElementReference, View, ViewInfo, FlattenedModuleGraphs } from '@lwrjs/types';
2
- export declare function getViewBootstrapConfigurationResource(viewInfo: ViewInfo, config: ClientBootstrapConfig, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, debugMessage?: string): ResourceDefinition;
2
+ export declare function getViewBootstrapConfigurationResource(viewInfo: ViewInfo, config: ClientBootstrapConfig, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, warnings?: (Error | string)[]): ResourceDefinition;
3
3
  export declare function getViewHmrConfigurationResource(view: View, viewMetadata: RenderedViewMetadata): ResourceDefinition;
4
4
  export declare function flattenCustomElements(arr: CustomElementReference[], isSSR?: boolean): CustomElementReference[];
5
5
  export declare function getBundleIntegrity(bootstrapModuleGraph: FlattenedModuleGraphs, versionedSpecifier: string): string | undefined;
@@ -1,6 +1,7 @@
1
+ import { stringifyError } from '@lwrjs/diagnostics';
1
2
  import { buildEnvironmentContext, getMappingUriPrefix, getModuleUriPrefix, getClientBootstrapConfigurationUri, hashContent, } from '@lwrjs/shared-utils';
2
3
  const CONTENT_TYPE = 'application/javascript';
3
- export function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironment, runtimeParams, debugMessage) {
4
+ export function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironment, runtimeParams, warnings) {
4
5
  const { compat, debug, hmrEnabled, apiVersion, format } = runtimeEnvironment;
5
6
  const isESM = format === 'esm';
6
7
  const defaultUrl = getModuleUriPrefix(runtimeEnvironment, runtimeParams);
@@ -27,6 +28,9 @@ export function getViewBootstrapConfigurationResource(viewInfo, config, runtimeE
27
28
  // Used by `lwr/environment`
28
29
  ...buildEnvironmentContext(runtimeParams),
29
30
  };
31
+ let warnMessages = `console.group('Server-side rendering warnings:');`;
32
+ warnings?.forEach((warning) => (warnMessages += `console.warn('${stringifyError(warning)}');`));
33
+ warnMessages += 'console.groupEnd();';
30
34
  const configString = [
31
35
  '/* This script is generated */',
32
36
  '/* Client Bootstrap configuration */',
@@ -39,7 +43,7 @@ export function getViewBootstrapConfigurationResource(viewInfo, config, runtimeE
39
43
  `globalThis.process={...globalThis.process,env:{...globalThis.process?.env,...${JSON.stringify(nodeEnv)}}};`,
40
44
  // TODO: evaluate moving these to app layer
41
45
  `globalThis.lwcRuntimeFlags = { ENABLE_MIXED_SHADOW_MODE: ${viewInfo.mixedMode}, ENABLE_WIRE_SYNC_EMIT: ${viewInfo.ssr} };`,
42
- debug && debugMessage && `console.error(${JSON.stringify(debugMessage)});`,
46
+ warnings?.length && warnMessages,
43
47
  ]
44
48
  .filter(Boolean)
45
49
  .join('\n');
@@ -262,7 +262,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
262
262
  importMetadata = await toImportMetadata(graph, importMetadata, moduleRegistry, runtimeEnvironment, runtimeParams);
263
263
  }
264
264
  }
265
- if (viewContainsLiveElements || serverDebug?.message) {
265
+ if (viewContainsLiveElements || serverDebug?.warnings?.length) {
266
266
  // ADD configuration of the bootstrapModule
267
267
  configResources.unshift(getViewBootstrapConfigurationResource({
268
268
  id: view.id,
@@ -282,7 +282,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
282
282
  ...(isAMD && { requiredModules: requiredAmdModules }),
283
283
  // in AMD we need to tell the loader what modules we are preloading
284
284
  ...(isAMD && { preloadModules: viewPreloads.specifiers }),
285
- }, runtimeEnvironment, runtimeParams, serverDebug?.message));
285
+ }, runtimeEnvironment, runtimeParams, serverDebug?.warnings));
286
286
  }
287
287
  if (!isAMD && hmrEnabled) {
288
288
  configResources.unshift(getViewHmrConfigurationResource(view, viewMetadata));
@@ -1,7 +1,7 @@
1
1
  import type { AssetReference, JsonCompatible, LinkedViewDefinition, LwrErrorRoute, LwrRoute, ModuleBundler, ModuleId, ModuleJson, ModuleRegistry, NormalizedRenderingResult, PublicModuleRegistry, RenderOptions, RenderedAssetReference, RenderingResult, ResourceDefinition, RouteHandlerViewResponse, RuntimeEnvironment, RuntimeParams, ViewModuleResourceContext, ViewPageContext, ViewParams, ViewRequest, ViewResponse, ViewDefinitionResponse, View } from '@lwrjs/types';
2
2
  export type HTMLResource = Partial<ResourceDefinition>;
3
3
  export declare function generateHtmlTag(definition: HTMLResource): Promise<string>;
4
- export declare function normalizeRenderedResult({ renderedView, metadata, options, cache, status, }: RenderingResult): NormalizedRenderingResult;
4
+ export declare function normalizeRenderedResult({ renderedView, metadata, options, cache, }: RenderingResult): NormalizedRenderingResult;
5
5
  export declare function reduceSourceAssetReferences(assets: AssetReference[]): RenderedAssetReference[];
6
6
  export declare function normalizeRenderOptions(runtimeEnvironment: RuntimeEnvironment, overrideRenderOptions?: RenderOptions, baseRenderOptions?: RenderOptions): Required<RenderOptions>;
7
7
  export declare function generatePageContext({ requestPath: url }: ViewRequest, { id, contentTemplate, properties }: LwrRoute | LwrErrorRoute, runtimeParams: RuntimeParams): JsonCompatible<ViewPageContext>;
package/build/es/utils.js CHANGED
@@ -66,7 +66,7 @@ export async function generateHtmlTag(definition) {
66
66
  }
67
67
  throw new Error(`Invalid external Resource Definition: missing a "src": "${definition.specifier}"`);
68
68
  }
69
- export function normalizeRenderedResult({ renderedView, metadata, options, cache, status, }) {
69
+ export function normalizeRenderedResult({ renderedView, metadata, options, cache, }) {
70
70
  return {
71
71
  renderedView,
72
72
  metadata: {
@@ -80,15 +80,15 @@ export function normalizeRenderedResult({ renderedView, metadata, options, cache
80
80
  skipMetadataCollection: options ? options.skipMetadataCollection : false,
81
81
  },
82
82
  cache: cache || {},
83
- status,
84
83
  };
85
84
  }
86
85
  export function reduceSourceAssetReferences(assets) {
87
- return assets.map(({ url, tagName, override }) => {
86
+ return assets.map(({ url, tagName, override, integrity }) => {
88
87
  return {
89
88
  url,
90
89
  tagName,
91
90
  override,
91
+ integrity,
92
92
  };
93
93
  });
94
94
  }
@@ -300,7 +300,7 @@ export function generateLinkHeaders(assets, patterns) {
300
300
  for (const filePattern of matchPatterns) {
301
301
  const regex = new RegExp(filePattern);
302
302
  if (regex.test(path)) {
303
- matched = pattern.attributes;
303
+ matched = pattern.attributes || {};
304
304
  break;
305
305
  }
306
306
  }
@@ -309,6 +309,10 @@ export function generateLinkHeaders(assets, patterns) {
309
309
  }
310
310
  if (matched) {
311
311
  assetConfig[path] = matched;
312
+ if (assetRef.integrity) {
313
+ // always add the integrity attribute, if it exists
314
+ assetConfig[path].integrity = assetRef.integrity;
315
+ }
312
316
  }
313
317
  }
314
318
  // Use the assetConfig to construct the Link Header
@@ -2,7 +2,7 @@ import { resolve } from 'path';
2
2
  import { normalizeResourcePath, shortestTtl } from '@lwrjs/shared-utils';
3
3
  import { getTracer, ViewSpan } from '@lwrjs/instrumentation';
4
4
  import { generateHtmlTag, generateLinkHeaders, generatePageContext, isViewResponse, isViewDefinitionResponse, toJsonFormat, } from './utils.js';
5
- import { DiagnosticsError, LwrApplicationError, createSingleDiagnosticError, descriptions, } from '@lwrjs/diagnostics';
5
+ import { LwrApplicationError, LwrError, descriptions } from '@lwrjs/diagnostics';
6
6
  export class LwrViewHandler {
7
7
  constructor(context, globalConfig) {
8
8
  this.globalConfig = globalConfig;
@@ -166,11 +166,11 @@ export class LwrViewHandler {
166
166
  return await routeHandlerFn({ ...viewRequest, locale, basePath, assetBasePath, uiBasePath }, { route: route, viewApi, ...paths }, routeHandlerOptions);
167
167
  }
168
168
  catch (err) {
169
- if (err instanceof DiagnosticsError) {
169
+ if (err instanceof LwrError) {
170
170
  throw err;
171
171
  }
172
172
  const message = err instanceof Error ? err.message : String(err);
173
- throw createSingleDiagnosticError({ description: descriptions.APPLICATION.ROUTE_HANDLER_ERROR(route.id, message) }, LwrApplicationError);
173
+ throw new LwrApplicationError(descriptions.APPLICATION.ROUTE_HANDLER_ERROR(route.id, message));
174
174
  }
175
175
  });
176
176
  // if the locale was returned by the route handler update the runtime time params
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.17.2-alpha.0",
7
+ "version": "0.17.2-alpha.10",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -33,14 +33,14 @@
33
33
  "build": "tsc -b"
34
34
  },
35
35
  "dependencies": {
36
- "@lwrjs/app-service": "0.17.2-alpha.0",
37
- "@lwrjs/diagnostics": "0.17.2-alpha.0",
38
- "@lwrjs/instrumentation": "0.17.2-alpha.0",
39
- "@lwrjs/shared-utils": "0.17.2-alpha.0",
36
+ "@lwrjs/app-service": "0.17.2-alpha.10",
37
+ "@lwrjs/diagnostics": "0.17.2-alpha.10",
38
+ "@lwrjs/instrumentation": "0.17.2-alpha.10",
39
+ "@lwrjs/shared-utils": "0.17.2-alpha.10",
40
40
  "lru-cache": "^10.4.3"
41
41
  },
42
42
  "devDependencies": {
43
- "@lwrjs/types": "0.17.2-alpha.0"
43
+ "@lwrjs/types": "0.17.2-alpha.10"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=18.0.0"
@@ -48,5 +48,5 @@
48
48
  "volta": {
49
49
  "extends": "../../../package.json"
50
50
  },
51
- "gitHead": "128e2d6e17d05fb780523b60c2717ef8ebfd9b57"
51
+ "gitHead": "3938f97f31973d7953bb61cff8388c71980c22c7"
52
52
  }