Skip to content

Commit 934c680

Browse files
committed
docs: describe the boot check by what it checks, not by what readers do
Two rounds in a row went to claims about whether a given reader warns when it rejects a value, and both were wrong somewhere. It varies per reader and per branch inside one reader: compileHeaderRules warns only when Headers.set rejects a key, and drops a typo'd `source`, a bad URLPattern, and a non-object entry in silence; readAllowedOrigins filters bad entries with no warning at all; compileRedirectRules warns on every branch but one. So the docs stop asserting it. Each surface now states what the validator checks and what it does not, and leaves downstream behaviour to the reader that owns it. The sweeping "that key-level case is the whole of the gap" line is gone too, since it contradicted the nested-key sentence beside it. The built-ins lead sentence claiming every malformed entry warns was pre-existing and is corrected in passing, since this paragraph leaned on it.
1 parent 0690bfa commit 934c680

4 files changed

Lines changed: 19 additions & 20 deletions

File tree

.agents/skills/webjs/references/built-ins.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ setFileStore(diskStore({ dir: '/var/data/uploads', baseUrl: '/files' }));
145145

146146
## The `"webjs"` config block (package.json)
147147

148-
All keys are optional, and a malformed entry in a key the SERVER reads is dropped at boot with a warning, never crashing the pipeline. An UNKNOWN top-level key is treated the same way (#1300): the block is validated against the published JSON Schema once per boot, in dev and in prod alike, and one aggregated warning names what was ignored. So a `"redirect"` typed for `"redirects"` now says so in the server output instead of silently leaving the feature at its default. It never fails the boot. Know what it does NOT cover, since the remaining silent case looks like the one it fixed. It reports an unknown TOP-LEVEL key, a bad `enum` value, and a wrong-typed `boolean` / `integer`, which is 9 of the 17 keys. It does not descend into a nested object, so a misspelling inside `dev` or `start` is uncaught, and it does not type-check the other 8 (`headers`, `redirects`, `basePath`, `allowedOrigins`, `csp`, `dev`, `start`, `doctor`), which are the free-form array, string, and object shapes where a blunt check would start refusing configs that work. Give one of those the wrong TYPE outright (`"headers": "x"`) and it passes the boot check, and the reader falls back to its default in silence. That key-level case is the whole of what goes unreported: a malformed ENTRY inside a well-shaped `headers` or `redirects` array already warns from the reader, per the sentence above, and `doctor` is not a silent drop either, per the exception below. The one exception is `doctor.gate`, which is read by the `webjs doctor` CLI rather than the server and rejects a bad entry outright (see the doctor severity gate below): a gate whose typo was quietly ignored would leave CI un-gated while looking gated, which is the one thing that mechanism cannot afford.
148+
All keys are optional, and a malformed entry in a key the SERVER reads is dropped at boot rather than crashing the pipeline, some readers warning as they drop it. An UNKNOWN top-level key no longer passes in silence either (#1300): the block is validated against the published JSON Schema once per boot, in dev and in prod alike, and one aggregated warning names what was ignored. So a `"redirect"` typed for `"redirects"` now says so in the server output instead of silently leaving the feature at its default. It never fails the boot. Know its exact reach, since what it passes over looks like what it catches. It reports an unknown TOP-LEVEL key, a bad `enum` value, and a wrong-typed `boolean` / `integer`, which is 9 of the 17 keys. It does not descend into a nested object, so a misspelling inside `dev` or `start` is not reported, and it does not type-check the other 8 (`headers`, `redirects`, `basePath`, `allowedOrigins`, `csp`, `dev`, `start`, `doctor`), which are the free-form array, string, and object shapes where a blunt check would start refusing configs that work. Whether anything else notices a value this check passes over is up to that value's own reader, and it varies by reader. The one exception is `doctor.gate`, which is read by the `webjs doctor` CLI rather than the server and rejects a bad entry outright (see the doctor severity gate below): a gate whose typo was quietly ignored would leave CI un-gated while looking gated, which is the one thing that mechanism cannot afford.
149149

150150
### Security headers
151151

packages/core/src/webjs-config.d.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,12 @@
1818
* top-level key, every bad `enum` value, and every wrong-typed `boolean` /
1919
* `integer` (9 of the 17 keys). It does NOT type-check the other 8
2020
* (`headers`, `redirects`, `basePath`, `allowedOrigins`, `csp`, `dev`,
21-
* `start`, `doctor`), so giving one of THOSE the wrong type outright
22-
* (`"headers": "x"`) passes the boot check and its reader then falls back
23-
* to the default without a word. Only that key-level case is unreported: a
24-
* malformed ENTRY inside a well-shaped `headers` / `redirects` array
25-
* already warns from the reader, and a bad `doctor` shape exits
26-
* `webjs doctor` non-zero rather than being dropped. The boot check warns
27-
* and never throws, so a typo costs one feature its setting rather than
28-
* the whole app its boot.
21+
* `start`, `doctor`), whose schema types are the free-form array, string,
22+
* and object shapes, and it does not descend into a nested object, so a
23+
* misspelling inside `dev` or `start` is not reported either. Whether
24+
* anything else notices what this check passes over is up to the
25+
* individual reader. The boot check warns and never throws, so a typo
26+
* costs one feature its setting rather than the whole app its boot.
2927
*
3028
* LOCKSTEP: this file, the JSON Schema at
3129
* packages/server/webjs-config.schema.json, and the reader functions MUST

packages/server/src/webjs-config-validate.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,17 @@ import { fileURLToPath } from 'node:url';
4949
* unchecked are the free-form ones, where a naive check starts refusing
5050
* configs that work.
5151
*
52-
* What that actually leaves unreported is narrower than the list looks, and
53-
* worth stating precisely so nobody widens this on a wrong premise. It is the
54-
* KEY-level type only (`"headers": "x"`, which the reader then ignores in
55-
* silence). A malformed ENTRY inside a well-shaped `headers` / `redirects`
56-
* array already warns from the reader itself, and a wrong-shaped `doctor` is
57-
* not a silent drop at all: the server never reads that key, and `webjs doctor`
58-
* exits 1 on it (`readDoctorPolicy`).
52+
* That is enough for the case this exists to close, which is a typo'd top-level
53+
* key silently dropped, plus the two leaf kinds a schema can decide
54+
* unambiguously.
5955
*
60-
* That is enough for the case this exists to close, which is a typo'd key
61-
* silently dropped, plus the two leaf kinds a schema can decide unambiguously.
56+
* Describe this by what IT checks, never by what each reader does when the
57+
* check stays quiet. Whether a given reader warns on a value it rejects varies
58+
* per reader and per branch within one reader, so a summary of it is wrong
59+
* somewhere almost immediately, and three review rounds on this PR were spent
60+
* proving exactly that. `webjs.doctor` is the sharpest example: the server never
61+
* reads that key at all, and `webjs doctor` exits 1 on a bad shape rather than
62+
* dropping it.
6263
*
6364
* @param {Record<string, unknown>} schema the webjs-block schema
6465
* @param {Record<string, unknown>} value a candidate `webjs` object

website/app/docs/configuration/page.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ webjs routes --help # one command's help (flag form)</code-block>
162162
<h2>Config typos are reported at boot</h2>
163163
<p>Every key in the <code>webjs</code> block is optional, which means an unknown one has no way to announce itself: write <code>"redirect"</code> for <code>"redirects"</code> and the key is simply never read, the feature sits at its default, and the app looks configured. So WebJs validates the block against its published JSON Schema once per boot, in development and in production alike, and prints a single warning naming what it ignored.</p>
164164
<p>It is a <strong>warning, never a failure</strong>. A typo costs one feature its setting, and refusing to boot over that would cost the whole app, usually mid-deploy.</p>
165-
<p><strong>What it catches, and what it does not.</strong> It reports an unknown top-level key, a value outside an allowed set (<code>trailingSlash</code>), and a wrong-typed boolean or whole number (<code>elide</code>, <code>seed</code>, <code>clientRouter</code>, <code>maxBodyBytes</code>, <code>maxMultipartBytes</code>, and the three timeouts). That is 9 of the 17 keys. It does not descend into nested objects, so a key misspelled inside <code>webjs.dev</code> or <code>webjs.start</code> is missed, and it does not check the type of the other 8 (<code>headers</code>, <code>redirects</code>, <code>basePath</code>, <code>allowedOrigins</code>, <code>csp</code>, <code>dev</code>, <code>start</code>, <code>doctor</code>), which are the free-form array, string, and object shapes where a blunt check would start rejecting configs that work. Give one of those the wrong type outright, as in <code>"headers": "x"</code>, and it passes this check and its reader quietly falls back to the default.</p>
166-
<p>That key-level case is the whole of the gap. A malformed <em>entry</em> inside a well-shaped <code>headers</code> or <code>redirects</code> array already warns on its own, and a wrong-shaped <code>doctor</code> block is not dropped at all: <code>webjs doctor</code> exits non-zero naming it, because a silently ignored gate would leave CI un-gated while looking gated. Editors catch more of this earlier still: a scaffolded app wires the schema into <code>.vscode/settings.json</code>, and the <code>WebjsConfig</code> type from <code>@webjsdev/core</code> types the block while you author it.</p>
165+
<p><strong>What it catches, and what it does not.</strong> It reports an unknown top-level key, a value outside an allowed set (<code>trailingSlash</code>), and a wrong-typed boolean or whole number (<code>elide</code>, <code>seed</code>, <code>clientRouter</code>, <code>maxBodyBytes</code>, <code>maxMultipartBytes</code>, and the three timeouts). That is 9 of the 17 keys. It does not descend into nested objects, so a key misspelled inside <code>webjs.dev</code> or <code>webjs.start</code> is missed, and it does not check the type of the other 8 (<code>headers</code>, <code>redirects</code>, <code>basePath</code>, <code>allowedOrigins</code>, <code>csp</code>, <code>dev</code>, <code>start</code>, <code>doctor</code>), which are the free-form array, string, and object shapes where a blunt check would start rejecting configs that work. Give one of those the wrong type outright, as in <code>"headers": "x"</code>, and it passes this check. Whether anything downstream then says so is up to that key's own reader.</p>
166+
<p>Editors catch more of this earlier still: a scaffolded app wires the schema into <code>.vscode/settings.json</code>, and the <code>WebjsConfig</code> type from <code>@webjsdev/core</code> types the block while you author it. The one part of the block that is never merely dropped is <code>webjs.doctor</code>, which the server does not read at all and which <code>webjs doctor</code> exits non-zero on when it is malformed, because a silently ignored gate would leave CI un-gated while looking gated.</p>
167167
168168
<h2>Environment Variables</h2>
169169
<p>Use <code>process.env</code> in server-side code (pages, actions, route handlers, middleware). WebJs auto-loads <code>&lt;appDir&gt;/.env</code> into <code>process.env</code> once at boot using Node 24+'s built-in <code>process.loadEnvFile</code>, so a scaffolded app with a committed <code>.env.example</code> and a developer-copied <code>.env</code> just works without installing <code>dotenv</code> or wiring up the file path. The auto-load fires before any server-only module is imported, which matters for code that reads <code>process.env</code> at module-init time (e.g. <code>createAuth({ secret: process.env.AUTH_SECRET })</code>).</p>

0 commit comments

Comments
 (0)