[]previous and next page

Merging the entry's options

The generated server entry combines the file's server options with the entry's with mergeHandlerOptions, which @shadow-js/dusk/server exports.

For agentsUpdated

The generated server entry combines the file's server options with the entry's with mergeHandlerOptions, which @shadow-js/dusk/server exports. It works key by key, at every depth of a plain object:

The file and the entry both have Result
a plain object (security, security.headers, one path of routeRules) merged: the entry's keys win, and the file's other keys stay
any other value: a string, number, true, false, null, an array, a function, a class instance the entry's value replaces the file's
undefined in the entry not set: the file's value stays, so an explicit undefined does not erase it

A key only one side sets is kept as it is. Neither file is changed by the merge. The keys __proto__, constructor and prototype are ignored at every depth. A merge creates at most 10 000 objects, so a cyclic object fails at start with a RangeError instead of running on.

ts
// dusk.config.ts
export default defineConfig({
  security: { allowedHosts: ["example.com"], headers: { frameOptions: "DENY" } },
});

// src/entry-server.ts
export const options = {
  security: { csp: { directives: { "default-src": ["'self'"] } } },
};
// The handler has allowedHosts and headers from the file, and csp from the entry.

Some consequences, all of them intended:

  • false and null override. security: { csp: false } in the entry turns off a Content-Security-Policy that the file declares.
  • Arrays replace and never concatenate. security.allowedHosts in the entry replaces the file's list, so an entry that sets allowedHosts: [] leaves no host check (production then also needs acknowledgeNoHostCheck: true). Middleware arrays replace as well (the file has no middleware today). Concatenation would keep a redirect or a host the entry meant to remove.
  • security.cors merges per key: an origins list in the entry replaces the file's list and the file's methods stay.
  • Routing: redirects and the headers rule list replace as whole lists. rewrites merges per phase (beforeFiles, afterFiles, fallback): the entry's array for a phase replaces that phase's array. routeRules merges per path, and within a path per rule key, so a revalidate from the file and a cacheControl from the entry both apply.
  • A function (onError, requestId as a function) and a class instance replace whatever the file had.

Conflicts between the file and the entry#

When the same leaf is set in both files with different values, the build prints one warning per leaf, and the entry's value is the one in force:

text
dusk: security.headers.frameOptions is set in dusk.config.ts ("DENY") and in src/entry-server.ts ("SAMEORIGIN"). src/entry-server.ts wins; keep one of the two, or set the same value in both.

The check reads the entry as text and compares literal values: strings, numbers, booleans, null, and arrays of those. A value the entry computes (a call, a variable that is not a literal, a spread) is not compared, so a conflict through such a value is not reported. The merge rules above still apply to it. A file that cannot be read as TypeScript is reported as "not compared" and the build continues to its own error.