@askrjs/node 0.0.12 → 0.2.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/README.md +7 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -94,7 +94,10 @@ import { serve } from "@askrjs/node";
|
|
|
94
94
|
|
|
95
95
|
const running = await serve(app, {
|
|
96
96
|
port: 3000,
|
|
97
|
-
assets: {
|
|
97
|
+
assets: {
|
|
98
|
+
root: "./dist/client",
|
|
99
|
+
exclude: (pathname) => pathname.startsWith("/files/"),
|
|
100
|
+
},
|
|
98
101
|
});
|
|
99
102
|
|
|
100
103
|
await running.close();
|
|
@@ -103,7 +106,9 @@ await running.close();
|
|
|
103
106
|
`serve` handles static assets and closes both the HTTP server and the application during shutdown.
|
|
104
107
|
When an asset root is configured, extension-bearing `GET` and `HEAD` paths are reserved for static
|
|
105
108
|
files: missing files return `404` without falling through to application routing, source maps are not
|
|
106
|
-
served, and resolved files must remain inside the configured root.
|
|
109
|
+
served, and resolved files must remain inside the configured root. Use `assets.exclude(pathname)` to
|
|
110
|
+
bypass static interception for dotted application routes; the predicate receives the decoded URL
|
|
111
|
+
pathname. Fingerprinted files under
|
|
107
112
|
`/assets/` receive immutable caching; other files receive `no-cache`.
|
|
108
113
|
Both `listen` and `serve` bind to `127.0.0.1` by default. A non-loopback
|
|
109
114
|
`host` also requires `allowPublicBind: true` so public exposure is explicit. Public listeners should
|
package/dist/index.d.ts
CHANGED
|
@@ -58,6 +58,8 @@ interface ServeOptions extends ListenOptions {
|
|
|
58
58
|
/** Serves static files from this directory before falling back to the application. */
|
|
59
59
|
readonly assets?: {
|
|
60
60
|
readonly root: string;
|
|
61
|
+
/** Returns true when an extension-bearing path must bypass static serving. */
|
|
62
|
+
readonly exclude?: (pathname: string) => boolean;
|
|
61
63
|
};
|
|
62
64
|
/** OS signals that trigger a graceful shutdown; pass `false` to disable automatic shutdown handling. */
|
|
63
65
|
readonly signals?: false | readonly NodeJS.Signals[];
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createReadStream } from "node:fs";
|
|
|
6
6
|
import { realpath, stat } from "node:fs/promises";
|
|
7
7
|
import { extname, resolve, sep } from "node:path";
|
|
8
8
|
import { pipeline } from "node:stream";
|
|
9
|
+
import * as serverHttp from "@askrjs/server/http";
|
|
9
10
|
//#region src/client-address.ts
|
|
10
11
|
/**
|
|
11
12
|
* Reserved request header containing the TCP peer address authenticated by the Node adapter.
|
|
@@ -623,6 +624,12 @@ const mimeTypes = {
|
|
|
623
624
|
".woff": "font/woff",
|
|
624
625
|
".woff2": "font/woff2"
|
|
625
626
|
};
|
|
627
|
+
const parseContentType = serverHttp.contentType ?? ((value) => {
|
|
628
|
+
if (!value) return void 0;
|
|
629
|
+
const separator = value.indexOf(";");
|
|
630
|
+
const type = (separator === -1 ? value : value.slice(0, separator)).trim();
|
|
631
|
+
return type ? type.toLowerCase() : void 0;
|
|
632
|
+
});
|
|
626
633
|
function isAssetPath(pathname) {
|
|
627
634
|
return extname(pathname) !== "";
|
|
628
635
|
}
|
|
@@ -658,8 +665,8 @@ async function serve(app, options = {}) {
|
|
|
658
665
|
const preparedHandlerOptions = prepareNodeHandlerOptions(handlerOptions);
|
|
659
666
|
const applicationHandler = createNodeHandler({ async fetch(request, dispatchOptions) {
|
|
660
667
|
const result = await app.fetch(request, dispatchOptions);
|
|
661
|
-
const
|
|
662
|
-
if (!result.headers.has("cache-control") &&
|
|
668
|
+
const responseContentType = parseContentType(result.headers.get("content-type"));
|
|
669
|
+
if (!result.headers.has("cache-control") && responseContentType === "text/html") {
|
|
663
670
|
const headers = new Headers(result.headers);
|
|
664
671
|
headers.set("cache-control", "no-cache");
|
|
665
672
|
return new Response(result.body, {
|
|
@@ -683,7 +690,7 @@ async function serve(app, options = {}) {
|
|
|
683
690
|
return;
|
|
684
691
|
}
|
|
685
692
|
const method = request.method ?? "GET";
|
|
686
|
-
if ((method === "GET" || method === "HEAD") && isAssetPath(pathname)) {
|
|
693
|
+
if ((method === "GET" || method === "HEAD") && isAssetPath(pathname) && !options.assets?.exclude?.(pathname)) {
|
|
687
694
|
const extension = extname(pathname).toLowerCase();
|
|
688
695
|
const unresolvedCandidate = resolve(root, `.${pathname}`);
|
|
689
696
|
const inside = isWithinRoot(root, unresolvedCandidate);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askrjs/node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Node http adapter for @askrjs/server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"askr",
|
|
@@ -57,13 +57,13 @@
|
|
|
57
57
|
"prepublishOnly": "npm run check"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@askrjs/auth": ">=0.0
|
|
61
|
-
"@askrjs/server": ">=0.0
|
|
60
|
+
"@askrjs/auth": ">=0.2.0 <0.3.0",
|
|
61
|
+
"@askrjs/server": ">=0.2.0 <0.3.0",
|
|
62
62
|
"@types/ws": "^8.18.1",
|
|
63
63
|
"ws": "^8.21.3"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@askrjs/schema": ">=0.0
|
|
66
|
+
"@askrjs/schema": ">=0.2.0 <0.3.0",
|
|
67
67
|
"@types/node": "^26.2.0",
|
|
68
68
|
"publint": "^0.3.23",
|
|
69
69
|
"typescript": "^7.0.2",
|