@jsenv/core 41.5.35 → 41.5.37

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.
@@ -17,6 +17,7 @@ import {
17
17
  browserDefaultRuntimeCompat,
18
18
  inferRuntimeCompatFromClosestPackage,
19
19
  } from "@jsenv/runtime-compat";
20
+ import { X509Certificate } from "node:crypto";
20
21
  import { existsSync } from "node:fs";
21
22
  import { pathToFileURL } from "node:url";
22
23
 
@@ -31,6 +32,10 @@ import { jsenvPluginPatches } from "../plugins/patches/jsenv_plugin_patches.js";
31
32
  import { getCorePlugins } from "../plugins/plugins.js";
32
33
  import { jsenvPluginServerEvents } from "../plugins/server_events/jsenv_plugin_server_events.js";
33
34
  import { devServerPluginChromeDevToolsJson } from "./dev_server_plugins/dev_server_plugin_chrome_devtools_json.js";
35
+ import {
36
+ HTTPS_TRUST_PATHNAME,
37
+ devServerPluginHttpsTrust,
38
+ } from "./dev_server_plugins/dev_server_plugin_https_trust.js";
34
39
  import { devServerPluginInjectServerResponseHeader } from "./dev_server_plugins/dev_server_plugin_inject_server_response_header.js";
35
40
  import { devServerPluginOmegaErrorHandler } from "./dev_server_plugins/dev_server_plugin_omega_error_handler.js";
36
41
  import { devServerPluginServeSourceFiles } from "./dev_server_plugins/dev_server_plugin_serve_source_files.js";
@@ -51,7 +56,7 @@ const EXECUTED_BY_TEST_PLAN = process.argv.includes("--jsenv-test");
51
56
  * @param {number} [params.port=3456] - Port to listen on (0 = a free port).
52
57
  * @param {string} [params.hostname] - Hostname to bind to.
53
58
  * @param {boolean} [params.acceptAnyIp=false] - Also accept connections on the machine's IPs (so other devices on the network — a phone — can reach the server). Off by default: exposing the dev server beyond localhost is an explicit choice, not something a dev tool decides.
54
- * @param {boolean|object} [params.https=false] - HTTPS as `{ certificate, privateKey }`.
59
+ * @param {object} [params.https] - HTTPS as `{ certificate, privateKey }` (PEM strings). With `rootCertificate` (the PEM of the authority that signed `certificate`) and `acceptAnyIp`, the dev server also serves `http://<ip>:<port>/.internal/https/`, a page to trust that authority on a phone (see docs/users/b_dev/b_dev.md, "Trusting it on a phone").
55
60
  * @param {boolean} [params.http2=false] - HTTP/2 (requires https).
56
61
  * @param {Array} [params.plugins=[]] - jsenv plugins (transformUrlContent, serverRoutes, serverEvents, effect, …).
57
62
  * @param {Array} [params.serverPlugins=[]] - `@jsenv/server`-level plugins.
@@ -184,6 +189,7 @@ export const startDevServer = async ({
184
189
  );
185
190
  }
186
191
  }
192
+ let httpsTrust = null;
187
193
  // params normalization
188
194
  {
189
195
  if (runtimeCompat === undefined) {
@@ -198,6 +204,16 @@ export const startDevServer = async ({
198
204
  if (clientAutoreload === false) {
199
205
  clientAutoreload = { enabled: false };
200
206
  }
207
+ if (https) {
208
+ const { certificate, privateKey, rootCertificate } = https;
209
+ https = { certificate, privateKey };
210
+ // a localhost-only server has no phone to serve
211
+ if (rootCertificate && acceptAnyIp) {
212
+ httpsTrust = {
213
+ rootCertificate: new X509Certificate(rootCertificate),
214
+ };
215
+ }
216
+ }
201
217
  }
202
218
 
203
219
  const logger = createLogger({ logLevel });
@@ -355,6 +371,7 @@ export const startDevServer = async ({
355
371
  }),
356
372
  // chrome devtools
357
373
  devServerPluginChromeDevToolsJson({ sourceDirectoryUrl }),
374
+ ...(httpsTrust ? [devServerPluginHttpsTrust(httpsTrust)] : []),
358
375
  ...serverPlugins,
359
376
  devServerPluginServeSourceFiles({
360
377
  packageDirectory,
@@ -393,6 +410,12 @@ export const startDevServer = async ({
393
410
  startLog: false,
394
411
 
395
412
  https,
413
+ ...(httpsTrust
414
+ ? {
415
+ redirectHttpToHttps: ({ resource }) =>
416
+ !resource.startsWith(HTTPS_TRUST_PATHNAME),
417
+ }
418
+ : {}),
396
419
  http2,
397
420
  acceptAnyIp,
398
421
  hostname,
@@ -418,6 +441,17 @@ export const startDevServer = async ({
418
441
  logger.info(``);
419
442
  Object.keys(server.origins).forEach((key) => {
420
443
  logger.info(`- ${server.origins[key]}`);
444
+ if (httpsTrust && key === "externalip") {
445
+ const externalHostname = new URL(server.origins.externalip).hostname;
446
+ const httpsTrustPageUrl = new URL(
447
+ HTTPS_TRUST_PATHNAME,
448
+ `http://${externalHostname}:${server.port}`,
449
+ );
450
+ logger.info(` trust it on a phone: ${httpsTrustPageUrl}`);
451
+ logger.info(
452
+ ` root certificate SHA-256: ${httpsTrust.rootCertificate.fingerprint256}`,
453
+ );
454
+ }
421
455
  });
422
456
  logger.info(``);
423
457
  return {