@adobe/helix-html-pipeline 5.13.9 → 5.13.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.
- package/CHANGELOG.md +7 -0
- package/package.json +2 -1
- package/src/PipelineResponse.js +1 -1
- package/src/html-pipe.js +4 -0
- package/src/json-pipe.js +3 -2
- package/src/sitemap-pipe.js +3 -0
- package/src/steps/fetch-404.js +2 -2
- package/src/steps/fetch-config-all.js +2 -2
- package/src/steps/fetch-config.js +3 -3
- package/src/steps/fetch-content.js +2 -2
- package/src/utils/last-modified.js +36 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [5.13.10](https://github.com/adobe/helix-html-pipeline/compare/v5.13.9...v5.13.10) (2024-11-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* respect last-modified from mapped metadata ([e77e3b6](https://github.com/adobe/helix-html-pipeline/commit/e77e3b6392e6e49e1ea5220ae4a278122b24a5df)), closes [#737](https://github.com/adobe/helix-html-pipeline/issues/737)
|
|
7
|
+
|
|
1
8
|
## [5.13.9](https://github.com/adobe/helix-html-pipeline/compare/v5.13.8...v5.13.9) (2024-11-18)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-html-pipeline",
|
|
3
|
-
"version": "5.13.
|
|
3
|
+
"version": "5.13.10",
|
|
4
4
|
"description": "Helix HTML Pipeline",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"lodash.escape": "4.0.1",
|
|
59
59
|
"mdast-util-to-hast": "13.2.0",
|
|
60
60
|
"mdast-util-to-string": "4.0.0",
|
|
61
|
+
"micromark-util-subtokenize": "2.0.1",
|
|
61
62
|
"mime": "4.0.4",
|
|
62
63
|
"rehype-format": "5.0.1",
|
|
63
64
|
"rehype-parse": "9.0.1",
|
package/src/PipelineResponse.js
CHANGED
package/src/html-pipe.js
CHANGED
|
@@ -38,6 +38,7 @@ import { PipelineResponse } from './PipelineResponse.js';
|
|
|
38
38
|
import { validatePathInfo } from './utils/path.js';
|
|
39
39
|
import { initAuthRoute } from './utils/auth.js';
|
|
40
40
|
import fetchMappedMetadata from './steps/fetch-mapped-metadata.js';
|
|
41
|
+
import { applyMetaLastModified, setLastModified } from './utils/last-modified.js';
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* Fetches the content and if not found, fetches the 404.html
|
|
@@ -162,6 +163,7 @@ export async function htmlPipe(state, req) {
|
|
|
162
163
|
log[level](`pipeline status: ${res.status} ${res.error}`);
|
|
163
164
|
res.headers.set('x-error', cleanupHeaderValue(res.error));
|
|
164
165
|
if (res.status < 500) {
|
|
166
|
+
setLastModified(state, res);
|
|
165
167
|
await setCustomResponseHeaders(state, req, res);
|
|
166
168
|
}
|
|
167
169
|
return res;
|
|
@@ -188,8 +190,10 @@ export async function htmlPipe(state, req) {
|
|
|
188
190
|
await render(state, req, res);
|
|
189
191
|
state.timer?.update('serialize');
|
|
190
192
|
await tohtml(state, req, res);
|
|
193
|
+
await applyMetaLastModified(state, res);
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
setLastModified(state, res);
|
|
193
197
|
await setCustomResponseHeaders(state, req, res);
|
|
194
198
|
await setXSurrogateKeyHeader(state, req, res);
|
|
195
199
|
} catch (e) {
|
package/src/json-pipe.js
CHANGED
|
@@ -14,7 +14,7 @@ import fetchConfigAll from './steps/fetch-config-all.js';
|
|
|
14
14
|
import setCustomResponseHeaders from './steps/set-custom-response-headers.js';
|
|
15
15
|
import { PipelineResponse } from './PipelineResponse.js';
|
|
16
16
|
import jsonFilter from './utils/json-filter.js';
|
|
17
|
-
import { extractLastModified,
|
|
17
|
+
import { extractLastModified, recordLastModified, setLastModified } from './utils/last-modified.js';
|
|
18
18
|
import { authenticate } from './steps/authenticate.js';
|
|
19
19
|
import fetchConfig from './steps/fetch-config.js';
|
|
20
20
|
import { getPathInfo } from './utils/path.js';
|
|
@@ -85,7 +85,7 @@ async function fetchJsonContent(state, req, res) {
|
|
|
85
85
|
state.content.sourceLocation = ret.headers.get('x-amz-meta-x-source-location');
|
|
86
86
|
log.info(`source-location: ${state.content.sourceLocation}`);
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
recordLastModified(state, res, 'content', extractLastModified(ret.headers));
|
|
89
89
|
} else {
|
|
90
90
|
// also add code surrogate key in case json is later added to code bus (#688)
|
|
91
91
|
state.content.sourceBus = 'code|content';
|
|
@@ -195,6 +195,7 @@ export async function jsonPipe(state, req) {
|
|
|
195
195
|
const keys = await computeSurrogateKeys(state);
|
|
196
196
|
res.headers.set('x-surrogate-key', keys.join(' '));
|
|
197
197
|
|
|
198
|
+
setLastModified(state, res);
|
|
198
199
|
await setCustomResponseHeaders(state, req, res);
|
|
199
200
|
return res;
|
|
200
201
|
} catch (e) {
|
package/src/sitemap-pipe.js
CHANGED
|
@@ -20,6 +20,7 @@ import setXSurrogateKeyHeader from './steps/set-x-surrogate-key-header.js';
|
|
|
20
20
|
import setCustomResponseHeaders from './steps/set-custom-response-headers.js';
|
|
21
21
|
import { PipelineStatusError } from './PipelineStatusError.js';
|
|
22
22
|
import { PipelineResponse } from './PipelineResponse.js';
|
|
23
|
+
import { extractLastModified, recordLastModified, setLastModified } from './utils/last-modified.js';
|
|
23
24
|
|
|
24
25
|
async function generateSitemap(state) {
|
|
25
26
|
const {
|
|
@@ -118,6 +119,7 @@ export async function sitemapPipe(state, req) {
|
|
|
118
119
|
const ret = await generateSitemap(state);
|
|
119
120
|
if (ret.status === 200) {
|
|
120
121
|
res.status = 200;
|
|
122
|
+
recordLastModified(state, res, 'content', extractLastModified(ret.headers));
|
|
121
123
|
delete res.error;
|
|
122
124
|
state.content.data = ret.body;
|
|
123
125
|
}
|
|
@@ -129,6 +131,7 @@ export async function sitemapPipe(state, req) {
|
|
|
129
131
|
|
|
130
132
|
state.timer?.update('serialize');
|
|
131
133
|
await renderCode(state, req, res);
|
|
134
|
+
setLastModified(state, res);
|
|
132
135
|
await setCustomResponseHeaders(state, req, res);
|
|
133
136
|
await setXSurrogateKeyHeader(state, req, res);
|
|
134
137
|
} catch (e) {
|
package/src/steps/fetch-404.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
import { extractLastModified } from '../utils/last-modified.js';
|
|
12
|
+
import { extractLastModified, recordLastModified } from '../utils/last-modified.js';
|
|
13
13
|
import { getPathKey } from './set-x-surrogate-key-header.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -29,7 +29,7 @@ export default async function fetch404(state, req, res) {
|
|
|
29
29
|
// override last-modified if source-last-modified is set
|
|
30
30
|
const lastModified = extractLastModified(ret.headers);
|
|
31
31
|
if (lastModified) {
|
|
32
|
-
|
|
32
|
+
recordLastModified(state, res, 'content', lastModified);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// keep 404 response status
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { PipelineStatusError } from '../PipelineStatusError.js';
|
|
14
|
-
import { extractLastModified,
|
|
14
|
+
import { extractLastModified, recordLastModified } from '../utils/last-modified.js';
|
|
15
15
|
import { globToRegExp, Modifiers } from '../utils/modifiers.js';
|
|
16
16
|
import { getOriginalHost } from './utils.js';
|
|
17
17
|
|
|
@@ -74,7 +74,7 @@ export default async function fetchConfigAll(state, req, res) {
|
|
|
74
74
|
|
|
75
75
|
if (state.type === 'html' && state.info.selector !== 'plain') {
|
|
76
76
|
// also update last-modified (only for extensionless html pipeline)
|
|
77
|
-
|
|
77
|
+
recordLastModified(state, res, 'configAll', extractLastModified(ret.headers));
|
|
78
78
|
}
|
|
79
79
|
// set custom preview and live hosts
|
|
80
80
|
state.previewHost = replaceParams(state.config.cdn?.preview?.host, state);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
import { extractLastModified,
|
|
12
|
+
import { extractLastModified, recordLastModified } from '../utils/last-modified.js';
|
|
13
13
|
import { PipelineStatusError } from '../PipelineStatusError.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -67,11 +67,11 @@ export default async function fetchConfig(state, req, res) {
|
|
|
67
67
|
const configLastModified = extractLastModified(ret.headers);
|
|
68
68
|
|
|
69
69
|
// update last modified of fstab
|
|
70
|
-
|
|
70
|
+
recordLastModified(state, res, 'config', config.fstab?.lastModified || configLastModified);
|
|
71
71
|
|
|
72
72
|
// for html requests, also consider the HEAD config
|
|
73
73
|
if (state.type === 'html' && state.info.selector !== 'plain' && config.head?.lastModified) {
|
|
74
|
-
|
|
74
|
+
recordLastModified(state, res, 'head', config.head.lastModified);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
import { computeSurrogateKey } from '@adobe/helix-shared-utils';
|
|
13
|
-
import { extractLastModified,
|
|
13
|
+
import { extractLastModified, recordLastModified } from '../utils/last-modified.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Loads the content from either the content-bus or code-bus and stores it in `state.content`
|
|
@@ -67,7 +67,7 @@ export default async function fetchContent(state, req, res) {
|
|
|
67
67
|
state.content.sourceLocation = ret.headers.get('x-amz-meta-x-source-location');
|
|
68
68
|
log.info(`source-location: ${state.content.sourceLocation}`);
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
recordLastModified(state, res, 'content', extractLastModified(ret.headers));
|
|
71
71
|
|
|
72
72
|
// reject requests to /index *after* checking for redirects
|
|
73
73
|
// (https://github.com/adobe/helix-pipeline-service/issues/290)
|
|
@@ -11,30 +11,43 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
* one if none exists yet. please note that it generates helper property `lastModifiedTime` in
|
|
16
|
-
* unix epoch format.
|
|
17
|
-
*
|
|
18
|
-
* the date string will be a "http-date": https://httpwg.org/specs/rfc7231.html#http.date
|
|
14
|
+
* Records the last modified for the given source.
|
|
19
15
|
*
|
|
20
16
|
* @param {PipelineState} state
|
|
21
|
-
* @param {PipelineResponse} res the pipeline
|
|
17
|
+
* @param {PipelineResponse} res the pipeline response
|
|
18
|
+
* @param {string} source the source providing a last-modified date
|
|
22
19
|
* @param {string} httpDate http-date string
|
|
23
20
|
*/
|
|
24
|
-
export function
|
|
21
|
+
export function recordLastModified(state, res, source, httpDate) {
|
|
25
22
|
if (!httpDate) {
|
|
26
23
|
return;
|
|
27
24
|
}
|
|
28
25
|
const { log } = state;
|
|
29
|
-
const
|
|
30
|
-
if (Number.isNaN(
|
|
31
|
-
log.warn(`
|
|
26
|
+
const date = new Date(httpDate);
|
|
27
|
+
if (Number.isNaN(date.valueOf())) {
|
|
28
|
+
log.warn(`last-modified date is invalid: ${httpDate} for ${source}`);
|
|
32
29
|
return;
|
|
33
30
|
}
|
|
31
|
+
res.lastModifiedSources[source] = {
|
|
32
|
+
time: date.valueOf(),
|
|
33
|
+
date: date.toUTCString(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Calculates the last modified by using the latest date from all the recorded sources
|
|
39
|
+
* and sets it on the `last-modified` header.
|
|
40
|
+
*
|
|
41
|
+
* @param {PipelineState} state
|
|
42
|
+
* @param {PipelineResponse} res the pipeline response
|
|
43
|
+
*/
|
|
44
|
+
export function setLastModified(state, res) {
|
|
45
|
+
let latestTime = 0;
|
|
46
|
+
for (const { time, date } of Object.values(res.lastModifiedSources)) {
|
|
47
|
+
if (time > latestTime) {
|
|
48
|
+
latestTime = time;
|
|
49
|
+
res.headers.set('last-modified', date);
|
|
50
|
+
}
|
|
38
51
|
}
|
|
39
52
|
}
|
|
40
53
|
|
|
@@ -55,3 +68,13 @@ export function extractLastModified(headers) {
|
|
|
55
68
|
}
|
|
56
69
|
return headers.get('last-modified');
|
|
57
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Sets the metadata last modified entry to the one define in the page specific metadata if
|
|
74
|
+
* it exists. this allows to control the last-modified per metadata record.
|
|
75
|
+
* @param {PipelineState} state
|
|
76
|
+
* @param {PipelineResponse} res the pipeline response
|
|
77
|
+
*/
|
|
78
|
+
export function applyMetaLastModified(state, res) {
|
|
79
|
+
recordLastModified(state, res, 'metadata', state.content.meta.page['last-modified']);
|
|
80
|
+
}
|