@ultimat3/http 22.8.0 → 22.8.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/http",
3
- "version": "22.8.0",
3
+ "version": "22.8.2",
4
4
  "description": "Owned request lifecycle over Bun.serve: router, ordered pipeline, problem+json errors",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,9 +31,9 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/core": "22.8.0",
35
- "@ultimat3/i18n": "22.8.0",
36
- "@ultimat3/schema": "22.8.0",
37
- "@ultimat3/time": "22.8.0"
34
+ "@ultimat3/core": "22.8.2",
35
+ "@ultimat3/i18n": "22.8.2",
36
+ "@ultimat3/schema": "22.8.2",
37
+ "@ultimat3/time": "22.8.2"
38
38
  }
39
39
  }
package/src/index.ts CHANGED
@@ -111,7 +111,13 @@ export { DEFAULT_LOCALE_CONFIG, readCookie } from './locale';
111
111
  export type { Middleware } from './middleware';
112
112
  export { compose } from './middleware';
113
113
  export type { NavigationPurpose } from './navigation';
114
- export { navigationGate, navigationPurpose, redirectForRouter, relocate } from './navigation';
114
+ export {
115
+ locationFor,
116
+ navigationGate,
117
+ navigationPurpose,
118
+ redirectForRouter,
119
+ relocate,
120
+ } from './navigation';
115
121
  export type { OverlayMeta, OverlayNotice } from './overlay';
116
122
  export { overlayResponse, wantsOverlay } from './overlay';
117
123
  export { OVERLAY_STYLE } from './overlay-style';
package/src/navigation.ts CHANGED
@@ -24,6 +24,19 @@ export function navigationPurpose(request: Request): NavigationPurpose | null {
24
24
 
25
25
  const SAFE = new Set(['GET', 'HEAD']);
26
26
 
27
+ /**
28
+ * What `x-ultimate-location` says. A target on THIS origin is its path, query and fragment and
29
+ * nothing else: behind a TLS-terminating proxy the request URL this process sees is
30
+ * `http://internal…`, so an absolute URL built from it named the public site as `http://` (HSTS
31
+ * hid it; it was still wrong). The router resolves a path against the page it runs in, whose origin
32
+ * is the one the visitor typed. A target on ANOTHER origin is kept exactly as the app gave it.
33
+ */
34
+ export function locationFor(target: string, base: URL): string {
35
+ const resolved = new URL(target, base);
36
+ if (resolved.origin !== base.origin) return target;
37
+ return `${resolved.pathname}${resolved.search}${resolved.hash}`;
38
+ }
39
+
27
40
  /** "Load this with a real navigation" — never stored anywhere, it answers one request. */
28
41
  export function relocate(location: string, headers?: Headers): Response {
29
42
  const out = new Headers(headers);
@@ -54,7 +67,7 @@ export function navigationGate(
54
67
  const ours = navigation !== undefined && navigation.surface === surface;
55
68
  if (ours && (purpose === 'soft' || navigation.prefetch)) return undefined;
56
69
  if (purpose === 'prefetch') return new Response(null, { status: 204, headers: NO_STORE });
57
- return relocate(url.href);
70
+ return relocate(locationFor(url.href, url));
58
71
  }
59
72
 
60
73
  const NO_STORE = { 'cache-control': 'no-store' } as const;
@@ -75,5 +88,5 @@ export function redirectForRouter(
75
88
  if (response.status < 300 || response.status > 399) return undefined;
76
89
  const location = response.headers.get('location');
77
90
  if (location === null) return undefined;
78
- return relocate(new URL(location, base).href, response.headers);
91
+ return relocate(locationFor(location, base), response.headers);
79
92
  }