@stacksjs/server 0.70.294 → 0.70.296
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 +74 -5
- package/dist/proxy.js +1 -1
- package/package.json +5 -5
package/dist/proxy.d.ts
CHANGED
|
@@ -1,7 +1,76 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* Normalise app configuration into the rules the predicate reads.
|
|
3
|
+
*
|
|
4
|
+
* `/api/` is always present: it is the prefix the framework's own generated
|
|
5
|
+
* routes use, and an app that dropped it would find its auto-CRUD endpoints
|
|
6
|
+
* unreachable with no indication why. Configuring `prefixes` ADDS to it.
|
|
7
|
+
*
|
|
8
|
+
* Resolve this once at boot rather than per request. `@stacksjs/config`
|
|
9
|
+
* populates `overrides` asynchronously, so a per-request read would answer
|
|
10
|
+
* differently depending on how far boot had progressed.
|
|
5
11
|
*/
|
|
6
|
-
export declare function
|
|
12
|
+
export declare function resolveApiProxyRules(input?: ApiProxyConfig): ApiProxyRules;
|
|
13
|
+
/**`
|
|
14
|
+
* prefix, or a mutating verb. Callers that have resolved the app's
|
|
15
|
+
* configuration pass it in.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isApiBoundRequest(req: Request, pathname: string, rules?: ApiProxyRules): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* A one-line summary of what will be forwarded, for the dev server's boot
|
|
20
|
+
* output.
|
|
21
|
+
*
|
|
22
|
+
* Asked for directly in stacksjs/stacks#2230: a 404 on a route you know you
|
|
23
|
+
* registered is very hard to diagnose when the rule that swallowed it is
|
|
24
|
+
* invisible.
|
|
25
|
+
*/
|
|
26
|
+
export declare function describeApiProxyRules(rules: ApiProxyRules): string;
|
|
7
27
|
export declare function proxyToBackend(req: Request, backendBase: string, stripPrefix?: string): Promise<Response>;
|
|
28
|
+
/**` traffic behaves identically
|
|
29
|
+
* in both topologies (stacksjs/stacks#1950).
|
|
30
|
+
*/
|
|
31
|
+
/** The prefix that is always API-bound, with or without configuration. */
|
|
32
|
+
export declare const DEFAULT_API_PREFIX: '/api/';
|
|
33
|
+
/**
|
|
34
|
+
* Verbs that never match a static stx page render, so they always belong to
|
|
35
|
+
* bun-router. Without this rule, `route.post('/subscribe', ...)` declared at
|
|
36
|
+
* the root hits stx-serve and 404s.
|
|
37
|
+
*/
|
|
38
|
+
export declare const DEFAULT_API_METHODS: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* What the views server forwards to the API process.
|
|
41
|
+
*
|
|
42
|
+
* ## Why this is configuration and not a route lookup
|
|
43
|
+
*
|
|
44
|
+
* The obvious fix for stacksjs/stacks#2230 is to ask the router:
|
|
45
|
+
* `route.getAllowedMethods(pathname)` already answers exactly the right
|
|
46
|
+
* question. It is not reachable from here. `isApiBoundRequest` is called from
|
|
47
|
+
* an `onRequest` hook inside the VIEWS process, and the route table is
|
|
48
|
+
* registered in the API process — a different process in `buddy dev`, and
|
|
49
|
+
* potentially a different host in production, where `production-server.ts`
|
|
50
|
+
* proxies to an `apiBase` URL. Importing the app's route files into the views
|
|
51
|
+
* process to consult them would execute route registration there, pulling the
|
|
52
|
+
* models and database in with it, and in a split deployment the views site may
|
|
53
|
+
* not ship that code at all.
|
|
54
|
+
*
|
|
55
|
+
* So the app declares it, the way Next's `rewrites()` does. Empty by default,
|
|
56
|
+
* so nothing changes for an app that does not set it.
|
|
57
|
+
*
|
|
58
|
+
* ## The cost of adding a path here
|
|
59
|
+
*
|
|
60
|
+
* stx's `onRequest` runs BEFORE static file serving, so any path this matches
|
|
61
|
+
* permanently shadows a `public/` file of the same name. Adding `/script.js`
|
|
62
|
+
* means `public/script.js` stops being reachable. That is a fine trade when
|
|
63
|
+
* the API really does own the path, and a confusing one otherwise, which is
|
|
64
|
+
* why `describeApiProxyRules` exists and the dev server prints it at boot.
|
|
65
|
+
*/
|
|
66
|
+
export declare interface ApiProxyRules {
|
|
67
|
+
prefixes: string[]
|
|
68
|
+
paths: string[]
|
|
69
|
+
methods: Set<string>
|
|
70
|
+
}
|
|
71
|
+
/** The shape an app writes in `config/server.ts` under `proxy`. */
|
|
72
|
+
export declare interface ApiProxyConfig {
|
|
73
|
+
prefixes?: readonly string[]
|
|
74
|
+
paths?: readonly string[]
|
|
75
|
+
methods?: readonly string[]
|
|
76
|
+
}
|
package/dist/proxy.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
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(req.method);if(rules.methods.has(req.method))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 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})}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/server",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.296",
|
|
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.70.
|
|
61
|
+
"@stacksjs/config": "0.70.296",
|
|
62
62
|
"better-dx": "^0.2.17",
|
|
63
|
-
"@stacksjs/path": "0.70.
|
|
64
|
-
"@stacksjs/router": "0.70.
|
|
65
|
-
"@stacksjs/validation": "0.70.
|
|
63
|
+
"@stacksjs/path": "0.70.296",
|
|
64
|
+
"@stacksjs/router": "0.70.296",
|
|
65
|
+
"@stacksjs/validation": "0.70.296"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"bun-plugin-auto-imports": "^0.4.0"
|