@stacksjs/server 0.72.19 → 0.72.20

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,13 +11,9 @@
11
11
  * here rather than in production.
12
12
  */
13
13
  export declare function resolveRedirectRules(input?: RedirectConfig): RedirectRules;
14
- /**
15
- * The response for a redirected path, or undefined to carry on routing.
16
- *
17
- * Matching is exact on the normalised path. Prefix and pattern rules are
18
- * deliberately not supported: a redirect table is read by whoever is debugging
19
- * a URL at two in the morning, and every wildcard in it is a thing they have
20
- * to simulate in their head to know what a given path does.
14
+ /*': 'https://dash.example.com'` lands on
15
+ * `https://dash.example.com/events/42`. No regular expressions, no captures,
16
+ * no ordering to reason about.
21
17
  */
22
18
  export declare function resolveRedirect(url: URL, rules?: RedirectRules): Response | undefined;
23
19
  /**
@@ -39,6 +35,7 @@ export declare interface RedirectRule {
39
35
  to: string
40
36
  status: number
41
37
  preserveQuery: boolean
38
+ subtree: boolean
42
39
  }
43
40
  /** What an app writes in `config/server.ts` under `redirects`. */
44
41
  export type RedirectConfig = Record<string, string | RedirectTarget>;
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 from=normalizePath(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(from,{from,to,status:Number(target?.status)||DEFAULT_REDIRECT_STATUS,preserveQuery:target?.preserveQuery!==!1})}return rules}export function resolveRedirect(url,rules){if(!rules?.size)return;const rule=rules.get(normalizePath(url.pathname));if(!rule)return;let location=rule.to;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"}})}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
+ 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(", ")}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/server",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.72.19",
5
+ "version": "0.72.20",
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.72.19",
61
+ "@stacksjs/config": "0.72.20",
62
62
  "better-dx": "^0.2.23",
63
- "@stacksjs/path": "0.72.19",
64
- "@stacksjs/router": "0.72.19",
65
- "@stacksjs/validation": "0.72.19"
63
+ "@stacksjs/path": "0.72.20",
64
+ "@stacksjs/router": "0.72.20",
65
+ "@stacksjs/validation": "0.72.20"
66
66
  },
67
67
  "dependencies": {
68
68
  "bun-plugin-auto-imports": "^0.4.0"