@adobe/aem-cli 16.0.6 → 16.0.8
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 +14 -0
- package/package.json +2 -2
- package/src/server/HeadHtmlSupport.js +20 -1
- package/src/server/HelixProject.js +1 -3
- package/src/server/HelixServer.js +17 -3
- package/src/server/RequestContext.js +12 -0
- package/src/server/utils.js +25 -33
- package/src/up.cmd.js +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [16.0.8](https://github.com/adobe/helix-cli/compare/v16.0.7...v16.0.8) (2023-11-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* don't respond when headers already sent ([#2278](https://github.com/adobe/helix-cli/issues/2278)) ([6be2f00](https://github.com/adobe/helix-cli/commit/6be2f00c9bf540433eb7836af7a1b7dcfbc174b4)), closes [#2276](https://github.com/adobe/helix-cli/issues/2276)
|
|
7
|
+
|
|
8
|
+
## [16.0.7](https://github.com/adobe/helix-cli/compare/v16.0.6...v16.0.7) (2023-11-16)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* defer loading of head.html to allow inclusion of request cookie ([1a8d2ac](https://github.com/adobe/helix-cli/commit/1a8d2ac65af58f07e2ad8911f8e9c3619f83ac7b)), closes [#2274](https://github.com/adobe/helix-cli/issues/2274)
|
|
14
|
+
|
|
1
15
|
## [16.0.6](https://github.com/adobe/helix-cli/compare/v16.0.5...v16.0.6) (2023-11-13)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/aem-cli",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.8",
|
|
4
4
|
"description": "AEM CLI",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"yargs": "17.7.2"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@adobe/eslint-config-helix": "2.0.
|
|
77
|
+
"@adobe/eslint-config-helix": "2.0.5",
|
|
78
78
|
"@adobe/helix-shared-dom": "2.0.1",
|
|
79
79
|
"@adobe/helix-testutils": "0.4.15",
|
|
80
80
|
"@semantic-release/changelog": "6.0.3",
|
|
@@ -81,6 +81,7 @@ export default class HeadHtmlSupport {
|
|
|
81
81
|
this.remoteStatus = 0;
|
|
82
82
|
this.localHtml = '';
|
|
83
83
|
this.localStatus = 0;
|
|
84
|
+
this.cookie = '';
|
|
84
85
|
this.url = new URL(proxyUrl);
|
|
85
86
|
this.url.pathname = '/head.html';
|
|
86
87
|
this.filePath = resolve(directory, 'head.html');
|
|
@@ -89,8 +90,13 @@ export default class HeadHtmlSupport {
|
|
|
89
90
|
|
|
90
91
|
async loadRemote() {
|
|
91
92
|
// load head from server
|
|
93
|
+
const headers = {};
|
|
94
|
+
if (this.cookie) {
|
|
95
|
+
headers.cookie = this.cookie;
|
|
96
|
+
}
|
|
92
97
|
const resp = await getFetch()(this.url, {
|
|
93
98
|
cache: 'no-store',
|
|
99
|
+
headers,
|
|
94
100
|
});
|
|
95
101
|
this.remoteStatus = resp.status;
|
|
96
102
|
if (resp.ok) {
|
|
@@ -103,6 +109,18 @@ export default class HeadHtmlSupport {
|
|
|
103
109
|
}
|
|
104
110
|
}
|
|
105
111
|
|
|
112
|
+
setCookie(cookie) {
|
|
113
|
+
if (this.cookie !== cookie) {
|
|
114
|
+
this.log.trace('registering head-html cookie to ', cookie);
|
|
115
|
+
this.cookie = cookie;
|
|
116
|
+
this.remoteStatus = 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
invalidateLocal() {
|
|
121
|
+
this.localStatus = 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
106
124
|
async loadLocal() {
|
|
107
125
|
try {
|
|
108
126
|
this.localHtml = (await fs.readFile(this.filePath, 'utf-8')).trim();
|
|
@@ -114,7 +132,7 @@ export default class HeadHtmlSupport {
|
|
|
114
132
|
}
|
|
115
133
|
}
|
|
116
134
|
|
|
117
|
-
async
|
|
135
|
+
async update() {
|
|
118
136
|
if (!this.localStatus) {
|
|
119
137
|
await this.loadLocal();
|
|
120
138
|
}
|
|
@@ -127,6 +145,7 @@ export default class HeadHtmlSupport {
|
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
async replace(source) {
|
|
148
|
+
await this.update();
|
|
130
149
|
if (!this.isModified) {
|
|
131
150
|
this.log.trace('head.html ignored: not modified locally.');
|
|
132
151
|
return source;
|
|
@@ -78,15 +78,13 @@ export class HelixProject extends BaseProject {
|
|
|
78
78
|
log: this.log,
|
|
79
79
|
proxyUrl: this.proxyUrl,
|
|
80
80
|
});
|
|
81
|
-
await this._headHtml.init();
|
|
82
81
|
|
|
83
82
|
// register local head in live-reload
|
|
84
83
|
if (this.liveReload) {
|
|
85
84
|
this.liveReload.registerFiles([this._headHtml.filePath], '/');
|
|
86
85
|
this.liveReload.on('modified', async (modified) => {
|
|
87
86
|
if (modified.indexOf('/') >= 0) {
|
|
88
|
-
|
|
89
|
-
await this._headHtml.init();
|
|
87
|
+
this._headHtml.invalidateLocal();
|
|
90
88
|
}
|
|
91
89
|
});
|
|
92
90
|
}
|
|
@@ -42,6 +42,7 @@ export class HelixServer extends BaseServer {
|
|
|
42
42
|
async handleProxyModeRequest(req, res) {
|
|
43
43
|
const sendFile = promisify(res.sendFile).bind(res);
|
|
44
44
|
const ctx = new RequestContext(req, this._project);
|
|
45
|
+
const { id } = ctx;
|
|
45
46
|
const { log } = this;
|
|
46
47
|
const proxyUrl = new URL(this._project.proxyUrl);
|
|
47
48
|
|
|
@@ -56,10 +57,19 @@ export class HelixServer extends BaseServer {
|
|
|
56
57
|
if (liveReload) {
|
|
57
58
|
liveReload.startRequest(ctx.requestId, ctx.path);
|
|
58
59
|
}
|
|
60
|
+
const pfx = log.level === 'silly' ? `[${id}] ` : '';
|
|
61
|
+
|
|
62
|
+
if (log.level === 'silly') {
|
|
63
|
+
log.trace(`${pfx}>>>--------------------------`);
|
|
64
|
+
log.trace(`${pfx}> ${req.method} ${req.url}`);
|
|
65
|
+
Object.entries(req.headers).forEach(([name, value]) => {
|
|
66
|
+
log.trace(`${pfx}> ${name}: ${value}`);
|
|
67
|
+
});
|
|
68
|
+
log.trace(`[${id}]`);
|
|
69
|
+
}
|
|
59
70
|
|
|
60
71
|
// try to serve static
|
|
61
72
|
try {
|
|
62
|
-
log.debug('trying to serve local file', filePath);
|
|
63
73
|
await sendFile(filePath, {
|
|
64
74
|
dotfiles: 'allow',
|
|
65
75
|
headers: {
|
|
@@ -69,9 +79,13 @@ export class HelixServer extends BaseServer {
|
|
|
69
79
|
if (liveReload) {
|
|
70
80
|
liveReload.registerFile(ctx.requestId, filePath);
|
|
71
81
|
}
|
|
82
|
+
log.debug(`${pfx}served local file ${filePath}`);
|
|
72
83
|
return;
|
|
73
84
|
} catch (e) {
|
|
74
|
-
log.debug(
|
|
85
|
+
log.debug(`${pfx}unable to deliver local file ${ctx.path} - ${e.stack || e}`);
|
|
86
|
+
if (res.headersSent) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
75
89
|
} finally {
|
|
76
90
|
if (liveReload) {
|
|
77
91
|
liveReload.endRequest(ctx.requestId);
|
|
@@ -92,7 +106,7 @@ export class HelixServer extends BaseServer {
|
|
|
92
106
|
file404html: this._project.file404html,
|
|
93
107
|
});
|
|
94
108
|
} catch (err) {
|
|
95
|
-
log.error(
|
|
109
|
+
log.error(`${pfx}failed to proxy AEM request ${ctx.path}: ${err.message}`);
|
|
96
110
|
res.status(502).send(`Failed to proxy AEM request: ${err.message}`);
|
|
97
111
|
}
|
|
98
112
|
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import { parse } from 'url';
|
|
13
13
|
import utils from './utils.js';
|
|
14
14
|
|
|
15
|
+
let id = 0;
|
|
16
|
+
|
|
15
17
|
/**
|
|
16
18
|
* Context that is used during request handling.
|
|
17
19
|
*
|
|
@@ -26,6 +28,8 @@ export default class RequestContext {
|
|
|
26
28
|
const { url } = req;
|
|
27
29
|
this._cfg = cfg || {};
|
|
28
30
|
this._url = url;
|
|
31
|
+
// eslint-disable-next-line no-plusplus
|
|
32
|
+
this._id = id++;
|
|
29
33
|
const purl = parse(url);
|
|
30
34
|
this._path = purl.pathname || '/';
|
|
31
35
|
this._queryString = purl.search || '';
|
|
@@ -74,6 +78,14 @@ export default class RequestContext {
|
|
|
74
78
|
this._resourcePath = relPath;
|
|
75
79
|
}
|
|
76
80
|
|
|
81
|
+
/**
|
|
82
|
+
* returns the request id / counter
|
|
83
|
+
* @return {*|number}
|
|
84
|
+
*/
|
|
85
|
+
get id() {
|
|
86
|
+
return this._id;
|
|
87
|
+
}
|
|
88
|
+
|
|
77
89
|
/**
|
|
78
90
|
* the original request url
|
|
79
91
|
*/
|
package/src/server/utils.js
CHANGED
|
@@ -215,7 +215,8 @@ window.LiveReloadOptions = {
|
|
|
215
215
|
* @return {Promise} A promise that resolves when the stream is done.
|
|
216
216
|
*/
|
|
217
217
|
async proxyRequest(ctx, url, req, res, opts = {}) {
|
|
218
|
-
|
|
218
|
+
const { id } = ctx;
|
|
219
|
+
ctx.log.debug(`[${id}] Proxy ${req.method} request to ${url}`);
|
|
219
220
|
|
|
220
221
|
if (opts.cacheDirectory) {
|
|
221
222
|
const cached = await utils.getFromCache(url, opts.cacheDirectory, ctx.log);
|
|
@@ -263,7 +264,7 @@ window.LiveReloadOptions = {
|
|
|
263
264
|
});
|
|
264
265
|
const contentType = ret.headers.get('content-type') || 'text/plain';
|
|
265
266
|
const level = utils.status2level(ret.status, true);
|
|
266
|
-
ctx.log[level](`Proxy ${req.method} request to ${url}: ${ret.status} (${contentType})`);
|
|
267
|
+
ctx.log[level](`[${id}] Proxy ${req.method} request to ${url}: ${ret.status} (${contentType})`);
|
|
267
268
|
|
|
268
269
|
// because fetch decodes the response, we need to reset content encoding and length
|
|
269
270
|
const respHeaders = Object.fromEntries(ret.headers.entries());
|
|
@@ -271,11 +272,12 @@ window.LiveReloadOptions = {
|
|
|
271
272
|
delete respHeaders['content-length'];
|
|
272
273
|
delete respHeaders['x-frame-options'];
|
|
273
274
|
delete respHeaders['content-security-policy'];
|
|
275
|
+
delete respHeaders.connection;
|
|
274
276
|
respHeaders['access-control-allow-origin'] = '*';
|
|
275
277
|
respHeaders.via = `${ret.httpVersion ?? '1.0'} ${new URL(url).hostname}`;
|
|
276
278
|
|
|
277
279
|
if (ret.status === 404 && contentType.indexOf('text/html') === 0 && opts.file404html) {
|
|
278
|
-
ctx.log.debug(
|
|
280
|
+
ctx.log.debug(`[${id}] serve local 404.html ${opts.file404html}`);
|
|
279
281
|
let textBody = await fs.readFile(opts.file404html, 'utf-8');
|
|
280
282
|
if (opts.injectLiveReload) {
|
|
281
283
|
textBody = utils.injectLiveReloadScript(textBody, ctx.config.server);
|
|
@@ -289,39 +291,25 @@ window.LiveReloadOptions = {
|
|
|
289
291
|
|
|
290
292
|
const isHTML = ret.status === 200 && contentType.indexOf('text/html') === 0;
|
|
291
293
|
const livereload = isHTML && opts.injectLiveReload;
|
|
292
|
-
const replaceHead = isHTML && opts.headHtml
|
|
294
|
+
const replaceHead = isHTML && opts.headHtml;
|
|
293
295
|
const doIndex = isHTML && opts.indexer && url.indexOf('.plain.html') < 0;
|
|
294
296
|
|
|
297
|
+
if (ctx.log.level === 'silly') {
|
|
298
|
+
ctx.log.trace(`[${id}] -----------------------------`);
|
|
299
|
+
ctx.log.trace(`[${id}] < http/${ret.httpVersion} ${ret.status} ${ret.statusText}`);
|
|
300
|
+
Object.entries(ret.headers.raw()).forEach(([name, value]) => {
|
|
301
|
+
ctx.log.trace(`[${id}] < ${name}: ${value}`);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
295
305
|
if (isHTML) {
|
|
296
|
-
let
|
|
297
|
-
let textBody;
|
|
298
|
-
if (contentType.startsWith('text/')) {
|
|
299
|
-
textBody = await ret.text();
|
|
300
|
-
} else {
|
|
301
|
-
respBody = await ret.buffer();
|
|
302
|
-
}
|
|
303
|
-
const lines = ['----------------------------->'];
|
|
306
|
+
let textBody = await ret.text();
|
|
304
307
|
if (ctx.log.level === 'silly') {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
lines.push(`${name}: ${value}`);
|
|
308
|
-
});
|
|
309
|
-
lines.push('');
|
|
310
|
-
lines.push('<-----------------------------');
|
|
311
|
-
lines.push('');
|
|
312
|
-
lines.push(`http/${ret.httpVersion} ${ret.status} ${ret.statusText}`);
|
|
313
|
-
ret.headers.forEach((name, value) => {
|
|
314
|
-
lines.push(`${name}: ${value}`);
|
|
315
|
-
});
|
|
316
|
-
lines.push('');
|
|
317
|
-
if (respBody) {
|
|
318
|
-
lines.push(`<binary ${respBody.length} bytes>`);
|
|
319
|
-
} else {
|
|
320
|
-
lines.push(textBody);
|
|
321
|
-
}
|
|
322
|
-
ctx.log.trace(lines.join('\n'));
|
|
308
|
+
ctx.log.trace(textBody);
|
|
309
|
+
ctx.log.trace(`[${id}] <<<--------------------------`);
|
|
323
310
|
}
|
|
324
311
|
if (replaceHead) {
|
|
312
|
+
await opts.headHtml.setCookie(req.headers.cookie);
|
|
325
313
|
textBody = await opts.headHtml.replace(textBody);
|
|
326
314
|
}
|
|
327
315
|
if (livereload) {
|
|
@@ -343,7 +331,7 @@ window.LiveReloadOptions = {
|
|
|
343
331
|
url,
|
|
344
332
|
opts.cacheDirectory,
|
|
345
333
|
{
|
|
346
|
-
body:
|
|
334
|
+
body: textBody,
|
|
347
335
|
headers: respHeaders,
|
|
348
336
|
status: ret.status,
|
|
349
337
|
},
|
|
@@ -352,12 +340,16 @@ window.LiveReloadOptions = {
|
|
|
352
340
|
}
|
|
353
341
|
|
|
354
342
|
res
|
|
355
|
-
.status(ret.status)
|
|
356
343
|
.set(respHeaders)
|
|
357
|
-
.
|
|
344
|
+
.status(ret.status)
|
|
345
|
+
.send(textBody);
|
|
358
346
|
return;
|
|
359
347
|
}
|
|
360
348
|
|
|
349
|
+
if (ctx.log.level === 'silly') {
|
|
350
|
+
ctx.log.trace(`[${id}] <<<--------------------------`);
|
|
351
|
+
}
|
|
352
|
+
|
|
361
353
|
if (opts.cacheDirectory) {
|
|
362
354
|
const buffer = await ret.buffer();
|
|
363
355
|
await utils.writeToCache(
|
package/src/up.cmd.js
CHANGED
|
@@ -119,7 +119,9 @@ export default class UpCommand extends AbstractServerCommand {
|
|
|
119
119
|
const resp = await getFetch()(fstabUrl);
|
|
120
120
|
await resp.buffer();
|
|
121
121
|
if (!resp.ok) {
|
|
122
|
-
if (
|
|
122
|
+
if (resp.status === 401) {
|
|
123
|
+
this.log.warn(chalk`Unable to verify {yellow ${ref}} branch via {blue ${fstabUrl}} for authenticated sites.`);
|
|
124
|
+
} else if (ref === 'main') {
|
|
123
125
|
this.log.warn(chalk`Unable to verify {yellow main} branch via {blue ${fstabUrl}} (${resp.status}). Maybe not pushed yet?`);
|
|
124
126
|
} else {
|
|
125
127
|
this.log.warn(chalk`Unable to verify {yellow ${ref}} branch on {blue ${fstabUrl}} (${resp.status}). Fallback to {yellow main} branch.`);
|