@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 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.6",
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.4",
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 init() {
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
- await this._headHtml.loadLocal();
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(`Error while delivering resource ${ctx.path} - ${e.stack || e}`);
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(`Failed to proxy AEM request ${ctx.path}: ${err.message}`);
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
  */
@@ -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
- ctx.log.debug(`Proxy ${req.method} request to ${url}`);
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('serve local 404.html', opts.file404html);
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 && opts.headHtml.isModified;
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 respBody;
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
- lines.push(`${req.method} ${url}`);
306
- Object.entries(headers).forEach(([name, value]) => {
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: respBody || textBody,
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
- .send(respBody || textBody);
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 (ref === 'main') {
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.`);