@stacksjs/server 0.75.237 → 0.75.239

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.
@@ -11,6 +11,22 @@
11
11
  * here rather than in production.
12
12
  */
13
13
  export declare function resolveRedirectRules(input?: RedirectConfig): RedirectRules;
14
+ /**
15
+ * The page redirects the route files declare, as a {@link RedirectConfig}.
16
+ *
17
+ * `route.redirect('/old', '/new')` and `route.permanentRedirect()` register in
18
+ * the API process, but a page path is answered here, and this process never
19
+ * loads the route files. `generate:types` writes their page-path redirects to
20
+ * `storage/framework/auto-imports/routes.ts` (`routeRedirects`), and this reads
21
+ * that map, so a redirect declared in `routes/` needs no proxy rule and no hop
22
+ * to the API.
23
+ *
24
+ * A path with a `{parameter}` is skipped: rules here match exact paths. Merge
25
+ * with `config/server.ts` as `{ ...routeRedirects, ...config.server.redirects }`,
26
+ * so a rule written in config wins a clash. Missing or unreadable, it is empty:
27
+ * a redirect must never stop a site from booting.
28
+ */
29
+ export declare function loadRouteRedirects(file?: string): Promise<RedirectConfig>;
14
30
  /*': 'https://dash.example.com'` lands on
15
31
  * `https://dash.example.com/events/42`. No regular expressions, no captures,
16
32
  * no ordering to reason about.
package/dist/redirects.js CHANGED
@@ -1 +1 @@
1
- const PROTECTED_PREFIX="/api/";export const DEFAULT_REDIRECT_STATUS=301;function normalizePath(value){const path=String(value).trim();if(!path.startsWith("/"))return"";return path.length>1&&path.endsWith("/")?path.replace(/\/+$/,""):path}export function resolveRedirectRules(input={}){const rules=new Map;for(const[rawFrom,rawTarget]of Object.entries(input??{})){const subtree=rawFrom.endsWith("/*"),from=normalizePath(subtree?rawFrom.slice(0,-2)||"/":rawFrom);if(!from)continue;if(from===PROTECTED_PREFIX.slice(0,-1)||from.startsWith(PROTECTED_PREFIX))continue;const target=typeof rawTarget==="string"?{to:rawTarget}:rawTarget,to=String(target?.to??"").trim();if(!to)continue;if(!/^https?:\/\//i.test(to)&&normalizePath(to)===from)continue;rules.set(subtree?`${from}/*`:from,{from,to,status:Number(target?.status)||DEFAULT_REDIRECT_STATUS,preserveQuery:target?.preserveQuery!==!1,subtree})}return rules}export function resolveRedirect(url,rules){if(!rules?.size)return;const pathname=normalizePath(url.pathname),rule=rules.get(pathname)??matchSubtree(pathname,rules);if(!rule)return;let location=rule.to;if(rule.subtree){const remainder=pathname.slice(rule.from.length);if(remainder)location=location.replace(/\/$/,"")+remainder}if(rule.preserveQuery&&url.search)location+=location.includes("?")?`&${url.search.slice(1)}`:url.search;return new Response(null,{status:rule.status,headers:{Location:location,"Cache-Control":rule.status===301||rule.status===308?"public, max-age=3600":"no-store"}})}function matchSubtree(pathname,rules){let best;for(const rule of rules.values()){if(!rule.subtree)continue;if(pathname!==rule.from&&!pathname.startsWith(`${rule.from}/`))continue;if(!best||rule.from.length>best.from.length)best=rule}return best}export function describeRedirectRules(rules){if(!rules.size)return"none";const shown=[...rules.values()].slice(0,3).map((rule)=>`${rule.from} \u2192 ${rule.to}`),rest=rules.size-shown.length;return rest>0?`${shown.join(", ")} (+${rest} more)`:shown.join(", ")}
1
+ import{existsSync}from"node:fs";import{path as p}from"@stacksjs/path";const PROTECTED_PREFIX="/api/";export const DEFAULT_REDIRECT_STATUS=301;function normalizePath(value){const path=String(value).trim();if(!path.startsWith("/"))return"";return path.length>1&&path.endsWith("/")?path.replace(/\/+$/,""):path}export function resolveRedirectRules(input={}){const rules=new Map;for(const[rawFrom,rawTarget]of Object.entries(input??{})){const subtree=rawFrom.endsWith("/*"),from=normalizePath(subtree?rawFrom.slice(0,-2)||"/":rawFrom);if(!from)continue;if(from===PROTECTED_PREFIX.slice(0,-1)||from.startsWith(PROTECTED_PREFIX))continue;const target=typeof rawTarget==="string"?{to:rawTarget}:rawTarget,to=String(target?.to??"").trim();if(!to)continue;if(!/^https?:\/\//i.test(to)&&normalizePath(to)===from)continue;rules.set(subtree?`${from}/*`:from,{from,to,status:Number(target?.status)||DEFAULT_REDIRECT_STATUS,preserveQuery:target?.preserveQuery!==!1,subtree})}return rules}export async function loadRouteRedirects(file=p.storagePath("framework/auto-imports/routes.ts")){if(!existsSync(file))return{};try{const generated=await import(file),redirects={};for(const[from,target]of Object.entries(generated.routeRedirects??{})){if(from.includes("{")||!target?.to)continue;redirects[from]={to:target.to,status:target.status}}return redirects}catch{return{}}}export function resolveRedirect(url,rules){if(!rules?.size)return;const pathname=normalizePath(url.pathname),rule=rules.get(pathname)??matchSubtree(pathname,rules);if(!rule)return;let location=rule.to;if(rule.subtree){const remainder=pathname.slice(rule.from.length);if(remainder)location=location.replace(/\/$/,"")+remainder}if(rule.preserveQuery&&url.search)location+=location.includes("?")?`&${url.search.slice(1)}`:url.search;return new Response(null,{status:rule.status,headers:{Location:location,"Cache-Control":rule.status===301||rule.status===308?"public, max-age=3600":"no-store"}})}function matchSubtree(pathname,rules){let best;for(const rule of rules.values()){if(!rule.subtree)continue;if(pathname!==rule.from&&!pathname.startsWith(`${rule.from}/`))continue;if(!best||rule.from.length>best.from.length)best=rule}return best}export function describeRedirectRules(rules){if(!rules.size)return"none";const shown=[...rules.values()].slice(0,3).map((rule)=>`${rule.from} \u2192 ${rule.to}`),rest=rules.size-shown.length;return rest>0?`${shown.join(", ")} (+${rest} more)`:shown.join(", ")}
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.237",
5
+ "version": "0.75.239",
6
6
  "description": "Local development and production-ready.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,21 +58,21 @@
58
58
  "prepublishOnly": "bun run build"
59
59
  },
60
60
  "dependencies": {
61
- "@stacksjs/auth": "0.75.237",
61
+ "@stacksjs/auth": "0.75.239",
62
62
  "@stacksjs/bun-router": "^0.1.24",
63
- "@stacksjs/config": "0.75.237",
64
- "@stacksjs/events": "0.75.237",
65
- "@stacksjs/i18n": "0.75.237",
66
- "@stacksjs/logging": "0.75.237",
67
- "@stacksjs/orm": "0.75.237",
68
- "@stacksjs/path": "0.75.237",
69
- "@stacksjs/payments": "0.75.237",
70
- "@stacksjs/router": "0.75.237",
71
- "@stacksjs/storage": "0.75.237",
63
+ "@stacksjs/config": "0.75.239",
64
+ "@stacksjs/events": "0.75.239",
65
+ "@stacksjs/i18n": "0.75.239",
66
+ "@stacksjs/logging": "0.75.239",
67
+ "@stacksjs/orm": "0.75.239",
68
+ "@stacksjs/path": "0.75.239",
69
+ "@stacksjs/payments": "0.75.239",
70
+ "@stacksjs/router": "0.75.239",
71
+ "@stacksjs/storage": "0.75.239",
72
72
  "bun-plugin-auto-imports": "^0.4.4"
73
73
  },
74
74
  "devDependencies": {
75
- "@stacksjs/validation": "0.75.237",
75
+ "@stacksjs/validation": "0.75.239",
76
76
  "better-dx": "^0.2.24"
77
77
  }
78
78
  }