@beignet/core 0.0.46 → 0.0.47

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +39 -22
  3. package/dist/server/context.d.ts +7 -0
  4. package/dist/server/context.d.ts.map +1 -1
  5. package/dist/server/context.js.map +1 -1
  6. package/dist/server/hooks/logging.d.ts +3 -0
  7. package/dist/server/hooks/logging.d.ts.map +1 -1
  8. package/dist/server/hooks/logging.js +18 -7
  9. package/dist/server/hooks/logging.js.map +1 -1
  10. package/dist/server/hooks/rate-limit.d.ts +7 -7
  11. package/dist/server/hooks/rate-limit.d.ts.map +1 -1
  12. package/dist/server/hooks/rate-limit.js +15 -9
  13. package/dist/server/hooks/rate-limit.js.map +1 -1
  14. package/dist/server/hooks/security.d.ts +4 -3
  15. package/dist/server/hooks/security.d.ts.map +1 -1
  16. package/dist/server/hooks/security.js +5 -3
  17. package/dist/server/hooks/security.js.map +1 -1
  18. package/dist/server/http.d.ts +12 -0
  19. package/dist/server/http.d.ts.map +1 -1
  20. package/dist/server/request-executor.d.ts +5 -2
  21. package/dist/server/request-executor.d.ts.map +1 -1
  22. package/dist/server/request-executor.js +21 -2
  23. package/dist/server/request-executor.js.map +1 -1
  24. package/dist/server/server-context.d.ts +2 -1
  25. package/dist/server/server-context.d.ts.map +1 -1
  26. package/dist/server/server-context.js +2 -1
  27. package/dist/server/server-context.js.map +1 -1
  28. package/dist/server/server.d.ts +9 -0
  29. package/dist/server/server.d.ts.map +1 -1
  30. package/dist/server/server.js +14 -2
  31. package/dist/server/server.js.map +1 -1
  32. package/dist/server/trusted-proxy-internal.d.ts +4 -0
  33. package/dist/server/trusted-proxy-internal.d.ts.map +1 -0
  34. package/dist/server/trusted-proxy-internal.js +27 -0
  35. package/dist/server/trusted-proxy-internal.js.map +1 -0
  36. package/dist/server/trusted-proxy.d.ts +11 -6
  37. package/dist/server/trusted-proxy.d.ts.map +1 -1
  38. package/dist/server/trusted-proxy.js +6 -10
  39. package/dist/server/trusted-proxy.js.map +1 -1
  40. package/package.json +1 -1
  41. package/skills/app-architecture/SKILL.md +5 -2
  42. package/src/server/context.ts +7 -0
  43. package/src/server/hooks/logging.ts +35 -7
  44. package/src/server/hooks/rate-limit.ts +25 -15
  45. package/src/server/hooks/security.ts +11 -7
  46. package/src/server/http.ts +18 -1
  47. package/src/server/request-executor.ts +43 -2
  48. package/src/server/server-context.ts +2 -1
  49. package/src/server/server.ts +30 -1
  50. package/src/server/trusted-proxy-internal.ts +41 -0
  51. package/src/server/trusted-proxy.ts +20 -17
@@ -1 +1 @@
1
- {"version":3,"file":"trusted-proxy.js","sourceRoot":"","sources":["../../src/server/trusted-proxy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"trusted-proxy.js","sourceRoot":"","sources":["../../src/server/trusted-proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAiF5E,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AACpD,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAE/C,SAAS,oBAAoB,CAAC,KAAoB;IAChD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,gBAAgB,CACvB,GAAoB,EACpB,MAAc;IAEd,OAAO,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAyB;IAEzB,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACvD,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC;IACjE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,IACE,IAAI,IAAI,EAAE;YACV,IAAI,KAAK,GAAG;YACZ,IAAI,KAAK,GAAG;YACZ,IAAI,KAAK,IAAI;YACb,IAAI,KAAK,GAAG;YACZ,IAAI,KAAK,GAAG;YACZ,IAAI,KAAK,GAAG,EACZ,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CACpB,KAAyB,EACzB,QAA0B;IAE1B,IAAI,CAAC,KAAK,IAAI,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,QAAQ,MAAM,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,GAAoB;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,GAAQ;IACtC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAAoB,EACpB,MAAsD;IAEtD,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAC1C,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,gBAAgB,CACrB,GAAG,EACH,6BAA6B,CAC3B,MAAM,CAAC,MAAM,EACb,8BAA8B,CAC/B,CACF,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,KAAK,uBAAuB,IAAI,MAAM,KAAK,sBAAsB,EAAE,CAAC;QAC5E,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACzE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3C,OAAO,MAAM,KAAK,uBAAuB;YACvC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACZ,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAoB,EACpB,SAAyC,KAAK;IAE9C,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,YAAY,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAE9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,GAAG,EAAE,OAAO;YACZ,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,QAAQ;YACd,eAAe,EAAE,KAAK;YACtB,YAAY,EAAE,KAAK;SACpB,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAClB,MAAM,CAAC,cAAc,KAAK,KAAK;QAC7B,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,6BAA6B,CAC3B,MAAM,CAAC,cAAc,IAAI,uBAAuB,EAChD,6BAA6B,CAC9B,CAAC;IACR,MAAM,UAAU,GACd,MAAM,CAAC,UAAU,KAAK,KAAK;QACzB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,6BAA6B,CAC3B,MAAM,CAAC,UAAU,IAAI,mBAAmB,EACxC,yBAAyB,CAC1B,CAAC;IAER,MAAM,QAAQ,GACZ,iBAAiB,CACf,cAAc,CAAC,CAAC,CAAC,gBAAgB,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CACnE,IAAI,YAAY,CAAC;IACpB,MAAM,IAAI,GACR,aAAa,CACX,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,EAC1D,QAAQ,CACT,IAAI,QAAQ,CAAC;IAChB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,GAAG,CAAC,QAAQ,GAAG,GAAG,QAAQ,GAAG,CAAC;IAC9B,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,MAAM,QAAQ,GAAG,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE9D,OAAO;QACL,GAAG;QACH,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,QAAQ;QACR,IAAI;QACJ,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;QACzC,YAAY,EAAE,IAAI;KACnB,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beignet/core",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "type": "module",
5
5
  "description": "Core framework primitives for Beignet",
6
6
  "exports": {
@@ -65,8 +65,11 @@ themselves.
65
65
  - Server-only request context helpers may bridge Server Components to
66
66
  Beignet's request-scoped `AppContext`. Keep them outside `client/`, usually
67
67
  in `lib/server-context.ts`, and mark them with `@beignet/core/server-only`.
68
- Layouts may read `ctx.auth`, `ctx.tenant`, and request metadata for redirects
69
- and shell state; feature data still belongs behind use cases.
68
+ The context factory receives `requestInfo` from the server's explicit
69
+ `trustedProxy` policy; store it on request contexts when layouts need the
70
+ external URL, origin, host, protocol, or client IP. Layouts may read that
71
+ metadata with `ctx.auth` and `ctx.tenant` for redirects and shell state;
72
+ feature data still belongs behind use cases.
70
73
  - Server-only React Query prefetch helpers may consume that context for
71
74
  hydrated client state. Use them only for direct `{ contract, useCase }` route
72
75
  bindings whose use-case output matches the contract success body.
@@ -7,6 +7,7 @@ import type {
7
7
  } from "../ports/index.js";
8
8
  import type { TraceContext } from "../tracing/index.js";
9
9
  import type { HttpRequestLike, MaybePromise } from "./http.js";
10
+ import type { TrustedRequestInfo } from "./trusted-proxy.js";
10
11
 
11
12
  /**
12
13
  * Arguments passed to the `context.request` factory after a route is matched.
@@ -16,6 +17,12 @@ export type RequestContextArgs<Ports extends AnyPorts = AnyPorts> = {
16
17
  * Framework-neutral request.
17
18
  */
18
19
  req: HttpRequestLike;
20
+ /**
21
+ * Request metadata resolved from the server's explicit trusted-proxy policy.
22
+ * Forwarding headers never affect this value unless
23
+ * `createServer({ trustedProxy })` opts into them.
24
+ */
25
+ requestInfo: TrustedRequestInfo;
19
26
  /**
20
27
  * Final app ports, including ports contributed by providers.
21
28
  */
@@ -3,6 +3,10 @@
3
3
  */
4
4
 
5
5
  import type { HttpContractConfig } from "../../contracts/index.js";
6
+ import {
7
+ resolveTrustedRequest,
8
+ type TrustedRequestInfo,
9
+ } from "../trusted-proxy.js";
6
10
  import type { HttpRequestLike, ServerHook } from "../types.js";
7
11
  import { getRequestIdFromContext } from "./utils.js";
8
12
 
@@ -46,6 +50,7 @@ export interface LoggingConfig<Ctx> {
46
50
  onRequestStart?: (args: {
47
51
  ctx?: Ctx;
48
52
  req: HttpRequestLike;
53
+ requestInfo: TrustedRequestInfo;
49
54
  contract?: HttpContractConfig;
50
55
  }) => void;
51
56
  /**
@@ -54,6 +59,7 @@ export interface LoggingConfig<Ctx> {
54
59
  onRequestEnd?: (args: {
55
60
  ctx?: Ctx;
56
61
  req: HttpRequestLike;
62
+ requestInfo: TrustedRequestInfo;
57
63
  res: { status: number; headers: Record<string, string> };
58
64
  durationMs: number;
59
65
  contract?: HttpContractConfig;
@@ -61,6 +67,13 @@ export interface LoggingConfig<Ctx> {
61
67
  }) => void;
62
68
  }
63
69
 
70
+ function loggingRequestInfo(
71
+ req: HttpRequestLike,
72
+ requestInfo: TrustedRequestInfo | undefined,
73
+ ): TrustedRequestInfo {
74
+ return requestInfo ?? resolveTrustedRequest(req);
75
+ }
76
+
64
77
  /**
65
78
  * Create request logging hooks.
66
79
  *
@@ -74,10 +87,15 @@ export function createLoggingHooks<Ctx>(
74
87
 
75
88
  return {
76
89
  name: "logging",
77
- onRequest: ({ req, contract }) => {
90
+ onRequest: ({ req, requestInfo, contract }) => {
78
91
  if (config.onRequestStart) {
79
92
  try {
80
- config.onRequestStart({ req, contract, ctx: undefined });
93
+ config.onRequestStart({
94
+ req,
95
+ requestInfo: loggingRequestInfo(req, requestInfo),
96
+ contract,
97
+ ctx: undefined,
98
+ });
81
99
  } catch {
82
100
  // Ignore logging errors
83
101
  }
@@ -86,9 +104,11 @@ export function createLoggingHooks<Ctx>(
86
104
 
87
105
  if (!config.logger) return undefined;
88
106
  try {
89
- const url = new URL(req.url);
90
107
  config.logger.info(
91
- { method: req.method, url: url.pathname },
108
+ {
109
+ method: req.method,
110
+ url: loggingRequestInfo(req, requestInfo).url.pathname,
111
+ },
92
112
  "Request start",
93
113
  );
94
114
  } catch {
@@ -114,13 +134,22 @@ export function createLoggingHooks<Ctx>(
114
134
  },
115
135
  }
116
136
  : {}),
117
- afterSend: ({ ctx, req, response, durationMs, contract, error }) => {
137
+ afterSend: ({
138
+ ctx,
139
+ req,
140
+ requestInfo,
141
+ response,
142
+ durationMs,
143
+ contract,
144
+ error,
145
+ }) => {
118
146
  const headers = response.headers ?? {};
119
147
  if (config.onRequestEnd) {
120
148
  try {
121
149
  config.onRequestEnd({
122
150
  ctx,
123
151
  req,
152
+ requestInfo: loggingRequestInfo(req, requestInfo),
124
153
  res: { status: response.status, headers },
125
154
  durationMs,
126
155
  contract,
@@ -134,11 +163,10 @@ export function createLoggingHooks<Ctx>(
134
163
 
135
164
  if (!config.logger) return;
136
165
  try {
137
- const url = new URL(req.url);
138
166
  const requestId = getRequestIdFromContext(ctx);
139
167
  const payload = {
140
168
  method: req.method,
141
- url: url.pathname,
169
+ url: loggingRequestInfo(req, requestInfo).url.pathname,
142
170
  status: response.status,
143
171
  durationMs: Math.round(durationMs),
144
172
  ...(requestId !== undefined ? { requestId } : {}),
@@ -13,6 +13,7 @@ import {
13
13
  resolveTrustedClientIp,
14
14
  type TrustedProxyClientIpSource,
15
15
  type TrustedProxyConfig,
16
+ type TrustedRequestInfo,
16
17
  } from "../trusted-proxy.js";
17
18
  import type { HttpRequestLike, ServerHook } from "../types.js";
18
19
 
@@ -78,16 +79,16 @@ export interface RateLimitOptions<Ctx> {
78
79
  * Resolve the client IP for `ip`-scoped limits.
79
80
  *
80
81
  * There is no default: when any contract declares an `ip`-scoped rate limit
81
- * and neither `trustedProxy.clientIp`, `ipSource`, nor a custom `earlyKey`
82
- * is configured, the hook fails at startup. Prefer `trustedProxy.clientIp`
83
- * for production proxy headers; keep `ipSource` for custom keying or
84
- * compatibility, or pass `"none"` to explicitly accept one shared
85
- * `ip:unknown` bucket for all clients.
82
+ * and neither the server-level `trustedProxy.clientIp`,
83
+ * this hook's `trustedProxy.clientIp`, `ipSource`, nor a custom `earlyKey`
84
+ * is configured, the hook fails at startup. Prefer the server-level policy
85
+ * for production proxy headers; keep these hook-local options for an
86
+ * intentional override or custom keying.
86
87
  */
87
88
  ipSource?: RateLimitIpSource;
88
89
  /**
89
- * Shared trusted-proxy policy used to resolve client IPs when `ipSource` is
90
- * not set.
90
+ * Hook-local trusted-proxy policy used to resolve client IPs when
91
+ * `ipSource` is not set. This overrides the server-level policy.
91
92
  *
92
93
  * Configure this only when the app is always behind a platform or reverse
93
94
  * proxy that strips or normalizes forwarding headers. For `ip`-scoped rate
@@ -257,23 +258,32 @@ export function createRateLimitHooks<Ctx extends CtxWithRateLimit>(
257
258
  const { ipSource, trustedProxy } = options;
258
259
  const getClientIp = (
259
260
  req: HttpRequestLike,
261
+ requestInfo: TrustedRequestInfo | undefined,
260
262
  contractName: string,
261
263
  ): string | undefined => {
262
264
  if (ipSource !== undefined) {
263
265
  return resolveClientIp(req, ipSource);
264
266
  }
265
- if (hasTrustedProxyClientIp(trustedProxy) && trustedProxy) {
266
- return resolveTrustedClientIp(req, trustedProxy.clientIp);
267
+ if (trustedProxy !== undefined) {
268
+ if (hasTrustedProxyClientIp(trustedProxy) && trustedProxy) {
269
+ return resolveTrustedClientIp(req, trustedProxy.clientIp);
270
+ }
271
+ throw ipSourceConfigurationError(formatContractNames([contractName]));
272
+ }
273
+ if (requestInfo?.clientIpTrusted) {
274
+ return requestInfo.clientIp;
267
275
  }
268
276
  throw ipSourceConfigurationError(formatContractNames([contractName]));
269
277
  };
270
278
 
271
279
  return {
272
280
  name: "rate-limit",
273
- validate: ({ contracts }) => {
281
+ validate: ({ contracts, trustedProxy: serverTrustedProxy }) => {
274
282
  if (
275
283
  ipSource !== undefined ||
276
- hasTrustedProxyClientIp(trustedProxy) ||
284
+ hasTrustedProxyClientIp(
285
+ trustedProxy !== undefined ? trustedProxy : serverTrustedProxy,
286
+ ) ||
277
287
  options.earlyKey
278
288
  ) {
279
289
  return;
@@ -286,7 +296,7 @@ export function createRateLimitHooks<Ctx extends CtxWithRateLimit>(
286
296
  }
287
297
  throw ipSourceConfigurationError(formatContractNames(ipScoped));
288
298
  },
289
- onRequest: async ({ contract, ports, req }) => {
299
+ onRequest: async ({ contract, ports, req, requestInfo }) => {
290
300
  const rlMeta = contract.metadata?.rateLimit;
291
301
  if (!rlMeta) {
292
302
  return undefined;
@@ -300,7 +310,7 @@ export function createRateLimitHooks<Ctx extends CtxWithRateLimit>(
300
310
  const key =
301
311
  options.earlyKey?.({ req, scope }) ??
302
312
  defaultEarlyRateLimitKey({ req, scope }, (r) =>
303
- getClientIp(r, contract.name),
313
+ getClientIp(r, requestInfo, contract.name),
304
314
  );
305
315
 
306
316
  await enforceRateLimit(ports, {
@@ -312,7 +322,7 @@ export function createRateLimitHooks<Ctx extends CtxWithRateLimit>(
312
322
 
313
323
  return undefined;
314
324
  },
315
- beforeHandle: async ({ ctx, contract, req }) => {
325
+ beforeHandle: async ({ ctx, contract, req, requestInfo }) => {
316
326
  const rlMeta = contract.metadata?.rateLimit;
317
327
  if (!rlMeta) {
318
328
  return undefined;
@@ -326,7 +336,7 @@ export function createRateLimitHooks<Ctx extends CtxWithRateLimit>(
326
336
  const key =
327
337
  options.key?.({ ctx, req, scope }) ??
328
338
  defaultRateLimitKey({ ctx, req, scope }, (r) =>
329
- getClientIp(r, contract.name),
339
+ getClientIp(r, requestInfo, contract.name),
330
340
  );
331
341
 
332
342
  await enforceRateLimit(ctx.ports, {
@@ -154,12 +154,13 @@ export interface CsrfHooksOptions {
154
154
  */
155
155
  token?: false | CsrfTokenOptions;
156
156
  /**
157
- * Shared trusted-proxy policy used when comparing the request's external
158
- * origin against `Origin` or `Referer`.
157
+ * Hook-local trusted-proxy policy used when comparing the request's external
158
+ * origin against `Origin` or `Referer`. This overrides the server-level
159
+ * policy.
159
160
  *
160
161
  * Configure this only when the app is always behind a platform or reverse
161
162
  * proxy that strips or normalizes forwarding headers. Without this option,
162
- * CSRF uses `req.url` exactly as the adapter provided it.
163
+ * CSRF uses the `requestInfo` resolved by `createServer(...)`.
163
164
  */
164
165
  trustedProxy?: TrustedProxyConfig;
165
166
  /**
@@ -410,7 +411,7 @@ function enforceOrigin(args: {
410
411
  contract: HttpContractConfig;
411
412
  allowMissingOrigin: boolean;
412
413
  trustedOrigins: CsrfHooksOptions["trustedOrigins"];
413
- trustedProxy: TrustedProxyConfig | undefined;
414
+ requestOrigin: string;
414
415
  }): void {
415
416
  const origin = originFromRequestHeaders(args.req);
416
417
  if (!origin) {
@@ -426,7 +427,7 @@ function enforceOrigin(args: {
426
427
  origin,
427
428
  req: args.req,
428
429
  contract: args.contract,
429
- requestOrigin: requestOrigin(args.req, args.trustedProxy),
430
+ requestOrigin: args.requestOrigin,
430
431
  trustedOrigins: args.trustedOrigins,
431
432
  })
432
433
  ) {
@@ -485,7 +486,7 @@ export function createCsrfHooks<Ctx>(
485
486
 
486
487
  return {
487
488
  name: "csrf",
488
- onRequest: async ({ req, contract, params }) => {
489
+ onRequest: async ({ req, requestInfo, contract, params }) => {
489
490
  if (!protectedMethods.has(req.method.toUpperCase())) return undefined;
490
491
  if (await options.skip?.({ req, contract, params })) return undefined;
491
492
 
@@ -494,7 +495,10 @@ export function createCsrfHooks<Ctx>(
494
495
  contract,
495
496
  allowMissingOrigin,
496
497
  trustedOrigins,
497
- trustedProxy: options.trustedProxy,
498
+ requestOrigin:
499
+ options.trustedProxy !== undefined
500
+ ? requestOrigin(req, options.trustedProxy)
501
+ : (requestInfo?.origin ?? requestOrigin(req, false)),
498
502
  });
499
503
  enforceToken(req, token);
500
504
  return undefined;
@@ -5,6 +5,10 @@ import type {
5
5
  StandardSchema,
6
6
  } from "../contracts/index.js";
7
7
  import type { AnyPorts } from "../ports/index.js";
8
+ import type {
9
+ TrustedProxyConfig,
10
+ TrustedRequestInfo,
11
+ } from "./trusted-proxy.js";
8
12
 
9
13
  /**
10
14
  * Framework-neutral request shape consumed by Beignet server adapters.
@@ -276,6 +280,7 @@ export type OnRequestHook<
276
280
  C extends HttpContractConfig = HttpContractConfig,
277
281
  > = (args: {
278
282
  req: HttpRequestLike;
283
+ requestInfo: TrustedRequestInfo;
279
284
  ports: Ports;
280
285
  contract: C;
281
286
  params: Record<string, string>;
@@ -303,6 +308,7 @@ export type BeforeHandleHook<
303
308
  C extends HttpContractConfig = HttpContractConfig,
304
309
  > = (args: {
305
310
  req: HttpRequestLike;
311
+ requestInfo: TrustedRequestInfo;
306
312
  ctx: Ctx;
307
313
  contract: C;
308
314
  path: InferPath<C>;
@@ -323,6 +329,7 @@ export type BeforeSendHook<
323
329
  C extends HttpContractConfig = HttpContractConfig,
324
330
  > = (args: {
325
331
  req: HttpRequestLike;
332
+ requestInfo: TrustedRequestInfo;
326
333
  ctx?: Ctx;
327
334
  contract: C;
328
335
  path?: InferPath<C>;
@@ -371,6 +378,7 @@ export type AfterSendHook<
371
378
  C extends HttpContractConfig = HttpContractConfig,
372
379
  > = (args: {
373
380
  req: HttpRequestLike;
381
+ requestInfo: TrustedRequestInfo;
374
382
  ctx?: Ctx;
375
383
  contract: C;
376
384
  path?: InferPath<C>;
@@ -395,6 +403,7 @@ export type ServerCaughtErrorHook<
395
403
  > = (args: {
396
404
  err: unknown;
397
405
  req: HttpRequestLike;
406
+ requestInfo?: TrustedRequestInfo;
398
407
  ctx?: Ctx;
399
408
  contract: C;
400
409
  path?: InferPath<C>;
@@ -415,6 +424,7 @@ export type ServerUnhandledErrorMapper<
415
424
  > = (args: {
416
425
  err: unknown;
417
426
  req: HttpRequestLike;
427
+ requestInfo?: TrustedRequestInfo;
418
428
  ctx?: Ctx;
419
429
  contract: C;
420
430
  path?: InferPath<C>;
@@ -447,7 +457,14 @@ export interface ServerHook<Ctx, Ports extends AnyPorts = AnyPorts> {
447
457
  * registered later through `server.route(...)` are not seen here, so hooks
448
458
  * that need full coverage should keep a runtime check as a backstop.
449
459
  */
450
- validate?: (args: { contracts: readonly HttpContractConfig[] }) => void;
460
+ validate?: (args: {
461
+ contracts: readonly HttpContractConfig[];
462
+ /**
463
+ * Server-level trusted-proxy policy. Request phases receive its resolved
464
+ * value as `requestInfo`.
465
+ */
466
+ trustedProxy?: TrustedProxyConfig;
467
+ }) => void;
451
468
  /**
452
469
  * Runs after route matching and before body/query/header parsing.
453
470
  */
@@ -81,6 +81,7 @@ import {
81
81
  decodeMatchedParams,
82
82
  PathDecodeError,
83
83
  } from "./route-matching.js";
84
+ import type { TrustedRequestInfo } from "./trusted-proxy.js";
84
85
 
85
86
  function withoutHeadResponseBody(
86
87
  response: HttpResponse,
@@ -133,6 +134,23 @@ type ExecutionTarget<Ctx, C extends HttpContractConfig> = {
133
134
  responseValidationExemptStatus?: number;
134
135
  };
135
136
 
137
+ function copyTrustedRequestInfo(
138
+ requestInfo: TrustedRequestInfo,
139
+ ): TrustedRequestInfo;
140
+ function copyTrustedRequestInfo(requestInfo: undefined): undefined;
141
+ function copyTrustedRequestInfo(
142
+ requestInfo: TrustedRequestInfo | undefined,
143
+ ): TrustedRequestInfo | undefined;
144
+ function copyTrustedRequestInfo(
145
+ requestInfo: TrustedRequestInfo | undefined,
146
+ ): TrustedRequestInfo | undefined {
147
+ if (!requestInfo) return undefined;
148
+ return {
149
+ ...requestInfo,
150
+ url: new URL(requestInfo.url),
151
+ };
152
+ }
153
+
136
154
  /**
137
155
  * Build the per-request execution pipeline once.
138
156
  *
@@ -151,8 +169,10 @@ export function createRequestExecutor<
151
169
  createRequestContext: (
152
170
  req: HttpRequestLike,
153
171
  contract: HttpContractConfig,
172
+ requestInfo: TrustedRequestInfo,
154
173
  ) => Promise<Ctx>;
155
174
  finalizeContext: (seed: ContextSeed<Ctx> | Ctx) => Ctx;
175
+ resolveRequestInfo: (req: HttpRequestLike) => TrustedRequestInfo;
156
176
  },
157
177
  hooks: ServerHook<Ctx, FinalPorts>[],
158
178
  routeHooks: readonly RouteHook<unknown, object>[] = [],
@@ -179,6 +199,7 @@ export function createRequestExecutor<
179
199
  preMatchedParams?: Record<string, string>,
180
200
  ) => {
181
201
  const { contract, handler: userHandler } = target;
202
+ let requestInfo: TrustedRequestInfo | undefined;
182
203
  let baseCtx: Ctx | undefined;
183
204
  let pathValue: HandlerArgs<Ctx, C>["path"] | undefined;
184
205
  let queryValue: HandlerArgs<Ctx, C>["query"] | undefined;
@@ -224,6 +245,7 @@ export function createRequestExecutor<
224
245
  const args = {
225
246
  err: caught,
226
247
  req,
248
+ requestInfo: copyTrustedRequestInfo(requestInfo),
227
249
  ctx,
228
250
  contract,
229
251
  path,
@@ -364,6 +386,7 @@ export function createRequestExecutor<
364
386
  const handled = await hook.mapUnhandledError({
365
387
  err: currentError,
366
388
  req,
389
+ requestInfo: copyTrustedRequestInfo(requestInfo),
367
390
  ctx,
368
391
  contract,
369
392
  path,
@@ -391,6 +414,7 @@ export function createRequestExecutor<
391
414
  const handled = await options.mapUnhandledError({
392
415
  err: currentError,
393
416
  req,
417
+ requestInfo: copyTrustedRequestInfo(requestInfo),
394
418
  ctx,
395
419
  contract,
396
420
  path,
@@ -422,6 +446,8 @@ export function createRequestExecutor<
422
446
  };
423
447
 
424
448
  try {
449
+ const resolvedRequestInfo = contextRuntime.resolveRequestInfo(req);
450
+ requestInfo = resolvedRequestInfo;
425
451
  const url = new URL(req.url);
426
452
 
427
453
  let matchedParams: Record<string, string>;
@@ -460,6 +486,7 @@ export function createRequestExecutor<
460
486
  if (!hook.beforeSend) continue;
461
487
  const nextResponse = await hook.beforeSend({
462
488
  req,
489
+ requestInfo: copyTrustedRequestInfo(resolvedRequestInfo),
463
490
  ctx: initialResult.ctx,
464
491
  contract,
465
492
  path: pathValue,
@@ -515,6 +542,7 @@ export function createRequestExecutor<
515
542
  if (!hook.beforeSend) continue;
516
543
  const nextResponse = await hook.beforeSend({
517
544
  req,
545
+ requestInfo: copyTrustedRequestInfo(resolvedRequestInfo),
518
546
  ctx: initialResult.ctx,
519
547
  contract,
520
548
  path: pathValue,
@@ -612,6 +640,7 @@ export function createRequestExecutor<
612
640
  try {
613
641
  const hookResult = await hook.onRequest({
614
642
  req,
643
+ requestInfo: copyTrustedRequestInfo(resolvedRequestInfo),
615
644
  ports: finalPorts,
616
645
  contract,
617
646
  params: matchedParams,
@@ -644,7 +673,11 @@ export function createRequestExecutor<
644
673
  let createdCtx!: Ctx;
645
674
  try {
646
675
  createdCtx = await timeStage("contextMs", () =>
647
- contextRuntime.createRequestContext(req, contract),
676
+ contextRuntime.createRequestContext(
677
+ req,
678
+ contract,
679
+ resolvedRequestInfo,
680
+ ),
648
681
  );
649
682
  baseCtx = createdCtx;
650
683
  } catch (error) {
@@ -727,7 +760,11 @@ export function createRequestExecutor<
727
760
  let createdCtx!: Ctx;
728
761
  try {
729
762
  createdCtx = await timeStage("contextMs", () =>
730
- contextRuntime.createRequestContext(req, contract),
763
+ contextRuntime.createRequestContext(
764
+ req,
765
+ contract,
766
+ resolvedRequestInfo,
767
+ ),
731
768
  );
732
769
  baseCtx = createdCtx;
733
770
  } catch (error) {
@@ -792,6 +829,7 @@ export function createRequestExecutor<
792
829
  try {
793
830
  const hookResult = await hook.beforeHandle({
794
831
  req,
832
+ requestInfo: copyTrustedRequestInfo(resolvedRequestInfo),
795
833
  ctx: currentCtx,
796
834
  contract,
797
835
  path,
@@ -951,6 +989,7 @@ export function createRequestExecutor<
951
989
  try {
952
990
  await hook.afterSend({
953
991
  req,
992
+ requestInfo: copyTrustedRequestInfo(resolvedRequestInfo),
954
993
  ctx: result.ctx,
955
994
  contract,
956
995
  path: pathValue,
@@ -1110,8 +1149,10 @@ export function buildHandler<
1110
1149
  createRequestContext: (
1111
1150
  req: HttpRequestLike,
1112
1151
  contract: HttpContractConfig,
1152
+ requestInfo: TrustedRequestInfo,
1113
1153
  ) => Promise<Ctx>;
1114
1154
  finalizeContext: (seed: ContextSeed<Ctx> | Ctx) => Ctx;
1155
+ resolveRequestInfo: (req: HttpRequestLike) => TrustedRequestInfo;
1115
1156
  },
1116
1157
  contract: C,
1117
1158
  userHandler: Handler<Ctx, C>,
@@ -16,10 +16,11 @@ import type { ServerContextConfig } from "./context.js";
16
16
  * // server/context.ts
17
17
  * export const appContext = defineServerContext<AppContext, AppPorts>()({
18
18
  * gate: (ports) => ports.gate,
19
- * request: async ({ req, ports, requestId, trace }) => ({
19
+ * request: async ({ req, ports, requestId, requestInfo, trace }) => ({
20
20
  * actor: await resolveActor(req),
21
21
  * auth: null,
22
22
  * requestId,
23
+ * requestInfo,
23
24
  * ...trace,
24
25
  * ports,
25
26
  * }),
@@ -73,6 +73,12 @@ import {
73
73
  } from "./route-matching.js";
74
74
  import type { RuntimeIntegrityCheck } from "./runtime-integrity.js";
75
75
  import { runRuntimeIntegrityCheck } from "./runtime-integrity.js";
76
+ import {
77
+ resolveTrustedRequest,
78
+ type TrustedProxyConfig,
79
+ type TrustedRequestInfo,
80
+ } from "./trusted-proxy.js";
81
+ import { assertValidTrustedProxyConfig } from "./trusted-proxy-internal.js";
76
82
  import {
77
83
  createUseCaseRouteHandler,
78
84
  isUseCaseRouteDef,
@@ -107,6 +113,15 @@ type AnyServiceProvider = ServiceProvider<
107
113
  any
108
114
  >;
109
115
 
116
+ function copyTrustedRequestInfo(
117
+ requestInfo: TrustedRequestInfo,
118
+ ): TrustedRequestInfo {
119
+ return {
120
+ ...requestInfo,
121
+ url: new URL(requestInfo.url),
122
+ };
123
+ }
124
+
110
125
  /**
111
126
  * Options for creating a Beignet server instance.
112
127
  */
@@ -155,6 +170,14 @@ export type CreateServerOptions<
155
170
  Ports & InferProviderPorts<Providers>,
156
171
  ServiceInput
157
172
  >;
173
+ /**
174
+ * Explicit policy for trusting proxy- or edge-provided request metadata.
175
+ *
176
+ * Forwarding headers are ignored by default. Configure this only when every
177
+ * request reaches the app through a trusted platform or reverse proxy that
178
+ * strips or normalizes those headers.
179
+ */
180
+ trustedProxy?: TrustedProxyConfig;
158
181
  /**
159
182
  * Server hooks that wrap every registered route.
160
183
  */
@@ -387,10 +410,12 @@ export async function createServer<
387
410
  ...((options.hooks ?? []) as ServerHook<Ctx, FinalPorts>[]),
388
411
  ];
389
412
  const contracts = options.routes ? contractsFromRoutes(options.routes) : [];
413
+ const trustedProxy = options.trustedProxy ?? false;
414
+ assertValidTrustedProxyConfig(trustedProxy);
390
415
 
391
416
  // Fail startup on hook misconfiguration before provider setup runs.
392
417
  for (const hook of hooks) {
393
- hook.validate?.({ contracts });
418
+ hook.validate?.({ contracts, trustedProxy });
394
419
  }
395
420
 
396
421
  const resolvedContext = resolveServerContext<Ctx, FinalPorts, ServiceInput>(
@@ -403,11 +428,13 @@ export async function createServer<
403
428
  const createRequestContext = async (
404
429
  req: HttpRequestLike,
405
430
  contract?: HttpContractConfig,
431
+ requestInfo: TrustedRequestInfo = resolveTrustedRequest(req, trustedProxy),
406
432
  ): Promise<Ctx> => {
407
433
  const { requestId, trace } = instrumentation.prepareRequest(req);
408
434
  return finalizeContext(
409
435
  await resolvedContext.request({
410
436
  req,
437
+ requestInfo: copyTrustedRequestInfo(requestInfo),
411
438
  ports: finalPorts,
412
439
  contract,
413
440
  requestId,
@@ -502,6 +529,8 @@ export async function createServer<
502
529
  const contextRuntime = {
503
530
  createRequestContext,
504
531
  finalizeContext,
532
+ resolveRequestInfo: (req: HttpRequestLike) =>
533
+ resolveTrustedRequest(req, trustedProxy),
505
534
  };
506
535
 
507
536
  let serviceContextsAvailable = false;