@adobe/helix-html-pipeline 6.15.11 → 6.16.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.
- 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 -2
- package/src/steps/fetch-404.js +2 -2
- package/src/steps/fetch-content.js +2 -2
- package/src/steps/fetch-mapped-metadata.js +2 -2
- package/src/steps/init-config.js +2 -2
- package/src/utils/last-modified.js +36 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [6.16.0](https://github.com/adobe/helix-html-pipeline/compare/v6.15.11...v6.16.0) (2024-11-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* respect last-modified from metadata sheet ([#744](https://github.com/adobe/helix-html-pipeline/issues/744)) ([50b3e07](https://github.com/adobe/helix-html-pipeline/commit/50b3e07d909651368e4b5e2c473391b63aa05913)), closes [#743](https://github.com/adobe/helix-html-pipeline/issues/743)
|
|
7
|
+
|
|
1
8
|
## [6.15.11](https://github.com/adobe/helix-html-pipeline/compare/v6.15.10...v6.15.11) (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": "6.
|
|
3
|
+
"version": "6.16.0",
|
|
4
4
|
"description": "Helix HTML Pipeline",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"lodash.escape": "4.0.1",
|
|
56
56
|
"mdast-util-to-hast": "13.2.0",
|
|
57
57
|
"mdast-util-to-string": "4.0.0",
|
|
58
|
+
"micromark-util-subtokenize": "2.0.1",
|
|
58
59
|
"mime": "4.0.4",
|
|
59
60
|
"rehype-format": "5.0.1",
|
|
60
61
|
"rehype-parse": "9.0.1",
|
package/src/PipelineResponse.js
CHANGED
package/src/html-pipe.js
CHANGED
|
@@ -35,6 +35,7 @@ import { PipelineStatusError } from './PipelineStatusError.js';
|
|
|
35
35
|
import { PipelineResponse } from './PipelineResponse.js';
|
|
36
36
|
import { validatePathInfo } from './utils/path.js';
|
|
37
37
|
import fetchMappedMetadata from './steps/fetch-mapped-metadata.js';
|
|
38
|
+
import { applyMetaLastModified, setLastModified } from './utils/last-modified.js';
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Fetches the content and if not found, fetches the 404.html
|
|
@@ -140,6 +141,7 @@ export async function htmlPipe(state, req) {
|
|
|
140
141
|
log[level](`pipeline status: ${res.status} ${res.error}`);
|
|
141
142
|
res.headers.set('x-error', cleanupHeaderValue(res.error));
|
|
142
143
|
if (res.status < 500) {
|
|
144
|
+
setLastModified(state, res);
|
|
143
145
|
await setCustomResponseHeaders(state, req, res);
|
|
144
146
|
}
|
|
145
147
|
return res;
|
|
@@ -166,8 +168,10 @@ export async function htmlPipe(state, req) {
|
|
|
166
168
|
await render(state, req, res);
|
|
167
169
|
state.timer?.update('serialize');
|
|
168
170
|
await tohtml(state, req, res);
|
|
171
|
+
await applyMetaLastModified(state, res);
|
|
169
172
|
}
|
|
170
173
|
|
|
174
|
+
setLastModified(state, res);
|
|
171
175
|
await setCustomResponseHeaders(state, req, res);
|
|
172
176
|
await setXSurrogateKeyHeader(state, req, res);
|
|
173
177
|
} catch (e) {
|
package/src/json-pipe.js
CHANGED
|
@@ -14,7 +14,7 @@ import initConfig from './steps/init-config.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 { getPathInfo } from './utils/path.js';
|
|
19
19
|
import { PipelineStatusError } from './PipelineStatusError.js';
|
|
20
20
|
|
|
@@ -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';
|
|
@@ -191,6 +191,7 @@ export async function jsonPipe(state, req) {
|
|
|
191
191
|
const keys = await computeSurrogateKeys(state);
|
|
192
192
|
res.headers.set('x-surrogate-key', keys.join(' '));
|
|
193
193
|
|
|
194
|
+
setLastModified(state, res);
|
|
194
195
|
await setCustomResponseHeaders(state, req, res);
|
|
195
196
|
return res;
|
|
196
197
|
} catch (e) {
|
package/src/sitemap-pipe.js
CHANGED
|
@@ -18,7 +18,7 @@ import setCustomResponseHeaders from './steps/set-custom-response-headers.js';
|
|
|
18
18
|
import { PipelineStatusError } from './PipelineStatusError.js';
|
|
19
19
|
import { PipelineResponse } from './PipelineResponse.js';
|
|
20
20
|
import initConfig from './steps/init-config.js';
|
|
21
|
-
import { extractLastModified,
|
|
21
|
+
import { extractLastModified, recordLastModified, setLastModified } from './utils/last-modified.js';
|
|
22
22
|
|
|
23
23
|
async function generateSitemap(state) {
|
|
24
24
|
const {
|
|
@@ -103,7 +103,7 @@ export async function sitemapPipe(state, req) {
|
|
|
103
103
|
const ret = await generateSitemap(state);
|
|
104
104
|
if (ret.status === 200) {
|
|
105
105
|
res.status = 200;
|
|
106
|
-
|
|
106
|
+
recordLastModified(state, res, 'content', extractLastModified(ret.headers));
|
|
107
107
|
delete res.error;
|
|
108
108
|
state.content.data = ret.body;
|
|
109
109
|
}
|
|
@@ -115,6 +115,7 @@ export async function sitemapPipe(state, req) {
|
|
|
115
115
|
|
|
116
116
|
state.timer?.update('serialize');
|
|
117
117
|
await renderCode(state, req, res);
|
|
118
|
+
setLastModified(state, res);
|
|
118
119
|
await setCustomResponseHeaders(state, req, res);
|
|
119
120
|
await setXSurrogateKeyHeader(state, req, res);
|
|
120
121
|
} 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
|
|
@@ -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`
|
|
@@ -65,7 +65,7 @@ export default async function fetchContent(state, req, res) {
|
|
|
65
65
|
state.content.sourceLocation = ret.headers.get('x-amz-meta-x-source-location');
|
|
66
66
|
log.info(`source-location: ${state.content.sourceLocation}`);
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
recordLastModified(state, res, 'content', extractLastModified(ret.headers));
|
|
69
69
|
|
|
70
70
|
// reject requests to /index *after* checking for redirects
|
|
71
71
|
// (https://github.com/adobe/helix-pipeline-service/issues/290)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { PipelineStatusError } from '../PipelineStatusError.js';
|
|
14
14
|
import { Modifiers } from '../utils/modifiers.js';
|
|
15
|
-
import { extractLastModified,
|
|
15
|
+
import { extractLastModified, recordLastModified } from '../utils/last-modified.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Loads metadata for a mapped path and puts it into `state.mappedMetadata`. If path is not
|
|
@@ -53,7 +53,7 @@ export default async function fetchMappedMetadata(state, res) {
|
|
|
53
53
|
state.mappedMetadata = Modifiers.fromModifierSheet(
|
|
54
54
|
data,
|
|
55
55
|
);
|
|
56
|
-
|
|
56
|
+
recordLastModified(state, res, 'metadata', extractLastModified(ret.headers));
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
59
|
if (ret.status !== 404) {
|
package/src/steps/init-config.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { Modifiers } from '../utils/modifiers.js';
|
|
13
13
|
import { getOriginalHost } from './utils.js';
|
|
14
|
-
import {
|
|
14
|
+
import { recordLastModified } from '../utils/last-modified.js';
|
|
15
15
|
|
|
16
16
|
function replaceParams(str, info) {
|
|
17
17
|
if (!str) {
|
|
@@ -44,5 +44,5 @@ export default function initConfig(state, req, res) {
|
|
|
44
44
|
state.previewHost = replaceParams(config.cdn?.preview?.host, state);
|
|
45
45
|
state.liveHost = replaceParams(config.cdn?.live?.host, state);
|
|
46
46
|
state.prodHost = config.cdn?.prod?.host || getOriginalHost(req.headers);
|
|
47
|
-
|
|
47
|
+
recordLastModified(state, res, 'config', state.config.lastModified);
|
|
48
48
|
}
|
|
@@ -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
|
+
}
|