@stacksjs/server 0.72.92 → 0.72.94
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/dist/proxy.d.ts +33 -0
- package/dist/proxy.js +2 -1
- package/package.json +5 -5
package/dist/proxy.d.ts
CHANGED
|
@@ -15,6 +15,16 @@ export declare function resolveApiProxyRules(input?: ApiProxyConfig): ApiProxyRu
|
|
|
15
15
|
* configuration pass it in.
|
|
16
16
|
*/
|
|
17
17
|
export declare function isApiBoundRequest(req: Request, pathname: string, rules?: ApiProxyRules): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The same decision, without a `Request` to ask.
|
|
20
|
+
*
|
|
21
|
+
* Boot-time checks want to know whether a route the API registered can ever
|
|
22
|
+
* be reached through the views server, and constructing a `Request` per route
|
|
23
|
+
* to find out would be silly. More to the point, a second implementation of
|
|
24
|
+
* this rule would drift from the one that actually routes traffic, and a
|
|
25
|
+
* diagnostic that disagrees with the thing it describes is worse than none.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isApiBoundPath(method: string, pathname: string, rules?: ApiProxyRules): boolean;
|
|
18
28
|
/**
|
|
19
29
|
* A one-line summary of what will be forwarded, for the dev server's boot
|
|
20
30
|
* output.
|
|
@@ -24,6 +34,25 @@ export declare function isApiBoundRequest(req: Request, pathname: string, rules?
|
|
|
24
34
|
* invisible.
|
|
25
35
|
*/
|
|
26
36
|
export declare function describeApiProxyRules(rules: ApiProxyRules): string;
|
|
37
|
+
/**` and the mutating verbs go by
|
|
38
|
+
* default, which is why `route.post('/subscribe', …)` at the root works and
|
|
39
|
+
* `route.get('/sitemap.xml', …)` does not: the GET is not forwarded, stx
|
|
40
|
+
* renders it as a page, finds no page, and answers its own 404. The handler
|
|
41
|
+
* never runs and nothing says so (stacksjs/stacks#2326).
|
|
42
|
+
*
|
|
43
|
+
* Give this the app's own root-mounted routes, not the whole table. The
|
|
44
|
+
* framework registers around a hundred non-`/api` GETs that are reached in
|
|
45
|
+
* topologies of their own, and listing those buries the one that is broken.
|
|
46
|
+
*/
|
|
47
|
+
export declare function unforwardableRoutes<T extends RoutePathLike>(routes: readonly T[], rules: ApiProxyRules): T[];
|
|
48
|
+
/**
|
|
49
|
+
* The boot warning for routes that cannot be reached, naming the fix.
|
|
50
|
+
*
|
|
51
|
+
* The silence is the part the report called worst: the route is declared, it
|
|
52
|
+
* reads correctly in review, and it 404s in production the same way a typo
|
|
53
|
+
* would.
|
|
54
|
+
*/
|
|
55
|
+
export declare function describeUnforwardableRoutes(routes: readonly (RoutePathLike & { file?: string })[]): string;
|
|
27
56
|
export declare function proxyToBackend(req: Request, backendBase: string, stripPrefix?: string): Promise<Response>;
|
|
28
57
|
/**` traffic goes, or null when there is no safe answer.
|
|
29
58
|
*
|
|
@@ -93,3 +122,7 @@ export declare interface ApiProxyConfig {
|
|
|
93
122
|
paths?: readonly string[]
|
|
94
123
|
methods?: readonly string[]
|
|
95
124
|
}
|
|
125
|
+
export declare interface RoutePathLike {
|
|
126
|
+
method: string
|
|
127
|
+
path: string
|
|
128
|
+
}
|
package/dist/proxy.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export const DEFAULT_API_PREFIX="/api/",DEFAULT_API_METHODS=["POST","PUT","PATCH","DELETE"];export function resolveApiProxyRules(input={}){const prefixes=[DEFAULT_API_PREFIX];for(const raw of input.prefixes??[]){const prefix=String(raw).trim();if(!prefix||!prefix.startsWith("/"))continue;const normalized=prefix.endsWith("/")?prefix:`${prefix}/`;if(!prefixes.includes(normalized))prefixes.push(normalized)}const paths=[];for(const raw of input.paths??[]){const path=String(raw).trim();if(!path||!path.startsWith("/"))continue;const normalized=path.length>1&&path.endsWith("/")?path.slice(0,-1):path;if(!paths.includes(normalized))paths.push(normalized)}const methods=new Set((input.methods??DEFAULT_API_METHODS).map((method)=>String(method).toUpperCase()));return{prefixes,paths,methods}}function matchesPrefix(pathname,prefix){return pathname.startsWith(prefix)||pathname===prefix.slice(0,-1)}export function isApiBoundRequest(req,pathname,rules){if(!rules)return pathname.startsWith(DEFAULT_API_PREFIX)||DEFAULT_API_METHODS.includes(
|
|
1
|
+
export const DEFAULT_API_PREFIX="/api/",DEFAULT_API_METHODS=["POST","PUT","PATCH","DELETE"];export function resolveApiProxyRules(input={}){const prefixes=[DEFAULT_API_PREFIX];for(const raw of input.prefixes??[]){const prefix=String(raw).trim();if(!prefix||!prefix.startsWith("/"))continue;const normalized=prefix.endsWith("/")?prefix:`${prefix}/`;if(!prefixes.includes(normalized))prefixes.push(normalized)}const paths=[];for(const raw of input.paths??[]){const path=String(raw).trim();if(!path||!path.startsWith("/"))continue;const normalized=path.length>1&&path.endsWith("/")?path.slice(0,-1):path;if(!paths.includes(normalized))paths.push(normalized)}const methods=new Set((input.methods??DEFAULT_API_METHODS).map((method)=>String(method).toUpperCase()));return{prefixes,paths,methods}}function matchesPrefix(pathname,prefix){return pathname.startsWith(prefix)||pathname===prefix.slice(0,-1)}export function isApiBoundRequest(req,pathname,rules){return isApiBoundPath(req.method,pathname,rules)}export function isApiBoundPath(method,pathname,rules){const verb=method.toUpperCase();if(!rules)return pathname.startsWith(DEFAULT_API_PREFIX)||DEFAULT_API_METHODS.includes(verb);if(rules.methods.has(verb))return!0;if(rules.paths.includes(pathname))return!0;return rules.prefixes.some((prefix)=>matchesPrefix(pathname,prefix))}export function describeApiProxyRules(rules){const parts=[`prefixes ${rules.prefixes.join(" ")}`,`methods ${[...rules.methods].sort().join("/")}`];if(rules.paths.length>0)parts.push(`paths ${rules.paths.join(" ")}`);return parts.join(", ")}export function unforwardableRoutes(routes,rules){return routes.filter((route)=>!isApiBoundPath(route.method,route.path,rules))}export function describeUnforwardableRoutes(routes){const lines=routes.map((route)=>{const origin=route.file?` (${route.file})`:"";return` ${route.method} ${route.path}${origin}`});return[`${routes.length} root-mounted route${routes.length===1?"":"s"} the views server will not forward, so ${routes.length===1?"it":"they"} will answer the view 404 rather than reaching the handler:`,...lines,"Add them to `proxy.paths` in config/server.ts (or their prefix to `proxy.prefixes`) so the views server forwards them to the API."].join(`
|
|
2
|
+
`)}export async function proxyToBackend(req,backendBase,stripPrefix){const incoming=new URL(req.url);let pathname=incoming.pathname;if(stripPrefix&&(pathname===stripPrefix||pathname.startsWith(`${stripPrefix}/`)))pathname=pathname.slice(stripPrefix.length)||"/";const target=`${backendBase}${pathname}${incoming.search}`,fwd=new Headers(req.headers);fwd.delete("host");fwd.delete("content-length");fwd.set("x-forwarded-host",incoming.host);fwd.set("x-forwarded-proto",incoming.protocol.replace(":",""));const body=req.method==="GET"||req.method==="HEAD"?void 0:await req.arrayBuffer(),upstream=await fetch(target,{method:req.method,headers:fwd,body,redirect:"manual"}),out=new Headers(upstream.headers);out.delete("content-length");out.delete("content-encoding");return new Response(upstream.body,{status:upstream.status,statusText:upstream.statusText,headers:out})}export function resolveApiBase(configuredPort,env=process.env){if(env.API_URL)return env.API_URL;const explicitPort=Number(env.PORT_API);if(explicitPort)return`http://127.0.0.1:${explicitPort}`;if(["production","staging","development"].includes((env.APP_ENV||"").toLowerCase()))return null;return`http://127.0.0.1:${configuredPort||3008}`}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/server",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.72.
|
|
5
|
+
"version": "0.72.94",
|
|
6
6
|
"description": "Local development and production-ready.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"prepublishOnly": "bun run build"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@stacksjs/config": "0.72.
|
|
61
|
+
"@stacksjs/config": "0.72.94",
|
|
62
62
|
"better-dx": "^0.2.24",
|
|
63
|
-
"@stacksjs/path": "0.72.
|
|
64
|
-
"@stacksjs/router": "0.72.
|
|
65
|
-
"@stacksjs/validation": "0.72.
|
|
63
|
+
"@stacksjs/path": "0.72.94",
|
|
64
|
+
"@stacksjs/router": "0.72.94",
|
|
65
|
+
"@stacksjs/validation": "0.72.94"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"bun-plugin-auto-imports": "^0.4.0"
|