@stacksjs/server 0.75.68 → 0.75.70

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 CHANGED
@@ -1,3 +1,4 @@
1
+ import { registerPeerSource } from '@stacksjs/bun-router';
1
2
  /**
2
3
  * Normalise app configuration into the rules the predicate reads.
3
4
  *
@@ -53,6 +54,13 @@ export declare function unforwardableRoutes<T extends RoutePathLike>(routes: rea
53
54
  * would.
54
55
  */
55
56
  export declare function describeUnforwardableRoutes(routes: readonly (RoutePathLike & { file?: string })[]): string;
57
+ /**
58
+ * The client address an stx view sees as its ambient `ip` - stx-serve's
59
+ * `clientAddress` option. stx calls it before `onRequest`, so it registers the
60
+ * server that names the socket peer first; without one, `clientAddress()`
61
+ * believes every forwarding header.
62
+ */
63
+ export declare function viewClientAddress(req: Request, server?: Parameters<typeof registerPeerSource>[0]): string | null;
56
64
  /**
57
65
  * Forward `req` to `backendBase`. `peerOf` is the request whose socket peer is
58
66
  * appended to `X-Forwarded-For`: `req` itself, or - when `req` is a rewritten
package/dist/proxy.js CHANGED
@@ -1,2 +1,2 @@
1
- import{peerAddress}from"@stacksjs/bun-router";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,peerOf=req){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");const peer=peerAddress(peerOf);if(peer){const forwardedFor=fwd.get("x-forwarded-for");fwd.set("x-forwarded-for",forwardedFor?`${forwardedFor}, ${peer}`:peer)}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}`}
1
+ import{clientAddress,peerAddress,registerPeerSource}from"@stacksjs/bun-router";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 function viewClientAddress(req,server){registerPeerSource(server);return clientAddress(req)}export async function proxyToBackend(req,backendBase,stripPrefix,peerOf=req){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");const peer=peerAddress(peerOf);if(peer){const forwardedFor=fwd.get("x-forwarded-for");fwd.set("x-forwarded-for",forwardedFor?`${forwardedFor}, ${peer}`:peer)}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.75.68",
5
+ "version": "0.75.70",
6
6
  "description": "Local development and production-ready.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,20 +58,20 @@
58
58
  "prepublishOnly": "bun run build"
59
59
  },
60
60
  "dependencies": {
61
- "@stacksjs/auth": "0.75.68",
61
+ "@stacksjs/auth": "0.75.70",
62
62
  "@stacksjs/bun-router": "^0.1.22",
63
- "@stacksjs/config": "0.75.68",
64
- "@stacksjs/events": "0.75.68",
65
- "@stacksjs/i18n": "0.75.68",
66
- "@stacksjs/logging": "0.75.68",
67
- "@stacksjs/orm": "0.75.68",
68
- "@stacksjs/path": "0.75.68",
69
- "@stacksjs/router": "0.75.68",
70
- "@stacksjs/storage": "0.75.68",
63
+ "@stacksjs/config": "0.75.70",
64
+ "@stacksjs/events": "0.75.70",
65
+ "@stacksjs/i18n": "0.75.70",
66
+ "@stacksjs/logging": "0.75.70",
67
+ "@stacksjs/orm": "0.75.70",
68
+ "@stacksjs/path": "0.75.70",
69
+ "@stacksjs/router": "0.75.70",
70
+ "@stacksjs/storage": "0.75.70",
71
71
  "bun-plugin-auto-imports": "^0.4.3"
72
72
  },
73
73
  "devDependencies": {
74
- "@stacksjs/validation": "0.75.68",
74
+ "@stacksjs/validation": "0.75.70",
75
75
  "better-dx": "^0.2.24"
76
76
  }
77
77
  }