jeasx 2.10.7 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,1485 @@
1
+ # Changelog
2
+
3
+ ## 2026-11-11 - Jeasx 3.0.1 released
4
+
5
+ 🔒 This patch release includes a security update for Fastify ([GHSA-3qgw-9275-xf8w](https://github.com/fastify/fastify/security/advisories/GHSA-3qgw-9275-xf8w)). Please update.
6
+
7
+ ### 🐛 Fixes
8
+
9
+ - **`null` from a route now means "no response".** Before, a route returning
10
+ `null` sent an empty 200. Now it behaves like `undefined`: the next route
11
+ is tried and the 404 fallback applies.
12
+ - **Dev server serves the same `dist` files as production.** In development,
13
+ a `dist` file is now hidden only when its last path segment is a route
14
+ output (`[name].ext`). Before, a file inside a bracketed directory, like
15
+ `[x].d/style.css`, was hidden in development but served in production.
16
+
17
+ ### 🐛 Fixes in `jsx-async-runtime`
18
+
19
+ - **No more crash from deeply nested promises.** `jsx()` only guarded promises
20
+ one level deep in a plain object. A promise nested deeper (e.g.
21
+ `data={[{ p }]}`) could crash Node with an unhandled rejection when the
22
+ render failed on it. It is now marked as handled first.
23
+ - **Streams are unlocked after rendering.** `readStream()` never called
24
+ `releaseLock()`, so a `ReadableStream` child stayed locked after it was
25
+ fully rendered. The lock is now released when the stream ends, like
26
+ `for await` does.
27
+
28
+ ### 📦 Dependency updates
29
+
30
+ - `fastify@5.12.8`
31
+ - `@fastify/send@4.2.1`
32
+ - `jsx-async-runtime@3.0.1`
33
+
34
+ ## 2026-11-02 - Jeasx 3.0.0 released
35
+
36
+ > **The promise you made.** Await anything, stream everything.
37
+
38
+ 🚀 Jeasx 3 streams your pages. JSX is now sent to the browser while it is
39
+ still rendering, powered by `jsx-async-runtime@3`, which also renders about
40
+ 2x faster than v2. This release also hardens request routing, the build and
41
+ the dev server.
42
+
43
+ There are breaking changes in Jeasx and in `jsx-async-runtime`. Each one has
44
+ a migration recipe below. Most projects only need these steps:
45
+
46
+ 1. Replace `{{ html: "..." }}` with `{html("...")}`.
47
+ 2. Replace `class`/`style` objects with strings.
48
+ 3. Move `this.errorHandler` to `setErrorHandler` in `jeasx.config.js`.
49
+ 4. If your `responseHandler` changes the HTML, read the stream into a string first.
50
+
51
+ 👀 Want to see it in action? [Jeasx Expo](https://expo.jeasx.dev) has many
52
+ new examples that show off the new features, e.g.
53
+ [Streaming HTML](https://expo.jeasx.dev/streaming),
54
+ [Out-of-order streaming](https://expo.jeasx.dev/out-of-order) and
55
+ [Server-Sent Events](https://expo.jeasx.dev/sse).
56
+
57
+ ### 💥 Breaking changes in Jeasx
58
+
59
+ #### JSX responses are streamed, so `responseHandler` gets a stream
60
+
61
+ Pages are rendered with `jsxToStream` instead of `jsxToString`. The browser
62
+ gets the first bytes while slow parts of the page (e.g. awaited data) are
63
+ still rendering. For JSX, the `payload` passed to `this.responseHandler` is
64
+ now a `ReadableStream<string>` instead of a `string`.
65
+
66
+ **Migration:** if your `responseHandler` only sets headers or passes the
67
+ payload through, nothing changes. If it changes the HTML (e.g. to format or
68
+ minify it), read the stream into a string first:
69
+
70
+ ```js
71
+ this.responseHandler = async (payload) => {
72
+ if (payload instanceof ReadableStream) {
73
+ payload = (await Array.fromAsync(payload)).join("");
74
+ }
75
+ return typeof payload === "string" ? format(payload) : payload;
76
+ };
77
+ ```
78
+
79
+ Pages that are post-processed this way are not streamed.
80
+
81
+ #### `this.errorHandler` was removed
82
+
83
+ Errors thrown by routes and guards now go to Fastify's error handler. Jeasx
84
+ no longer looks for `this.errorHandler`.
85
+
86
+ **Migration:** register an error handler in `jeasx.config.js`. It runs for
87
+ errors from all routes:
88
+
89
+ ```js
90
+ // jeasx.config.js
91
+ export default {
92
+ FASTIFY_SERVER: (fastify) =>
93
+ fastify.setErrorHandler(async (error, request, reply) => {
94
+ request.log.error(error);
95
+ reply.status(error.statusCode ?? 500).type("text/html; charset=utf-8");
96
+ return "<h1>Something went wrong</h1>";
97
+ }),
98
+ };
99
+ ```
100
+
101
+ Without your own handler, Fastify answers with a JSON error. `jeasx.config.js` is not compiled by esbuild by default, so it can't contain JSX. If you want JSX in your config, write it as `jeasx.config.jsx` and compile it with your own npm script:
102
+
103
+ ```sh
104
+ esbuild jeasx.config.jsx --outfile=jeasx.config.js --platform=node --format=esm --jsx=automatic --jsx-import-source=jsx-async-runtime
105
+ ```
106
+
107
+ **Errors while streaming:** the error handler only runs for errors thrown
108
+ before the first chunk is sent, e.g. in a guard or before the route returns
109
+ its JSX. Once streaming has started, the status and headers are already sent,
110
+ and an error thrown by an async component cuts the page off. Wrap risky
111
+ components in an error boundary that renders them to a string first:
112
+
113
+ ```jsx
114
+ import { html, jsxToString } from "jsx-async-runtime";
115
+
116
+ async function ErrorBoundary({ fallback, children }) {
117
+ try {
118
+ return html(await jsxToString.call(this, children));
119
+ } catch (error) {
120
+ this.request.log.error(error);
121
+ return fallback;
122
+ }
123
+ }
124
+
125
+ <ErrorBoundary fallback={<p>This section could not be loaded.</p>}>
126
+ <RiskyComponent />
127
+ </ErrorBoundary>;
128
+ ```
129
+
130
+ See [Catch errors](https://expo.jeasx.dev/error-handler) for all three cases.
131
+
132
+ #### `request.path` is decoded
133
+
134
+ `request.path` is now URL-decoded, e.g. `/caf%C3%A9` becomes `/café`.
135
+ Backslashes become slashes, and repeated slashes are collapsed (`//a//b`
136
+ becomes `/a/b`).
137
+
138
+ **Migration:** remove `decodeURIComponent(request.path)` from your routes.
139
+ Decoding twice fails for paths that contain a `%`. Use `request.url` if
140
+ you need the raw path.
141
+
142
+ #### The dev server only listens on `localhost`
143
+
144
+ `jeasx dev` binds to `localhost`, so other machines on your network can't
145
+ reach it. `jeasx start` still listens on all interfaces (`::`).
146
+
147
+ **Migration:** if you run `jeasx dev` in a container or VM, or test from
148
+ another device, set `HOST`:
149
+
150
+ ```sh
151
+ HOST=:: npx jeasx dev
152
+ ```
153
+
154
+ #### Missing imports in routes are errors
155
+
156
+ Before, if a route imported a module that doesn't exist, the route was
157
+ skipped as if it didn't exist (usually ending in a 404). Now the error is
158
+ thrown, so you see the real cause.
159
+
160
+ **Migration:** fix or remove the import that is reported.
161
+
162
+ #### Changed responses
163
+
164
+ - If no route answers and there is no `[404]` route, the response is a
165
+ `404` instead of an empty `200`.
166
+ - Objects returned by guards are only used as props. They are never sent as
167
+ the response, even if no route answers.
168
+ - Static files are only served for `GET` and `HEAD`. Other methods (e.g. a
169
+ `POST` to `/form`) go to your routes, even if a file with that path exists.
170
+ - If a static file is missing or ignored (e.g. a dotfile), `[...path]` and
171
+ `[404]` routes answer instead of the plain error page of `@fastify/send`.
172
+ - A `ReadableStream` or Node.js stream returned without a content type is
173
+ sent as `text/html; charset=utf-8`, like strings and buffers.
174
+
175
+ **Migration:** set a status code or content type explicitly where you
176
+ relied on the old behavior. For example, register a Fastify route in
177
+ `FASTIFY_SERVER` for non-`GET` requests to a static file.
178
+
179
+ #### `FASTIFY_SEND_OPTIONS.root` is always the project directory
180
+
181
+ Jeasx passes file paths relative to the project directory, so a custom
182
+ `root` broke file serving. It is now always set to the project directory.
183
+
184
+ **Migration:** remove `root` from `FASTIFY_SEND_OPTIONS`.
185
+
186
+ #### `dist` is cleaned before every build
187
+
188
+ `jeasx build` and `jeasx dev` delete `dist` before building. This removes
189
+ outputs of deleted routes, which could otherwise still be served.
190
+
191
+ **Migration:** don't put files into `dist` by hand. Use `public` instead.
192
+
193
+ ### 🔒 Security
194
+
195
+ - **Compiled routes were exposed in development:** server routes
196
+ (e.g. `/[index].js`), their sourcemaps and other outputs could be fetched
197
+ from `dist` as static files. This exposed server-side code. They are no
198
+ longer served, including Windows tricks like `/[index].js.` or
199
+ `/[index].js::$DATA`.
200
+ - **Special routes can't be requested by name:** requests like `/[index]`,
201
+ `/[...guard]`, `/[...path]` or `/[--metadata--]` no longer call these
202
+ routes directly. The check ignores case, because case-insensitive file
203
+ systems (macOS, Windows) would otherwise resolve `/[--METADATA--]`.
204
+ `/a/404` is still answered by `/a/[404]`.
205
+ - **Malformed paths are rejected:** paths with invalid URL encoding, null
206
+ bytes or `.`/`..` segments are rejected with `400`. Repeated slashes are
207
+ collapsed, so `//evil.com` can't be mistaken for another host
208
+ (e.g. in a `<base>` tag).
209
+ - **Route and file lookups ignore inherited keys:** a path like
210
+ `/constructor` no longer matches properties of `Object.prototype`.
211
+ - **Fewer leaked file handles:** file streams are closed when the response
212
+ ends or is aborted. This includes routes that send a stream with
213
+ `reply.send()`.
214
+ - **In production, other outputs of server routes aren't static files:**
215
+ CSS or other files that esbuild writes for server routes
216
+ (e.g. `[index].css`) are no longer served. Before, only sourcemaps were
217
+ skipped.
218
+
219
+ ### ✨ New features
220
+
221
+ - **Async config options:** all options in `jeasx.config.js`
222
+ (`ESBUILD_SERVER_OPTIONS`, `ESBUILD_BROWSER_OPTIONS`,
223
+ `FASTIFY_SERVER_OPTIONS`, `FASTIFY_SEND_OPTIONS`) and `FASTIFY_SERVER`
224
+ can be async. You can import build-only plugins lazily, so they aren't
225
+ loaded in production:
226
+
227
+ ```js
228
+ export default {
229
+ ESBUILD_BROWSER_OPTIONS: async () => ({
230
+ plugins: [(await import("esbuild-sass-plugin")).sassPlugin()],
231
+ }),
232
+ };
233
+ ```
234
+
235
+ - **Dotfiles in production:** files like `public/.well-known/security.txt`
236
+ are now part of the production build. Whether they are served depends on
237
+ the `dotfiles` option of `FASTIFY_SEND_OPTIONS` (default: ignored), the
238
+ same as in development:
239
+
240
+ ```js
241
+ export default {
242
+ FASTIFY_SEND_OPTIONS: () => ({ dotfiles: "allow" }),
243
+ };
244
+ ```
245
+
246
+ - **Graceful shutdown:** the server closes on `SIGINT` (Ctrl+C) and
247
+ `SIGTERM` and then exits. It stops cleanly in containers (where Node runs
248
+ as PID 1) and while the esbuild watcher is running. Press Ctrl+C a second
249
+ time to kill it if the shutdown hangs.
250
+
251
+ ### 🛠️ Fixes
252
+
253
+ - `jeasx dev` waits for the first build before starting the server, so the
254
+ first requests don't fail on an empty `dist`. Build errors are logged and
255
+ the watcher keeps running.
256
+ - If a file exists in both `dist` and `public`, `dist` wins in production,
257
+ as it does in development. Symbolic links are skipped in production builds.
258
+ - Config, routes and metadata are imported with `pathToFileURL`, which
259
+ fixes paths on Windows and paths with special characters.
260
+ - A directly requested `/a/404` page keeps its status only if it is answered
261
+ by `/a/[404]`. If a parent `[404]` route answers, the status is `404`.
262
+ - File paths with special characters (e.g. `#` or `%`) are served correctly.
263
+ - esbuild no longer forces colored output, so logs in CI and log files
264
+ don't contain color codes.
265
+ - The docs in `env.js` explain the load order: a variable that is already
266
+ set is never overwritten, and `NODE_ENV` must come from the environment.
267
+
268
+ ### 📦 Updates to `jsx-async-runtime@3.0.0`
269
+
270
+ Jeasx depends on `jsx-async-runtime` for all rendering, so these changes
271
+ affect every Jeasx project. See the
272
+ [full changelog](https://github.com/jeasx/jsx-async-runtime/blob/main/CHANGELOG.md)
273
+ for every detail.
274
+
275
+ #### 💥 Breaking changes
276
+
277
+ **Raw markup needs `html()`**
278
+
279
+ `{{ html: "..." }}` objects render nothing. This closes an injection path,
280
+ e.g. through parsed JSON with an `html` key.
281
+
282
+ ```jsx
283
+ // Before
284
+ {
285
+ {
286
+ html: "<!DOCTYPE html>";
287
+ }
288
+ }
289
+
290
+ // After
291
+ import { html } from "jsx-async-runtime";
292
+ {
293
+ html("<!DOCTYPE html>");
294
+ }
295
+ ```
296
+
297
+ **Escaping can't be turned off**
298
+
299
+ `this.jsxEscapeHTML = false` was removed.
300
+
301
+ **Migration:** wrap trusted markup in `html()`. To pass trusted text through
302
+ unescaped everywhere, use a `this.jsxToString` override that returns it as is.
303
+
304
+ **`class` and `style` must be strings**
305
+
306
+ Objects and arrays are no longer converted. They render as JSON, like other
307
+ attributes, and TypeScript only accepts strings.
308
+
309
+ ```jsx
310
+ // Before
311
+ <div class={["card", { active }]} style={{ color, "font-size": "2em" }} />;
312
+
313
+ // After (e.g. with clsx)
314
+ import clsx from "clsx";
315
+ <div class={clsx("card", { active })} style={`color: ${color}; font-size: 2em`} />;
316
+ ```
317
+
318
+ **Components get `children` as written**
319
+
320
+ As in React, a single child is passed as is, several children as an array,
321
+ and there is no `children` prop without children. Before, `children` was
322
+ always a flattened array without `null` and booleans.
323
+
324
+ **Migration:** most components that only render `{children}` keep working.
325
+ If you call array methods on `children`, normalize it first:
326
+
327
+ ```jsx
328
+ function List({ children }) {
329
+ return (
330
+ <ul>
331
+ {[children ?? []].flat().map((child) => (
332
+ <li>{child}</li>
333
+ ))}
334
+ </ul>
335
+ );
336
+ }
337
+ ```
338
+
339
+ Generators passed as children are read at render time, so they can only be
340
+ rendered once.
341
+
342
+ **Other breaking changes**
343
+
344
+ - **Text is plain values:** text children are strings, numbers or bigints
345
+ instead of `{ type: "textNode", text }`. The `textNode` type was removed.
346
+ - **Only real elements render:** hand-built objects like
347
+ `{ type: "tag", tag: "b", props }` render nothing. Use `createElement`
348
+ instead. Copies of real elements (`{ ...element, tag: "i" }`) still render.
349
+ - **Invalid component types throw:** a tag that is neither a string nor a
350
+ function (e.g. `undefined` from a missing import) fails with a `TypeError`
351
+ instead of rendering nothing. With the Jeasx error handling above, you now
352
+ see these errors.
353
+ - **`jsxToString` can return synchronously:** it returns
354
+ `string | Promise<string>`. `await jsxToString(...)` keeps working.
355
+ - **Void tags ignore children:** `<br>text</br>` renders `<br>`.
356
+ - **Override scope:** a `this.jsxToString` override set by a component also
357
+ applies to the siblings that follow it.
358
+ - **Exports:** the `Tag` type was renamed to `ElementNode`. `jsxsDEV` was
359
+ removed from `jsx-async-runtime/jsx-dev-runtime`.
360
+ - **Stricter types:** `aria-*` attributes no longer accept booleans, and
361
+ `contenteditable`, `spellcheck`, `translate` and `writingsuggestions` no
362
+ longer accept `false`. Use strings like `aria-hidden="true"` or
363
+ `spellcheck="false"`. Add custom elements to `JSX.IntrinsicElementAttributes`
364
+ instead of `JSX.IntrinsicElements`.
365
+
366
+ #### ✨ New features
367
+
368
+ - **`jsxToStream`** renders to a `ReadableStream<string>`. Jeasx uses it for
369
+ all JSX responses. It sends what is rendered so far whenever rendering
370
+ waits for async work, splits large pages into ~16 KB chunks, waits for
371
+ slow clients and stops when the client disconnects.
372
+ - **`isJsxNode`** checks whether a value is an element or `html()` markup
373
+ and narrows the type in TypeScript.
374
+ - **More child types:**
375
+ - Promises: `<div>{fetchUser()}</div>`
376
+ - Iterables: `Set`, generators, `map.values().map(...)`
377
+ - Async iterables: async generator components and `ReadableStream`,
378
+ rendered in order as items arrive
379
+ - Bytes (`Uint8Array`, `Buffer`) as escaped UTF-8 text:
380
+ `<pre>{response.body}</pre>`
381
+ - Objects with their own `toString` (`Date`, `URL`, `Error`, your classes)
382
+ as escaped text
383
+ - Functions as render props:
384
+ `<Loader>{(data) => <p>{data}</p>}</Loader>`
385
+ - **Promise attributes:** `<img src={loadImageUrl()} />`. All promise
386
+ attributes of a tag are awaited in parallel.
387
+ - **Better types:** async components with a `Promise<JSX.Element>` return
388
+ type, `key` on every component, `null` attributes, and JSX types for
389
+ `"jsx": "react-jsxdev"`.
390
+
391
+ #### 🔒 Security
392
+
393
+ - Only markup created by `html()` is rendered raw.
394
+ - Elements carry a Symbol marker, so JSON look-alikes such as
395
+ `{"type":"tag","tag":"script",...}` render nothing.
396
+ - Attribute names with whitespace, quotes, `<`, `>`, `/`, `=`, `&` or control
397
+ characters are skipped, and so are invalid tag names (with their children).
398
+ Valid names like `onmouseover` or `script` still render, so allowlist
399
+ attributes and tags that come from untrusted input.
400
+ - Keys inherited from a polluted `Object.prototype` are ignored.
401
+
402
+ #### 🛠️ Fixes
403
+
404
+ - Rejected promises in props no longer crash Node with an unhandled
405
+ rejection. The error goes to the render.
406
+ - Streams that are never rendered (e.g. in a void tag or after a failed
407
+ render) are closed, so they don't keep connections or files open.
408
+ - Functions and symbols in attributes are skipped. Event handlers no longer
409
+ print their source code.
410
+ - `key` is never rendered, and components don't receive it in their props.
411
+ - `createElement` no longer modifies the props you pass in.
412
+ - Rendering without a context gives each render its own empty context, so
413
+ data no longer leaks between renders.
414
+ - `escapeEntities` converts non-strings with `String()` before escaping.
415
+
416
+ Dependency updates: `jsx-async-runtime@3.0.0`, `@types/node@26.6.6`
417
+
418
+ ## 2026-10-23 - Jeasx 2.10.7 released
419
+
420
+ ### Core changes
421
+
422
+ - New: Routes and guards can return a web `Response` or `ReadableStream`,
423
+ which is served directly.
424
+
425
+ - Fixed: File handles leaked when a guard or route responded before a
426
+ static file was served. The file stream is now always closed.
427
+
428
+ - Security: Requesting a guard file by its literal path
429
+ (e.g. `/admin/[...guard]`) was treated as a static file request and
430
+ bypassed the guard. Guards now run for these requests as well.
431
+
432
+ ### Updates to `jsx-async-runtime`
433
+
434
+ - Attribute rendering is about 1.5x faster, because it no longer builds
435
+ intermediate arrays.
436
+
437
+ - Empty style objects and `null`, `undefined` or `false` style values
438
+ are no longer rendered.
439
+
440
+ - `0` is now rendered when it is the only child (e.g. `<h1>{0}</h1>`).
441
+
442
+ - `bigint` children are now rendered.
443
+
444
+ - Arrays returned by components are now rendered.
445
+
446
+ Dependency updates: `jsx-async-runtime@2.3.0`, `@fastify/send@4.2.0`
447
+
448
+ ## 2026-10-13 - Jeasx 2.10.6 released
449
+
450
+ 🎉 Security update for Fastify ([GHSA-389g-gv7q-r4x8](https://github.com/fastify/fastify/security/advisories/GHSA-389g-gv7q-r4x8)).
451
+
452
+ This release adds a minor performance improvement by switching checks for guards which return additional props.
453
+
454
+ Dependency updates: `fastify@5.12.7`, `@fastify/send@4.1.2`, `@types/node@26.6.5`
455
+
456
+ ## 2026-10-01 - Jeasx 2.10.5 released
457
+
458
+ 🎉 Security update for Fastify ([GHSA-4mh8-r7rc-xpvc](https://github.com/fastify/fastify/security/advisories/GHSA-4mh8-r7rc-xpvc)).
459
+
460
+ Dependency updates: `fastify@5.12.5`, `@types/node@26.6.3`
461
+
462
+ ## 2026-09-11 - Jeasx 2.10.4 released
463
+
464
+ 🎉 Just some dependency updates...
465
+
466
+ Dependency updates: `fastify@5.12.4`, `jsx-async-runtime@2.2.1`, `@types/node@26.5.1`
467
+
468
+ ## 2026-09-04 - Jeasx 2.10.3 released
469
+
470
+ ### 🚀 More efficient route handling
471
+
472
+ The central method `generateRoutes` has been updated to use a JavaScript generator instead of building and checking an array of all routes. This change reduces unnecessary memory usage and computational overhead, as most routes are matched early in the sequence and no longer require being pushed to an array.
473
+
474
+ ### 🔒 Security updates
475
+
476
+ This version addresses four security vulnerabilities in the `fastify` dependency:
477
+
478
+ - [GHSA-9q9j-q6p8-xq58](https://github.com/fastify/fastify/security/advisories/GHSA-9q9j-q6p8-xq58)
479
+ - [GHSA-hwr6-493r-vm6h](https://github.com/fastify/fastify/security/advisories/GHSA-hwr6-493r-vm6h)
480
+ - [GHSA-p68q-wchp-6fh7](https://github.com/fastify/fastify/security/advisories/GHSA-p68q-wchp-6fh7)
481
+ - [GHSA-667r-xxjv-c9mm](https://github.com/fastify/fastify/security/advisories/GHSA-667r-xxjv-c9mm)
482
+
483
+ We strongly recommend updating to this version to ensure the security of your application.
484
+
485
+ Dependency updates: `fastify@5.12.3`, `@types/node@26.4.1`
486
+
487
+ ## 2026-08-22 - Jeasx 2.10.2 released
488
+
489
+ 🎉 Security fixes for Fastify ([GHSA-w2qp-rph6-63g4](https://github.com/fastify/fastify/security/advisories/GHSA-w2qp-rph6-63g4) and [GHSA-3m5p-2c4r-xxw2](https://github.com/fastify/fastify/security/advisories/GHSA-3m5p-2c4r-xxw2))
490
+
491
+ Dependency updates: `fastify@5.12.1`
492
+
493
+ ## 2026-08-15 - Jeasx 2.10.1 released
494
+
495
+ 🎉 Just a bunch of dependency updates...
496
+
497
+ Dependency updates: `fastify@5.12.0`, `@fastify/send@4.1.1`, `esbuild@0.28.2`, `@types/node@26.2.0`
498
+
499
+ ## 2026-08-04 - Jeasx 2.10.0 released
500
+
501
+ ### 🎉 `BUILD_TIME` moved to userland
502
+
503
+ As part of my ongoing goal to move features from the core into userland, I’ve removed the core-level `BUILD_TIME` variable. While this simplifies the core, I want to ensure your builds aren't affected. If you use `BUILD_TIME` for cache busting, you can now let `esbuild` handle it directly using the powerful [define option](https://esbuild.github.io/api/#define) in your `jeasx.config.js`:
504
+
505
+ ```js
506
+ /** @type {() => import("esbuild").BuildOptions} */
507
+ ESBUILD_SERVER_OPTIONS: () => ({
508
+ define: { "process.env.BUILD_TIME": `"${Date.now().toString(36)}"` },
509
+ });
510
+ ```
511
+
512
+ This configuration replaces all references to `process.env.BUILD_TIME` with a Base36 (a-z0-9) encoded timestamp as string (note the additional quotes above).
513
+
514
+ I have also removed the automatic provision of `BROWSER_PUBLIC_` environment variables to frontend bundles. This gives you the freedom to define your own schema; simply add the appropriate define option to `ESBUILD_BROWSER_OPTIONS` to specify which variables should be available to the frontend.
515
+
516
+ Dependency updates: `fastify@5.11.2`
517
+
518
+ ## 2026-07-30 - Jeasx 2.9.1 released
519
+
520
+ ### 🎉 Routine maintenance release.
521
+
522
+ `BUILD_TIME` is now injected into the environment during the build process, before `jeasx.config.js` is loaded. This enables you to capture the build timestamp for use during runtime (e.g. for using it in [FASTIFY_SERVER_OPTIONS#rewriteUrl](https://fastify.dev/docs/latest/Reference/Server/#rewriteurl)).
523
+
524
+ Dependency updates: `fastify@5.11.0`, `@types/node@26.1.2`
525
+
526
+ ## 2026-07-17 - Jeasx 2.9.0 released
527
+
528
+ ### ✨ Less is more... trimming the fat
529
+
530
+ This release **removes** `@fastify/formbody`, `@fastify/cookie`, and `@fastify/multipart` as core dependencies. Since Jeasx is capable of powering serious, real-world projects without these dependencies, removing them keeps your project's footprint as small as possible. This allows you to enable them individually, only when your specific use case requires them.
531
+
532
+ - **Forms:** Install `@fastify/formbody` if your project requires _POST_ form support.
533
+ - **Cookies:** Install `@fastify/cookie` if you need to handle HTTP cookies.
534
+ - **Uploads:** Install `@fastify/multipart` if your project handles file uploads.
535
+
536
+ Jeasx is built on the philosophy of providing a powerful core while pushing customizations to the user level. By avoiding opinionated defaults, Jeasx gives you the freedom to build a setup that is uniquely your own.
537
+
538
+ Need these features? No problem! Jeasx makes it easy to customize your stack. To get back to the exact setup from previous releases, just add these dependencies directly to your project:
539
+
540
+ ```bash
541
+ npm install @fastify/formbody @fastify/cookie @fastify/multipart
542
+ ```
543
+
544
+ Next, update your `jeasx.config.js` with the following configuration to customize your Fastify server instance:
545
+
546
+ ```js
547
+ /** @type {(fastify: import("fastify").FastifyInstance) => import("fastify").FastifyInstance} */
548
+ FASTIFY_SERVER: (fastify) =>
549
+ fastify
550
+ .register(import("@fastify/formbody"), {/* insert options from FASTIFY_FORMBODY_OPTIONS */})
551
+ .register(import("@fastify/multipart"), {/* insert options from FASTIFY_MULTIPART_OPTIONS */})
552
+ .register(import("@fastify/cookie"), {/* insert options from FASTIFY_COOKIE_OPTIONS */});
553
+ ```
554
+
555
+ Because plugins like `@fastify/cookie` and `@fastify/multipart` extend the `request` and `reply` objects, you should add the following imports to `src/types.d.ts` (or a similar definition file in your project) to ensure proper TypeScript support for your editor:
556
+
557
+ ```js
558
+ import "@fastify/cookie";
559
+ import "@fastify/multipart";
560
+ ```
561
+
562
+ This release also removes the additional encapsulation context from the Fastify initialization. This change allows for a more direct and flexible customization of the underlying Fastify instance.
563
+
564
+ Dependency updates: `jsx-async-runtime@2.2.0`, `@types/node@26.1.1`
565
+
566
+ ## 2026-07-07 - Jeasx 2.8.1 released
567
+
568
+ 🎉 You can now access static file metadata in guards directly via `reply.file`. If a file exists for the requested path, this object will be pre-populated with `headers`, `statusCode`, and `stream` for the current file. This eliminates the need for manual checks when performing logic on static files, including:
569
+
570
+ - Skipping basic authentication.
571
+ - Dynamically modifying headers.
572
+ - Replacing content on the fly.
573
+
574
+ Check out the new [basic auth example](https://expo.jeasx.dev/basic-auth) for implementation details.
575
+
576
+ Also refined type definitions in `serverless.ts` to improve code readability and maintainability.
577
+
578
+ Dependency updates: `fastify@5.10.0`
579
+
580
+ ## 2026-07-03 - Jeasx 2.8.0 released
581
+
582
+ ### 🚀 Unified Asset & Route Handling
583
+
584
+ This release completes the architectural transition of merging dynamic server routes and static browser assets into a common directory tree. While Jeasx 2.6 removed the strict directory separation, this version further unifies how these files are processed.
585
+
586
+ Previously, browser assets were served directly by `@fastify/static`, while server routes were handled by the route handler. This created a limitation where server-side logic (like authentication or custom headers) couldn't be applied to static assets.
587
+
588
+ With this release, all files - regardless of type - are now handled directly by the route handler. This introduces several major improvements:
589
+
590
+ - **Enhanced Control:** You can now use guards to modify responses for static assets, such as adding security headers or implementing password protection.
591
+
592
+ - **Hot-Reloading for Assets:** Static files in development mode are now picked up instantly. No application restarts are required to make new files accessible.
593
+
594
+ - **Production Optimization & Security:** In production, static files are mapped into a metadata export at build time. This improves server startup performance by eliminating file globbing and provides robust protection against path traversal attacks.
595
+
596
+ #### ⚠️ Important Migration Note
597
+
598
+ To achieve these improvements, `@fastify/static` has been removed as a direct dependency in favor of `@fastify/send`. Since `@fastify/static` relied on `@fastify/send` internally, this change provides more flexibility while maintaining the same functionality with fewer dependencies.
599
+
600
+ A minor configuration change is required to make your application work as expected. You'll need to change references in `jeasx.config.js` from `@fastify/static` to `@fastify/send` .
601
+
602
+ ```js
603
+ // BEFORE //
604
+ /** @type {() => import("@fastify/static").FastifyStaticOptions} */
605
+ FASTIFY_STATIC_OPTIONS: () => ({/* configuraton options */}),
606
+
607
+ // AFTER //
608
+ /** @type {() => import("@fastify/send").SendOptions} */
609
+ FASTIFY_SEND_OPTIONS: () => ({/* configuraton options */}),
610
+ ```
611
+
612
+ Because the configuration options for `@fastify/static` and `@fastify/send` are almost identical, no configuration changes are required for most users.
613
+
614
+ Dependency updates: `@fastify/send@4.1.0`, `@types/node@26.1.0`
615
+
616
+ ## 2026-06-28 - Jeasx 2.7.5 released
617
+
618
+ 🎉 This release focuses on internal improvements and codebase organization. I have renamed `esbuild.config.js` to `build.js` and `server.js` to `start.js` to more accurately reflect their roles within `cli.js`. The renamed scripts are intended for internal use and should not be called directly. If your current workflow requires calling them, please update your project configuration accordingly.
619
+
620
+ Additionally, minor refactorings were applied to `cli.js` and `env.js` to improve maintainability.
621
+
622
+ Dependency updates: `fastify@5.9.0`, `@types/node@26.0.1`
623
+
624
+ ## 2026-06-21 - Jeasx 2.7.4 released
625
+
626
+ 🎉 Introduced pre-calculation of all module paths during server startup for production environments. This provides a robust defense against path traversal attacks from malformed requests, even in scenarios where Fastify’s built-in path sanitization might fail.
627
+
628
+ Optimized path processing by skipping empty segments directly, removing the overhead of filtering segments into new arrays before iteration.
629
+
630
+ Improved the module loading logic for development mode to ensure better compatibility with Node.js and alternative JavaScript runtimes like Bun.
631
+
632
+ Dependency updates: `@types/node@26.0.0`
633
+
634
+ ## 2026-06-12 - Jeasx 2.7.3 released
635
+
636
+ 🎉 Routine maintenance release.
637
+
638
+ Dependency updates: `jsx-async-runtime@2.1.3`, `esbuild@0.28.1`, `@types/node@25.9.3`
639
+
640
+ ## 2026-06-10 - Jeasx 2.7.2 released
641
+
642
+ 🎉 This release introduces significant performance and memory optimizations:
643
+
644
+ - The core `generateRoutes` method has been rewritten to improve execution speed and significantly reduce temporary array creation. This minimizes garbage collection overhead during high-load scenarios.
645
+
646
+ - The internal cache has been migrated from a `Map` to a plain object to leverage engine-level optimizations for small, read-heavy collections. I've also added a missing type definition for the module cache.
647
+
648
+ - The `.js` extension is now stripped from route export metadata during the build process, reducing the number of string operations required when resolving modules from the cache.
649
+
650
+ ## 2026-06-07 - Jeasx 2.7.1 released
651
+
652
+ 🎉 This patch release requires no changes to your project but introduces significant improvements to internal module handling: Previously, a dynamic runtime cache was used to resolve modules for routes. This cache stored entries for both existing and non-existent routes, leading to potential growth over time - despite a predefined maximum cache size.
653
+
654
+ With this update, the cache strategy has been optimized for more efficient and sustainable memory usage: This release leverages [esbuild’s metadata](https://esbuild.github.io/api/#build-metadata) to generate a static metadata file at build time, containing all existing routes. This metadata is loaded during server startup, ensuring that only modules included in the routes list are loaded on the first request.
655
+
656
+ The result? Fast cold starts, efficient handling of existing and non-existent routes (without caching overhead), and enhanced security. Since only modules generated at build time can be loaded, even in the unlikely case where a malformed request could bypass Fastify’s strict request sanitation, it cannot escape the `dist` directory.
657
+
658
+ The changes above have no impact on your development workflow. You can continue creating new routes or modifying existing ones - your updates will be loaded automatically, without requiring a restart of the application.
659
+
660
+ Dependency updates: `@types/node@25.9.2`
661
+
662
+ ## 2026-06-02 - Jeasx 2.7.0 released
663
+
664
+ 🎉 This release introduces an explicit configuration file named `jeasx.config.js`, replacing the previously used `.env.js`. This change improves clarity and usability for configuring `esbuild` and `Fastify` options, which could no longer be passed as environment variables in recent releases.
665
+
666
+ To upgrade:
667
+
668
+ - If you used `.env.js` before, just rename it to `jeasx.config.js`.
669
+ - If you didn’t use `.env.js`, create a new file called `jeasx.config.js` in your project folder. Add the following code as default to get a reminder of all available configuration options:
670
+
671
+ ```js
672
+ export default {
673
+ /** @type {() => import("esbuild").BuildOptions} */
674
+ // ESBUILD_SERVER_OPTIONS: () => ({}),
675
+ /** @type {() => import("esbuild").BuildOptions} */
676
+ // ESBUILD_BROWSER_OPTIONS: () => ({}),
677
+ /** @type {(fastify: import("fastify").FastifyInstance) => import("fastify").FastifyInstance} */
678
+ // FASTIFY_SERVER: (fastify) => fastify,
679
+ /** @type {() => import("fastify").FastifyServerOptions} */
680
+ // FASTIFY_SERVER_OPTIONS: () => ({}),
681
+ /** @type {() => import("@fastify/static").FastifyStaticOptions} */
682
+ // FASTIFY_STATIC_OPTIONS: () => ({}),
683
+ /** @type {() => import("@fastify/cookie").FastifyCookieOptions} */
684
+ // FASTIFY_COOKIE_OPTIONS: () => ({}),
685
+ /** @type {() => import("@fastify/formbody").FastifyFormbodyOptions} */
686
+ // FASTIFY_FORMBODY_OPTIONS: () => ({}),
687
+ /** @type {() => import("@fastify/multipart").FastifyMultipartOptions} */
688
+ // FASTIFY_MULTIPART_OPTIONS: () => ({}),
689
+ };
690
+ ```
691
+
692
+ If you’re using Docker, make sure to create an entry for `jeasx.config.js` in your `.dockerignore` file.
693
+
694
+ **Please note:** Support for `.env.js` has been discontinued in this release to streamline and simplify the codebase. If you previously used `.env.js` to set or modify environment variables besides the existing configuration options, you can now do this directly in `jeasx.config.js` by modifying `process.env` using JavaScript.
695
+
696
+ ```js
697
+ process.env.API_ENDPOINT = "https://expo.jeasx.dev/jokes/api/";
698
+
699
+ export default {/* ... */};
700
+ ```
701
+
702
+ Dependency updates: `@types/node@25.9.1`
703
+
704
+ ## 2026-05-14 - Jeasx 2.6.2 released
705
+
706
+ 🎉 This release moves the configuration for esbuild’s sourcemap settings from core to userland. Since there is no universal default that fits all environments and policies, this change allows for greater flexibility.
707
+
708
+ To enable sourcemaps for debugging your code, configure `ESBUILD_SERVER_OPTIONS` or `ESBUILD_BROWSER_OPTIONS` in your `.env.js` file based on your specific needs. While I recommend using `inline` as the configuration value for simplicity, the optimal choice may depend on your setup and tooling.
709
+
710
+ Please study the [esbuild documentation](https://esbuild.github.io/api/#sourcemap) for the different configuration options.
711
+
712
+ ```js
713
+ /** @type {() => import("esbuild").BuildOptions} */
714
+ ESBUILD_SERVER_OPTIONS: () => ({
715
+ sourcemap: "inline",
716
+ });
717
+ ```
718
+
719
+ ## 2026-05-12 - Jeasx 2.6.1 released
720
+
721
+ 🎉 This release includes a small update to the `tsconfig.json` file: The `types` attribute has been adjusted from `["*"]` to `["node"]` to align with the TypeScript team’s best practices. This change ensures better type resolution and resolves a warning that appeared when using Zed as the editor.
722
+
723
+ Dependency updates: `@types/node@25.7.0`
724
+
725
+ ## 2026-05-08 - Jeasx 2.6.0 released
726
+
727
+ 🎉 Back in the early days, Jeasx enforced a strict separation between browser and server code. This meant developers had to organize their projects into specific directories like `browser` and `routes`. Over time, this limitation was lifted, allowing for more flexible project structures. While you could freely arrange your application code in the `src` directory, the `dist` directory still maintained the separation between browser and server files.
728
+
729
+ This created a significant challenge: [esbuild loaders](https://esbuild.github.io/api/#loader) (such as those for importing files or using CSS modules) couldn’t be applied to server routes. The reason? Assets generated for the server and stored in `dist/server` were inaccessible to the browser, making it impossible to leverage these powerful tools in server-side code.
730
+
731
+ Here are the good news: everything in the `dist` folder is now organized exactly as you’ve structured your application, hard coded sub-directories for `browser` and `server` are gone for good. All files in the `dist` directory are delivered directly to the browser via `@fastify/static`, except the code for server routes (JavaScript files enclosed in square brackets) - this code is still handled on the server. Now you are free to utilize esbuild loaders for your routes and enjoy all the advanced esbuild configurations for bundling server-side code.
732
+
733
+ ### ⚠️ Required updates to `.env.js`
734
+
735
+ Previously, common browser asset extensions (e.g., `.ttf, .woff2, .svg, .jpg`) were automatically treated as `external` by Jeasx. This meant they were excluded from the bundling process by esbuild. For example, font files or icons stored in the `public` directory and referenced in your bundled CSS files would not be processed by esbuild.
736
+
737
+ Now, no extensions are marked as `external` by default anymore. If your build fails with an error like `ERROR: No loader is configured for ".woff2"`, you’ll need to explicitly mark the missing extensions as `external` in your `.env.js` configuration by updating the `ESBUILD_BROWSER_OPTIONS`.
738
+
739
+ Also Jeasx no longer sets a default value for the esbuild `target` option. I recommend configuring it explicitly based on your project’s needs. If you don’t specify a value, esbuild will default to `esnext`.
740
+
741
+ The example below restores the former esbuild settings used by Jeasx, ensuring a smooth transition to the current release.
742
+
743
+ ```js
744
+ /** @type {() => import("esbuild").BuildOptions} */
745
+ ESBUILD_BROWSER_OPTIONS: () => ({
746
+ // It’s best to start with an empty list and
747
+ // only add extensions that cause build errors.
748
+ external: [
749
+ "*.avif",
750
+ "*.gif",
751
+ "*.jpg",
752
+ "*.jpeg",
753
+ "*.png",
754
+ "*.svg",
755
+ "*.webp",
756
+ "*.eot",
757
+ "*.ttf",
758
+ "*.otf",
759
+ "*.woff",
760
+ "*.woff2",
761
+ ],
762
+ target: ["chrome130", "edge130", "firefox130", "safari18"],
763
+ });
764
+ ```
765
+
766
+ After the update, you should start using the [esbuild loader](https://esbuild.github.io/api/#loader) to handle assets. To help you get started, here’s an example of how you can import images directly in your server routes. Simply configure the `file` loader for `.jpg` files in your `.env.js` file:
767
+
768
+ ```js
769
+ /** @type {() => import("esbuild").BuildOptions} */
770
+ ESBUILD_SERVER_OPTIONS: () => ({
771
+ loader: { ".jpg": "file" },
772
+ });
773
+ ```
774
+
775
+ You can now import an image located next to your server route and reference it in your code. This way esbuild automatically hashes the image path, so you don’t need to manually update references - even with caching headers applied.
776
+
777
+ ```jsx
778
+ import Image from "./image.jpg";
779
+
780
+ export default function () {
781
+ return (
782
+ <figure>
783
+ <img src={Image} alt="Example image" />
784
+ </figure>
785
+ );
786
+ }
787
+ ```
788
+
789
+ If you encounter a TypeScript warning for the import, you can resolve it by adding a definition file (e.g., `jpg.d.ts`) to specify the correct typing.
790
+
791
+ ```ts
792
+ declare module "*.jpg" {
793
+ const content: string;
794
+ export default content;
795
+ }
796
+ ```
797
+
798
+ Dependency updates: `jsx-async-runtime@2.1.2`, `@types/node@25.6.2`
799
+
800
+ ## 2026-04-24 - Jeasx 2.5.3 released
801
+
802
+ 🎉 Just dependency updates...
803
+
804
+ Dependency updates: `@fastify/static@9.1.3`
805
+
806
+ ## 2026-04-20 - Jeasx 2.5.2 released
807
+
808
+ 🎉 This release resolves an issue where existing routes failed to load if a JavaScript error occurred during initialization (for example, when setting up a global scoped variable). Previously, this error was masked by returning a 404 route, causing the route to never be re-evaluated.
809
+
810
+ ## 2026-04-16 - Jeasx 2.5.1 released
811
+
812
+ 🎉 Upgrade to latest `@fastify/static` to fix [CVE-2026-6410](https://github.com/fastify/fastify-static/security/advisories/GHSA-pr96-94w5-mx2h) and [CVE-2026-6414](https://github.com/fastify/fastify-static/security/advisories/GHSA-x428-ghpx-8j92).
813
+
814
+ Dependency updates: `@fastify/static@9.1.1`
815
+
816
+ ## 2026-04-15 - Jeasx 2.5.0 released
817
+
818
+ 🎉 This release removes all non-essential Fastify configuration options from the Jeasx core, aligning it more closely with Fastify’s well-chosen default settings.
819
+
820
+ To maintain your previous behavior, I recommend making minor adjustments to your configuration via `.env.js`:
821
+
822
+ ```js
823
+ export default {
824
+ // ...
825
+
826
+ FASTIFY_SERVER_OPTIONS: () => ({
827
+ logger: { level: process.env.NODE_ENV === "development" ? "error" : "info" },
828
+ // 'disableRequestLogging' should be removed.
829
+ }),
830
+
831
+ FASTIFY_MULTIPART_OPTIONS: () => ({
832
+ // Only relevant if you use file uploads in your project.
833
+ attachFieldsToBody: "keyValues",
834
+ }),
835
+
836
+ // ...
837
+ };
838
+ ```
839
+
840
+ Additionally, the configured Fastify logger is now used for error logging, replacing the previous use of `console.error`.
841
+
842
+ This release also addresses [CVE-2026-33806](https://github.com/fastify/fastify/security/advisories/GHSA-247c-9743-5963) by upgrading to the latest Fastify version.
843
+
844
+ Dependency updates: `fastify@5.8.5`, `@types/node@25.6.0`
845
+
846
+ ## 2026-04-09 - Jeasx 2.4.8 released
847
+
848
+ 🎉 Just some dependency updates...
849
+
850
+ Dependency updates: `@fastify/multipart@10.0.0`, `@fastify/static@9.1.0`
851
+
852
+ ## 2026-04-04 - Jeasx 2.4.7 released
853
+
854
+ 🎉 Just some dependency updates...
855
+
856
+ Dependency updates: `jsx-async-runtime@2.1.1`, `esbuild@0.28.0`, `@types/node@25.5.2`
857
+
858
+ ## 2026-04-02 - Jeasx 2.4.6 released
859
+
860
+ 🎉 This release updates the `tsconfig.json` settings to fully support the latest TypeScript 6 changes. Now, the latest version of VSCode runs smoothly without any warnings.
861
+
862
+ Dependency updates: `esbuild@0.27.7`
863
+
864
+ ## 2026-03-25 - Jeasx 2.4.5 released
865
+
866
+ 🎉 Just a patch update to stay synced with latest `jsx-async-runtime`.
867
+
868
+ Dependency updates: `jsx-async-runtime@2.1.0`
869
+
870
+ ## 2026-03-23 - Jeasx 2.4.4 released
871
+
872
+ 🎉 Just a patch release to update Fastify to fix [CVE-2026-3635](https://github.com/fastify/fastify/security/advisories/GHSA-444r-cwp2-x5xf).
873
+
874
+ Dependency updates: `fastify@5.8.4`
875
+
876
+ ## 2026-03-14 - Jeasx 2.4.3 released
877
+
878
+ 🎉 Just some dependency updates...
879
+
880
+ Dependency updates: `fastify@5.8.2`, `esbuild@0.27.4`, `@types/node@25.5.0`
881
+
882
+ ## 2026-03-05 - Jeasx 2.4.2 released
883
+
884
+ 🎉 Just a patch release to update Fastify to fix [CVE-2026-3419](https://github.com/fastify/fastify/security/advisories/GHSA-573f-x89g-hqp9).
885
+
886
+ Jeasx uses from now on [trusted publishing for npm packages](https://docs.npmjs.com/trusted-publishers) to release new versions.
887
+
888
+ Dependency updates: `fastify@5.8.1`, `jsx-async-runtime@2.0.3`
889
+
890
+ ## 2026-03-02 - Jeasx 2.4.1 released
891
+
892
+ 🎉 This release introduces route prop inheritance from guards. Guards can return objects whose entries are used as additional props for your routes. Previously, only props from the closest guard were used. With this update, props from all guards along the route are collected and passed down. If multiple guards provide props with the same key, the value from the guard nearest to the route takes precedence and overwrites earlier ones.
893
+
894
+ Dependency updates: `@types/node@25.3.3`
895
+
896
+ ## 2026-02-20 - Jeasx 2.4.0 released
897
+
898
+ 🎉 This release is both a step forward and a step back: the recently introduced support for MDX has been removed from the core project. Jeasx aims to keep its core as lean as possible, so this change aligns with the project's overall goals. Since MDX is not essential for every Jeasx website or application, it makes sense to move it out of the core.
899
+
900
+ Another key goal of Jeasx is to empower users to implement their own custom solutions in userland, providing the right tools to do so. In short, while MDX remains a fantastic technology for content-driven websites, Jeasx now lets you configure MDX support yourself through a much improved configuration system for its base technologies like esbuild and Fastify.
901
+
902
+ For esbuild, two new configuration options are available: `ESBUILD_SERVER_OPTIONS` and `ESBUILD_BROWSER_OPTIONS`. These can be defined as functions in `.env.js` that return additional configuration settings for esbuild.
903
+
904
+ Why use functions instead of plain objects for the configuration? Because using functions allows configurations to be created lazily - only when needed - which is especially helpful for more complex setups over time.
905
+
906
+ If you want to use MDX with Jeasx, simply run `npm install @mdx-js/esbuild` and add the configuration below to `ESBUILD_SERVER_OPTIONS` in `.env.js`.
907
+
908
+ If you've used `ESBUILD_BROWSER_TARGET` in the past, you have to move this configuration to `ESBUILD_BROWSER_OPTIONS` as shown below.
909
+
910
+ ```jsx
911
+ import mdx from "@mdx-js/esbuild";
912
+
913
+ export default {
914
+ /** @type {() => import("esbuild").BuildOptions} */
915
+ ESBUILD_SERVER_OPTIONS: () => ({
916
+ plugins: [
917
+ mdx({
918
+ development: process.env.NODE_ENV === "development",
919
+ jsxImportSource: "jsx-async-runtime",
920
+ elementAttributeNameCase: "html",
921
+ stylePropertyNameCase: "css",
922
+ }),
923
+ ],
924
+ }),
925
+
926
+ /** @type {() => import("esbuild").BuildOptions} */
927
+ ESBUILD_BROWSER_OPTIONS: () => ({
928
+ target: ["chrome130", "edge130", "firefox130", "safari18"],
929
+ }),
930
+ };
931
+ ```
932
+
933
+ The existing configuration options for Fastify (such as `FASTIFY_SERVER_OPTIONS`, `FASTIFY_COOKIE_OPTIONS`, `FASTIFY_MULTIPART_OPTIONS`, `FASTIFY_STATIC_OPTIONS`) now require a minor change: they must be defined as functions instead of plain objects.
934
+
935
+ Additionally, you can now customize the fastify server instance using the optional `FASTIFY_SERVER` function. This function receives the created server instance and should return the modified version. This feature is especially useful when integrating existing Fastify plugins like `@fastify/compress`, as demonstrated below.
936
+
937
+ ```jsx
938
+ import fastifyCompress from "@fastify/compress";
939
+
940
+ const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
941
+
942
+ export default {
943
+ /** @type {(fastify: import("fastify").FastifyInstance) => import("fastify").FastifyInstance} */
944
+ FASTIFY_SERVER: (fastify) => fastify.register(fastifyCompress),
945
+
946
+ /** @type {() => import("fastify").FastifyServerOptions} */
947
+ FASTIFY_SERVER_OPTIONS: () => ({
948
+ disableRequestLogging: NODE_ENV_IS_DEVELOPMENT,
949
+ bodyLimit: 1024 * 1024,
950
+ }),
951
+
952
+ /** @type {() => import("@fastify/static").FastifyStaticOptions} */
953
+ FASTIFY_STATIC_OPTIONS: () => ({
954
+ immutable: !NODE_ENV_IS_DEVELOPMENT,
955
+ maxAge: NODE_ENV_IS_DEVELOPMENT ? 0 : "365d",
956
+ }),
957
+ };
958
+ ```
959
+
960
+ Another notable change is that the fallback content-type (`text/html`) for routes is now set as late as possible. This means you can return plain JavaScript objects from routes, which will be automatically sent as `application/json` by default - without needing to explicitly specify the content-type in your code. This update brings Jeasx in line with Fastify’s default behavior.
961
+
962
+ Additionally, you can now define routes directly from simple `*.json` or `*.txt` files - perfect for creating static endpoints like a health check or a `robots.txt` file right in your routes folder.
963
+
964
+ Dependency updates: `@types/node@25.3.0`
965
+
966
+ ## 2026-02-12 - Jeasx 2.3.2 released
967
+
968
+ 🎉 Upgraded to @types/node@25, enabling seamless development with Node 25 while maintaining compatibility for Node 24 users.
969
+
970
+ Dependency updates: `esbuild@0.27.3`, `@types/node@25.2.3`
971
+
972
+ ## 2026-02-04 - Jeasx 2.3.1 released
973
+
974
+ 🎉 Just a patch release to update Fastify to fix [CVE-2026-25224](https://github.com/fastify/fastify/security/advisories/GHSA-mrq3-vjjr-p77c).
975
+
976
+ Additionally, I performed minor refactorings identified by [oxlint](https://oxc.rs/docs/guide/usage/linter.html) and replaced `prettier` with [oxfmt](https://oxc.rs/docs/guide/usage/formatter), a high-performance formatter optimized for the JavaScript ecosystem.
977
+
978
+ Dependency updates: `fastify@5.7.4`, `@types/node@24.10.10`
979
+
980
+ ## 2026-01-30 - Jeasx 2.3.0 released
981
+
982
+ 🎉 This release introduces support for [MDX](https://mdxjs.com), enabling you to seamlessly embed JSX within Markdown content. Just create a route with a `.mdx` extension, and you’re all set to enhance your websites and blogs with Markdown enriched by dynamic JSX components.
983
+
984
+ ```jsx
985
+ import Layout from "./Layout";
986
+
987
+ <Layout title="MDX - Markdown for the component era">
988
+ # MDX as content companion alongside JSX You can easily access existing `props` in MDX: - Current
989
+ url: {props.request.url}
990
+ </Layout>;
991
+ ```
992
+
993
+ You can also create MDX-based components for use within JSX by importing them with their full `.mdx` file extension into your JSX routes or components.
994
+
995
+ Since MDX supports a variety of plugins - and Jeasx provides only the MDX core to stay focused on infrastructure while letting users handle customization - the overall configuration for Jeasx has been significantly improved. Now, the configuration object from an `.env.js` file is imported directly into both the build process and server runtime, allowing you to use package imports seamlessly. Previously, (de)serializing the configuration via `process.env` restricted this capability and limited advanced setups.
996
+
997
+ **Please note:** Variables loaded from `.env.js` now consistently overwrite any existing environment variables. This ensures predictable and consistent behavior across your configuration.
998
+
999
+ Here’s an example of how to configure the MDX engine: if you want to enable GitHub-flavored Markdown (`remark-gfm`), add syntax highlighting (`rehype-prism-plus`), and generate IDs for your headings (`rehype-slug`), you can install and configure these plugins accordingly in `.env.js`.
1000
+
1001
+ ```js
1002
+ import rehypePrismPlus from "rehype-prism-plus";
1003
+ import rehypeSlug from "rehype-slug";
1004
+ import remarkGFM from "remark-gfm";
1005
+
1006
+ export default {
1007
+ /** @type import("@mdx-js/esbuild").Options */
1008
+ ESBUILD_MDX_OPTIONS: {
1009
+ remarkPlugins: [[remarkGFM, { singleTilde: false }]],
1010
+ rehypePlugins: [rehypePrismPlus, [rehypeSlug, { prefix: "jeasx-" }]],
1011
+ },
1012
+ //...
1013
+ };
1014
+ ```
1015
+
1016
+ For a full overview of available configuration options and plugins, check out the excellent documentation of [@mdx-js/esbuild](https://mdxjs.com/packages/esbuild).
1017
+
1018
+ **Please note:** The update to the Jeasx configuration introduced a minor change in how `ESBUILD_BROWSER_TARGET` is specified to ensure consistency across the configuration. Previously, a comma-separated string was accepted and parsed as customization. Going forward, you must provide a proper JSON array (or its stringified form when using traditional `.env` files or the process environment).
1019
+
1020
+ ```js
1021
+ export default {
1022
+ /** @type import("esbuild").BuildOptions["target"] */
1023
+ ESBUILD_BROWSER_TARGET: ["chrome130", "edge130", "firefox130", "safari18"],
1024
+ //...
1025
+ };
1026
+ ```
1027
+
1028
+ Dependency updates: `fastify@5.7.2`, `@fastify/multipart@9.4.0`
1029
+
1030
+ ## 2026-01-17 - Jeasx 2.2.2 released
1031
+
1032
+ 🎉 This release now preserves the original status code when a 404 page is accessed directly (defaults to 200). This improvement makes it easier to use Jeasx as a static site generator and to fetch the 404 page with common tools for saving it to a file system.
1033
+
1034
+ While Jeasx is fundamentally a server-side rendering framework, there are valid use cases where serving a static page alone is sufficient. For example, you can use `wget` to download a Jeasx website to a www-directory with just a single line:
1035
+
1036
+ ```bash
1037
+ wget --mirror --page-requisites --no-host-directories --directory-prefix=www http://localhost:3000 http://localhost:3000/404
1038
+ ```
1039
+
1040
+ Have a look at the [Dockerfile](https://github.com/jeasx/jeasx-website/blob/main/Dockerfile) of the Jeasx website to see how things can be wired up for serving a static export with Caddy as web server.
1041
+
1042
+ If you want to restore the old behaviour (directly calling /404 resulting in status code 404), you can simple add `reply.status(404)` to your `/[404]` handler.
1043
+
1044
+ Dependency updates: `fastify@5.7.1`, `@fastify/static@9.0.0`, `@types/node@24.10.9`
1045
+
1046
+ ## 2025-12-21 - Jeasx 2.2.1 released
1047
+
1048
+ 🎉 Just a patch release with a minor cleanup for explicit path joins in `serverless.ts`.
1049
+
1050
+ Dependency updates: `esbuild@0.27.2`, `jsx-async-runtime@2.0.2`, `@types/node@24.10.4`
1051
+
1052
+ ## 2025-12-01 - Jeasx 2.2.0 released
1053
+
1054
+ 🎉 This release introduces a more flexible configuration approach for the underlying Fastify server. You can now customize all Fastify options (including those for all used plugins) according to your needs, without having to use the formerly fixed and very restrictive set of environment variables. This change was made to eliminate the need for increasingly specific environment variables to customise the default behaviour of Jeasx.
1055
+
1056
+ **Breaking change**: The previously supported environment variables (`FASTIFY_​BODY_​LIMIT, FASTIFY_​DISABLE_​REQUEST_​LOGGING, FASTIFY_​REWRITE_​URL, FASTIFY_​STATIC_​HEADERS, FASTIFY_​TRUST_​PROXY, FASTIFY_​MULTIPART_​ATTACH_​FIELDS_​TO_​BODY`) have been completely removed. While this may seem inconvenient for a minor release, the process of migrating your setup to the new configuration approach usually takes less than a minute. This streamlines the code base and documentation, as these features are presumably seldom used.
1057
+
1058
+ To configure Fastify (or a specific plugin), you can now use simple JSON objects which mirror the corresponding Fastify options. Have a look at the linked Fastify documentation for a reference of all existing options:
1059
+
1060
+ - [`FASTIFY_SERVER_OPTIONS`](https://fastify.dev/docs/latest/Reference/Server/)
1061
+ - [`FASTIFY_COOKIE_OPTIONS`](https://github.com/fastify/fastify-cookie#options)
1062
+ - [`FASTIFY_FORMBODY_OPTIONS`](https://github.com/fastify/fastify-formbody#options)
1063
+ - [`FASTIFY_MULTIPART_OPTIONS`](https://github.com/fastify/fastify-multipart#options)
1064
+ - [`FASTIFY_STATIC_OPTIONS`](https://github.com/fastify/fastify-static#options)
1065
+
1066
+ To optimise the developer experience, it is highly recommended that you use the recently introduced `.env.js` file to provide these configuration options. Alternatively, you can also provide them via `.env` or your process environment. Jeasx comes with a minimal set of reasonable [Fastify defaults](https://github.com/jeasx/jeasx/blob/main/serverless.ts), but you can also overwrite them if necessary.
1067
+
1068
+ Some Fastify options, such as `rewriteUrl` or `setHeaders`, take a function as a parameter. Jeasx supports this use case by deserialising the stringified function code when the server starts up.
1069
+
1070
+ ### Example configuration
1071
+
1072
+ ```js
1073
+ const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
1074
+
1075
+ export default {
1076
+ /** @type import("fastify").FastifyServerOptions */
1077
+ FASTIFY_SERVER_OPTIONS: {
1078
+ disableRequestLogging: NODE_ENV_IS_DEVELOPMENT,
1079
+ bodyLimit: 2 * 1024 * 1024,
1080
+ rewriteUrl: (req) => String(req.url).replace(/^\/jeasx/, ""),
1081
+ },
1082
+
1083
+ /** @type import("@fastify/static").FastifyStaticOptions */
1084
+ FASTIFY_STATIC_OPTIONS: {
1085
+ maxAge: NODE_ENV_IS_DEVELOPMENT ? 0 : "365d",
1086
+ },
1087
+
1088
+ /** @type import("@fastify/cookie").FastifyCookieOptions */
1089
+ // FASTIFY_COOKIE_OPTIONS: {},
1090
+
1091
+ /** @type import("@fastify/formbody").FastifyFormbodyOptions */
1092
+ // FASTIFY_FORMBODY_OPTIONS: {},
1093
+
1094
+ /** @type import("@fastify/multipart").FastifyMultipartOptions */
1095
+ // FASTIFY_MULTIPART_OPTIONS: {},
1096
+ };
1097
+ ```
1098
+
1099
+ Another improvement has been made by introducing an automatic approach to determine the maximum size of the internal route cache. Depending on the amount of free memory available at startup, the maximum number of cache entries is calculated. This approach strikes a balance, ensuring the cache is large enough for large-scale projects while keeping maximum memory consumption within reasonable limits given the available resources. This means that you no longer need to worry about providing ~~`JEASX_ROUTE_CACHE_LIMIT`~~ via the environment.
1100
+
1101
+ Dependency updates: `@types/node@24.10.1`
1102
+
1103
+ ## 2025-11-10 - Jeasx 2.1.1 released
1104
+
1105
+ 🎉 Enhanced configuration for @fastify/static, so you can serve pre-compressed static files (see [fastify docs](https://github.com/fastify/fastify-static?tab=readme-ov-file#precompressed)) from `public` and `dist/browser`. Just run `gzip -rk public dist/browser` as post build for gzipping your static assets. This might be useful if you don't want to run a reverse proxy in front of your Jeasx application and serve compressed files nevertheless. Setting up compression for dynamic content can be wired up in userland via a root guard:
1106
+
1107
+ ```js
1108
+ import { promisify } from "node:util";
1109
+ import { gzip } from "node:zlib";
1110
+
1111
+ export default function ({ request, reply }) {
1112
+ this.responseHandler = (payload) => {
1113
+ if (typeof payload === "string" && request.headers["accept-encoding"]?.includes("gzip")) {
1114
+ reply.header("content-encoding", "gzip");
1115
+ return promisify(gzip)(payload);
1116
+ } else {
1117
+ return payload;
1118
+ }
1119
+ };
1120
+ }
1121
+ ```
1122
+
1123
+ Updated `moduleResolution` to `bundler` in `tsconfig.json`.
1124
+
1125
+ Dependency updates: `jsx-async-runtime@2.0.1`, `fastify@5.6.2`, `esbuild@0.27.0`, `@types/node@24.9.2`
1126
+
1127
+ ## 2025-10-28 - Jeasx 2.1.0 released
1128
+
1129
+ 🎉 Environment vars can now be loaded from a JavaScript file (`.env.js`) additionally to existing .env-files. This allows enhanced environment setups depending on your workflows.
1130
+
1131
+ Node 24 (LTS) is the official default runtime from now on.
1132
+
1133
+ Dependency updates: `@fastify/multipart@9.3.0`, `@fastify/static@8.3.0`, `@types/node@24.9.1`
1134
+
1135
+ ## 2025-10-15 - Jeasx 2.0.1 released
1136
+
1137
+ 🎉 This releases fixes status codes for fallback 404 routes. Due to an unnoticed bug introduced by a minor refactoring, 404-routes were delivered with `status=200`, now it is the correct `status=404` again. This might impact your SEO score, so an update is highly recommended.
1138
+
1139
+ Dependency updates: `esbuild@0.25.11`
1140
+
1141
+ ## 2025-10-12 - Jeasx 2.0.0 released
1142
+
1143
+ 🎉 Approximately one year after the release of Jeasx 1.0 I'm proud to announce the release of Jeasx 2.0.
1144
+
1145
+ It's a funny story... every time I think Jeasx is feature-complete, there is still some more room to improve. Although the main idea behind Jeasx development still holds true: focus on a lean and stable core and let developers do all their magic in userland.
1146
+
1147
+ This release is focused on security and comes with a major breaking change: all HTML markup is escaped by default from now on, so you don't have to escape dangerous user input on your own anymore. This way the developer experience is improved and the actual performance costs for automatic escaping are neglible due to the reuse of the highly optimized fast-escape-html library.
1148
+
1149
+ If you need to include literal HTML in your JSX templates (e.g. HTML snippets from a CMS), you can use a special object syntax to opt out of escaping: `{{ html: "<p>Some HTML from a CMS</p>" }}`
1150
+
1151
+ If you want to migrate from Jeasx 1.0 to Jeasx 2.0 with automatic HTML escaping enabled, you'll need to remove all calls to #escapeEntities() and modify the HTML declaration in your layouts (`{{ html: <!DOCTYPE html>" }}`). Then you should check where you need to render literal HTML (or other code) and apply the required changes to opt out of escaping (e.g. `<div>{ wysiwygContent }</div>; to <div>{{ html: wysiwygContent }}</div>`).
1152
+
1153
+ If you want to restore the non-escaping behaviour of Jeasx &lt; v2, you can set `jsxEscapeHTML = false` in the root guard. This way HTML escaping is disabled globally.
1154
+
1155
+ Another internal change is the renaming of the directories in the output directory (`dist`): `routes` is now called `server` alongside the `browser` directory.
1156
+
1157
+ Dependency updates: `jsx-async-runtime@2.0.0, @types/node@22.18.10`
1158
+
1159
+ ## 2025-09-29 - Jeasx 1.9.0 released
1160
+
1161
+ 🎉 This release drops the constraint that you had to put all routes into a dedicated routes-directory and all JavaScript & CSS into a dedicated browser-directory. From now on you can use any directory layout in your projects as you like. You can still use the proven `browser/routes` layout, but you don't have to.
1162
+
1163
+ This feature enables the co-location of server and browser code in the same directory which might be a better default for your workflows.
1164
+
1165
+ The only remaining constraint is to mark server routes with brackets (e.g. `[news].jsx`) and browser-bundled assets as index-files (e.g. `index.js` or `index.css`).
1166
+
1167
+ Please note: This feature is enabled by dropping the hard coded outbase-directories in the esbuild configuration. If the outbase directory isn't specified, it defaults to the lowest common ancestor directory among all input entry point paths.
1168
+
1169
+ If you run into an edge case (e.g. your browser bundles won't load anymore), here's how to fix it: if you store all your assets in browser/assets and request your assets via `/assets/...`, this won't work anymore, because assets is now the lowest common ancestor directory and is removed by esbuild. Simple fix: just put an empty `index.js` into browser directory, so this directory is lowest common ancestor directory again.
1170
+
1171
+ Bumbed the default `ESBUILD_BROWSER_TARGET` to `"chrome130", "edge130", "firefox130", "safari18"`.
1172
+
1173
+ Dependency updates: `fastify@5.6.1, esbuild@0.25.10, @types/node@22.18.6`
1174
+
1175
+ ## 2025-09-11 - Jeasx 1.8.6 released
1176
+
1177
+ 🎉 This release bumps dependencies to the latest and greatest versions.
1178
+
1179
+ Dependency updates: `fastify@5.6.0, fastify/multipart@9.2.1, @types/node@22.18.1`
1180
+
1181
+ ## 2025-08-13 - Jeasx 1.8.5 released
1182
+
1183
+ 🎉 This release bumps dependencies to the latest and greatest versions.
1184
+
1185
+ Dependency updates: `fastify@5.5.0, jsx-async-runtime@1.0.4, esbuild@0.25.9, @types/node@22.17.1`
1186
+
1187
+ ## 2025-08-03 - Jeasx 1.8.4 released
1188
+
1189
+ 🎉 This release bumps dependencies to the latest and greatest versions.
1190
+
1191
+ Dependency updates: `esbuild@0.25.8, @types/node@22.17.0`
1192
+
1193
+ ## 2025-07-11 - Jeasx 1.8.3 released
1194
+
1195
+ 🎉 This release bumps dependencies to the latest and greatest versions.
1196
+
1197
+ Dependency updates: `jsx-async-runtime@1.0.3, esbuild@0.25.6, @types/node@22.16.3`
1198
+
1199
+ ## 2025-06-13 - Jeasx 1.8.2 released
1200
+
1201
+ 🎉 This release changes the default options for @fastify/multipart. From now on the default for `attachFieldsToBody` is `keyValues` which provides all data for form body requests (e.g. uploads) directly via request.body. Have a look at the Fastify documentation for code examples and options.
1202
+
1203
+ This change makes the required code for handling form body requests much easier:
1204
+
1205
+ ```js
1206
+ // Change this code...
1207
+ const file = await request.file();
1208
+ const upload = await file.toBuffer();
1209
+ const format = file.fields["format"]["value"];
1210
+
1211
+ // ... to this code.
1212
+ const upload = request.body["upload"];
1213
+ const format = request.body["format"];
1214
+ ```
1215
+
1216
+ Please note: This change might break your code. If you want to revert to the old behaviour, you can set the following environment variable: `FASTIFY_​MULTIPART_​ATTACH_​FIELDS_​TO_​BODY=false`
1217
+
1218
+ Dependency updates: `fastify@5.4.0, @types/node@22.15.31`
1219
+
1220
+ ## 2025-05-28 - Jeasx 1.8.1 released
1221
+
1222
+ 🎉 Just some dependency updates...
1223
+
1224
+ Dependency updates: `jsx-async-runtime@1.0.2, fastify@5.3.3, fastify/static@8.2.0, esbuild@0.25.5, @types/node@22.15.23`
1225
+
1226
+ ## 2025-05-12 - Jeasx 1.8.0 released
1227
+
1228
+ 🎉 This release introduces a custom error handler to provide user-friendly error messages for internal server errors and to facilitate team notifications.
1229
+
1230
+ To set up an error handler, simply register it in a route of your choice:
1231
+
1232
+ ```js
1233
+ this.errorHandler = async (error) => {
1234
+ console.error("❌", error);
1235
+ return <h1>Internal error</h1>;
1236
+ };
1237
+ ```
1238
+
1239
+ An error handler is called with this as context, allowing easy access to your context setup.
1240
+
1241
+ **Breaking change:** If you use a response handler, you'll need to change the name from this.response to this.responseHandler. This aligns the response handler with the introduced error handler. We apologize for any inconvenience, but since this is a seldom-used feature, we aim to streamline the codebase by aligning it without maintaining deprecated code.
1242
+
1243
+ As additional feature, a response handler is now called with this as the context, so you can access your existing context.
1244
+
1245
+ Dependency updates: `esbuild@0.25.4, @types/node@22.15.17`
1246
+
1247
+ ## 2025-05-03 - Jeasx 1.7.3 released
1248
+
1249
+ 🎉 This release introduces a performance improvement by switching the internal route-to-module cache implementation from a JavaScript object to a Map. This change allows for better management of cache entries, enabling the configuration of a maximum cache limit. To take advantage of this, a new configuration option `JEASX_ROUTE_CACHE_LIMIT` has been added.
1250
+
1251
+ Dependency updates: `jsx-async-runtime@1.0.1, esbuild@0.25.3, @types/node@22.15.3`
1252
+
1253
+ ## 2025-04-21 - Jeasx 1.7.2 released
1254
+
1255
+ 🎉 This release is brings only minor changes:
1256
+
1257
+ `FASTIFY_STATIC_HEADERS`: Apply all matching headers to the current path. Use an empty string ("") as first rule to set default headers, which can be overridden by more specific rules later. Please checkout the updated configuration.
1258
+
1259
+ Please note: You may need to adjust your existing configuration by moving the wildcard rule to the top of the JSON file to ensure it can be overridden by more specific rules defined below.
1260
+ env.sh: Removed logging of loaded environment files. Minor refactoring to clean up the code.
1261
+
1262
+ Dependency updates: `fastify@5.3.2, @types/node@22.14.1`
1263
+
1264
+ ## 2025-03-31 - Jeasx 1.7.1 released
1265
+
1266
+ 🎉 This release enhances support for Bun as an alternative JavaScript runtime for both development and production. Use `bun -b dev` to start development with Jeasx and Bun. With Bun 1.2.8, the entire Jeasx expo functions without any issues. While Node.js remains the primary focus of the project, Bun support will continue to improve. Having multiple options is always beneficial.
1267
+
1268
+ Route loading in development has been enhanced. It now relies on the modification time of the module, eliminating the need to calculate a hash for the file content. Additionally, a redundant file existence check for route handlers has been removed, resulting in more streamlined core code.
1269
+
1270
+ From now on, source maps for `serverless.js` are provided to enhance debugging.
1271
+
1272
+ Dependency updates: `esbuild@0.25.2`
1273
+
1274
+ ## 2025-03-27 - Jeasx 1.7.0 released
1275
+
1276
+ 🎉 This release removes `pm2` as a dependency and utilizes the powerful file watching capabilities of esbuild directly. This enhancement significantly improves build performance because esbuild only re-compiles linked files. Additionally, sharing code between the server and browser now works seamlessly without any additional configurations.
1277
+
1278
+ As an added benefit, Jeasx now works with Bun as an alternative JavaScript runtime, although this setup is not yet recommended for development or production.
1279
+
1280
+ Dependency updates: `@types/node@22.13.14`
1281
+
1282
+ ## 2025-03-26 - Jeasx 1.6.3 released
1283
+
1284
+ 🎉 This release fixes a bug with the recently introduced env file loading. The env files were loaded in the wrong order, so that overwriting existing env variables didn't work.
1285
+
1286
+ Dependency updates: `fastify@5.2.2, @types/node@22.13.13`
1287
+
1288
+ ## 2025-03-19 - Jeasx 1.6.2 released
1289
+
1290
+ 🎉 This release introduces a `try/catch` block in the central request handler, ensuring that proper error messages are logged. Additionally, it enables sourcemaps for both server and browser code, making debugging a breeze.
1291
+
1292
+ To enable sourcemap support for Node.js, add the following code to the root of your project as a .npmrc file:
1293
+
1294
+ `node-options=--enable-source-maps`
1295
+
1296
+ If you are using Docker, you need to modify the following lines in your Dockerfile to enable support for `.npmrc`:
1297
+
1298
+ ```bash
1299
+ # RUN npx jeasx build
1300
+ RUN npm run build
1301
+ # CMD ["npx","jeasx","start"]
1302
+ CMD ["npm","start"]
1303
+ ```
1304
+
1305
+ ## 2025-03-15 - Jeasx 1.6.1 released
1306
+
1307
+ 🎉 This releases replaces the dependency on `dotenv-flow` with a native implementation provided by Node.js (using `process.loadEnvFile` introduced with Node v20.12.0) to load environment variables from .env-files. The order of loading .env-files is the same as before:
1308
+
1309
+ ```text
1310
+ .env.defaults
1311
+ .env
1312
+ .env.local
1313
+ .env.[NODE_ENV] (e.g. .env.development or .env.production)
1314
+ .env.[NODE_ENV].local (e.g. .env.development.local or .env.production.local)
1315
+ ```
1316
+
1317
+ **Breaking change:** If you use .env-files to configure Jeasx and deploy to Vercel, please update your `vercel.json`. You'll need to change `"includeFiles": "{node_modules,dist,public}/**/*"` to `"includeFiles": "./**/*"to` make sure Vercel includes the .env-files in the deployment.
1318
+
1319
+ Additionally a fix for correctly parsing environment variables to configure Fastify (`FASTIFY_DISABLE_REQUEST_LOGGING, FASTIFY_TRUST_PROXY`) was implemented.
1320
+
1321
+ Dependency updates: `jsx-async-runtime@1.0.0, esbuild@0.25.1, pm2@6.0.5`
1322
+
1323
+ Please note: version 1.6.0 was unpublished from NPM right after the release due to a mistake.
1324
+
1325
+ ## 2025-03-09 - Jeasx 1.5.0 released
1326
+
1327
+ 🎉 This release features two new configurations:
1328
+
1329
+ `FASTIFY_REWRITE_URL` allows you to rewrite incoming URLs. Useful when running behind proxies or when you want to fake URLs.
1330
+ `JEASX_BUILD_ROUTES_IGNORE_WATCH` allows watching for changes in src/browser when importing browser code into server code.
1331
+
1332
+ Dependency updates: `@types/node@22.13.10`
1333
+
1334
+ ## 2025-03-01 - Jeasx 1.4.1 released
1335
+
1336
+ 🎉 This release features an updated `jsx-async-runtime@0.8.1` which brings typings for SVGs for a better developer experience in the IDE of your choice.
1337
+
1338
+ 3rd-party dependencies were updated to the latest versions: `@fastify/static@8.1.1, @types/node@22.13.5`
1339
+
1340
+ ## 2025-02-12 - Jeasx 1.4.0 released
1341
+
1342
+ 🎉 This release adds the route property to the current endpoint handler (e.g., `/[index]` or `/bar/[...path]`) to the request object (accessible via request.route). This makes it much easier to calculate trailing path segments for wildcard routes.
1343
+
1344
+ Also several dependencies were updated to the latest versions: `esbuild@0.25.0, @fastify/static@8.1.0, @fastify/multipart@9.0.3, @fastify/formbody@8.0.3, @types/node@22.13.1`
1345
+
1346
+ ## 2025-01-18 - Jeasx 1.3.0 released
1347
+
1348
+ 🎉 We are excited to announce the release of `jsx-async-runtime@0.7.1`, which now includes proper typings for all HTML attributes in accordance with the Mozilla Developer Network. This ensures that code completion in your IDE for all HTML attributes works now as expected in Jeasx. Special thanks to Rebecca for highlighting this issue!
1349
+
1350
+ The updated version of `jsx-async-runtime` now supports using an array of strings for the class attribute, making it easier to create complex classnames. You can now construct classnames using plain strings or template strings, an array of strings, or an object, covering most use-cases known from other libraries like classnames.
1351
+
1352
+ As always, we've updated to the latest versions of our dependencies: `fastify/formbody@8.0.2, fastify/multipart@9.0.2, fastify/static@8.0.4, types/node@22.10.7`
1353
+
1354
+ ## 2025-01-06 - Jeasx 1.2.2 released
1355
+
1356
+ 🎉 This release is just a minor dependency update: `fastify@5.2.1, fastify/cookie@11.0.2, types/node@22.10.5`
1357
+
1358
+ ## 2024-12-12 - Jeasx 1.2.1 released
1359
+
1360
+ 🎉 This release fixes a bug introduced by the recently introduced route caching feature: if a guard returned different response types (e.g. JSX code for a forbidden route, otherwise props for the guarded routes), the guarded routes weren't resolved anymore.
1361
+
1362
+ ## 2024-12-07 - Jeasx 1.2.0 released
1363
+
1364
+ 🎉 This release brings a major performance boost (about 2-5 times faster in benchmarks) by introducing runtime caches for resolved routes and loaded JavaScript modules. The caches are only used in production and won't interfere with your development workflow. This change was triggered by a PR submitted by Bryce, Kudos to him for bringing this topic onto the radar.
1365
+
1366
+ It also features an update to `jsx-async-runtime` which provides more accurate and also deprecated typings for HTML tags and attributes according to the fantastic HTML reference from the Mozilla Development Network.
1367
+
1368
+ Added two new environment variables (`FASTIFY_DISABLE_REQUEST_LOGGING` and `FASTIFY_TRUST_PROXY`) to give you more control over how Jeasx should behave in different environments.
1369
+
1370
+ ## 2024-12-01 - Jeasx 1.1.0 released
1371
+
1372
+ 🎉 Migrated from dotenv to dotenv-flow, so you can use NODE_ENV-specific .env\* files (like `.env.development`) to configure different environments for production and development. This is useful to disable caching headers (e.g. via `FASTIFY_STATIC_HEADERS`) in development, as Jeasx applies `FASTIFY_STATIC_HEADERS` in development from now on for a more consistent developer experience. See updated .env-files in the quickstart-project for an example how to disable caching in development. This is only needed if you have configured `FASTIFY_STATIC_HEADERS` for your existing projects.
1373
+
1374
+ Bumped default environment variable `ESBUILD_BROWSER_TARGET` to more recent browser versions (e.g. `chrome126, edge126, firefox128, safari17`). If you want to stick with older versions, you can override it via the environment. Learn more about possible values at the esbuild website.
1375
+
1376
+ Updated `jsx-async-runtime` which fixes a bug in escapeEntities which escaped existing &amp; two times. This release also removes the deprecated `renderToString` function. Simply replace it with `jsxToString`.
1377
+
1378
+ The default host is now :: which binds to all available network interfaces (e.g. `IPv6`). You can change it via the `HOST` environment variable (e.g. `HOST=0.0.0.0` for the old behaviour). The change is especially useful to connect to Jeasx via private networking on hosting platforms like Railway.
1379
+
1380
+ ## 2024-11-15 - Jeasx 1.0.2 released
1381
+
1382
+ 🎉 Disabled cache-control for fastify-static, so proper Cache-Control response header could be applied via the environment variable `FASTIFY_STATIC_HEADERS`. Have a look at the env-file in the quickstart project for an example.
1383
+
1384
+ ## 2024-11-01 - Jeasx 1.0.1 released
1385
+
1386
+ 🎉 This version brings official support for Node 22. Also dependencies were updated to latest versions of `fastify@5.0.1, fastify/static@8.0.2, fastify/cookie@11.0.1`. Added new examples for template fragments in combination with HTMX and image optimization with sharp.
1387
+
1388
+ ## 2024-10-04 - Jeasx 1.0.0 released
1389
+
1390
+ It's finally here! Jeasx 1.0.0 is ready for production. We are proud to announce that the framework has reached feature completeness and is now ready for the masses. Lots of hours of work have been put into this project to make sure you have the best experience possible.
1391
+
1392
+ ## 2024-09-23 - Jeasx 0.15.2 released
1393
+
1394
+ Updates to all `@fastify-plugins, esbuild@0.24.0`
1395
+
1396
+ ## 2024-09-21 - Jeasx 0.15.1 released
1397
+
1398
+ Decorate the Fastify request object with `path` property, so you can easily access the route path without query parameters via request.path. This solves 99% of all use-cases for recently removed `@fastity/url-data`.
1399
+
1400
+ ## 2024-09-20 - Jeasx 0.15.0 released
1401
+
1402
+ This release comes with Fastify 5. It also removes `@fastify/url-data` and `@fastify/accepts` as dependencies, therefore your code might break. The reason behind this removal is to depend on less dependencies in the long run which makes maintenance of the core easier.
1403
+
1404
+ If you rely on either accepts or url-data, you must provide appropriate changes in userland code. For `@fastify/url-data` I would recommend to use `fast-uri` and for `@fastify/accepts` `jshttp/accepts` for parsing accept-header if needed.
1405
+
1406
+ Please note: there were some intermediate releases since the past proper release which shouldn't be used (and are mostly unpublished from npm).
1407
+
1408
+ ## 2024-08-21 - Jeasx 0.11.2 released
1409
+
1410
+ Added simple Dockerfile to quickstart template. Dependency updates: `esbuild@0.23.1, @fastify/cookie@9.4.0, @types/node@20.16.1`
1411
+
1412
+ ## 2024-07-27 - Jeasx 0.11.1 released
1413
+
1414
+ Performance optimization: if response is string or buffer, break evaluation loop early. This allows to build caches for pages and binary assets in userland.
1415
+
1416
+ ## 2024-07-26 - Jeasx 0.11.0 released
1417
+
1418
+ Updated to `jsx-async-runtime@0.5.0` which allows to override `jsxToString` from `jsx-async-runtime` to intercept / modify / replace JSX components via this context. Have a look at the example to see how it works.
1419
+
1420
+ ## 2024-07-19 - Jeasx 0.10.1 released
1421
+
1422
+ Breaking change: removed `@fastify/request-context` in favor of the recently introduced this context. Changing your code should be straightforward. This change decouples userland code from Fastify. Additionally code for Jeasx and `jsx-async-runtime` is provided unminified, so debugging and testing is far easier now.
1423
+
1424
+ ## 2024-07-12 - Jeasx 0.9.1 released
1425
+
1426
+ Updated to `jsx-async-runtime@0.4.1` which fixes a problem when running tests with Vitest.
1427
+
1428
+ ## 2024-07-10 - Jeasx 0.9.0 released
1429
+
1430
+ Updated to `jsx-async-runtime@0.4.0` which allows to use `this` as context object to avoid prop drilling. Have a look at the demo to see how things work.
1431
+
1432
+ ## 2024-07-05 - Jeasx 0.8.0 released
1433
+
1434
+ Updated to `jsx-async-runtime@0.3.0` (deprecated `renderToString` in favor of `jsxToString`)
1435
+
1436
+ ## 2024-07-03 - Jeasx 0.7.6 released
1437
+
1438
+ Updated to `esbuild@0.23.0`
1439
+
1440
+ ## 2024-07-01 - Jeasx 0.7.5 released
1441
+
1442
+ Updated to `fastify@4.28.1, esbuild@0.22.0, pm2@5.4.1`
1443
+
1444
+ ## 2024-05-28 - Jeasx 0.7.4 released
1445
+
1446
+ Updated to `esbuild@0.21.4, pm2@5.4.0`
1447
+
1448
+ ## 2024-05-17 - Jeasx 0.7.3 released
1449
+
1450
+ Updated to `esbuild@0.21.3, @types/node@20.12.12`
1451
+
1452
+ ## 2024-05-08 - Jeasx 0.7.2 released
1453
+
1454
+ Updated to `fastify@4.27.0, esbuild@0.21.1, @fastify/static@7.0.4`
1455
+
1456
+ ## 2024-05-07 - Jeasx 0.7.1 released
1457
+
1458
+ Updated `esbuild@0.21.0`
1459
+
1460
+ ## 2024-04-27 - Jeasx 0.7.0 released
1461
+
1462
+ This release introduces a new feature that allows you to post-process the resulting payloads, such as prettifying the HTML output. You can now set up a response handler, for example, in a guard. The response handler takes the resulting payload as a parameter and returns the modified payload.
1463
+
1464
+ ```js
1465
+ import * as prettier from "prettier";
1466
+
1467
+ export default function RootGuard({ request, reply }) {
1468
+ this.response = async (payload) => {
1469
+ return typeof payload === "string" &&
1470
+ String(reply.getHeader("content-type")).startsWith("text/html")
1471
+ ? await prettier.format(payload, { parser: "html" })
1472
+ : payload;
1473
+ };
1474
+ }
1475
+ ```
1476
+
1477
+ Shame on me... I have recently started writing news as we approach the 1.0 release. But you can study the changelog at GitHub to see what has happend in the past.
1478
+
1479
+ ## 2023-12-30 - First public commit
1480
+
1481
+ Our Journey Begins! 🎉 Introducing Jeasx, a revolutionary web development framework born out of a summer experiment in 2023.
1482
+
1483
+ Our mission?
1484
+
1485
+ To simplify web development using server-rendered JSX as the cornerstone, bringing back the essence of the web: HTML, CSS, and progressive enhancing JavaScript. Join us as we redefine the future of web development together!