@adobe/helix-deploy 4.12.2 → 4.15.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 CHANGED
@@ -1,3 +1,59 @@
1
+ # [4.15.0](https://github.com/adobe/helix-deploy/compare/v4.14.1...v4.15.0) (2021-11-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * improve development server setup ([#338](https://github.com/adobe/helix-deploy/issues/338)) ([9d2b44a](https://github.com/adobe/helix-deploy/commit/9d2b44a6a8d14953fe351dd0b8362ebdfd4a9a6b))
7
+
8
+ ## [4.14.1](https://github.com/adobe/helix-deploy/compare/v4.14.0...v4.14.1) (2021-11-15)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * update package in gateway is missing dicts ([854b87e](https://github.com/adobe/helix-deploy/commit/854b87e929dcbdc22bbc33d03f60dcf3dcd82a7b))
14
+
15
+ # [4.14.0](https://github.com/adobe/helix-deploy/compare/v4.13.0...v4.14.0) (2021-11-12)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **bundler:** add browser-based shims as fallbacks for node core API when running in edge compute ([8f54188](https://github.com/adobe/helix-deploy/commit/8f541884f456c6c28dcb252a4fb97b88c355c272))
21
+ * **bundler:** exclude google storage dependency (included in Google Runtime) ([024f09f](https://github.com/adobe/helix-deploy/commit/024f09f2a123b8f23643f7a894a0e195515113a9))
22
+ * **bundler:** use `globalThis` for increased compatibility with Cloudflare Workers ([8fef7bf](https://github.com/adobe/helix-deploy/commit/8fef7bf86ef0dd42e2b209d9774df3af6402adb1))
23
+ * **cloudflare:** remove hard-coded account id ([3a11aa1](https://github.com/adobe/helix-deploy/commit/3a11aa1602b4766c7327029a521f03068ef2824e))
24
+ * **fastly:** fix package param code ([c77fe82](https://github.com/adobe/helix-deploy/commit/c77fe82c5b5cff3cc27efaeb57ecb2043acbb66e))
25
+ * **fastly:** fix test domain subdomain ([ec60b2a](https://github.com/adobe/helix-deploy/commit/ec60b2a527ac8946ee5e6b44045e4ceedf8aec51))
26
+ * **gateway:** do not attempt to perform empty updates ([e99f4be](https://github.com/adobe/helix-deploy/commit/e99f4be0af2c6a82ee098f88399b16c7d59bd027))
27
+ * **gateway:** do not create duplicate dictionaries ([2ca6eca](https://github.com/adobe/helix-deploy/commit/2ca6eca36af22b74a3eacb43ea3a9adbda869bb8))
28
+ * **gateway:** further massaging of gateway syntax ([4935538](https://github.com/adobe/helix-deploy/commit/49355385adfeb9589ff676e23fa0077a6682208b))
29
+ * **gateway:** set content type of synthetic response ([873ea06](https://github.com/adobe/helix-deploy/commit/873ea067c7ff42e598605e7e0fb0d576fec51b07))
30
+ * **gateway:** tweak synthetic syntax, again ([00f47e3](https://github.com/adobe/helix-deploy/commit/00f47e3cdc820333a56bdf1bc6524474a90d2eed))
31
+ * **gateway:** use correct dictionary name ([91d219e](https://github.com/adobe/helix-deploy/commit/91d219e7deefb43d69850d7022cfe5aba1c894da))
32
+ * **gateway:** use correct number of commas in JSON ([3250f6c](https://github.com/adobe/helix-deploy/commit/3250f6c2b11bd63541e8d45d28cdd55301f7bd75))
33
+ * **template:** better check for existence of `addEventListener` ([c5d8568](https://github.com/adobe/helix-deploy/commit/c5d85684edb130a473930516b617b4a78214801d))
34
+
35
+
36
+ ### Features
37
+
38
+ * **bundler:** add working cloudflare support ([09635c2](https://github.com/adobe/helix-deploy/commit/09635c2c13679e401bcc6d2b9bebe0208399b38b))
39
+ * **cloudflare:** add support for `context.env` in wrapper ([ecc0e7b](https://github.com/adobe/helix-deploy/commit/ecc0e7bda6a55a43b81c81e0279813688889c599))
40
+ * **cloudflare:** create KV store for package params, use env vars for action params ([d63144d](https://github.com/adobe/helix-deploy/commit/d63144d8569e3ef71f96b9eb41cdc1dfbc0aae25))
41
+ * **cloudflare:** deploy edge script as cloudflare worker script ([66ed931](https://github.com/adobe/helix-deploy/commit/66ed931101e3b560f665d3b9d0f151c18facc72b))
42
+ * **cloudflare:** minimal wrapper script ([bb56dcd](https://github.com/adobe/helix-deploy/commit/bb56dcd7ea5967ddccbc1aa354e43d63873d735a))
43
+ * **fastly:** create bundle and deploy to compute at edge ([476a605](https://github.com/adobe/helix-deploy/commit/476a605e7f19aec316e093d82f7f411f2fbc0a85))
44
+ * **fastly:** use parameters from edge dictionary ([0c586a7](https://github.com/adobe/helix-deploy/commit/0c586a78b0c0a2aa2314f914d562a5008338c568))
45
+ * **gateway:** add basic support for package parameters in gateway ([36519e4](https://github.com/adobe/helix-deploy/commit/36519e40a1a24b4a329318b97557d06797ed8cd2))
46
+ * **gateway:** set shared secret (token) for package parameters ([adcbd92](https://github.com/adobe/helix-deploy/commit/adcbd92b27a91ffb44a8f115da7230a6095fe37f))
47
+ * **gateway:** update package parameters in dictionary ([5315dd4](https://github.com/adobe/helix-deploy/commit/5315dd46098cd5d4ed1c0d8eb391487d6c2dad5a))
48
+ * **template:** support service worker API as main entry point for Fastly/Cloudflare ([ba0ec25](https://github.com/adobe/helix-deploy/commit/ba0ec25b148de242ae207e4013641dac9223df22))
49
+
50
+ # [4.13.0](https://github.com/adobe/helix-deploy/compare/v4.12.2...v4.13.0) (2021-11-10)
51
+
52
+
53
+ ### Features
54
+
55
+ * option to use rollup as bundler that supports ESM modules ([186ceaa](https://github.com/adobe/helix-deploy/commit/186ceaad5dbcaa931335f2dc519e4cb4cc0c5d31)), closes [#228](https://github.com/adobe/helix-deploy/issues/228) [#227](https://github.com/adobe/helix-deploy/issues/227)
56
+
1
57
  ## [4.12.2](https://github.com/adobe/helix-deploy/compare/v4.12.1...v4.12.2) (2021-11-06)
2
58
 
3
59
 
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Helix Deploy
2
- > A multi-cloud deployment tool for serverless functions running on AWS Lambda, Adobe I/O Runtime, Azure Functions, and Google Cloud Functions. Write once, run everywhere.
2
+ > A multi-cloud deployment tool for serverless and edge-compute functions running on AWS Lambda, Adobe I/O Runtime, Azure Functions, Google Cloud Functions, Cloudflare Workers, and Fastly Compute@Edge. Write once, run everywhere.
3
3
 
4
4
  ## Status
5
5
  [![GitHub license](https://img.shields.io/github/license/adobe/helix-deploy.svg)](https://github.com/adobe/helix-deploy/blob/main/LICENSE.txt)
package/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
  const ActionBuilder = require('./src/ActionBuilder.js');
13
- const Bundler = require('./src/Bundler.js');
13
+ const Bundler = require('./src/bundler/WebpackBundler.js');
14
14
  const BaseConfig = require('./src/BaseConfig.js');
15
15
  const CLI = require('./src/cli.js');
16
16
  const DevelopmentServer = require('./src/DevelopmentServer.js');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-deploy",
3
- "version": "4.12.2",
3
+ "version": "4.15.0",
4
4
  "description": "Library and Commandline Tools to build and deploy OpenWhisk Actions",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -11,7 +11,8 @@
11
11
  "url": "https://github.com/adobe/helix-deploy/issues"
12
12
  },
13
13
  "keywords": [
14
- "openwhisk"
14
+ "helix",
15
+ "serverless"
15
16
  ],
16
17
  "main": "index.js",
17
18
  "bin": {
@@ -30,35 +31,46 @@
30
31
  "require": "test/setup-env.js"
31
32
  },
32
33
  "peerDependencies": {
33
- "@adobe/helix-universal": ">=1.6.0"
34
+ "@adobe/helix-universal": ">=1.9.0"
34
35
  },
35
36
  "dependencies": {
36
37
  "@adobe/fastly-native-promises": "2.0.0",
37
38
  "@adobe/helix-fetch": "3.0.0",
38
- "@aws-sdk/client-apigatewayv2": "3.40.0",
39
- "@aws-sdk/client-lambda": "3.40.0",
40
- "@aws-sdk/client-s3": "3.40.0",
41
- "@aws-sdk/client-secrets-manager": "3.40.0",
42
- "@aws-sdk/client-ssm": "3.40.0",
39
+ "@aws-sdk/client-apigatewayv2": "3.41.0",
40
+ "@aws-sdk/client-lambda": "3.41.0",
41
+ "@aws-sdk/client-s3": "3.41.0",
42
+ "@aws-sdk/client-secrets-manager": "3.41.0",
43
+ "@aws-sdk/client-ssm": "3.41.0",
43
44
  "@azure/arm-appservice": "9.0.0",
44
45
  "@azure/ms-rest-nodeauth": "3.1.0",
46
+ "@fastly/js-compute": "0.2.1",
45
47
  "@google-cloud/functions": "1.2.0",
46
48
  "@google-cloud/secret-manager": "3.10.1",
47
- "@google-cloud/storage": "5.15.5",
49
+ "@google-cloud/storage": "5.16.0",
50
+ "@rollup/plugin-alias": "3.1.8",
51
+ "@rollup/plugin-commonjs": "21.0.1",
52
+ "@rollup/plugin-json": "4.1.0",
53
+ "@rollup/plugin-node-resolve": "13.0.6",
48
54
  "archiver": "5.3.0",
49
55
  "chalk": "4.1.2",
56
+ "constants-browserify": "1.0.0",
50
57
  "dotenv": "10.0.0",
51
58
  "express": "4.17.1",
59
+ "form-data": "4.0.0",
52
60
  "fs-extra": "10.0.0",
61
+ "get-stream": "6.0.1",
53
62
  "isomorphic-git": "1.10.1",
54
- "openwhisk": "3.21.4",
63
+ "openwhisk": "3.21.5",
55
64
  "proxyquire": "2.1.3",
65
+ "rollup": "2.60.0",
66
+ "rollup-plugin-terser": "7.0.2",
56
67
  "semver": "7.3.5",
57
- "webpack": "5.62.1",
68
+ "tar": "6.1.11",
69
+ "webpack": "5.64.0",
58
70
  "yargs": "17.2.1"
59
71
  },
60
72
  "devDependencies": {
61
- "@adobe/eslint-config-helix": "1.3.0",
73
+ "@adobe/eslint-config-helix": "1.3.1",
62
74
  "@adobe/helix-shared-wrap": "1.0.0",
63
75
  "@adobe/helix-status": "9.2.4",
64
76
  "@adobe/helix-universal-logger": "2.0.0",
@@ -69,16 +81,16 @@
69
81
  "codecov": "3.8.3",
70
82
  "eslint": "8.2.0",
71
83
  "eslint-plugin-header": "3.1.1",
72
- "eslint-plugin-import": "2.25.2",
84
+ "eslint-plugin-import": "2.25.3",
73
85
  "ghooks": "2.0.4",
74
- "lint-staged": "11.2.6",
86
+ "lint-staged": "12.0.2",
75
87
  "mocha": "9.1.3",
76
88
  "mocha-junit-reporter": "2.0.2",
77
89
  "mocha-multi-reporters": "1.5.1",
78
- "nock": "13.1.4",
90
+ "nock": "13.2.1",
79
91
  "nyc": "15.1.0",
80
92
  "semantic-release": "18.0.0",
81
- "sinon": "11.1.2",
93
+ "sinon": "12.0.1",
82
94
  "yauzl": "2.10.0"
83
95
  },
84
96
  "engines": {
@@ -9,14 +9,28 @@
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
+ /* eslint-disable no-await-in-loop */
12
13
 
13
14
  const path = require('path');
14
15
  const fs = require('fs');
15
16
  const fse = require('fs-extra');
16
17
  const chalk = require('chalk');
17
18
  const git = require('isomorphic-git');
18
- const Bundler = require('./Bundler');
19
+ const WebpackBundler = require('./bundler/WebpackBundler');
20
+ const EdgeBundler = require('./bundler/EdgeBundler');
21
+ const RollupBundler = require('./bundler/RollupBundler');
19
22
  const { version } = require('../package.json');
23
+
24
+ const Bundlers = {
25
+ node: {
26
+ webpack: WebpackBundler,
27
+ rollup: RollupBundler,
28
+ },
29
+ edge: {
30
+ webpack: EdgeBundler,
31
+ },
32
+ };
33
+
20
34
  /**
21
35
  * Returns the `origin` remote url or `''` if none is defined.
22
36
  *
@@ -184,7 +198,11 @@ module.exports = class ActionBuilder {
184
198
  cfg.zipFile = path.resolve(cfg.distDir, cfg.packageName, `${cfg.name}.zip`);
185
199
  }
186
200
  if (!cfg.bundle) {
187
- cfg.bundle = path.resolve(cfg.distDir, cfg.packageName, `${cfg.name}-bundle.cjs`);
201
+ const ext = cfg.esm ? '.mjs' : '.cjs';
202
+ cfg.bundle = path.resolve(cfg.distDir, cfg.packageName, `${cfg.name}-bundle${ext}`);
203
+ }
204
+ if (!cfg.edgeBundle) {
205
+ cfg.edgeBundle = path.resolve(cfg.distDir, cfg.packageName, `${cfg.name}-edge-bundle.cjs`);
188
206
  }
189
207
  if (!cfg.depFile) {
190
208
  cfg.depFile = path.resolve(cfg.distDir, cfg.packageName, `${cfg.name}-dependencies.json`);
@@ -254,6 +272,30 @@ module.exports = class ActionBuilder {
254
272
  this.validated = true;
255
273
  }
256
274
 
275
+ async validateBundlers() {
276
+ if (this.validated) {
277
+ return;
278
+ }
279
+ // disable edge build
280
+
281
+ const { cfg } = this;
282
+ this.bundlers = [];
283
+ cfg.archs.forEach((arch) => {
284
+ const bnds = Bundlers[arch];
285
+ if (!bnds) {
286
+ throw Error(`Invalid arch '${arch}' specified. Valid options are: ${Object.keys(Bundlers)}`);
287
+ }
288
+ const BundlerClass = bnds[cfg.bundler];
289
+ if (!BundlerClass) {
290
+ throw Error(`Invalid bundler '${cfg.bundler}' for '${arch}'. Valid options are: ${Object.keys(bnds)}`);
291
+ }
292
+ this.bundlers.push(new BundlerClass().withConfig(cfg));
293
+ });
294
+ for (const bundler of this.bundlers) {
295
+ await bundler.init();
296
+ }
297
+ }
298
+
257
299
  async execute(fnName, msg, ...args) {
258
300
  const { cfg } = this;
259
301
  const deps = Object.values(this._deployers)
@@ -333,14 +375,14 @@ module.exports = class ActionBuilder {
333
375
  cfg.log.info(chalk`{grey universal-action-builder v${version}}`);
334
376
  await this.validate();
335
377
  await this.validateAdditionalTasks();
336
-
337
- const bundler = new Bundler().withConfig(cfg);
338
- await bundler.init();
378
+ await this.validateBundlers();
339
379
 
340
380
  if (cfg.build) {
341
- await bundler.createBundle();
342
- await bundler.createArchive();
343
- await bundler.validateBundle();
381
+ for (const bundler of this.bundlers) {
382
+ await bundler.createBundle();
383
+ await bundler.createArchive();
384
+ await bundler.validateBundle();
385
+ }
344
386
  }
345
387
 
346
388
  if (cfg.updatePackage) {
@@ -354,7 +396,9 @@ module.exports = class ActionBuilder {
354
396
  const relZip = path.relative(process.cwd(), cfg.zipFile);
355
397
  cfg.log.info(chalk`{green ok:} using: {yellow ${relZip}}.`);
356
398
  cfg.dependencies = await fse.readJson(cfg.depFile);
357
- await bundler.validateBundle();
399
+ for (const bundler of this.bundlers) {
400
+ await bundler.validateBundle();
401
+ }
358
402
  }
359
403
  await this.deploy();
360
404
  }
@@ -379,22 +423,32 @@ module.exports = class ActionBuilder {
379
423
  await this.runCleanup();
380
424
  }
381
425
 
426
+ // update gateway
382
427
  if (this._gateways.fastly && this._gateways.fastly.ready()) {
383
- await this.validateDeployers();
384
- Object.values(this._deployers).forEach((d) => {
385
- this._gateways.fastly.withDeployer(d);
386
- });
428
+ Object.values(this._deployers)
429
+ .filter((deployer) => !deployer.noGatewayBackend)
430
+ .forEach((deplyer) => {
431
+ this._gateways.fastly.withDeployer(deplyer);
432
+ });
387
433
 
388
- this._gateways.fastly.init();
389
- await this._gateways.fastly.deploy();
390
- }
434
+ const updateLinks = cfg.links && cfg.links.length;
435
+ const updatePackage = cfg.updatePackage && cfg.packageParams && cfg.packageToken;
391
436
 
392
- if (this._gateways.fastly
393
- && this._gateways.fastly.updateable()
394
- && cfg.links && cfg.links.length) {
395
- await this.validateDeployers();
396
- this._gateways.fastly.init();
397
- await this._gateways.fastly.updateLinks(cfg.links, cfg.version);
437
+ // links and package also need a deployed gateway
438
+ if ((updateLinks || updatePackage || cfg.deploy)) {
439
+ this._gateways.fastly.init();
440
+ if (this._gateways.fastly.canDeploy()) {
441
+ await this._gateways.fastly.deploy();
442
+ }
443
+ }
444
+
445
+ if (updateLinks) {
446
+ await this._gateways.fastly.updateLinks(cfg.links, cfg.version);
447
+ }
448
+
449
+ if (updatePackage) {
450
+ await this._gateways.fastly.updatePackage();
451
+ }
398
452
  }
399
453
 
400
454
  if (cfg.deploy) {
package/src/BaseConfig.js CHANGED
@@ -87,6 +87,9 @@ class BaseConfig {
87
87
  updatedBy: null,
88
88
  targets: [],
89
89
  functionURL: '',
90
+ esm: false,
91
+ archs: ['node', 'edge'],
92
+ bundler: 'webpack',
90
93
  format: {
91
94
  aws: DEFAULT_ACTION_FORMAT,
92
95
  },
@@ -162,8 +165,11 @@ class BaseConfig {
162
165
  .withVerbose(argv.verbose)
163
166
  .withDirectory(argv.directory)
164
167
  .withTarget(argv.target)
168
+ .withArch(argv.arch)
165
169
  .withBuild(argv.build)
166
170
  .withMinify(argv.minify)
171
+ .withESM(argv.esm)
172
+ .withBundler(argv.bundler)
167
173
  .withDelete(argv.delete)
168
174
  .withDeploy(argv.deploy)
169
175
  .withTest(argv.test)
@@ -199,6 +205,7 @@ class BaseConfig {
199
205
  .withCleanupPatch(argv.cleanupPatch)
200
206
  .withCleanupMinor(argv.cleanupMinor)
201
207
  .withCleanupMajor(argv.cleanupMajor)
208
+ .withPackageToken(argv.packageToken)
202
209
  .withProperties(argv.property);
203
210
  }
204
211
 
@@ -222,6 +229,16 @@ class BaseConfig {
222
229
  return this;
223
230
  }
224
231
 
232
+ withArch(value) {
233
+ this.archs = [];
234
+ value.forEach((v) => {
235
+ v.split(',').forEach((t) => {
236
+ this.archs.push(t.trim());
237
+ });
238
+ });
239
+ return this;
240
+ }
241
+
225
242
  withDeploy(enable) {
226
243
  this.deploy = enable;
227
244
  return this;
@@ -237,6 +254,16 @@ class BaseConfig {
237
254
  return this;
238
255
  }
239
256
 
257
+ withESM(enable) {
258
+ this.esm = enable;
259
+ return this;
260
+ }
261
+
262
+ withBundler(bundler) {
263
+ this.bundler = bundler;
264
+ return this;
265
+ }
266
+
240
267
  withDelete(enable) {
241
268
  this.delete = enable;
242
269
  return this;
@@ -507,6 +534,11 @@ class BaseConfig {
507
534
  return this;
508
535
  }
509
536
 
537
+ withPackageToken(value) {
538
+ this.packageToken = value;
539
+ return this;
540
+ }
541
+
510
542
  get log() {
511
543
  if (!this._logger) {
512
544
  // poor men's logging...
@@ -572,12 +604,22 @@ class BaseConfig {
572
604
  default: false,
573
605
  })
574
606
 
575
- .group(['minify', 'static', 'entryFile', 'externals', 'modules', 'adapterFile'], 'Build Options')
607
+ .group(['minify', 'static', 'entryFile', 'externals', 'modules', 'adapterFile', 'esm', 'bundler'], 'Build Options')
576
608
  .option('minify', {
577
609
  description: 'Minify the final bundle',
578
610
  type: 'boolean',
579
611
  default: false,
580
612
  })
613
+ .option('bundler', {
614
+ description: 'Select bundler backend (webpack, rollup)',
615
+ type: 'string',
616
+ default: 'webpack',
617
+ })
618
+ .option('esm', {
619
+ description: 'Produce EcmaScript Module (experimental, disables edge arch)',
620
+ type: 'boolean',
621
+ default: false,
622
+ })
581
623
  .option('modules', {
582
624
  alias: 'm',
583
625
  description: 'Include a node_module as is.',
@@ -598,10 +640,16 @@ class BaseConfig {
598
640
  description: 'Specifies the adapter file (the exported module).',
599
641
  })
600
642
  .option('externals', {
601
- description: 'Defines the externals for webpack.',
643
+ description: 'Defines the externals for the bundler.',
602
644
  type: 'array',
603
645
  default: [],
604
646
  })
647
+ .option('arch', {
648
+ description: 'Select archs(s) for bundles (node,edge).',
649
+ type: 'string',
650
+ default: ['node'],
651
+ array: true,
652
+ })
605
653
 
606
654
  .group(['target', 'hints'], 'Deploy Options')
607
655
  .option('target', {
@@ -662,8 +710,10 @@ class BaseConfig {
662
710
  type: 'object',
663
711
  default: {},
664
712
  })
665
- .group(['cleanup-ci', 'cleanup-patch', 'cleanup-minor', 'cleanup-major'],
666
- 'Cleanup Old Deployments: automatically delete redundant versions older than specified. \n Use a pattern like 7d or 1m to specify time frames.\n Use a simple number like --cleanup-ci=5 to retain the last five CI builds')
713
+ .group(
714
+ ['cleanup-ci', 'cleanup-patch', 'cleanup-minor', 'cleanup-major'],
715
+ 'Cleanup Old Deployments: automatically delete redundant versions older than specified. \n Use a pattern like 7d or 1m to specify time frames.\n Use a simple number like --cleanup-ci=5 to retain the last five CI builds',
716
+ )
667
717
  .option('cleanup-ci', {
668
718
  description: 'Automatically delete redundant CI versions',
669
719
  coerce: coerceDate,
@@ -710,6 +760,11 @@ class BaseConfig {
710
760
  return value;
711
761
  },
712
762
  })
763
+ .option('package-token', {
764
+ description: 'Protects access to the gateway-stored package parameters with this token. leave empty to generate random token.',
765
+ type: 'string',
766
+ default: crypto.randomBytes(32).toString('base64'),
767
+ })
713
768
  .option('params', {
714
769
  alias: 'p',
715
770
  description: 'Include the given action param. can be json or env.',
@@ -70,6 +70,7 @@ module.exports = class DevelopmentServer {
70
70
  this._main = main;
71
71
  this._cwd = process.cwd();
72
72
  this._port = process.env.WEBSERVER_PORT || 3000;
73
+ this._xfh = 'helix-pages.anywhere.run';
73
74
  }
74
75
 
75
76
  withPort(value) {
@@ -77,6 +78,11 @@ module.exports = class DevelopmentServer {
77
78
  return this;
78
79
  }
79
80
 
81
+ withXFH(value) {
82
+ this._xfh = value;
83
+ return this;
84
+ }
85
+
80
86
  get port() {
81
87
  return this._port;
82
88
  }
@@ -131,21 +137,21 @@ module.exports = class DevelopmentServer {
131
137
  */
132
138
  async start() {
133
139
  this.app = express();
134
- this.app.use(rawBody());
135
- this.app.use(addRequestHeader('x-forwarded-host', 'helix-pages.anywhere.run'));
136
- this.app.all('*', this._handler);
137
140
  await new Promise((resolve, reject) => {
138
141
  try {
139
142
  this.server = this.app.listen(this._port, () => {
140
143
  this._port = this.server.address().port;
141
144
  // eslint-disable-next-line no-console
142
- console.log(`Started development server on port ${this._port}`);
145
+ console.log(`Started development server at http://localhost:${this._port}/`);
143
146
  resolve();
144
147
  });
145
148
  } catch (e) {
146
149
  reject(e);
147
150
  }
148
151
  });
152
+ this.app.use(rawBody());
153
+ this.app.use(addRequestHeader('x-forwarded-host', this._xfh.replace('{port}', this._port)));
154
+ this.app.all('*', this._handler);
149
155
  }
150
156
 
151
157
  /**