From 58e914586bff7f1773ec36ef8c1c854d5363ca10 Mon Sep 17 00:00:00 2001 From: alexander-akait <4567934+alexander-akait@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:02:27 +0000 Subject: [PATCH 1/3] feat(hot): let the client say whose runtime it is in the console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `hot.client.logging` takes an object as well as a level, carrying the name every message is labelled with: hot: { client: { logging: { level: "warn", name: "my-dev-server" } } } The reason is the one that made the overlay's element id an option. The package a developer installed is the one they would report a problem to, and a console labelled with a dependency's name sends them to the wrong repository. It matters most where this runtime is the whole of another package's client — webpack-dev-server, whose users have read `[webpack-dev-server]` for years and would otherwise start reading this package's name instead. Unset, it is this package's name as before. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA --- .changeset/client-logger-name.md | 15 +++++++++++++ README.md | 2 +- client-src/index.js | 27 ++++++++++++++++++++++-- client-src/utils/log.js | 15 +++++++++++-- src/options.check.js | 2 +- src/options.json | 36 ++++++++++++++++++++++++++++++-- test/e2e/messages.test.js | 22 +++++++++++++++++++ types/client/index.d.ts | 4 ++++ types/client/utils/log.d.ts | 4 ++++ 9 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 .changeset/client-logger-name.md diff --git a/.changeset/client-logger-name.md b/.changeset/client-logger-name.md new file mode 100644 index 000000000..244762617 --- /dev/null +++ b/.changeset/client-logger-name.md @@ -0,0 +1,15 @@ +--- +"webpack-dev-middleware": minor +--- + +`hot.client.logging` takes an object as well as a level, so a package embedding this runtime can label the console with its own name: + +```js +middleware(compiler, { + hot: { client: { logging: { level: "warn", name: "my-dev-server" } } }, +}); +``` + +Messages read `[my-dev-server] …` rather than `[webpack-dev-middleware] …`. Unset, it is this package's name as before. + +The reason is the one that made the overlay's element id an option: the package a developer installed is the one they would report a problem to, and a console labelled with a dependency's name sends them to the wrong repository. It matters most where this runtime is the whole of another package's client — webpack-dev-server, whose users have read `[webpack-dev-server]` for years. diff --git a/README.md b/README.md index 32167e518..c8d6bb724 100644 --- a/README.md +++ b/README.md @@ -825,7 +825,7 @@ the rest cannot — see [opting one page out](#opting-one-page-out). | `connect` | `boolean\|{ retries, timeout }` | `true` | Whether to connect when the entry runs, and how the connection is held open. `false` does not connect — call `setOptionsAndConnect()` yourself. `retries` is how many times to reconnect before giving up; unset, `"sse"` keeps trying for as long as the page is open while `"ws"` gives up after `10`. `timeout` is how long silence is tolerated before reconnecting, in milliseconds, and the interval between reconnections — `"sse"` only, since a `"ws"` heartbeat is a protocol ping the browser answers without telling JavaScript. | | `overlay` | `boolean\|Object` | `true` | In-page overlay for problems: a boolean, or a JSON object — see [overlay options](#client-overlay-options). Same value shape as webpack-dev-server's [`client.overlay`](https://webpack.js.org/configuration/dev-server/#overlay), plus a few webpack-dev-middleware extensions. | | `urlPrefix` | `string` | `"webpack-dev-middleware"` | Names the page-url parameter (`-apply`) that overrides [`apply`](#client-options) for a single page — see [opting one page out](#opting-one-page-out). Change it if you are building a server of your own and want parameters named after it. | -| `logging` | `string` | `"info"` | Logger level — one of `"none"`, `"error"`, `"warn"`, `"info"`, `"log"`, `"verbose"`. Uses webpack's runtime logger. | +| `logging` | `string\|Object` | `"info"` | Logger level — one of `"none"`, `"error"`, `"warn"`, `"info"`, `"log"`, `"verbose"`. Uses webpack's runtime logger. An object takes the same value as `level` plus a `name`, which is what every message is labelled with in the console: a package embedding this runtime is the one a developer installed and would report a problem to, so it can say its own name — `{ level: "warn", name: "my-dev-server" }` logs `[my-dev-server] …`. | | `name` | `string` | `""` | Restrict updates to a specific compilation name (useful with multi-compiler). | | `progress` | `boolean` | `true` | Show a small badge in the page while a rebuild is in progress (with the compilation percentage when the server enables `hot.progress`). Set to `false` to disable. | | `dynamicPublicPath` | `boolean` | `false` | Prefix `path` with `__webpack_public_path__` at runtime. The leading slash of `path` is stripped and no other normalization is applied, so the public path should end with `/`. | diff --git a/client-src/index.js b/client-src/index.js index 9d1d63f48..0cae96fe5 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -10,7 +10,7 @@ import createSocket from "./clients/createSocket.js"; import * as indicator from "./indicator.js"; import configureOverlay from "./overlay.js"; import applyUpdate from "./process-update.js"; -import { log, setLogLevel } from "./utils/log.js"; +import { log, setLogLevel, setLogName } from "./utils/log.js"; import reloadPage from "./utils/reload.js"; import sendMessage from "./utils/send-message.js"; import socketOptions from "./utils/socket-options.js"; @@ -63,6 +63,7 @@ import withToken from "./utils/with-token.js"; * @property {boolean | OverlayOptions} overlay enable the in-page error overlay (same value shape as webpack-dev-server's `client.overlay`) * @property {string} urlPrefix prefix of the page-url parameters that override `apply` for one page * @property {LogLevel} logging logger level + * @property {string=} loggerName what to label messages with in the console * @property {string} name limit updates to this compilation name * @property {string} token the secret the endpoint requires, when it requires one, put on the connection url — empty when it requires none * @property {boolean | "circular" | "linear"} progress show an indicator while a rebuild is in progress — `true` and `"circular"` a small badge, `"linear"` a thin bar across the top of the viewport @@ -77,6 +78,7 @@ const options = { overlay: true, urlPrefix: "webpack-dev-middleware", logging: "info", + loggerName: "", name: "", // The secret the endpoint requires, when it requires one. Put on the url // rather than sent as a header: neither `EventSource` nor `WebSocket` lets a @@ -351,7 +353,27 @@ function setOverrides(overrides) { } if (overrides.urlPrefix) options.urlPrefix = overrides.urlPrefix; if (overrides.logging) { - options.logging = /** @type {LogLevel} */ (overrides.logging); + // A level, or a json object carrying the level and the name to label + // messages with — the same two shapes the other options take. + let logging = overrides.logging; + + if (logging.charAt(0) === "{") { + try { + const parsed = JSON.parse(logging); + + logging = parsed.level; + + if (parsed.name) { + options.loggerName = parsed.name; + } + } catch { + // Not an object after all; taken as the level it looks like. + } + } + + if (logging) { + options.logging = /** @type {LogLevel} */ (logging); + } } if (overrides.name) { options.name = overrides.name; @@ -372,6 +394,7 @@ function setOverrides(overrides) { options.path = __webpack_public_path__ + options.path.replace(/^\//, ""); } + setLogName(options.loggerName); setLogLevel(options.logging); } diff --git a/client-src/utils/log.js b/client-src/utils/log.js index 4e8b99d3e..775d1a345 100644 --- a/client-src/utils/log.js +++ b/client-src/utils/log.js @@ -1,7 +1,7 @@ // @ts-expect-error -- no published types for this entry point import logger from "webpack/lib/logging/runtime.js"; -const LOGGER_NAME = "webpack-dev-middleware"; +const DEFAULT_NAME = "webpack-dev-middleware"; const DEFAULT_LEVEL = "info"; /** @typedef {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} LogLevel */ @@ -15,7 +15,18 @@ export function setLogLevel(level) { setLogLevel(DEFAULT_LEVEL); -const rawLog = logger.getLogger(LOGGER_NAME); +// What every message is labelled with in the console. A package embedding this +// runtime is the package the developer installed and the one they would report +// a problem to, so it says its own name rather than this one — the same reason +// the overlay's element id is settable. +let rawLog = logger.getLogger(DEFAULT_NAME); + +/** + * @param {string=} name what to label messages with + */ +export function setLogName(name) { + rawLog = logger.getLogger(name || DEFAULT_NAME); +} /** * Guard a logger method: under a `require-trusted-types-for 'script'` diff --git a/src/options.check.js b/src/options.check.js index a927d8599..ea139fadb 100644 --- a/src/options.check.js +++ b/src/options.check.js @@ -2,4 +2,4 @@ // DO NOT MODIFY BY HAND. Run `npm run fix:schema-check` to update. /* eslint-disable */ // @ts-nocheck -"use strict";module.exports = validate10;module.exports.default = validate10;const schema11 = {"definitions":{"CorsOrigin":{"description":"An origin, written as a browser sends it (scheme, host and port, no trailing slash), several of them, a pattern, or a function asked about each.","anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"},{"type":"array","items":{"anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"}]},"minItems":1},{"instanceof":"Function"}]}},"type":"object","properties":{"cache":{"description":"How responses are cached.","link":"https://github.com/webpack/webpack-dev-middleware#cache","type":"object","additionalProperties":false,"properties":{"etag":{"description":"Enable or disable etag generation.","enum":["weak","strong"]},"lastModified":{"description":"Enable or disable `Last-Modified` header. Uses the file system's last modified value.","type":"boolean"},"control":{"description":"Enable or disable setting `Cache-Control` response header.","anyOf":[{"type":"boolean"},{"type":"number"},{"type":"string","minLength":1},{"type":"object","properties":{"maxAge":{"type":"number"},"immutable":{"type":"boolean"}},"additionalProperties":false}]},"immutable":{"description":"Enable or disable setting `Cache-Control: public, max-age=31536000, immutable` response header for immutable assets (i.e. asset with a hash in file name like `image.a4c12bde.jpg`).","type":"boolean"}}},"mime":{"description":"How a file's media type is decided.","link":"https://github.com/webpack/webpack-dev-middleware#mime","type":"object","additionalProperties":false,"properties":{"types":{"description":"Allows a user to register custom mime types or extension mappings.","type":"object"},"default":{"description":"Allows a user to register a default mime type when we can't determine the content type.","type":"string"}}},"mimeTypes":{"description":"Allows a user to register custom mime types or extension mappings.","link":"https://github.com/webpack/webpack-dev-middleware#mimetypes","type":"object"},"mimeTypeDefault":{"description":"Allows a user to register a default mime type when we can't determine the content type.","link":"https://github.com/webpack/webpack-dev-middleware#mimetypedefault","type":"string"},"writeToDisk":{"description":"Allows to write generated files on disk.","link":"https://github.com/webpack/webpack-dev-middleware#writetodisk","anyOf":[{"type":"boolean"},{"instanceof":"Function"}]},"methods":{"description":"Allows to pass the list of HTTP request methods accepted by the middleware.","link":"https://github.com/webpack/webpack-dev-middleware#methods","type":"array","items":{"type":"string","minLength":1}},"headers":{"anyOf":[{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"key":{"description":"key of header.","type":"string"},"value":{"description":"value of header.","type":"string"}}},"minItems":1},{"type":"object"},{"instanceof":"Function"}],"description":"Allows to pass custom HTTP headers on each request","link":"https://github.com/webpack/webpack-dev-middleware#headers"},"publicPath":{"description":"The `publicPath` specifies the public URL address of the output files when referenced in a browser.","link":"https://github.com/webpack/webpack-dev-middleware#publicpath","anyOf":[{"enum":["auto"]},{"type":"string"},{"instanceof":"Function"}]},"stats":{"description":"Stats options object or preset name.","link":"https://github.com/webpack/webpack-dev-middleware#stats","anyOf":[{"enum":["none","summary","errors-only","errors-warnings","minimal","normal","detailed","verbose"]},{"type":"boolean"},{"type":"object","additionalProperties":true}]},"serverSideRender":{"description":"Instructs the module to enable or disable the server-side rendering mode.","link":"https://github.com/webpack/webpack-dev-middleware#serversiderender","type":"boolean"},"outputFileSystem":{"description":"Set the default file system which will be used by webpack as primary destination of generated files.","link":"https://github.com/webpack/webpack-dev-middleware#outputfilesystem","type":"object"},"index":{"description":"Allows to serve an index of the directory.","link":"https://github.com/webpack/webpack-dev-middleware#index","anyOf":[{"type":"boolean"},{"type":"string","minLength":1}]},"modifyResponseData":{"description":"Allows to set up a callback to change the response data.","link":"https://github.com/webpack/webpack-dev-middleware#modifyresponsedata","instanceof":"Function"},"etag":{"description":"Enable or disable etag generation.","link":"https://github.com/webpack/webpack-dev-middleware#etag","enum":["weak","strong"]},"lastModified":{"description":"Enable or disable `Last-Modified` header. Uses the file system's last modified value.","link":"https://github.com/webpack/webpack-dev-middleware#lastmodified","type":"boolean"},"cacheControl":{"description":"Enable or disable setting `Cache-Control` response header.","link":"https://github.com/webpack/webpack-dev-middleware#cachecontrol","anyOf":[{"type":"boolean"},{"type":"number"},{"type":"string","minLength":1},{"type":"object","properties":{"maxAge":{"type":"number"},"immutable":{"type":"boolean"}},"additionalProperties":false}]},"cacheImmutable":{"description":"Enable or disable setting `Cache-Control: public, max-age=31536000, immutable` response header for immutable assets (i.e. asset with a hash in file name like `image.a4c12bde.jpg`).","link":"https://github.com/webpack/webpack-dev-middleware#cacheimmutable","type":"boolean"},"forwardError":{"description":"Enable or disable forwarding errors to next middleware.","link":"https://github.com/webpack/webpack-dev-middleware#forwarderrors","type":"boolean"},"hot":{"description":"Enable hot module replacement over a Server-Sent Events or WebSocket endpoint.","link":"https://github.com/webpack/webpack-dev-middleware#hot","anyOf":[{"type":"boolean"},{"type":"object","additionalProperties":false,"properties":{"transport":{"description":"How events reach the clients: `sse`, `ws` (needs the optional `ws` dependency and an HTTP server to answer upgrades on, given as `server` or through the middleware's `attach` method), or a function building a transport of your own.","anyOf":[{"enum":["sse","ws"]},{"instanceof":"Function"}]},"path":{"description":"The path the endpoint is served at. Must start with a slash and carry no query string or fragment.","type":"string","pattern":"^/[^?#]*$"},"heartbeat":{"description":"Heartbeat interval (in milliseconds) used to keep the connection alive.","type":"number","minimum":1},"server":{"description":"HTTP server the `ws` transport answers upgrades on, when it is already built. Otherwise hand it over later with the middleware's `attach` method.","type":"object","additionalProperties":true},"progress":{"description":"Publish compilation progress events to the clients.","type":"boolean"},"token":{"description":"A secret the injected client carries and the endpoint requires, so reaching the stream takes something a page has to have been given rather than a header the browser may not send. `true` mints one per run, a string uses that one — for a client of your own that has to build the url itself — and `false` requires none. Defaults to `false`, because requiring one would refuse a client this middleware did not inject; `true` in the next major release.","link":"https://github.com/webpack/webpack-dev-middleware#hottoken","anyOf":[{"type":"boolean"},{"type":"string","minLength":1}]},"cors":{"description":"Which origins may read the Server-Sent Events endpoint from a page on another one. Local origins only by default. `true` grants every origin, which lets any site the developer has open read the build's errors, including the source frames webpack puts in them.","link":"https://github.com/webpack/webpack-dev-middleware#hotcors","anyOf":[{"type":"boolean"},{"$ref":"#/definitions/CorsOrigin"},{"type":"object","additionalProperties":false,"properties":{"origin":{"description":"Which origins may read it, as Vite and `expressjs/cors` are configured.","anyOf":[{"type":"boolean"},{"$ref":"#/definitions/CorsOrigin"}]}}}]},"statsOptions":{"description":"Deprecated, do not use, will be removed in the next major release. Use the `stats` option instead, which decides whether a payload carries errors and warnings.","type":"object","additionalProperties":true},"inject":{"description":"Add the hot client entry and HotModuleReplacementPlugin to the compilation. Turn it off to wire them yourself.","link":"https://github.com/webpack/webpack-dev-middleware#hot","type":"boolean"},"client":{"description":"Options handed to the browser runtime through its entry query.","link":"https://github.com/webpack/webpack-dev-middleware#hot","type":"object","additionalProperties":false,"properties":{"transport":{"description":"Which transport the runtime speaks: `sse` or `ws`. Defaults to the resolved `hot.transport`.","enum":["sse","ws"]},"path":{"description":"Where the runtime connects. Defaults to the resolved `hot.path`; may be an absolute url for an endpoint reached on another origin or through a proxy.","type":"string","minLength":1},"name":{"description":"Limit the runtime to one compilation's builds. Defaults to the compilation's own name.","type":"string"},"token":{"description":"The secret the runtime puts on its connection url. The middleware sets this to whatever 'hot.token' resolved to, so it only needs setting for a client pointed at another endpoint that requires a different one.","type":"string"},"overlay":{"description":"Show build problems and uncaught runtime errors in an overlay.","anyOf":[{"type":"boolean"},{"type":"object"}]},"progress":{"description":"Show an indicator while a rebuild is in progress.","anyOf":[{"type":"boolean"},{"enum":["circular","linear"]}]},"apply":{"description":"What a build does to the page: `hmr` applies the update and reloads if it cannot be applied, `hmr-only` applies it and stops with a message if it cannot, `reload` loads the page again on a build that changed something, and `nothing` leaves the page alone until you reload it.","enum":["hmr","hmr-only","reload","nothing"]},"connect":{"description":"Whether to connect when the entry runs, and how the connection is held open. `false` does not connect; an object sets `retries` and `timeout`.","anyOf":[{"type":"boolean"},{"type":"object","additionalProperties":false,"properties":{"retries":{"description":"How many times to reconnect before giving up. Server-Sent Events keep trying for as long as the page is open unless this is set, since a dev server is expected to come back.","type":"number","minimum":0},"timeout":{"description":"How long silence is tolerated before reconnecting, in milliseconds. Server-Sent Events only: their heartbeat arrives as data the client can see, whereas a WebSocket's is a protocol ping the browser answers without telling JavaScript.","type":"number","minimum":1}}}]},"urlPrefix":{"description":"Prefix of the page-url parameter that overrides `apply` for a single page.","type":"string","minLength":1},"logging":{"description":"How much the runtime logs to the browser console.","enum":["none","error","warn","info","log","verbose"]},"dynamicPublicPath":{"description":"Prefix the endpoint path with the bundle's public path at runtime.","type":"boolean"},"hot":{"description":"Apply a build through Hot Module Replacement. Deprecated, and removed in the next major release: use `apply`.","type":"boolean"},"liveReload":{"description":"Reload the page on a build that changed something, when `hot` is off. Deprecated, and removed in the next major release: use `apply`.","type":"boolean"},"reload":{"description":"Reload the page when an update cannot be applied. Deprecated, and removed in the next major release: use `apply`.","type":"boolean"},"autoConnect":{"description":"Connect as soon as the entry runs. Deprecated, and removed in the next major release: use `connect`.","type":"boolean"},"reconnect":{"description":"How many times to reconnect before giving up. Unset, Server-Sent Events keep trying for as long as the page is open — a dev server is expected to come back — while a WebSocket gives up after 10. Setting it applies to both. Deprecated, and removed in the next major release: use `connect`.","type":"number","minimum":0},"timeout":{"description":"How long the runtime tolerates silence before reconnecting, in milliseconds. Server-Sent Events only: their heartbeat arrives as data the client can see, whereas a WebSocket's is a protocol ping the browser answers without telling JavaScript — a half-open socket there is terminated by the server instead. Deprecated, and removed in the next major release: use `connect`.","type":"number","exclusiveMinimum":0}}}}}]}},"additionalProperties":false};const schema12 = {"description":"An origin, written as a browser sends it (scheme, host and port, no trailing slash), several of them, a pattern, or a function asked about each.","anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"},{"type":"array","items":{"anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"}]},"minItems":1},{"instanceof":"Function"}]};const func2 = Object.prototype.hasOwnProperty;const pattern0 = new RegExp("^/[^?#]*$", "u");function validate10(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){const _errs1 = errors;for(const key0 in data){if(!(func2.call(schema11.properties, key0))){validate10.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.cache !== undefined){let data0 = data.cache;const _errs2 = errors;if(errors === _errs2){if(data0 && typeof data0 == "object" && !Array.isArray(data0)){const _errs5 = errors;for(const key1 in data0){if(!((((key1 === "etag") || (key1 === "lastModified")) || (key1 === "control")) || (key1 === "immutable"))){validate10.errors = [{instancePath:instancePath+"/cache",schemaPath:"#/properties/cache/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"}];return false;break;}}if(_errs5 === errors){if(data0.etag !== undefined){let data1 = data0.etag;const _errs6 = errors;if(!((data1 === "weak") || (data1 === "strong"))){validate10.errors = [{instancePath:instancePath+"/cache/etag",schemaPath:"#/properties/cache/properties/etag/enum",keyword:"enum",params:{allowedValues: schema11.properties.cache.properties.etag.enum},message:"must be equal to one of the allowed values"}];return false;}var valid1 = _errs6 === errors;}else {var valid1 = true;}if(valid1){if(data0.lastModified !== undefined){const _errs7 = errors;if(typeof data0.lastModified !== "boolean"){validate10.errors = [{instancePath:instancePath+"/cache/lastModified",schemaPath:"#/properties/cache/properties/lastModified/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid1 = _errs7 === errors;}else {var valid1 = true;}if(valid1){if(data0.control !== undefined){let data3 = data0.control;const _errs9 = errors;const _errs10 = errors;let valid2 = false;const _errs11 = errors;if(typeof data3 !== "boolean"){const err0 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}var _valid0 = _errs11 === errors;valid2 = valid2 || _valid0;if(!valid2){const _errs13 = errors;if(!(typeof data3 == "number")){const err1 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/1/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}var _valid0 = _errs13 === errors;valid2 = valid2 || _valid0;if(!valid2){const _errs15 = errors;if(errors === _errs15){if(typeof data3 === "string"){if(data3.length < 1){const err2 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/2/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}else {const err3 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/2/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}var _valid0 = _errs15 === errors;valid2 = valid2 || _valid0;if(!valid2){const _errs17 = errors;if(errors === _errs17){if(data3 && typeof data3 == "object" && !Array.isArray(data3)){const _errs19 = errors;for(const key2 in data3){if(!((key2 === "maxAge") || (key2 === "immutable"))){const err4 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/3/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;break;}}if(_errs19 === errors){if(data3.maxAge !== undefined){const _errs20 = errors;if(!(typeof data3.maxAge == "number")){const err5 = {instancePath:instancePath+"/cache/control/maxAge",schemaPath:"#/properties/cache/properties/control/anyOf/3/properties/maxAge/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}var valid3 = _errs20 === errors;}else {var valid3 = true;}if(valid3){if(data3.immutable !== undefined){const _errs22 = errors;if(typeof data3.immutable !== "boolean"){const err6 = {instancePath:instancePath+"/cache/control/immutable",schemaPath:"#/properties/cache/properties/control/anyOf/3/properties/immutable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}var valid3 = _errs22 === errors;}else {var valid3 = true;}}}}else {const err7 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/3/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}var _valid0 = _errs17 === errors;valid2 = valid2 || _valid0;}}}if(!valid2){const err8 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;validate10.errors = vErrors;return false;}else {errors = _errs10;if(vErrors !== null){if(_errs10){vErrors.length = _errs10;}else {vErrors = null;}}}var valid1 = _errs9 === errors;}else {var valid1 = true;}if(valid1){if(data0.immutable !== undefined){const _errs24 = errors;if(typeof data0.immutable !== "boolean"){validate10.errors = [{instancePath:instancePath+"/cache/immutable",schemaPath:"#/properties/cache/properties/immutable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid1 = _errs24 === errors;}else {var valid1 = true;}}}}}}else {validate10.errors = [{instancePath:instancePath+"/cache",schemaPath:"#/properties/cache/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.mime !== undefined){let data7 = data.mime;const _errs26 = errors;if(errors === _errs26){if(data7 && typeof data7 == "object" && !Array.isArray(data7)){const _errs29 = errors;for(const key3 in data7){if(!((key3 === "types") || (key3 === "default"))){validate10.errors = [{instancePath:instancePath+"/mime",schemaPath:"#/properties/mime/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key3},message:"must NOT have additional properties"}];return false;break;}}if(_errs29 === errors){if(data7.types !== undefined){let data8 = data7.types;const _errs30 = errors;if(!(data8 && typeof data8 == "object" && !Array.isArray(data8))){validate10.errors = [{instancePath:instancePath+"/mime/types",schemaPath:"#/properties/mime/properties/types/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}var valid4 = _errs30 === errors;}else {var valid4 = true;}if(valid4){if(data7.default !== undefined){const _errs32 = errors;if(typeof data7.default !== "string"){validate10.errors = [{instancePath:instancePath+"/mime/default",schemaPath:"#/properties/mime/properties/default/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid4 = _errs32 === errors;}else {var valid4 = true;}}}}else {validate10.errors = [{instancePath:instancePath+"/mime",schemaPath:"#/properties/mime/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs26 === errors;}else {var valid0 = true;}if(valid0){if(data.mimeTypes !== undefined){let data10 = data.mimeTypes;const _errs34 = errors;if(!(data10 && typeof data10 == "object" && !Array.isArray(data10))){validate10.errors = [{instancePath:instancePath+"/mimeTypes",schemaPath:"#/properties/mimeTypes/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}var valid0 = _errs34 === errors;}else {var valid0 = true;}if(valid0){if(data.mimeTypeDefault !== undefined){const _errs37 = errors;if(typeof data.mimeTypeDefault !== "string"){validate10.errors = [{instancePath:instancePath+"/mimeTypeDefault",schemaPath:"#/properties/mimeTypeDefault/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid0 = _errs37 === errors;}else {var valid0 = true;}if(valid0){if(data.writeToDisk !== undefined){let data12 = data.writeToDisk;const _errs40 = errors;const _errs41 = errors;let valid5 = false;const _errs42 = errors;if(typeof data12 !== "boolean"){const err9 = {instancePath:instancePath+"/writeToDisk",schemaPath:"#/properties/writeToDisk/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}var _valid1 = _errs42 === errors;valid5 = valid5 || _valid1;if(!valid5){const _errs44 = errors;if(!(data12 instanceof Function)){const err10 = {instancePath:instancePath+"/writeToDisk",schemaPath:"#/properties/writeToDisk/anyOf/1/instanceof",keyword:"instanceof",params:{},message:"must pass \"instanceof\" keyword validation"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}var _valid1 = _errs44 === errors;valid5 = valid5 || _valid1;}if(!valid5){const err11 = {instancePath:instancePath+"/writeToDisk",schemaPath:"#/properties/writeToDisk/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;validate10.errors = vErrors;return false;}else {errors = _errs41;if(vErrors !== null){if(_errs41){vErrors.length = _errs41;}else {vErrors = null;}}}var valid0 = _errs40 === errors;}else {var valid0 = true;}if(valid0){if(data.methods !== undefined){let data13 = data.methods;const _errs46 = errors;if(errors === _errs46){if(Array.isArray(data13)){var valid6 = true;const len0 = data13.length;for(let i0=0; i0=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}else {const err50 = {instancePath:instancePath+"/hot/heartbeat",schemaPath:"#/properties/hot/anyOf/1/properties/heartbeat/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}var valid16 = _errs137 === errors;}else {var valid16 = true;}if(valid16){if(data32.server !== undefined){let data36 = data32.server;const _errs139 = errors;if(errors === _errs139){if(data36 && typeof data36 == "object" && !Array.isArray(data36)){}else {const err51 = {instancePath:instancePath+"/hot/server",schemaPath:"#/properties/hot/anyOf/1/properties/server/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}var valid16 = _errs139 === errors;}else {var valid16 = true;}if(valid16){if(data32.progress !== undefined){const _errs142 = errors;if(typeof data32.progress !== "boolean"){const err52 = {instancePath:instancePath+"/hot/progress",schemaPath:"#/properties/hot/anyOf/1/properties/progress/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}var valid16 = _errs142 === errors;}else {var valid16 = true;}if(valid16){if(data32.token !== undefined){let data38 = data32.token;const _errs144 = errors;const _errs145 = errors;let valid18 = false;const _errs146 = errors;if(typeof data38 !== "boolean"){const err53 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}var _valid9 = _errs146 === errors;valid18 = valid18 || _valid9;if(!valid18){const _errs148 = errors;if(errors === _errs148){if(typeof data38 === "string"){if(data38.length < 1){const err54 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf/1/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}}else {const err55 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}var _valid9 = _errs148 === errors;valid18 = valid18 || _valid9;}if(!valid18){const err56 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}else {errors = _errs145;if(vErrors !== null){if(_errs145){vErrors.length = _errs145;}else {vErrors = null;}}}var valid16 = _errs144 === errors;}else {var valid16 = true;}if(valid16){if(data32.cors !== undefined){let data39 = data32.cors;const _errs151 = errors;const _errs152 = errors;let valid19 = false;const _errs153 = errors;if(typeof data39 !== "boolean"){const err57 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/properties/hot/anyOf/1/properties/cors/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}var _valid10 = _errs153 === errors;valid19 = valid19 || _valid10;if(!valid19){const _errs155 = errors;const _errs157 = errors;let valid21 = false;const _errs158 = errors;if(errors === _errs158){if(typeof data39 === "string"){if(data39.length < 1){const err58 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/0/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}else {const err59 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/0/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}var _valid11 = _errs158 === errors;valid21 = valid21 || _valid11;if(!valid21){const _errs160 = errors;if(!(data39 instanceof RegExp)){const err60 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/1/instanceof",keyword:"instanceof",params:{},message:"must pass \"instanceof\" keyword validation"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}var _valid11 = _errs160 === errors;valid21 = valid21 || _valid11;if(!valid21){const _errs161 = errors;if(errors === _errs161){if(Array.isArray(data39)){if(data39.length < 1){const err61 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/2/minItems",keyword:"minItems",params:{limit: 1},message:"must NOT have fewer than 1 items"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}else {var valid22 = true;const len2 = data39.length;for(let i2=0; i2=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}}else {const err103 = {instancePath:instancePath+"/hot/client/connect/retries",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/properties/retries/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}}var valid34 = _errs227 === errors;}else {var valid34 = true;}if(valid34){if(data53.timeout !== undefined){let data55 = data53.timeout;const _errs229 = errors;if(errors === _errs229){if(typeof data55 == "number"){if(data55 < 1 || isNaN(data55)){const err104 = {instancePath:instancePath+"/hot/client/connect/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/properties/timeout/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}}else {const err105 = {instancePath:instancePath+"/hot/client/connect/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/properties/timeout/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}}var valid34 = _errs229 === errors;}else {var valid34 = true;}}}}else {const err106 = {instancePath:instancePath+"/hot/client/connect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}}var _valid18 = _errs224 === errors;valid33 = valid33 || _valid18;}if(!valid33){const err107 = {instancePath:instancePath+"/hot/client/connect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}else {errors = _errs221;if(vErrors !== null){if(_errs221){vErrors.length = _errs221;}else {vErrors = null;}}}var valid30 = _errs220 === errors;}else {var valid30 = true;}if(valid30){if(data45.urlPrefix !== undefined){let data56 = data45.urlPrefix;const _errs231 = errors;if(errors === _errs231){if(typeof data56 === "string"){if(data56.length < 1){const err108 = {instancePath:instancePath+"/hot/client/urlPrefix",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/urlPrefix/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}}else {const err109 = {instancePath:instancePath+"/hot/client/urlPrefix",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/urlPrefix/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}}var valid30 = _errs231 === errors;}else {var valid30 = true;}if(valid30){if(data45.logging !== undefined){let data57 = data45.logging;const _errs233 = errors;if(!((((((data57 === "none") || (data57 === "error")) || (data57 === "warn")) || (data57 === "info")) || (data57 === "log")) || (data57 === "verbose"))){const err110 = {instancePath:instancePath+"/hot/client/logging",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/enum",keyword:"enum",params:{allowedValues: schema11.properties.hot.anyOf[1].properties.client.properties.logging.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}var valid30 = _errs233 === errors;}else {var valid30 = true;}if(valid30){if(data45.dynamicPublicPath !== undefined){const _errs234 = errors;if(typeof data45.dynamicPublicPath !== "boolean"){const err111 = {instancePath:instancePath+"/hot/client/dynamicPublicPath",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/dynamicPublicPath/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err111];}else {vErrors.push(err111);}errors++;}var valid30 = _errs234 === errors;}else {var valid30 = true;}if(valid30){if(data45.hot !== undefined){const _errs236 = errors;if(typeof data45.hot !== "boolean"){const err112 = {instancePath:instancePath+"/hot/client/hot",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/hot/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err112];}else {vErrors.push(err112);}errors++;}var valid30 = _errs236 === errors;}else {var valid30 = true;}if(valid30){if(data45.liveReload !== undefined){const _errs238 = errors;if(typeof data45.liveReload !== "boolean"){const err113 = {instancePath:instancePath+"/hot/client/liveReload",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/liveReload/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err113];}else {vErrors.push(err113);}errors++;}var valid30 = _errs238 === errors;}else {var valid30 = true;}if(valid30){if(data45.reload !== undefined){const _errs240 = errors;if(typeof data45.reload !== "boolean"){const err114 = {instancePath:instancePath+"/hot/client/reload",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/reload/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err114];}else {vErrors.push(err114);}errors++;}var valid30 = _errs240 === errors;}else {var valid30 = true;}if(valid30){if(data45.autoConnect !== undefined){const _errs242 = errors;if(typeof data45.autoConnect !== "boolean"){const err115 = {instancePath:instancePath+"/hot/client/autoConnect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/autoConnect/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err115];}else {vErrors.push(err115);}errors++;}var valid30 = _errs242 === errors;}else {var valid30 = true;}if(valid30){if(data45.reconnect !== undefined){let data63 = data45.reconnect;const _errs244 = errors;if(errors === _errs244){if(typeof data63 == "number"){if(data63 < 0 || isNaN(data63)){const err116 = {instancePath:instancePath+"/hot/client/reconnect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/reconnect/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err116];}else {vErrors.push(err116);}errors++;}}else {const err117 = {instancePath:instancePath+"/hot/client/reconnect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/reconnect/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err117];}else {vErrors.push(err117);}errors++;}}var valid30 = _errs244 === errors;}else {var valid30 = true;}if(valid30){if(data45.timeout !== undefined){let data64 = data45.timeout;const _errs246 = errors;if(errors === _errs246){if(typeof data64 == "number"){if(data64 <= 0 || isNaN(data64)){const err118 = {instancePath:instancePath+"/hot/client/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/timeout/exclusiveMinimum",keyword:"exclusiveMinimum",params:{comparison: ">", limit: 0},message:"must be > 0"};if(vErrors === null){vErrors = [err118];}else {vErrors.push(err118);}errors++;}}else {const err119 = {instancePath:instancePath+"/hot/client/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/timeout/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err119];}else {vErrors.push(err119);}errors++;}}var valid30 = _errs246 === errors;}else {var valid30 = true;}}}}}}}}}}}}}}}}}}}else {const err120 = {instancePath:instancePath+"/hot/client",schemaPath:"#/properties/hot/anyOf/1/properties/client/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err120];}else {vErrors.push(err120);}errors++;}}var valid16 = _errs197 === errors;}else {var valid16 = true;}}}}}}}}}}}}else {const err121 = {instancePath:instancePath+"/hot",schemaPath:"#/properties/hot/anyOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err121];}else {vErrors.push(err121);}errors++;}}var _valid7 = _errs128 === errors;valid15 = valid15 || _valid7;}if(!valid15){const err122 = {instancePath:instancePath+"/hot",schemaPath:"#/properties/hot/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err122];}else {vErrors.push(err122);}errors++;validate10.errors = vErrors;return false;}else {errors = _errs125;if(vErrors !== null){if(_errs125){vErrors.length = _errs125;}else {vErrors = null;}}}var valid0 = _errs124 === errors;}else {var valid0 = true;}}}}}}}}}}}}}}}}}}}}}else {validate10.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}validate10.errors = vErrors;return errors === 0;} \ No newline at end of file +"use strict";module.exports = validate10;module.exports.default = validate10;const schema11 = {"definitions":{"CorsOrigin":{"description":"An origin, written as a browser sends it (scheme, host and port, no trailing slash), several of them, a pattern, or a function asked about each.","anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"},{"type":"array","items":{"anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"}]},"minItems":1},{"instanceof":"Function"}]}},"type":"object","properties":{"cache":{"description":"How responses are cached.","link":"https://github.com/webpack/webpack-dev-middleware#cache","type":"object","additionalProperties":false,"properties":{"etag":{"description":"Enable or disable etag generation.","enum":["weak","strong"]},"lastModified":{"description":"Enable or disable `Last-Modified` header. Uses the file system's last modified value.","type":"boolean"},"control":{"description":"Enable or disable setting `Cache-Control` response header.","anyOf":[{"type":"boolean"},{"type":"number"},{"type":"string","minLength":1},{"type":"object","properties":{"maxAge":{"type":"number"},"immutable":{"type":"boolean"}},"additionalProperties":false}]},"immutable":{"description":"Enable or disable setting `Cache-Control: public, max-age=31536000, immutable` response header for immutable assets (i.e. asset with a hash in file name like `image.a4c12bde.jpg`).","type":"boolean"}}},"mime":{"description":"How a file's media type is decided.","link":"https://github.com/webpack/webpack-dev-middleware#mime","type":"object","additionalProperties":false,"properties":{"types":{"description":"Allows a user to register custom mime types or extension mappings.","type":"object"},"default":{"description":"Allows a user to register a default mime type when we can't determine the content type.","type":"string"}}},"mimeTypes":{"description":"Allows a user to register custom mime types or extension mappings.","link":"https://github.com/webpack/webpack-dev-middleware#mimetypes","type":"object"},"mimeTypeDefault":{"description":"Allows a user to register a default mime type when we can't determine the content type.","link":"https://github.com/webpack/webpack-dev-middleware#mimetypedefault","type":"string"},"writeToDisk":{"description":"Allows to write generated files on disk.","link":"https://github.com/webpack/webpack-dev-middleware#writetodisk","anyOf":[{"type":"boolean"},{"instanceof":"Function"}]},"methods":{"description":"Allows to pass the list of HTTP request methods accepted by the middleware.","link":"https://github.com/webpack/webpack-dev-middleware#methods","type":"array","items":{"type":"string","minLength":1}},"headers":{"anyOf":[{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"key":{"description":"key of header.","type":"string"},"value":{"description":"value of header.","type":"string"}}},"minItems":1},{"type":"object"},{"instanceof":"Function"}],"description":"Allows to pass custom HTTP headers on each request","link":"https://github.com/webpack/webpack-dev-middleware#headers"},"publicPath":{"description":"The `publicPath` specifies the public URL address of the output files when referenced in a browser.","link":"https://github.com/webpack/webpack-dev-middleware#publicpath","anyOf":[{"enum":["auto"]},{"type":"string"},{"instanceof":"Function"}]},"stats":{"description":"Stats options object or preset name.","link":"https://github.com/webpack/webpack-dev-middleware#stats","anyOf":[{"enum":["none","summary","errors-only","errors-warnings","minimal","normal","detailed","verbose"]},{"type":"boolean"},{"type":"object","additionalProperties":true}]},"serverSideRender":{"description":"Instructs the module to enable or disable the server-side rendering mode.","link":"https://github.com/webpack/webpack-dev-middleware#serversiderender","type":"boolean"},"outputFileSystem":{"description":"Set the default file system which will be used by webpack as primary destination of generated files.","link":"https://github.com/webpack/webpack-dev-middleware#outputfilesystem","type":"object"},"index":{"description":"Allows to serve an index of the directory.","link":"https://github.com/webpack/webpack-dev-middleware#index","anyOf":[{"type":"boolean"},{"type":"string","minLength":1}]},"modifyResponseData":{"description":"Allows to set up a callback to change the response data.","link":"https://github.com/webpack/webpack-dev-middleware#modifyresponsedata","instanceof":"Function"},"etag":{"description":"Enable or disable etag generation.","link":"https://github.com/webpack/webpack-dev-middleware#etag","enum":["weak","strong"]},"lastModified":{"description":"Enable or disable `Last-Modified` header. Uses the file system's last modified value.","link":"https://github.com/webpack/webpack-dev-middleware#lastmodified","type":"boolean"},"cacheControl":{"description":"Enable or disable setting `Cache-Control` response header.","link":"https://github.com/webpack/webpack-dev-middleware#cachecontrol","anyOf":[{"type":"boolean"},{"type":"number"},{"type":"string","minLength":1},{"type":"object","properties":{"maxAge":{"type":"number"},"immutable":{"type":"boolean"}},"additionalProperties":false}]},"cacheImmutable":{"description":"Enable or disable setting `Cache-Control: public, max-age=31536000, immutable` response header for immutable assets (i.e. asset with a hash in file name like `image.a4c12bde.jpg`).","link":"https://github.com/webpack/webpack-dev-middleware#cacheimmutable","type":"boolean"},"forwardError":{"description":"Enable or disable forwarding errors to next middleware.","link":"https://github.com/webpack/webpack-dev-middleware#forwarderrors","type":"boolean"},"hot":{"description":"Enable hot module replacement over a Server-Sent Events or WebSocket endpoint.","link":"https://github.com/webpack/webpack-dev-middleware#hot","anyOf":[{"type":"boolean"},{"type":"object","additionalProperties":false,"properties":{"transport":{"description":"How events reach the clients: `sse`, `ws` (needs the optional `ws` dependency and an HTTP server to answer upgrades on, given as `server` or through the middleware's `attach` method), or a function building a transport of your own.","anyOf":[{"enum":["sse","ws"]},{"instanceof":"Function"}]},"path":{"description":"The path the endpoint is served at. Must start with a slash and carry no query string or fragment.","type":"string","pattern":"^/[^?#]*$"},"heartbeat":{"description":"Heartbeat interval (in milliseconds) used to keep the connection alive.","type":"number","minimum":1},"server":{"description":"HTTP server the `ws` transport answers upgrades on, when it is already built. Otherwise hand it over later with the middleware's `attach` method.","type":"object","additionalProperties":true},"progress":{"description":"Publish compilation progress events to the clients.","type":"boolean"},"token":{"description":"A secret the injected client carries and the endpoint requires, so reaching the stream takes something a page has to have been given rather than a header the browser may not send. `true` mints one per run, a string uses that one — for a client of your own that has to build the url itself — and `false` requires none. Defaults to `false`, because requiring one would refuse a client this middleware did not inject; `true` in the next major release.","link":"https://github.com/webpack/webpack-dev-middleware#hottoken","anyOf":[{"type":"boolean"},{"type":"string","minLength":1}]},"cors":{"description":"Which origins may read the Server-Sent Events endpoint from a page on another one. Local origins only by default. `true` grants every origin, which lets any site the developer has open read the build's errors, including the source frames webpack puts in them.","link":"https://github.com/webpack/webpack-dev-middleware#hotcors","anyOf":[{"type":"boolean"},{"$ref":"#/definitions/CorsOrigin"},{"type":"object","additionalProperties":false,"properties":{"origin":{"description":"Which origins may read it, as Vite and `expressjs/cors` are configured.","anyOf":[{"type":"boolean"},{"$ref":"#/definitions/CorsOrigin"}]}}}]},"statsOptions":{"description":"Deprecated, do not use, will be removed in the next major release. Use the `stats` option instead, which decides whether a payload carries errors and warnings.","type":"object","additionalProperties":true},"inject":{"description":"Add the hot client entry and HotModuleReplacementPlugin to the compilation. Turn it off to wire them yourself.","link":"https://github.com/webpack/webpack-dev-middleware#hot","type":"boolean"},"client":{"description":"Options handed to the browser runtime through its entry query.","link":"https://github.com/webpack/webpack-dev-middleware#hot","type":"object","additionalProperties":false,"properties":{"transport":{"description":"Which transport the runtime speaks: `sse` or `ws`. Defaults to the resolved `hot.transport`.","enum":["sse","ws"]},"path":{"description":"Where the runtime connects. Defaults to the resolved `hot.path`; may be an absolute url for an endpoint reached on another origin or through a proxy.","type":"string","minLength":1},"name":{"description":"Limit the runtime to one compilation's builds. Defaults to the compilation's own name.","type":"string"},"token":{"description":"The secret the runtime puts on its connection url. The middleware sets this to whatever 'hot.token' resolved to, so it only needs setting for a client pointed at another endpoint that requires a different one.","type":"string"},"overlay":{"description":"Show build problems and uncaught runtime errors in an overlay.","anyOf":[{"type":"boolean"},{"type":"object"}]},"progress":{"description":"Show an indicator while a rebuild is in progress.","anyOf":[{"type":"boolean"},{"enum":["circular","linear"]}]},"apply":{"description":"What a build does to the page: `hmr` applies the update and reloads if it cannot be applied, `hmr-only` applies it and stops with a message if it cannot, `reload` loads the page again on a build that changed something, and `nothing` leaves the page alone until you reload it.","enum":["hmr","hmr-only","reload","nothing"]},"connect":{"description":"Whether to connect when the entry runs, and how the connection is held open. `false` does not connect; an object sets `retries` and `timeout`.","anyOf":[{"type":"boolean"},{"type":"object","additionalProperties":false,"properties":{"retries":{"description":"How many times to reconnect before giving up. Server-Sent Events keep trying for as long as the page is open unless this is set, since a dev server is expected to come back.","type":"number","minimum":0},"timeout":{"description":"How long silence is tolerated before reconnecting, in milliseconds. Server-Sent Events only: their heartbeat arrives as data the client can see, whereas a WebSocket's is a protocol ping the browser answers without telling JavaScript.","type":"number","minimum":1}}}]},"urlPrefix":{"description":"Prefix of the page-url parameter that overrides `apply` for a single page.","type":"string","minLength":1},"logging":{"description":"Logger level in the browser, or an object carrying the level and the name every message is labelled with in the console — a package embedding this runtime is the one a developer would report a problem to, so it can say its own name.","anyOf":[{"enum":["none","error","warn","info","log","verbose"]},{"type":"object","additionalProperties":false,"properties":{"level":{"enum":["none","error","warn","info","log","verbose"]},"name":{"description":"What every message is labelled with in the console. Defaults to this package's name.","type":"string","minLength":1}}}]},"dynamicPublicPath":{"description":"Prefix the endpoint path with the bundle's public path at runtime.","type":"boolean"},"hot":{"description":"Apply a build through Hot Module Replacement. Deprecated, and removed in the next major release: use `apply`.","type":"boolean"},"liveReload":{"description":"Reload the page on a build that changed something, when `hot` is off. Deprecated, and removed in the next major release: use `apply`.","type":"boolean"},"reload":{"description":"Reload the page when an update cannot be applied. Deprecated, and removed in the next major release: use `apply`.","type":"boolean"},"autoConnect":{"description":"Connect as soon as the entry runs. Deprecated, and removed in the next major release: use `connect`.","type":"boolean"},"reconnect":{"description":"How many times to reconnect before giving up. Unset, Server-Sent Events keep trying for as long as the page is open — a dev server is expected to come back — while a WebSocket gives up after 10. Setting it applies to both. Deprecated, and removed in the next major release: use `connect`.","type":"number","minimum":0},"timeout":{"description":"How long the runtime tolerates silence before reconnecting, in milliseconds. Server-Sent Events only: their heartbeat arrives as data the client can see, whereas a WebSocket's is a protocol ping the browser answers without telling JavaScript — a half-open socket there is terminated by the server instead. Deprecated, and removed in the next major release: use `connect`.","type":"number","exclusiveMinimum":0}}}}}]}},"additionalProperties":false};const schema12 = {"description":"An origin, written as a browser sends it (scheme, host and port, no trailing slash), several of them, a pattern, or a function asked about each.","anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"},{"type":"array","items":{"anyOf":[{"type":"string","minLength":1},{"instanceof":"RegExp"}]},"minItems":1},{"instanceof":"Function"}]};const func2 = Object.prototype.hasOwnProperty;const pattern0 = new RegExp("^/[^?#]*$", "u");function validate10(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){const _errs1 = errors;for(const key0 in data){if(!(func2.call(schema11.properties, key0))){validate10.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.cache !== undefined){let data0 = data.cache;const _errs2 = errors;if(errors === _errs2){if(data0 && typeof data0 == "object" && !Array.isArray(data0)){const _errs5 = errors;for(const key1 in data0){if(!((((key1 === "etag") || (key1 === "lastModified")) || (key1 === "control")) || (key1 === "immutable"))){validate10.errors = [{instancePath:instancePath+"/cache",schemaPath:"#/properties/cache/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"}];return false;break;}}if(_errs5 === errors){if(data0.etag !== undefined){let data1 = data0.etag;const _errs6 = errors;if(!((data1 === "weak") || (data1 === "strong"))){validate10.errors = [{instancePath:instancePath+"/cache/etag",schemaPath:"#/properties/cache/properties/etag/enum",keyword:"enum",params:{allowedValues: schema11.properties.cache.properties.etag.enum},message:"must be equal to one of the allowed values"}];return false;}var valid1 = _errs6 === errors;}else {var valid1 = true;}if(valid1){if(data0.lastModified !== undefined){const _errs7 = errors;if(typeof data0.lastModified !== "boolean"){validate10.errors = [{instancePath:instancePath+"/cache/lastModified",schemaPath:"#/properties/cache/properties/lastModified/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid1 = _errs7 === errors;}else {var valid1 = true;}if(valid1){if(data0.control !== undefined){let data3 = data0.control;const _errs9 = errors;const _errs10 = errors;let valid2 = false;const _errs11 = errors;if(typeof data3 !== "boolean"){const err0 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}var _valid0 = _errs11 === errors;valid2 = valid2 || _valid0;if(!valid2){const _errs13 = errors;if(!(typeof data3 == "number")){const err1 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/1/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}var _valid0 = _errs13 === errors;valid2 = valid2 || _valid0;if(!valid2){const _errs15 = errors;if(errors === _errs15){if(typeof data3 === "string"){if(data3.length < 1){const err2 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/2/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}}else {const err3 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/2/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;}}var _valid0 = _errs15 === errors;valid2 = valid2 || _valid0;if(!valid2){const _errs17 = errors;if(errors === _errs17){if(data3 && typeof data3 == "object" && !Array.isArray(data3)){const _errs19 = errors;for(const key2 in data3){if(!((key2 === "maxAge") || (key2 === "immutable"))){const err4 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/3/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;break;}}if(_errs19 === errors){if(data3.maxAge !== undefined){const _errs20 = errors;if(!(typeof data3.maxAge == "number")){const err5 = {instancePath:instancePath+"/cache/control/maxAge",schemaPath:"#/properties/cache/properties/control/anyOf/3/properties/maxAge/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}var valid3 = _errs20 === errors;}else {var valid3 = true;}if(valid3){if(data3.immutable !== undefined){const _errs22 = errors;if(typeof data3.immutable !== "boolean"){const err6 = {instancePath:instancePath+"/cache/control/immutable",schemaPath:"#/properties/cache/properties/control/anyOf/3/properties/immutable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}var valid3 = _errs22 === errors;}else {var valid3 = true;}}}}else {const err7 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf/3/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;}}var _valid0 = _errs17 === errors;valid2 = valid2 || _valid0;}}}if(!valid2){const err8 = {instancePath:instancePath+"/cache/control",schemaPath:"#/properties/cache/properties/control/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;validate10.errors = vErrors;return false;}else {errors = _errs10;if(vErrors !== null){if(_errs10){vErrors.length = _errs10;}else {vErrors = null;}}}var valid1 = _errs9 === errors;}else {var valid1 = true;}if(valid1){if(data0.immutable !== undefined){const _errs24 = errors;if(typeof data0.immutable !== "boolean"){validate10.errors = [{instancePath:instancePath+"/cache/immutable",schemaPath:"#/properties/cache/properties/immutable/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];return false;}var valid1 = _errs24 === errors;}else {var valid1 = true;}}}}}}else {validate10.errors = [{instancePath:instancePath+"/cache",schemaPath:"#/properties/cache/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.mime !== undefined){let data7 = data.mime;const _errs26 = errors;if(errors === _errs26){if(data7 && typeof data7 == "object" && !Array.isArray(data7)){const _errs29 = errors;for(const key3 in data7){if(!((key3 === "types") || (key3 === "default"))){validate10.errors = [{instancePath:instancePath+"/mime",schemaPath:"#/properties/mime/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key3},message:"must NOT have additional properties"}];return false;break;}}if(_errs29 === errors){if(data7.types !== undefined){let data8 = data7.types;const _errs30 = errors;if(!(data8 && typeof data8 == "object" && !Array.isArray(data8))){validate10.errors = [{instancePath:instancePath+"/mime/types",schemaPath:"#/properties/mime/properties/types/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}var valid4 = _errs30 === errors;}else {var valid4 = true;}if(valid4){if(data7.default !== undefined){const _errs32 = errors;if(typeof data7.default !== "string"){validate10.errors = [{instancePath:instancePath+"/mime/default",schemaPath:"#/properties/mime/properties/default/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid4 = _errs32 === errors;}else {var valid4 = true;}}}}else {validate10.errors = [{instancePath:instancePath+"/mime",schemaPath:"#/properties/mime/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}var valid0 = _errs26 === errors;}else {var valid0 = true;}if(valid0){if(data.mimeTypes !== undefined){let data10 = data.mimeTypes;const _errs34 = errors;if(!(data10 && typeof data10 == "object" && !Array.isArray(data10))){validate10.errors = [{instancePath:instancePath+"/mimeTypes",schemaPath:"#/properties/mimeTypes/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}var valid0 = _errs34 === errors;}else {var valid0 = true;}if(valid0){if(data.mimeTypeDefault !== undefined){const _errs37 = errors;if(typeof data.mimeTypeDefault !== "string"){validate10.errors = [{instancePath:instancePath+"/mimeTypeDefault",schemaPath:"#/properties/mimeTypeDefault/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}var valid0 = _errs37 === errors;}else {var valid0 = true;}if(valid0){if(data.writeToDisk !== undefined){let data12 = data.writeToDisk;const _errs40 = errors;const _errs41 = errors;let valid5 = false;const _errs42 = errors;if(typeof data12 !== "boolean"){const err9 = {instancePath:instancePath+"/writeToDisk",schemaPath:"#/properties/writeToDisk/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}var _valid1 = _errs42 === errors;valid5 = valid5 || _valid1;if(!valid5){const _errs44 = errors;if(!(data12 instanceof Function)){const err10 = {instancePath:instancePath+"/writeToDisk",schemaPath:"#/properties/writeToDisk/anyOf/1/instanceof",keyword:"instanceof",params:{},message:"must pass \"instanceof\" keyword validation"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}var _valid1 = _errs44 === errors;valid5 = valid5 || _valid1;}if(!valid5){const err11 = {instancePath:instancePath+"/writeToDisk",schemaPath:"#/properties/writeToDisk/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;validate10.errors = vErrors;return false;}else {errors = _errs41;if(vErrors !== null){if(_errs41){vErrors.length = _errs41;}else {vErrors = null;}}}var valid0 = _errs40 === errors;}else {var valid0 = true;}if(valid0){if(data.methods !== undefined){let data13 = data.methods;const _errs46 = errors;if(errors === _errs46){if(Array.isArray(data13)){var valid6 = true;const len0 = data13.length;for(let i0=0; i0=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err49];}else {vErrors.push(err49);}errors++;}}else {const err50 = {instancePath:instancePath+"/hot/heartbeat",schemaPath:"#/properties/hot/anyOf/1/properties/heartbeat/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err50];}else {vErrors.push(err50);}errors++;}}var valid16 = _errs137 === errors;}else {var valid16 = true;}if(valid16){if(data32.server !== undefined){let data36 = data32.server;const _errs139 = errors;if(errors === _errs139){if(data36 && typeof data36 == "object" && !Array.isArray(data36)){}else {const err51 = {instancePath:instancePath+"/hot/server",schemaPath:"#/properties/hot/anyOf/1/properties/server/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err51];}else {vErrors.push(err51);}errors++;}}var valid16 = _errs139 === errors;}else {var valid16 = true;}if(valid16){if(data32.progress !== undefined){const _errs142 = errors;if(typeof data32.progress !== "boolean"){const err52 = {instancePath:instancePath+"/hot/progress",schemaPath:"#/properties/hot/anyOf/1/properties/progress/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err52];}else {vErrors.push(err52);}errors++;}var valid16 = _errs142 === errors;}else {var valid16 = true;}if(valid16){if(data32.token !== undefined){let data38 = data32.token;const _errs144 = errors;const _errs145 = errors;let valid18 = false;const _errs146 = errors;if(typeof data38 !== "boolean"){const err53 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err53];}else {vErrors.push(err53);}errors++;}var _valid9 = _errs146 === errors;valid18 = valid18 || _valid9;if(!valid18){const _errs148 = errors;if(errors === _errs148){if(typeof data38 === "string"){if(data38.length < 1){const err54 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf/1/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err54];}else {vErrors.push(err54);}errors++;}}else {const err55 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err55];}else {vErrors.push(err55);}errors++;}}var _valid9 = _errs148 === errors;valid18 = valid18 || _valid9;}if(!valid18){const err56 = {instancePath:instancePath+"/hot/token",schemaPath:"#/properties/hot/anyOf/1/properties/token/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err56];}else {vErrors.push(err56);}errors++;}else {errors = _errs145;if(vErrors !== null){if(_errs145){vErrors.length = _errs145;}else {vErrors = null;}}}var valid16 = _errs144 === errors;}else {var valid16 = true;}if(valid16){if(data32.cors !== undefined){let data39 = data32.cors;const _errs151 = errors;const _errs152 = errors;let valid19 = false;const _errs153 = errors;if(typeof data39 !== "boolean"){const err57 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/properties/hot/anyOf/1/properties/cors/anyOf/0/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err57];}else {vErrors.push(err57);}errors++;}var _valid10 = _errs153 === errors;valid19 = valid19 || _valid10;if(!valid19){const _errs155 = errors;const _errs157 = errors;let valid21 = false;const _errs158 = errors;if(errors === _errs158){if(typeof data39 === "string"){if(data39.length < 1){const err58 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/0/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err58];}else {vErrors.push(err58);}errors++;}}else {const err59 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/0/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err59];}else {vErrors.push(err59);}errors++;}}var _valid11 = _errs158 === errors;valid21 = valid21 || _valid11;if(!valid21){const _errs160 = errors;if(!(data39 instanceof RegExp)){const err60 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/1/instanceof",keyword:"instanceof",params:{},message:"must pass \"instanceof\" keyword validation"};if(vErrors === null){vErrors = [err60];}else {vErrors.push(err60);}errors++;}var _valid11 = _errs160 === errors;valid21 = valid21 || _valid11;if(!valid21){const _errs161 = errors;if(errors === _errs161){if(Array.isArray(data39)){if(data39.length < 1){const err61 = {instancePath:instancePath+"/hot/cors",schemaPath:"#/definitions/CorsOrigin/anyOf/2/minItems",keyword:"minItems",params:{limit: 1},message:"must NOT have fewer than 1 items"};if(vErrors === null){vErrors = [err61];}else {vErrors.push(err61);}errors++;}else {var valid22 = true;const len2 = data39.length;for(let i2=0; i2=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err102];}else {vErrors.push(err102);}errors++;}}else {const err103 = {instancePath:instancePath+"/hot/client/connect/retries",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/properties/retries/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err103];}else {vErrors.push(err103);}errors++;}}var valid34 = _errs227 === errors;}else {var valid34 = true;}if(valid34){if(data53.timeout !== undefined){let data55 = data53.timeout;const _errs229 = errors;if(errors === _errs229){if(typeof data55 == "number"){if(data55 < 1 || isNaN(data55)){const err104 = {instancePath:instancePath+"/hot/client/connect/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/properties/timeout/minimum",keyword:"minimum",params:{comparison: ">=", limit: 1},message:"must be >= 1"};if(vErrors === null){vErrors = [err104];}else {vErrors.push(err104);}errors++;}}else {const err105 = {instancePath:instancePath+"/hot/client/connect/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/properties/timeout/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err105];}else {vErrors.push(err105);}errors++;}}var valid34 = _errs229 === errors;}else {var valid34 = true;}}}}else {const err106 = {instancePath:instancePath+"/hot/client/connect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err106];}else {vErrors.push(err106);}errors++;}}var _valid18 = _errs224 === errors;valid33 = valid33 || _valid18;}if(!valid33){const err107 = {instancePath:instancePath+"/hot/client/connect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/connect/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err107];}else {vErrors.push(err107);}errors++;}else {errors = _errs221;if(vErrors !== null){if(_errs221){vErrors.length = _errs221;}else {vErrors = null;}}}var valid30 = _errs220 === errors;}else {var valid30 = true;}if(valid30){if(data45.urlPrefix !== undefined){let data56 = data45.urlPrefix;const _errs231 = errors;if(errors === _errs231){if(typeof data56 === "string"){if(data56.length < 1){const err108 = {instancePath:instancePath+"/hot/client/urlPrefix",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/urlPrefix/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err108];}else {vErrors.push(err108);}errors++;}}else {const err109 = {instancePath:instancePath+"/hot/client/urlPrefix",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/urlPrefix/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err109];}else {vErrors.push(err109);}errors++;}}var valid30 = _errs231 === errors;}else {var valid30 = true;}if(valid30){if(data45.logging !== undefined){let data57 = data45.logging;const _errs233 = errors;const _errs234 = errors;let valid35 = false;const _errs235 = errors;if(!((((((data57 === "none") || (data57 === "error")) || (data57 === "warn")) || (data57 === "info")) || (data57 === "log")) || (data57 === "verbose"))){const err110 = {instancePath:instancePath+"/hot/client/logging",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/anyOf/0/enum",keyword:"enum",params:{allowedValues: schema11.properties.hot.anyOf[1].properties.client.properties.logging.anyOf[0].enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err110];}else {vErrors.push(err110);}errors++;}var _valid19 = _errs235 === errors;valid35 = valid35 || _valid19;if(!valid35){const _errs236 = errors;if(errors === _errs236){if(data57 && typeof data57 == "object" && !Array.isArray(data57)){const _errs238 = errors;for(const key10 in data57){if(!((key10 === "level") || (key10 === "name"))){const err111 = {instancePath:instancePath+"/hot/client/logging",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/anyOf/1/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key10},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err111];}else {vErrors.push(err111);}errors++;break;}}if(_errs238 === errors){if(data57.level !== undefined){let data58 = data57.level;const _errs239 = errors;if(!((((((data58 === "none") || (data58 === "error")) || (data58 === "warn")) || (data58 === "info")) || (data58 === "log")) || (data58 === "verbose"))){const err112 = {instancePath:instancePath+"/hot/client/logging/level",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/anyOf/1/properties/level/enum",keyword:"enum",params:{allowedValues: schema11.properties.hot.anyOf[1].properties.client.properties.logging.anyOf[1].properties.level.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err112];}else {vErrors.push(err112);}errors++;}var valid36 = _errs239 === errors;}else {var valid36 = true;}if(valid36){if(data57.name !== undefined){let data59 = data57.name;const _errs240 = errors;if(errors === _errs240){if(typeof data59 === "string"){if(data59.length < 1){const err113 = {instancePath:instancePath+"/hot/client/logging/name",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/anyOf/1/properties/name/minLength",keyword:"minLength",params:{limit: 1},message:"must NOT have fewer than 1 characters"};if(vErrors === null){vErrors = [err113];}else {vErrors.push(err113);}errors++;}}else {const err114 = {instancePath:instancePath+"/hot/client/logging/name",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/anyOf/1/properties/name/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err114];}else {vErrors.push(err114);}errors++;}}var valid36 = _errs240 === errors;}else {var valid36 = true;}}}}else {const err115 = {instancePath:instancePath+"/hot/client/logging",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/anyOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err115];}else {vErrors.push(err115);}errors++;}}var _valid19 = _errs236 === errors;valid35 = valid35 || _valid19;}if(!valid35){const err116 = {instancePath:instancePath+"/hot/client/logging",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/logging/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err116];}else {vErrors.push(err116);}errors++;}else {errors = _errs234;if(vErrors !== null){if(_errs234){vErrors.length = _errs234;}else {vErrors = null;}}}var valid30 = _errs233 === errors;}else {var valid30 = true;}if(valid30){if(data45.dynamicPublicPath !== undefined){const _errs242 = errors;if(typeof data45.dynamicPublicPath !== "boolean"){const err117 = {instancePath:instancePath+"/hot/client/dynamicPublicPath",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/dynamicPublicPath/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err117];}else {vErrors.push(err117);}errors++;}var valid30 = _errs242 === errors;}else {var valid30 = true;}if(valid30){if(data45.hot !== undefined){const _errs244 = errors;if(typeof data45.hot !== "boolean"){const err118 = {instancePath:instancePath+"/hot/client/hot",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/hot/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err118];}else {vErrors.push(err118);}errors++;}var valid30 = _errs244 === errors;}else {var valid30 = true;}if(valid30){if(data45.liveReload !== undefined){const _errs246 = errors;if(typeof data45.liveReload !== "boolean"){const err119 = {instancePath:instancePath+"/hot/client/liveReload",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/liveReload/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err119];}else {vErrors.push(err119);}errors++;}var valid30 = _errs246 === errors;}else {var valid30 = true;}if(valid30){if(data45.reload !== undefined){const _errs248 = errors;if(typeof data45.reload !== "boolean"){const err120 = {instancePath:instancePath+"/hot/client/reload",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/reload/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err120];}else {vErrors.push(err120);}errors++;}var valid30 = _errs248 === errors;}else {var valid30 = true;}if(valid30){if(data45.autoConnect !== undefined){const _errs250 = errors;if(typeof data45.autoConnect !== "boolean"){const err121 = {instancePath:instancePath+"/hot/client/autoConnect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/autoConnect/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"};if(vErrors === null){vErrors = [err121];}else {vErrors.push(err121);}errors++;}var valid30 = _errs250 === errors;}else {var valid30 = true;}if(valid30){if(data45.reconnect !== undefined){let data65 = data45.reconnect;const _errs252 = errors;if(errors === _errs252){if(typeof data65 == "number"){if(data65 < 0 || isNaN(data65)){const err122 = {instancePath:instancePath+"/hot/client/reconnect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/reconnect/minimum",keyword:"minimum",params:{comparison: ">=", limit: 0},message:"must be >= 0"};if(vErrors === null){vErrors = [err122];}else {vErrors.push(err122);}errors++;}}else {const err123 = {instancePath:instancePath+"/hot/client/reconnect",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/reconnect/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err123];}else {vErrors.push(err123);}errors++;}}var valid30 = _errs252 === errors;}else {var valid30 = true;}if(valid30){if(data45.timeout !== undefined){let data66 = data45.timeout;const _errs254 = errors;if(errors === _errs254){if(typeof data66 == "number"){if(data66 <= 0 || isNaN(data66)){const err124 = {instancePath:instancePath+"/hot/client/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/timeout/exclusiveMinimum",keyword:"exclusiveMinimum",params:{comparison: ">", limit: 0},message:"must be > 0"};if(vErrors === null){vErrors = [err124];}else {vErrors.push(err124);}errors++;}}else {const err125 = {instancePath:instancePath+"/hot/client/timeout",schemaPath:"#/properties/hot/anyOf/1/properties/client/properties/timeout/type",keyword:"type",params:{type: "number"},message:"must be number"};if(vErrors === null){vErrors = [err125];}else {vErrors.push(err125);}errors++;}}var valid30 = _errs254 === errors;}else {var valid30 = true;}}}}}}}}}}}}}}}}}}}else {const err126 = {instancePath:instancePath+"/hot/client",schemaPath:"#/properties/hot/anyOf/1/properties/client/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err126];}else {vErrors.push(err126);}errors++;}}var valid16 = _errs197 === errors;}else {var valid16 = true;}}}}}}}}}}}}else {const err127 = {instancePath:instancePath+"/hot",schemaPath:"#/properties/hot/anyOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err127];}else {vErrors.push(err127);}errors++;}}var _valid7 = _errs128 === errors;valid15 = valid15 || _valid7;}if(!valid15){const err128 = {instancePath:instancePath+"/hot",schemaPath:"#/properties/hot/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err128];}else {vErrors.push(err128);}errors++;validate10.errors = vErrors;return false;}else {errors = _errs125;if(vErrors !== null){if(_errs125){vErrors.length = _errs125;}else {vErrors = null;}}}var valid0 = _errs124 === errors;}else {var valid0 = true;}}}}}}}}}}}}}}}}}}}}}else {validate10.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];return false;}}validate10.errors = vErrors;return errors === 0;} \ No newline at end of file diff --git a/src/options.json b/src/options.json index 84a9a9f7f..474977224 100644 --- a/src/options.json +++ b/src/options.json @@ -445,8 +445,40 @@ "minLength": 1 }, "logging": { - "description": "How much the runtime logs to the browser console.", - "enum": ["none", "error", "warn", "info", "log", "verbose"] + "description": "Logger level in the browser, or an object carrying the level and the name every message is labelled with in the console \u2014 a package embedding this runtime is the one a developer would report a problem to, so it can say its own name.", + "anyOf": [ + { + "enum": [ + "none", + "error", + "warn", + "info", + "log", + "verbose" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "level": { + "enum": [ + "none", + "error", + "warn", + "info", + "log", + "verbose" + ] + }, + "name": { + "description": "What every message is labelled with in the console. Defaults to this package's name.", + "type": "string", + "minLength": 1 + } + } + } + ] }, "dynamicPublicPath": { "description": "Prefix the endpoint path with the bundle's public path at runtime.", diff --git a/test/e2e/messages.test.js b/test/e2e/messages.test.js index db0ed586b..edbb7e3cb 100644 --- a/test/e2e/messages.test.js +++ b/test/e2e/messages.test.js @@ -148,6 +148,28 @@ describe("messages posted to the page (browser)", () => { ); }); + // A package embedding this runtime is the one the developer installed and + // the one they would report a problem to, so it labels the console with its + // own name — the same reason the overlay's element id is settable. + it("labels the console with the name it was given", async () => { + hotApp = await createHotApp({ + bare: true, + hot: { client: { logging: { level: "info", name: "my-dev-server" } } }, + code: recordingApp("v1"), + }); + ({ page, browser } = await runBrowser()); + const console_ = collectConsole(page); + + await page.goto(hotApp.url); + await waitForAppText(page, "v1"); + await console_.waitFor("connected"); + + const said = console_.messages.join("\n"); + + expect(said).toContain("[my-dev-server]"); + expect(said).not.toContain("[webpack-dev-middleware]"); + }); + it("says when the connection went away, once per outage", async () => { hotApp = await createHotApp({ query: "?timeout=1000", diff --git a/types/client/index.d.ts b/types/client/index.d.ts index 48b4aeb2c..c01004316 100644 --- a/types/client/index.d.ts +++ b/types/client/index.d.ts @@ -127,6 +127,10 @@ export type ClientOptions = { * logger level */ logging: LogLevel; + /** + * what to label messages with in the console + */ + loggerName?: string | undefined; /** * limit updates to this compilation name */ diff --git a/types/client/utils/log.d.ts b/types/client/utils/log.d.ts index 2b7992689..57c6d977d 100644 --- a/types/client/utils/log.d.ts +++ b/types/client/utils/log.d.ts @@ -3,6 +3,10 @@ * @param {LogLevel} level log level (or `false` for off, `true` for default) */ export function setLogLevel(level: LogLevel): void; +/** + * @param {string} name what to label messages with + */ +export function setLogName(name: string): void; export namespace log { let error: (...args: unknown[]) => void; let warn: (...args: unknown[]) => void; From b76449f240cc4cc7d5a4d8c9850de4294307bb5d Mon Sep 17 00:00:00 2001 From: alexander-akait <4567934+alexander-akait@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:56:36 +0000 Subject: [PATCH 2/3] fix(hot): read the logging object by what it parses to MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The second shape was detected by testing the first character for `{`, so json with a space before it fell through: the object text became the level and the name was lost. Asking what `JSON.parse` returned instead covers that, and covers more than trimming would — `JSON.parse` also accepts a bare number, boolean or quoted string, and a level is none of those, so requiring an object is the check that was meant. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA --- client-src/index.js | 23 ++++++++++++++--------- test/e2e/messages.test.js | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/client-src/index.js b/client-src/index.js index 0cae96fe5..d9e793b3e 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -356,18 +356,23 @@ function setOverrides(overrides) { // A level, or a json object carrying the level and the name to label // messages with — the same two shapes the other options take. let logging = overrides.logging; + let parsed; - if (logging.charAt(0) === "{") { - try { - const parsed = JSON.parse(logging); + try { + parsed = JSON.parse(logging); + } catch { + // Not json, so it is the level it looks like. + } - logging = parsed.level; + // Only an object is the second shape. `JSON.parse` also accepts a bare + // number, boolean or quoted string, and a level is none of those — asking + // what came back rather than what the text started with also means + // leading whitespace does not hide it. + if (parsed && typeof parsed === "object") { + logging = parsed.level; - if (parsed.name) { - options.loggerName = parsed.name; - } - } catch { - // Not an object after all; taken as the level it looks like. + if (parsed.name) { + options.loggerName = parsed.name; } } diff --git a/test/e2e/messages.test.js b/test/e2e/messages.test.js index edbb7e3cb..6d471287a 100644 --- a/test/e2e/messages.test.js +++ b/test/e2e/messages.test.js @@ -170,6 +170,24 @@ describe("messages posted to the page (browser)", () => { expect(said).not.toContain("[webpack-dev-middleware]"); }); + // Whitespace before the json, which a query can carry and a check on the + // first character would miss — the object would be read as the level, and + // the name silently lost. + it("reads the name whatever the json is padded with", async () => { + hotApp = await createHotApp({ + query: `?logging=${encodeURIComponent(' {"level":"info","name":"padded-server"} ')}`, + code: recordingApp("v1"), + }); + ({ page, browser } = await runBrowser()); + const console_ = collectConsole(page); + + await page.goto(hotApp.url); + await waitForAppText(page, "v1"); + await console_.waitFor("connected"); + + expect(console_.messages.join("\n")).toContain("[padded-server]"); + }); + it("says when the connection went away, once per outage", async () => { hotApp = await createHotApp({ query: "?timeout=1000", From e1a55f664671fa1c35db047a6c8ed0e8b08012ec Mon Sep 17 00:00:00 2001 From: alexander-akait <4567934+alexander-akait@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:26:30 +0000 Subject: [PATCH 3/3] fix(types): let the exported logging type take the object as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runtime schema accepted `{ level, name }` while the generated `.d.ts` only accepted a level, so a TypeScript configuration setting the object form got a type error for something that works. The level is a `LogLevel` alias now, which both halves of the union share. Also a case the two browser tests could not catch: both used `"info"`, the default, so the object's `name` being applied while its `level` was dropped would have passed them. The new one asks for `"warn"` and requires `connected` — logged at info — to be absent, so the level has to come from inside the object. Checked by breaking it on purpose: it fails. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA --- src/hot.js | 4 +++- test/e2e/messages.test.js | 27 +++++++++++++++++++++++++++ types/client/utils/log.d.ts | 4 ++-- types/hot.d.ts | 13 +++++++++++-- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/hot.js b/src/hot.js index 663a4655e..c3aa12728 100644 --- a/src/hot.js +++ b/src/hot.js @@ -15,6 +15,8 @@ /** @typedef {import("webpack").StatsOptions} StatsOptions */ /** @typedef {import("webpack").Configuration["stats"]} MiddlewareStatsOption */ +/** @typedef {("none" | "error" | "warn" | "info" | "log" | "verbose")} LogLevel */ + /** * Everything the browser runtime reads, as it is set in node. One for one with * what the entry query carries, so every option has both spellings: set it @@ -38,7 +40,7 @@ * @property {("hmr" | "hmr-only" | "reload" | "nothing")=} apply what a build does to the page — apply the update and reload if it cannot be applied, apply it and stop with a message if it cannot, load the page again on any build that changed something, or leave the page alone * @property {(boolean | { retries?: number, timeout?: number })=} connect whether to connect when the entry runs, and how the connection is held open * @property {string=} urlPrefix prefix of the page-url parameter that overrides `apply` for a single page - * @property {("none" | "error" | "warn" | "info" | "log" | "verbose")=} logging how much the runtime logs to the browser console + * @property {(LogLevel | { level?: LogLevel, name?: string })=} logging how much the runtime logs to the browser console, and the name every message is labelled with * @property {number=} reconnect how many times to reconnect before giving up; unset, Server-Sent Events keep trying for as long as the page is open while a WebSocket gives up after 10 * @property {number=} timeout how long the runtime tolerates silence before reconnecting, in milliseconds — Server-Sent Events only, since a WebSocket's heartbeat is a protocol ping JavaScript cannot see * @property {boolean=} autoConnect connect as soon as the entry runs diff --git a/test/e2e/messages.test.js b/test/e2e/messages.test.js index 6d471287a..788bd24ca 100644 --- a/test/e2e/messages.test.js +++ b/test/e2e/messages.test.js @@ -188,6 +188,33 @@ describe("messages posted to the page (browser)", () => { expect(console_.messages.join("\n")).toContain("[padded-server]"); }); + // Both cases above use `"info"`, the default — so neither would notice the + // object's `name` being applied while its `level` was dropped. `"warn"` + // silences `connected`, which is logged at info, and leaves a warning + // through: the level has to be read from inside the object for this to hold. + it("applies the level from inside the object too", async () => { + hotApp = await createHotApp({ + bare: true, + hot: { client: { logging: { level: "warn", name: "quiet-server" } } }, + code: recordingApp("v1"), + }); + ({ page, browser } = await runBrowser()); + const console_ = collectConsole(page); + + await page.goto(hotApp.url); + await waitForAppText(page, "v1"); + + // Logged at the error level, which `"warn"` lets through. + hotApp.instance.publish({ action: "error", message: "a warning level" }); + await console_.waitFor("a warning level"); + + const said = console_.messages.join("\n"); + + expect(said).toContain("[quiet-server]"); + // Logged at info, so `"warn"` has to have come from inside the object. + expect(said).not.toContain("connected"); + }); + it("says when the connection went away, once per outage", async () => { hotApp = await createHotApp({ query: "?timeout=1000", diff --git a/types/client/utils/log.d.ts b/types/client/utils/log.d.ts index 57c6d977d..e3dc9dc67 100644 --- a/types/client/utils/log.d.ts +++ b/types/client/utils/log.d.ts @@ -4,9 +4,9 @@ */ export function setLogLevel(level: LogLevel): void; /** - * @param {string} name what to label messages with + * @param {string=} name what to label messages with */ -export function setLogName(name: string): void; +export function setLogName(name?: string | undefined): void; export namespace log { let error: (...args: unknown[]) => void; let warn: (...args: unknown[]) => void; diff --git a/types/hot.d.ts b/types/hot.d.ts index b132248b8..1f34b3f37 100644 --- a/types/hot.d.ts +++ b/types/hot.d.ts @@ -48,6 +48,7 @@ declare namespace createHot { Duplex, StatsOptions, MiddlewareStatsOption, + LogLevel, HotClientOptions, HotOptions, CorsOrigin, @@ -166,6 +167,7 @@ type HttpServer = import("node:http").Server; type Duplex = import("node:stream").Duplex; type StatsOptions = import("webpack").StatsOptions; type MiddlewareStatsOption = import("webpack").Configuration["stats"]; +type LogLevel = "none" | "error" | "warn" | "info" | "log" | "verbose"; /** * Everything the browser runtime reads, as it is set in node. One for one with * what the entry query carries, so every option has both spellings: set it @@ -235,10 +237,17 @@ type HotClientOptions = { */ urlPrefix?: string | undefined; /** - * how much the runtime logs to the browser console + * how much the runtime logs to the browser console, and the name every message is labelled with */ logging?: - ("none" | "error" | "warn" | "info" | "log" | "verbose") | undefined; + | ( + | LogLevel + | { + level?: LogLevel; + name?: string; + } + ) + | undefined; /** * how many times to reconnect before giving up; unset, Server-Sent Events keep trying for as long as the page is open while a WebSocket gives up after 10 */