jeasx 2.1.1 → 2.2.0
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 +51 -0
- package/env.js +22 -16
- package/package.json +2 -2
- package/serverless.js +35 -25
- package/serverless.js.map +1 -1
- package/serverless.ts +50 -45
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2025-12-01 - Jeasx 2.2.0 released
|
|
4
|
+
|
|
5
|
+
🎉 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.
|
|
6
|
+
|
|
7
|
+
**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.
|
|
8
|
+
|
|
9
|
+
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:
|
|
10
|
+
|
|
11
|
+
- [`FASTIFY_SERVER_OPTIONS`](<https://fastify.dev/docs/latest/Reference/Server/>)
|
|
12
|
+
- [`FASTIFY_COOKIE_OPTIONS`](<https://github.com/fastify/fastify-cookie#options>)
|
|
13
|
+
- [`FASTIFY_FORMBODY_OPTIONS`](<https://github.com/fastify/fastify-formbody#options>)
|
|
14
|
+
- [`FASTIFY_MULTIPART_OPTIONS`](<https://github.com/fastify/fastify-multipart#options>)
|
|
15
|
+
- [`FASTIFY_STATIC_OPTIONS`](<https://github.com/fastify/fastify-static#options>)
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
21
|
+
### Example configuration
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
/** @type import("fastify").FastifyServerOptions */
|
|
28
|
+
FASTIFY_SERVER_OPTIONS: {
|
|
29
|
+
disableRequestLogging: NODE_ENV_IS_DEVELOPMENT,
|
|
30
|
+
bodyLimit: 2 * 1024 * 1024,
|
|
31
|
+
rewriteUrl: (req) => String(req.url).replace(/^\/jeasx/, ""),
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/** @type import("@fastify/static").FastifyStaticOptions */
|
|
35
|
+
FASTIFY_STATIC_OPTIONS: {
|
|
36
|
+
maxAge: NODE_ENV_IS_DEVELOPMENT ? 0 : "365d",
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/** @type import("@fastify/cookie").FastifyCookieOptions */
|
|
40
|
+
// FASTIFY_COOKIE_OPTIONS: {},
|
|
41
|
+
|
|
42
|
+
/** @type import("@fastify/formbody").FastifyFormbodyOptions */
|
|
43
|
+
// FASTIFY_FORMBODY_OPTIONS: {},
|
|
44
|
+
|
|
45
|
+
/** @type import("@fastify/multipart").FastifyMultipartOptions */
|
|
46
|
+
// FASTIFY_MULTIPART_OPTIONS: {},
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
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.
|
|
51
|
+
|
|
52
|
+
Dependency updates: `@types/node@24.10.1`
|
|
53
|
+
|
|
3
54
|
## 2025-11-10 - Jeasx 2.1.1 released
|
|
4
55
|
|
|
5
56
|
🎉 Enhanced configuration for @fastify/static, so you can serve pre-compressed static files (see <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:
|
package/env.js
CHANGED
|
@@ -30,23 +30,29 @@ export default async function env() {
|
|
|
30
30
|
}
|
|
31
31
|
try {
|
|
32
32
|
const envFile = `file://${join(process.cwd(), ".env.js")}`;
|
|
33
|
-
const envObject = (await import(envFile)).default;
|
|
34
|
-
Object.entries(envObject)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
case "function":
|
|
41
|
-
process.env[key] = value.toString();
|
|
42
|
-
break;
|
|
43
|
-
default:
|
|
44
|
-
process.env[key] = JSON.stringify(value);
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
});
|
|
33
|
+
const envObject = stringifyFunctions((await import(envFile)).default);
|
|
34
|
+
Object.entries(envObject)
|
|
35
|
+
.filter(([key]) => !(key in process.env))
|
|
36
|
+
.forEach(([key, value]) => {
|
|
37
|
+
process.env[key] =
|
|
38
|
+
typeof value === "string" ? value : JSON.stringify(value);
|
|
39
|
+
});
|
|
49
40
|
} catch (e) {
|
|
50
41
|
// ERR_MODULE_NOT_FOUND
|
|
51
42
|
}
|
|
52
43
|
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Convert all functions recursively to strings.
|
|
47
|
+
*/
|
|
48
|
+
function stringifyFunctions(obj) {
|
|
49
|
+
for (const key in obj) {
|
|
50
|
+
if (typeof obj[key] === "function") {
|
|
51
|
+
obj[key] = obj[key].toString();
|
|
52
|
+
}
|
|
53
|
+
if (typeof obj[key] === "object" && obj[key] !== null) {
|
|
54
|
+
stringifyFunctions(obj[key]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return obj;
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jeasx",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Jeasx - the ease of JSX with the power of SSR",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jsx",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@fastify/formbody": "8.0.2",
|
|
29
29
|
"@fastify/multipart": "9.3.0",
|
|
30
30
|
"@fastify/static": "8.3.0",
|
|
31
|
-
"@types/node": "24.10.
|
|
31
|
+
"@types/node": "24.10.1",
|
|
32
32
|
"esbuild": "0.27.0",
|
|
33
33
|
"fastify": "5.6.2",
|
|
34
34
|
"jsx-async-runtime": "2.0.1"
|
package/serverless.js
CHANGED
|
@@ -5,42 +5,39 @@ import fastifyStatic from "@fastify/static";
|
|
|
5
5
|
import Fastify from "fastify";
|
|
6
6
|
import { jsxToString } from "jsx-async-runtime";
|
|
7
7
|
import { stat } from "node:fs/promises";
|
|
8
|
+
import { freemem } from "node:os";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
import env from "./env.js";
|
|
10
11
|
await env();
|
|
11
|
-
const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
|
|
12
12
|
const CWD = process.cwd();
|
|
13
|
-
const
|
|
14
|
-
const JEASX_ROUTE_CACHE_LIMIT =
|
|
13
|
+
const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
|
|
14
|
+
const JEASX_ROUTE_CACHE_LIMIT = Math.floor(freemem() / 1024 / 1024);
|
|
15
15
|
var serverless_default = Fastify({
|
|
16
16
|
logger: true,
|
|
17
|
-
|
|
18
|
-
process.env.
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
...jsonToOptions(
|
|
18
|
+
process.env.FASTIFY_SERVER_OPTIONS
|
|
19
|
+
)
|
|
20
|
+
}).register(fastifyCookie, {
|
|
21
|
+
...jsonToOptions(
|
|
22
|
+
process.env.FASTIFY_COOKIE_OPTIONS
|
|
23
|
+
)
|
|
24
|
+
}).register(fastifyFormbody, {
|
|
25
|
+
...jsonToOptions(
|
|
26
|
+
process.env.FASTIFY_FORMBODY_OPTIONS
|
|
27
|
+
)
|
|
28
|
+
}).register(fastifyMultipart, {
|
|
29
|
+
attachFieldsToBody: "keyValues",
|
|
30
|
+
...jsonToOptions(
|
|
31
|
+
process.env.FASTIFY_MULTIPART_OPTIONS
|
|
26
32
|
)
|
|
27
33
|
}).register(fastifyStatic, {
|
|
28
34
|
root: ["public", "dist/browser"].map((dir) => join(CWD, dir)),
|
|
29
35
|
prefix: "/",
|
|
30
36
|
wildcard: false,
|
|
31
|
-
cacheControl: false,
|
|
32
37
|
preCompressed: true,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)) {
|
|
37
|
-
if (path.endsWith(suffix)) {
|
|
38
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
39
|
-
reply.setHeader(key, value);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
} : void 0
|
|
38
|
+
...jsonToOptions(
|
|
39
|
+
process.env.FASTIFY_STATIC_OPTIONS
|
|
40
|
+
)
|
|
44
41
|
}).decorateRequest("route", "").decorateRequest("path", "").addHook("onRequest", async (request, reply) => {
|
|
45
42
|
reply.header("Content-Type", "text/html; charset=utf-8");
|
|
46
43
|
const index = request.url.indexOf("?");
|
|
@@ -53,6 +50,19 @@ var serverless_default = Fastify({
|
|
|
53
50
|
throw error;
|
|
54
51
|
}
|
|
55
52
|
});
|
|
53
|
+
function jsonToOptions(json) {
|
|
54
|
+
const options = JSON.parse(json || "{}");
|
|
55
|
+
for (const key in options) {
|
|
56
|
+
if (typeof options[key] === "string" && options[key].includes("=>")) {
|
|
57
|
+
try {
|
|
58
|
+
options[key] = new Function(`return ${options[key]}`)();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.warn("\u26A0\uFE0F", error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return options;
|
|
65
|
+
}
|
|
56
66
|
const modules = /* @__PURE__ */ new Map();
|
|
57
67
|
async function handler(request, reply) {
|
|
58
68
|
let response;
|
|
@@ -86,7 +96,7 @@ async function handler(request, reply) {
|
|
|
86
96
|
}
|
|
87
97
|
continue;
|
|
88
98
|
} finally {
|
|
89
|
-
if (
|
|
99
|
+
if (modules.size > JEASX_ROUTE_CACHE_LIMIT) {
|
|
90
100
|
modules.delete(modules.keys().next().value);
|
|
91
101
|
}
|
|
92
102
|
}
|
package/serverless.js.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["serverless.ts"],
|
|
4
|
-
"mappings": "AAAA,OAAO,
|
|
4
|
+
"mappings": "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",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
package/serverless.ts
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import fastifyCookie from "@fastify/cookie";
|
|
2
|
-
import fastifyFormbody from "@fastify/formbody";
|
|
3
|
-
import fastifyMultipart from "@fastify/multipart";
|
|
4
|
-
import fastifyStatic from "@fastify/static";
|
|
5
|
-
import Fastify, {
|
|
1
|
+
import fastifyCookie, { FastifyCookieOptions } from "@fastify/cookie";
|
|
2
|
+
import fastifyFormbody, { FastifyFormbodyOptions } from "@fastify/formbody";
|
|
3
|
+
import fastifyMultipart, { FastifyMultipartOptions } from "@fastify/multipart";
|
|
4
|
+
import fastifyStatic, { FastifyStaticOptions } from "@fastify/static";
|
|
5
|
+
import Fastify, {
|
|
6
|
+
FastifyReply,
|
|
7
|
+
FastifyRequest,
|
|
8
|
+
FastifyServerOptions,
|
|
9
|
+
} from "fastify";
|
|
6
10
|
import { jsxToString } from "jsx-async-runtime";
|
|
7
11
|
import { stat } from "node:fs/promises";
|
|
12
|
+
import { freemem } from "node:os";
|
|
8
13
|
import { join } from "node:path";
|
|
9
14
|
import env from "./env.js";
|
|
10
15
|
|
|
11
16
|
await env();
|
|
12
17
|
|
|
13
|
-
const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
|
|
14
18
|
const CWD = process.cwd();
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
process.env.FASTIFY_STATIC_HEADERS &&
|
|
18
|
-
JSON.parse(process.env.FASTIFY_STATIC_HEADERS);
|
|
19
|
-
|
|
20
|
-
const JEASX_ROUTE_CACHE_LIMIT =
|
|
21
|
-
process.env.JEASX_ROUTE_CACHE_LIMIT &&
|
|
22
|
-
JSON.parse(process.env.JEASX_ROUTE_CACHE_LIMIT);
|
|
19
|
+
const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
|
|
20
|
+
const JEASX_ROUTE_CACHE_LIMIT = Math.floor(freemem() / 1024 / 1024);
|
|
23
21
|
|
|
24
22
|
declare module "fastify" {
|
|
25
23
|
interface FastifyRequest {
|
|
@@ -31,41 +29,34 @@ declare module "fastify" {
|
|
|
31
29
|
// Create and export a Fastify app instance
|
|
32
30
|
export default Fastify({
|
|
33
31
|
logger: true,
|
|
34
|
-
|
|
35
|
-
process.env.
|
|
36
|
-
),
|
|
37
|
-
bodyLimit: Number(process.env.FASTIFY_BODY_LIMIT) || undefined,
|
|
38
|
-
trustProxy: JSON.parse(process.env.FASTIFY_TRUST_PROXY || "false"),
|
|
39
|
-
rewriteUrl:
|
|
40
|
-
process.env.FASTIFY_REWRITE_URL &&
|
|
41
|
-
new Function(`return ${process.env.FASTIFY_REWRITE_URL}`)(),
|
|
32
|
+
...(jsonToOptions(
|
|
33
|
+
process.env.FASTIFY_SERVER_OPTIONS
|
|
34
|
+
) as FastifyServerOptions),
|
|
42
35
|
})
|
|
43
|
-
.register(fastifyCookie
|
|
44
|
-
|
|
36
|
+
.register(fastifyCookie, {
|
|
37
|
+
...(jsonToOptions(
|
|
38
|
+
process.env.FASTIFY_COOKIE_OPTIONS
|
|
39
|
+
) as FastifyCookieOptions),
|
|
40
|
+
})
|
|
41
|
+
.register(fastifyFormbody, {
|
|
42
|
+
...(jsonToOptions(
|
|
43
|
+
process.env.FASTIFY_FORMBODY_OPTIONS
|
|
44
|
+
) as FastifyFormbodyOptions),
|
|
45
|
+
})
|
|
45
46
|
.register(fastifyMultipart, {
|
|
46
|
-
attachFieldsToBody:
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
attachFieldsToBody: "keyValues",
|
|
48
|
+
...(jsonToOptions(
|
|
49
|
+
process.env.FASTIFY_MULTIPART_OPTIONS
|
|
50
|
+
) as FastifyMultipartOptions),
|
|
49
51
|
})
|
|
50
52
|
.register(fastifyStatic, {
|
|
51
53
|
root: ["public", "dist/browser"].map((dir) => join(CWD, dir)),
|
|
52
54
|
prefix: "/",
|
|
53
55
|
wildcard: false,
|
|
54
|
-
cacheControl: false,
|
|
55
56
|
preCompressed: true,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
FASTIFY_STATIC_HEADERS
|
|
60
|
-
)) {
|
|
61
|
-
if (path.endsWith(suffix)) {
|
|
62
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
63
|
-
reply.setHeader(key, value);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
: undefined,
|
|
57
|
+
...(jsonToOptions(
|
|
58
|
+
process.env.FASTIFY_STATIC_OPTIONS
|
|
59
|
+
) as FastifyStaticOptions),
|
|
69
60
|
})
|
|
70
61
|
.decorateRequest("route", "")
|
|
71
62
|
.decorateRequest("path", "")
|
|
@@ -85,6 +76,23 @@ export default Fastify({
|
|
|
85
76
|
}
|
|
86
77
|
});
|
|
87
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Parses JSON and instantiates all stringified functions.
|
|
81
|
+
*/
|
|
82
|
+
function jsonToOptions(json: string) {
|
|
83
|
+
const options = JSON.parse(json || "{}");
|
|
84
|
+
for (const key in options) {
|
|
85
|
+
if (typeof options[key] === "string" && options[key].includes("=>")) {
|
|
86
|
+
try {
|
|
87
|
+
options[key] = new Function(`return ${options[key]}`)();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn("⚠️", error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return options;
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
// Cache for resolved route modules, 'null' means no module exists.
|
|
89
97
|
const modules = new Map<string, { default: Function }>();
|
|
90
98
|
|
|
@@ -138,10 +146,7 @@ async function handler(request: FastifyRequest, reply: FastifyReply) {
|
|
|
138
146
|
continue;
|
|
139
147
|
} finally {
|
|
140
148
|
// Remove oldest entry from cache if limit is reached
|
|
141
|
-
if (
|
|
142
|
-
typeof JEASX_ROUTE_CACHE_LIMIT === "number" &&
|
|
143
|
-
modules.size > JEASX_ROUTE_CACHE_LIMIT
|
|
144
|
-
) {
|
|
149
|
+
if (modules.size > JEASX_ROUTE_CACHE_LIMIT) {
|
|
145
150
|
modules.delete(modules.keys().next().value);
|
|
146
151
|
}
|
|
147
152
|
}
|