@lwrjs/view-registry 0.15.0-alpha.2 → 0.15.0-alpha.20
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/build/cjs/index.cjs +25 -15
- package/build/cjs/linkers/legacy_view_bootstrap.cjs +2 -1
- package/build/cjs/linkers/link-lwr-resources.cjs +1 -1
- package/build/cjs/linkers/utils.cjs +1 -1
- package/build/cjs/linkers/view_bootstrap.cjs +2 -1
- package/build/cjs/utils.cjs +29 -2
- package/build/cjs/view-handler.cjs +18 -1
- package/build/es/index.js +28 -12
- package/build/es/linkers/legacy_view_bootstrap.js +1 -0
- package/build/es/linkers/link-lwr-resources.js +3 -1
- package/build/es/linkers/utils.js +2 -1
- package/build/es/linkers/view_bootstrap.js +1 -0
- package/build/es/utils.d.ts +4 -2
- package/build/es/utils.js +31 -1
- package/build/es/view-handler.js +25 -1
- package/package.json +7 -7
package/build/cjs/index.cjs
CHANGED
|
@@ -51,7 +51,12 @@ var LwrViewRegistry = class {
|
|
|
51
51
|
this.globalData = context.globalData;
|
|
52
52
|
this.appEmitter = context.appEmitter;
|
|
53
53
|
const observer = context.appObserver;
|
|
54
|
-
this.viewDefinitions = new import_lru_cache.LRUCache({
|
|
54
|
+
this.viewDefinitions = new import_lru_cache.LRUCache({
|
|
55
|
+
max: parseInt(process.env.VIEW_CACHE_SIZE ?? "500", 10),
|
|
56
|
+
dispose: (_value, key) => {
|
|
57
|
+
import_diagnostics.logger.verbose(`View evicted from cache ${key}`);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
55
60
|
observer.onViewSourceChange(({payload}) => this.onViewSourceChange(payload));
|
|
56
61
|
observer.onModuleDefinitionChange(({payload}) => this.onModuleDefinitionChange(payload));
|
|
57
62
|
observer.onAssetSourceChange(({payload}) => this.onAssetSourceChange(payload));
|
|
@@ -173,7 +178,8 @@ var LwrViewRegistry = class {
|
|
|
173
178
|
}
|
|
174
179
|
return false;
|
|
175
180
|
}
|
|
176
|
-
async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
181
|
+
async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams = {requestCache: {}}, renderOptions) {
|
|
182
|
+
runtimeParams.requestCache = runtimeParams.requestCache ?? {};
|
|
177
183
|
try {
|
|
178
184
|
const {freezeAssets, viewParamCacheKey} = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderOptions);
|
|
179
185
|
const viewDefCacheKey = (0, import_shared_utils.getCacheKeyFromJson)({
|
|
@@ -187,8 +193,8 @@ var LwrViewRegistry = class {
|
|
|
187
193
|
import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewDefCacheKey=${viewDefCacheKey}`);
|
|
188
194
|
const viewParamKey = viewParamCacheKey ? (0, import_shared_utils.getCacheKeyFromJson)(viewParamCacheKey) : (0, import_shared_utils.getCacheKeyFromJson)(viewParams);
|
|
189
195
|
import_diagnostics.logger.debug(`[view-registry][getViewDefinition] viewParamKey=${viewParamKey}`);
|
|
190
|
-
if (this.viewDefinitions.has(viewDefCacheKey)) {
|
|
191
|
-
const viewDefinition2 = this.viewDefinitions.get(viewDefCacheKey);
|
|
196
|
+
if (this.viewDefinitions.has(viewDefCacheKey) || runtimeParams.requestCache[viewDefCacheKey]) {
|
|
197
|
+
const viewDefinition2 = this.viewDefinitions.get(viewDefCacheKey) || runtimeParams.requestCache[viewDefCacheKey];
|
|
192
198
|
if (viewDefinition2 && viewDefinition2.paramKey === viewParamKey && viewDefinition2.viewDefinition.immutable) {
|
|
193
199
|
return viewDefinition2.viewDefinition;
|
|
194
200
|
}
|
|
@@ -209,11 +215,12 @@ var LwrViewRegistry = class {
|
|
|
209
215
|
const maxTtl = maxViewCacheTtl && parseInt(maxViewCacheTtl, 10);
|
|
210
216
|
const leastTtl = (0, import_shared_utils.shortestTtl)(viewDefinition.cache?.ttl, route?.cache?.ttl, maxTtl);
|
|
211
217
|
const ttl = leastTtl !== void 0 ? leastTtl === 0 ? 10 : leastTtl * 1e3 : void 0;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
218
|
+
const viewDefCacheEntry = {view, viewDefinition, paramKey: viewParamKey};
|
|
219
|
+
if (view.bootstrap?.includeCookiesForSSR) {
|
|
220
|
+
runtimeParams.requestCache[viewDefCacheKey] = viewDefCacheEntry;
|
|
221
|
+
} else {
|
|
222
|
+
this.viewDefinitions.set(viewDefCacheKey, viewDefCacheEntry, {ttl});
|
|
223
|
+
}
|
|
217
224
|
return viewDefinition;
|
|
218
225
|
} catch (err) {
|
|
219
226
|
if (err instanceof import_diagnostics.DiagnosticsError) {
|
|
@@ -225,10 +232,10 @@ var LwrViewRegistry = class {
|
|
|
225
232
|
throw (0, import_diagnostics.createSingleDiagnosticError)({description: import_diagnostics.descriptions.SERVER.UNEXPECTED_ERROR(message)}, import_diagnostics.LwrServerError);
|
|
226
233
|
}
|
|
227
234
|
}
|
|
228
|
-
async renderView(view, viewParams, runtimeEnvironment, runtimeParams
|
|
235
|
+
async renderView(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
229
236
|
const {id, contentTemplate, rootComponent, layoutTemplate} = view;
|
|
230
237
|
const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
|
|
231
|
-
const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, runtimeEnvironment
|
|
238
|
+
const renderedContent = await this.render({id, contentTemplate, rootComponent}, {...viewParams, lwr_resources: lwrResourcesId}, runtimeParams, runtimeEnvironment);
|
|
232
239
|
let normalizedRenderOptions = (0, import_utils.normalizeRenderOptions)(this.runtimeEnvironment, renderedContent.options, renderOptions);
|
|
233
240
|
const layout = layoutTemplate || renderedContent.compiledView.layoutTemplate;
|
|
234
241
|
if (!layout) {
|
|
@@ -274,7 +281,8 @@ var LwrViewRegistry = class {
|
|
|
274
281
|
...renderedLayout.metadata.serverDebug
|
|
275
282
|
}
|
|
276
283
|
},
|
|
277
|
-
cache: renderedContent.cache
|
|
284
|
+
cache: renderedContent.cache,
|
|
285
|
+
redirect: renderedContent.redirect
|
|
278
286
|
}, {
|
|
279
287
|
view: {...view, layoutTemplate: layoutTemplatePath},
|
|
280
288
|
viewParams,
|
|
@@ -286,7 +294,7 @@ var LwrViewRegistry = class {
|
|
|
286
294
|
});
|
|
287
295
|
return renderedViewDef;
|
|
288
296
|
}
|
|
289
|
-
async render(viewId, viewParams, runtimeParams, runtimeEnvironment
|
|
297
|
+
async render(viewId, viewParams, runtimeParams, runtimeEnvironment) {
|
|
290
298
|
const globalContext = this.globalData;
|
|
291
299
|
const {id, rootComponent, contentTemplate} = viewId;
|
|
292
300
|
const compiledView = await this.getView({id, contentTemplate, rootComponent});
|
|
@@ -354,7 +362,8 @@ var LwrViewRegistry = class {
|
|
|
354
362
|
assetReferences: (0, import_utils.reduceSourceAssetReferences)(linkedMetadata.assetReferences),
|
|
355
363
|
...viewRecord
|
|
356
364
|
},
|
|
357
|
-
cache: {ttl: pageTtl}
|
|
365
|
+
cache: {ttl: pageTtl},
|
|
366
|
+
redirect: renderedView.redirect
|
|
358
367
|
};
|
|
359
368
|
}
|
|
360
369
|
return {
|
|
@@ -364,7 +373,8 @@ var LwrViewRegistry = class {
|
|
|
364
373
|
assetReferences: (0, import_utils.reduceSourceAssetReferences)(linkedMetadata.assetReferences),
|
|
365
374
|
moduleResources: []
|
|
366
375
|
},
|
|
367
|
-
cache: {ttl: pageTtl}
|
|
376
|
+
cache: {ttl: pageTtl},
|
|
377
|
+
redirect: renderedView.redirect
|
|
368
378
|
};
|
|
369
379
|
}
|
|
370
380
|
};
|
|
@@ -231,7 +231,8 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
231
231
|
url: viewParams?.page?.url,
|
|
232
232
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
233
233
|
mixedMode: view.bootstrap?.mixedMode || false,
|
|
234
|
-
nonce: viewParams?.page?.nonce
|
|
234
|
+
nonce: viewParams?.page?.nonce,
|
|
235
|
+
ssr: view.bootstrap?.ssr || false
|
|
235
236
|
}, {
|
|
236
237
|
appId: appIdentity.appName,
|
|
237
238
|
bootstrapModule: versionedSpecifier,
|
|
@@ -41,7 +41,7 @@ async function linkLwrResources(source, view, viewParams, cxt) {
|
|
|
41
41
|
return LEGACY_LOADER ? (0, import_legacy_view_bootstrap.getHtmlResources)(view, viewParams, resourceContext) : (0, import_view_bootstrap.getHtmlResources)(view, viewParams, resourceContext);
|
|
42
42
|
});
|
|
43
43
|
return {
|
|
44
|
-
renderedView: source.replace(lwrResourcesId, partial),
|
|
44
|
+
renderedView: source.replace(lwrResourcesId, () => partial),
|
|
45
45
|
viewRecord
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -63,7 +63,7 @@ function getViewBootstrapConfigurationResource(viewInfo, config, runtimeEnvironm
|
|
|
63
63
|
})});`,
|
|
64
64
|
`globalThis.LWR = {...globalThis.LWR, env: ${JSON.stringify(lwrEnv)}};`,
|
|
65
65
|
`globalThis.process={...globalThis.process,env:{...globalThis.process?.env,...${JSON.stringify(nodeEnv)}}};`,
|
|
66
|
-
`globalThis.lwcRuntimeFlags = { ENABLE_MIXED_SHADOW_MODE: ${viewInfo.mixedMode} };`,
|
|
66
|
+
`globalThis.lwcRuntimeFlags = { ENABLE_MIXED_SHADOW_MODE: ${viewInfo.mixedMode}, ENABLE_WIRE_SYNC_EMIT: ${viewInfo.ssr} };`,
|
|
67
67
|
debug && debugMessage && `console.error(${JSON.stringify(debugMessage)});`
|
|
68
68
|
].filter(Boolean).join("\n");
|
|
69
69
|
if (viewInfo.configAsSrc) {
|
|
@@ -206,7 +206,8 @@ async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
206
206
|
url: viewParams?.page?.url,
|
|
207
207
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
208
208
|
mixedMode: view.bootstrap?.mixedMode || false,
|
|
209
|
-
nonce: viewParams?.page?.nonce
|
|
209
|
+
nonce: viewParams?.page?.nonce,
|
|
210
|
+
ssr: view.bootstrap?.ssr || false
|
|
210
211
|
}, {
|
|
211
212
|
appId: appIdentity.appName,
|
|
212
213
|
bootstrapModule: versionedSpecifier,
|
package/build/cjs/utils.cjs
CHANGED
|
@@ -27,11 +27,13 @@ __export(exports, {
|
|
|
27
27
|
addExternalScriptNonce: () => addExternalScriptNonce,
|
|
28
28
|
createJsonModule: () => createJsonModule,
|
|
29
29
|
generateHtmlTag: () => generateHtmlTag,
|
|
30
|
+
generateLinkHeaders: () => generateLinkHeaders,
|
|
30
31
|
generatePageContext: () => generatePageContext,
|
|
31
32
|
generateViewNonce: () => generateViewNonce,
|
|
32
33
|
getModuleResource: () => getModuleResource,
|
|
33
34
|
getModuleResourceByUri: () => getModuleResourceByUri,
|
|
34
35
|
getViewNonce: () => getViewNonce,
|
|
36
|
+
isViewDefinitionResponse: () => isViewDefinitionResponse,
|
|
35
37
|
isViewResponse: () => isViewResponse,
|
|
36
38
|
normalizeRenderOptions: () => normalizeRenderOptions,
|
|
37
39
|
normalizeRenderedResult: () => normalizeRenderedResult,
|
|
@@ -108,7 +110,8 @@ function normalizeRenderedResult({
|
|
|
108
110
|
renderedView,
|
|
109
111
|
metadata,
|
|
110
112
|
options,
|
|
111
|
-
cache
|
|
113
|
+
cache,
|
|
114
|
+
redirect
|
|
112
115
|
}) {
|
|
113
116
|
return {
|
|
114
117
|
renderedView,
|
|
@@ -121,7 +124,8 @@ function normalizeRenderedResult({
|
|
|
121
124
|
options: {
|
|
122
125
|
skipMetadataCollection: options ? options.skipMetadataCollection : false
|
|
123
126
|
},
|
|
124
|
-
cache: cache || {}
|
|
127
|
+
cache: cache || {},
|
|
128
|
+
redirect
|
|
125
129
|
};
|
|
126
130
|
}
|
|
127
131
|
function reduceSourceAssetReferences(assets) {
|
|
@@ -154,6 +158,9 @@ function generatePageContext({requestPath: url}, {id, contentTemplate, propertie
|
|
|
154
158
|
function isViewResponse(response) {
|
|
155
159
|
return response.body !== void 0;
|
|
156
160
|
}
|
|
161
|
+
function isViewDefinitionResponse(response) {
|
|
162
|
+
return response.view !== void 0;
|
|
163
|
+
}
|
|
157
164
|
async function toJsonFormat(viewRequest, viewDefinition, route, runtimeEnvironment, runtimeParams, moduleRegistry) {
|
|
158
165
|
const {viewRecord} = viewDefinition;
|
|
159
166
|
const {bootstrap, id: appName} = route;
|
|
@@ -291,3 +298,23 @@ function addExternalScriptNonce(def, nonce) {
|
|
|
291
298
|
def.nonce = nonce;
|
|
292
299
|
}
|
|
293
300
|
}
|
|
301
|
+
function generateLinkHeaders(assets, patterns) {
|
|
302
|
+
const assetConfig = {};
|
|
303
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
304
|
+
const pattern = patterns[i].match;
|
|
305
|
+
assets.forEach((asset) => {
|
|
306
|
+
const path = (asset.override?.uri || asset.url).split("?")[0];
|
|
307
|
+
if (path.endsWith(pattern)) {
|
|
308
|
+
assetConfig[path] = patterns[i].attributes;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return Object.keys(assetConfig).reduce((linkHeader, path) => {
|
|
313
|
+
linkHeader = `${linkHeader ? linkHeader + ", " : ""}<${path}>`;
|
|
314
|
+
const properties = assetConfig[path];
|
|
315
|
+
for (const prop in properties) {
|
|
316
|
+
linkHeader += properties[prop] !== "" ? `; ${prop}=${properties[prop]}` : `; ${prop}`;
|
|
317
|
+
}
|
|
318
|
+
return linkHeader;
|
|
319
|
+
}, "");
|
|
320
|
+
}
|
|
@@ -46,9 +46,11 @@ var LwrViewHandler = class {
|
|
|
46
46
|
}
|
|
47
47
|
const {view, viewParams, renderOptions} = normalizeViewProperties(viewRequest, response, route, this.globalConfig, runtimeParams);
|
|
48
48
|
const viewDefinition2 = await this.viewRegistry.getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions);
|
|
49
|
+
const link2 = !!route?.bootstrap?.preloadResources?.patterns?.length && (0, import_utils.generateLinkHeaders)(viewDefinition2.viewRecord.assetReferences || [], route?.bootstrap?.preloadResources?.patterns || []);
|
|
49
50
|
return {
|
|
50
51
|
...response,
|
|
51
52
|
body: viewDefinition2.renderedView,
|
|
53
|
+
...!!link2 && {headers: {link: link2}},
|
|
52
54
|
metadata: {
|
|
53
55
|
viewDefinition: viewDefinition2
|
|
54
56
|
},
|
|
@@ -56,12 +58,14 @@ var LwrViewHandler = class {
|
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
60
|
const viewDefinition = await this.getDefaultRouteViewDefinition(viewRequest, route, runtimeEnvironment, runtimeParams);
|
|
61
|
+
const link = !!route?.bootstrap?.preloadResources?.patterns?.length && (0, import_utils.generateLinkHeaders)(viewDefinition.viewRecord.assetReferences || [], route.bootstrap?.preloadResources?.patterns || []);
|
|
59
62
|
return {
|
|
60
63
|
body: viewDefinition.renderedView,
|
|
61
64
|
metadata: {
|
|
62
65
|
viewDefinition
|
|
63
66
|
},
|
|
64
|
-
cache: viewDefinition.cache
|
|
67
|
+
cache: viewDefinition.cache,
|
|
68
|
+
...!!link && {headers: {link}}
|
|
65
69
|
};
|
|
66
70
|
}
|
|
67
71
|
async getViewJson(viewRequest, route, runtimeEnvironment, runtimeParams = {}) {
|
|
@@ -153,6 +157,19 @@ var LwrViewHandler = class {
|
|
|
153
157
|
if (response?.locale) {
|
|
154
158
|
runtimeParams.locale = response.locale;
|
|
155
159
|
}
|
|
160
|
+
if ((0, import_utils.isViewDefinitionResponse)(response)) {
|
|
161
|
+
let viewDefintionMeta = response.metadata?.viewDefinition;
|
|
162
|
+
if (!viewDefintionMeta) {
|
|
163
|
+
viewDefintionMeta = (await viewApi.getViewResponse(response.view, response.viewParams, response.renderOptions)).metadata?.viewDefinition;
|
|
164
|
+
}
|
|
165
|
+
const link = !!route?.bootstrap?.preloadResources?.patterns?.length && (0, import_utils.generateLinkHeaders)(viewDefintionMeta?.viewRecord.assetReferences || [], route?.bootstrap?.preloadResources?.patterns || []);
|
|
166
|
+
if (link) {
|
|
167
|
+
response.headers = {
|
|
168
|
+
...response.headers,
|
|
169
|
+
link
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
156
173
|
return response;
|
|
157
174
|
}
|
|
158
175
|
getBoundApi(viewRequest, route, runtimeEnvironment, runtimeParams) {
|
package/build/es/index.js
CHANGED
|
@@ -30,7 +30,12 @@ export class LwrViewRegistry {
|
|
|
30
30
|
this.globalData = context.globalData;
|
|
31
31
|
this.appEmitter = context.appEmitter;
|
|
32
32
|
const observer = context.appObserver;
|
|
33
|
-
this.viewDefinitions = new LRUCache({
|
|
33
|
+
this.viewDefinitions = new LRUCache({
|
|
34
|
+
max: parseInt(process.env.VIEW_CACHE_SIZE ?? '500', 10),
|
|
35
|
+
dispose: (_value, key) => {
|
|
36
|
+
logger.verbose(`View evicted from cache ${key}`);
|
|
37
|
+
},
|
|
38
|
+
});
|
|
34
39
|
// Observers for cached entries external dependencies -- view templates, modules, and assets
|
|
35
40
|
observer.onViewSourceChange(({ payload }) => this.onViewSourceChange(payload));
|
|
36
41
|
observer.onModuleDefinitionChange(({ payload }) => this.onModuleDefinitionChange(payload));
|
|
@@ -168,7 +173,8 @@ export class LwrViewRegistry {
|
|
|
168
173
|
}
|
|
169
174
|
return false;
|
|
170
175
|
}
|
|
171
|
-
async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
176
|
+
async getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams = { requestCache: {} }, renderOptions) {
|
|
177
|
+
runtimeParams.requestCache = runtimeParams.requestCache ?? {};
|
|
172
178
|
try {
|
|
173
179
|
const { freezeAssets, viewParamCacheKey } = normalizeRenderOptions(this.runtimeEnvironment, renderOptions);
|
|
174
180
|
const viewDefCacheKey = getCacheKeyFromJson({
|
|
@@ -189,8 +195,9 @@ export class LwrViewRegistry {
|
|
|
189
195
|
: getCacheKeyFromJson(viewParams);
|
|
190
196
|
logger.debug(`[view-registry][getViewDefinition] viewParamKey=${viewParamKey}`);
|
|
191
197
|
// important: cache key does not include the unbounded viewParams
|
|
192
|
-
|
|
193
|
-
|
|
198
|
+
// check the cross-request cache, then the request cache (which is cleaned up after each base doc request)
|
|
199
|
+
if (this.viewDefinitions.has(viewDefCacheKey) || runtimeParams.requestCache[viewDefCacheKey]) {
|
|
200
|
+
const viewDefinition = this.viewDefinitions.get(viewDefCacheKey) || runtimeParams.requestCache[viewDefCacheKey];
|
|
194
201
|
if (viewDefinition &&
|
|
195
202
|
viewDefinition.paramKey === viewParamKey &&
|
|
196
203
|
viewDefinition.viewDefinition.immutable) {
|
|
@@ -218,11 +225,17 @@ export class LwrViewRegistry {
|
|
|
218
225
|
const leastTtl = shortestTtl(viewDefinition.cache?.ttl, route?.cache?.ttl, maxTtl);
|
|
219
226
|
const ttl = leastTtl !== undefined ? (leastTtl === 0 ? 10 : leastTtl * 1000) : undefined;
|
|
220
227
|
// cache view definition for the shortest ttl or until it is the least recently used when ttl is undefined
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
228
|
+
const viewDefCacheEntry = { view, viewDefinition, paramKey: viewParamKey };
|
|
229
|
+
if (view.bootstrap?.includeCookiesForSSR) {
|
|
230
|
+
// important: DO NOT cache authenticated view definitions beyond a single request.
|
|
231
|
+
// Keep the cache entry only during the request in case viewRegistry.getViewDefinition is called > once.
|
|
232
|
+
// This can happen when a route handler calls viewApi.getViewResponse then returns a ViewDefinitionResponse.
|
|
233
|
+
runtimeParams.requestCache[viewDefCacheKey] = viewDefCacheEntry;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Standard cross-request caching
|
|
237
|
+
this.viewDefinitions.set(viewDefCacheKey, viewDefCacheEntry, { ttl });
|
|
238
|
+
}
|
|
226
239
|
return viewDefinition;
|
|
227
240
|
}
|
|
228
241
|
catch (err) {
|
|
@@ -235,10 +248,10 @@ export class LwrViewRegistry {
|
|
|
235
248
|
throw createSingleDiagnosticError({ description: descriptions.SERVER.UNEXPECTED_ERROR(message) }, LwrServerError);
|
|
236
249
|
}
|
|
237
250
|
}
|
|
238
|
-
async renderView(view, viewParams, runtimeEnvironment, runtimeParams
|
|
251
|
+
async renderView(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions) {
|
|
239
252
|
const { id, contentTemplate, rootComponent, layoutTemplate } = view;
|
|
240
253
|
const lwrResourcesId = `__LWR_RESOURCES__${Date.now()}`;
|
|
241
|
-
const renderedContent = await this.render({ id, contentTemplate, rootComponent }, { ...viewParams, lwr_resources: lwrResourcesId }, runtimeParams, runtimeEnvironment
|
|
254
|
+
const renderedContent = await this.render({ id, contentTemplate, rootComponent }, { ...viewParams, lwr_resources: lwrResourcesId }, runtimeParams, runtimeEnvironment);
|
|
242
255
|
// normalize the renderOptions provided by the CompiledView content with the request options.
|
|
243
256
|
let normalizedRenderOptions = normalizeRenderOptions(this.runtimeEnvironment, renderedContent.options, renderOptions);
|
|
244
257
|
const layout = layoutTemplate || renderedContent.compiledView.layoutTemplate;
|
|
@@ -291,6 +304,7 @@ export class LwrViewRegistry {
|
|
|
291
304
|
},
|
|
292
305
|
},
|
|
293
306
|
cache: renderedContent.cache,
|
|
307
|
+
redirect: renderedContent.redirect,
|
|
294
308
|
},
|
|
295
309
|
// Render Content now contains a layout
|
|
296
310
|
{
|
|
@@ -306,7 +320,7 @@ export class LwrViewRegistry {
|
|
|
306
320
|
});
|
|
307
321
|
return renderedViewDef;
|
|
308
322
|
}
|
|
309
|
-
async render(viewId, viewParams, runtimeParams, runtimeEnvironment
|
|
323
|
+
async render(viewId, viewParams, runtimeParams, runtimeEnvironment) {
|
|
310
324
|
const globalContext = this.globalData;
|
|
311
325
|
const { id, rootComponent, contentTemplate } = viewId;
|
|
312
326
|
const compiledView = await this.getView({ id, contentTemplate, rootComponent });
|
|
@@ -383,6 +397,7 @@ export class LwrViewRegistry {
|
|
|
383
397
|
...viewRecord,
|
|
384
398
|
},
|
|
385
399
|
cache: { ttl: pageTtl },
|
|
400
|
+
redirect: renderedView.redirect,
|
|
386
401
|
};
|
|
387
402
|
}
|
|
388
403
|
return {
|
|
@@ -393,6 +408,7 @@ export class LwrViewRegistry {
|
|
|
393
408
|
moduleResources: [],
|
|
394
409
|
},
|
|
395
410
|
cache: { ttl: pageTtl },
|
|
411
|
+
redirect: renderedView.redirect,
|
|
396
412
|
};
|
|
397
413
|
}
|
|
398
414
|
}
|
|
@@ -266,6 +266,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
266
266
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
267
267
|
mixedMode: view.bootstrap?.mixedMode || false,
|
|
268
268
|
nonce: viewParams?.page?.nonce,
|
|
269
|
+
ssr: view.bootstrap?.ssr || false,
|
|
269
270
|
}, {
|
|
270
271
|
appId: appIdentity.appName,
|
|
271
272
|
bootstrapModule: versionedSpecifier,
|
|
@@ -16,7 +16,9 @@ export async function linkLwrResources(source, view, viewParams, cxt) {
|
|
|
16
16
|
});
|
|
17
17
|
// Finally replace the token with the real resources
|
|
18
18
|
return {
|
|
19
|
-
|
|
19
|
+
// partial may contain regex-token-like characters such as $$,
|
|
20
|
+
// so we want to bypass regex replacement
|
|
21
|
+
renderedView: source.replace(lwrResourcesId, () => partial),
|
|
20
22
|
viewRecord,
|
|
21
23
|
};
|
|
22
24
|
}
|
|
@@ -37,7 +37,8 @@ export function getViewBootstrapConfigurationResource(viewInfo, config, runtimeE
|
|
|
37
37
|
})});`,
|
|
38
38
|
`globalThis.LWR = {...globalThis.LWR, env: ${JSON.stringify(lwrEnv)}};`,
|
|
39
39
|
`globalThis.process={...globalThis.process,env:{...globalThis.process?.env,...${JSON.stringify(nodeEnv)}}};`,
|
|
40
|
-
|
|
40
|
+
// TODO: evaluate moving these to app layer
|
|
41
|
+
`globalThis.lwcRuntimeFlags = { ENABLE_MIXED_SHADOW_MODE: ${viewInfo.mixedMode}, ENABLE_WIRE_SYNC_EMIT: ${viewInfo.ssr} };`,
|
|
41
42
|
debug && debugMessage && `console.error(${JSON.stringify(debugMessage)});`,
|
|
42
43
|
]
|
|
43
44
|
.filter(Boolean)
|
|
@@ -229,6 +229,7 @@ export async function getHtmlResources(view, viewParams, resourceContext) {
|
|
|
229
229
|
configAsSrc: view.bootstrap?.configAsSrc || false,
|
|
230
230
|
mixedMode: view.bootstrap?.mixedMode || false,
|
|
231
231
|
nonce: viewParams?.page?.nonce,
|
|
232
|
+
ssr: view.bootstrap?.ssr || false,
|
|
232
233
|
}, {
|
|
233
234
|
appId: appIdentity.appName,
|
|
234
235
|
bootstrapModule: versionedSpecifier,
|
package/build/es/utils.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
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 } from '@lwrjs/types';
|
|
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 } 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, }: RenderingResult): NormalizedRenderingResult;
|
|
4
|
+
export declare function normalizeRenderedResult({ renderedView, metadata, options, cache, redirect, }: 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>;
|
|
8
8
|
export declare function isViewResponse(response: RouteHandlerViewResponse): response is ViewResponse;
|
|
9
|
+
export declare function isViewDefinitionResponse(response: RouteHandlerViewResponse): response is ViewDefinitionResponse;
|
|
9
10
|
export declare function toJsonFormat(viewRequest: ViewRequest, viewDefinition: LinkedViewDefinition, route: LwrRoute | LwrErrorRoute, runtimeEnvironment: RuntimeEnvironment, runtimeParams: RuntimeParams, moduleRegistry: ModuleRegistry): Promise<ViewResponse>;
|
|
10
11
|
export declare function getModuleResource(moduleId: Pick<ModuleId, 'specifier' | 'version'>, runtimeEnvironment: RuntimeEnvironment, moduleResourceMeta: ViewModuleResourceContext, defRegistry: ModuleRegistry | ModuleBundler, runtimeParams: RuntimeParams): Promise<ResourceDefinition>;
|
|
11
12
|
export declare function getModuleResourceByUri(uri: string, runtimeEnvironment: RuntimeEnvironment, moduleResourceMeta: ViewModuleResourceContext): ResourceDefinition;
|
|
@@ -22,4 +23,5 @@ export declare function generateViewNonce(viewParams: ViewParams): void;
|
|
|
22
23
|
* Add a nonce to a script definition in view if it is external (not inline)
|
|
23
24
|
*/
|
|
24
25
|
export declare function addExternalScriptNonce(def: ResourceDefinition, nonce?: string): void;
|
|
26
|
+
export declare function generateLinkHeaders(assets: RenderedAssetReference[], patterns: Record<string, any>[]): string;
|
|
25
27
|
//# sourceMappingURL=utils.d.ts.map
|
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, }) {
|
|
69
|
+
export function normalizeRenderedResult({ renderedView, metadata, options, cache, redirect, }) {
|
|
70
70
|
return {
|
|
71
71
|
renderedView,
|
|
72
72
|
metadata: {
|
|
@@ -79,6 +79,7 @@ export function normalizeRenderedResult({ renderedView, metadata, options, cache
|
|
|
79
79
|
skipMetadataCollection: options ? options.skipMetadataCollection : false,
|
|
80
80
|
},
|
|
81
81
|
cache: cache || {},
|
|
82
|
+
redirect,
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
export function reduceSourceAssetReferences(assets) {
|
|
@@ -117,6 +118,9 @@ export function generatePageContext({ requestPath: url }, { id, contentTemplate,
|
|
|
117
118
|
export function isViewResponse(response) {
|
|
118
119
|
return response.body !== undefined;
|
|
119
120
|
}
|
|
121
|
+
export function isViewDefinitionResponse(response) {
|
|
122
|
+
return response.view !== undefined;
|
|
123
|
+
}
|
|
120
124
|
export async function toJsonFormat(viewRequest, viewDefinition, route, runtimeEnvironment, runtimeParams, moduleRegistry) {
|
|
121
125
|
const { viewRecord } = viewDefinition;
|
|
122
126
|
const { bootstrap, id: appName } = route;
|
|
@@ -281,4 +285,30 @@ export function addExternalScriptNonce(def, nonce) {
|
|
|
281
285
|
def.nonce = nonce;
|
|
282
286
|
}
|
|
283
287
|
}
|
|
288
|
+
export function generateLinkHeaders(assets, patterns) {
|
|
289
|
+
// store each Path's config in this config
|
|
290
|
+
const assetConfig = {};
|
|
291
|
+
// Iterate through the patterns to filter the file paths and populate assetConfig
|
|
292
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
293
|
+
const pattern = patterns[i].match;
|
|
294
|
+
assets.forEach((asset) => {
|
|
295
|
+
const path = (asset.override?.uri || asset.url).split('?')[0];
|
|
296
|
+
if (path.endsWith(pattern)) {
|
|
297
|
+
assetConfig[path] = patterns[i].attributes;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
// Use the assetConfig to construct the Link Header
|
|
302
|
+
return Object.keys(assetConfig).reduce((linkHeader, path) => {
|
|
303
|
+
linkHeader = `${linkHeader ? linkHeader + ', ' : ''}<${path}>`;
|
|
304
|
+
const properties = assetConfig[path];
|
|
305
|
+
// Add the properties mentioned on the preloadResource patterns except match prop
|
|
306
|
+
for (const prop in properties) {
|
|
307
|
+
// Check if the prop has empty string, if so add the prop name alone
|
|
308
|
+
// eg: if crossorigin: '' => </global.css>; as=style; crossorigin
|
|
309
|
+
linkHeader += properties[prop] !== '' ? `; ${prop}=${properties[prop]}` : `; ${prop}`;
|
|
310
|
+
}
|
|
311
|
+
return linkHeader;
|
|
312
|
+
}, '');
|
|
313
|
+
}
|
|
284
314
|
//# sourceMappingURL=utils.js.map
|
package/build/es/view-handler.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'path';
|
|
2
2
|
import { normalizeResourcePath, shortestTtl } from '@lwrjs/shared-utils';
|
|
3
3
|
import { getTracer, ViewSpan } from '@lwrjs/instrumentation';
|
|
4
|
-
import { generateHtmlTag, generatePageContext, isViewResponse, toJsonFormat } from './utils.js';
|
|
4
|
+
import { generateHtmlTag, generateLinkHeaders, generatePageContext, isViewResponse, isViewDefinitionResponse, toJsonFormat, } from './utils.js';
|
|
5
5
|
import { DiagnosticsError, LwrApplicationError, createSingleDiagnosticError, descriptions, } from '@lwrjs/diagnostics';
|
|
6
6
|
export class LwrViewHandler {
|
|
7
7
|
constructor(context, globalConfig) {
|
|
@@ -27,9 +27,12 @@ export class LwrViewHandler {
|
|
|
27
27
|
// Process ViewDefinitionResponse
|
|
28
28
|
const { view, viewParams, renderOptions } = normalizeViewProperties(viewRequest, response, route, this.globalConfig, runtimeParams);
|
|
29
29
|
const viewDefinition = await this.viewRegistry.getViewDefinition(view, viewParams, runtimeEnvironment, runtimeParams, renderOptions);
|
|
30
|
+
const link = !!route?.bootstrap?.preloadResources?.patterns?.length &&
|
|
31
|
+
generateLinkHeaders(viewDefinition.viewRecord.assetReferences || [], route?.bootstrap?.preloadResources?.patterns || []);
|
|
30
32
|
return {
|
|
31
33
|
...response,
|
|
32
34
|
body: viewDefinition.renderedView,
|
|
35
|
+
...(!!link && { headers: { link } }),
|
|
33
36
|
metadata: {
|
|
34
37
|
viewDefinition,
|
|
35
38
|
},
|
|
@@ -38,12 +41,15 @@ export class LwrViewHandler {
|
|
|
38
41
|
}
|
|
39
42
|
// default static view
|
|
40
43
|
const viewDefinition = await this.getDefaultRouteViewDefinition(viewRequest, route, runtimeEnvironment, runtimeParams);
|
|
44
|
+
const link = !!route?.bootstrap?.preloadResources?.patterns?.length &&
|
|
45
|
+
generateLinkHeaders(viewDefinition.viewRecord.assetReferences || [], route.bootstrap?.preloadResources?.patterns || []);
|
|
41
46
|
return {
|
|
42
47
|
body: viewDefinition.renderedView,
|
|
43
48
|
metadata: {
|
|
44
49
|
viewDefinition,
|
|
45
50
|
},
|
|
46
51
|
cache: viewDefinition.cache,
|
|
52
|
+
...(!!link && { headers: { link } }),
|
|
47
53
|
};
|
|
48
54
|
}
|
|
49
55
|
// Get the View's JSON Manifest Response
|
|
@@ -171,6 +177,24 @@ export class LwrViewHandler {
|
|
|
171
177
|
if (response?.locale) {
|
|
172
178
|
runtimeParams.locale = response.locale;
|
|
173
179
|
}
|
|
180
|
+
// Add link headers if this is a viewDefinition response
|
|
181
|
+
if (isViewDefinitionResponse(response)) {
|
|
182
|
+
let viewDefintionMeta = response.metadata?.viewDefinition;
|
|
183
|
+
if (!viewDefintionMeta) {
|
|
184
|
+
viewDefintionMeta = (await viewApi.getViewResponse(response.view, response.viewParams, response.renderOptions)).metadata?.viewDefinition;
|
|
185
|
+
}
|
|
186
|
+
const link = !!route?.bootstrap?.preloadResources?.patterns?.length &&
|
|
187
|
+
generateLinkHeaders(
|
|
188
|
+
// idk when metadata?.viewDefinition would be undefined but
|
|
189
|
+
// it's optional....probably Darrell's fault
|
|
190
|
+
viewDefintionMeta?.viewRecord.assetReferences || [], route?.bootstrap?.preloadResources?.patterns || []);
|
|
191
|
+
if (link) {
|
|
192
|
+
response.headers = {
|
|
193
|
+
...response.headers,
|
|
194
|
+
link,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
174
198
|
return response;
|
|
175
199
|
}
|
|
176
200
|
/*
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.15.0-alpha.
|
|
7
|
+
"version": "0.15.0-alpha.20",
|
|
8
8
|
"homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -33,17 +33,17 @@
|
|
|
33
33
|
"build": "tsc -b"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@lwrjs/app-service": "0.15.0-alpha.
|
|
37
|
-
"@lwrjs/diagnostics": "0.15.0-alpha.
|
|
38
|
-
"@lwrjs/instrumentation": "0.15.0-alpha.
|
|
39
|
-
"@lwrjs/shared-utils": "0.15.0-alpha.
|
|
36
|
+
"@lwrjs/app-service": "0.15.0-alpha.20",
|
|
37
|
+
"@lwrjs/diagnostics": "0.15.0-alpha.20",
|
|
38
|
+
"@lwrjs/instrumentation": "0.15.0-alpha.20",
|
|
39
|
+
"@lwrjs/shared-utils": "0.15.0-alpha.20",
|
|
40
40
|
"lru-cache": "^10.4.3"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@lwrjs/types": "0.15.0-alpha.
|
|
43
|
+
"@lwrjs/types": "0.15.0-alpha.20"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=18.0.0"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "112ed1a3472b68a90c6884422137006a5dcf032c"
|
|
49
49
|
}
|