bun-plugin-stx 0.2.410 → 0.2.412
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/{chunk-51r5j16q.js → chunk-g7br5ehx.js} +17 -2
- package/dist/domain-routes.d.ts +27 -1
- package/dist/serve.d.ts +2 -2
- package/dist/serve.js +37 -4
- package/package.json +3 -3
|
@@ -1113,7 +1113,7 @@ function routerSource() {
|
|
|
1113
1113
|
// whole fix \u2014 the click then goes to the network, where navigate does
|
|
1114
1114
|
// the reload properly.
|
|
1115
1115
|
function readPrefetchResponse(r,wantsFragment){
|
|
1116
|
-
if(r.redirected)return Promise.resolve(null);
|
|
1116
|
+
if(r.redirected||r.headers.get('X-STX-Location'))return Promise.resolve(null);
|
|
1117
1117
|
if(isBuildSkew(r.headers.get('X-STX-Build')||''))return Promise.resolve(null);
|
|
1118
1118
|
var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
|
|
1119
1119
|
var layout=r.headers.get('X-STX-Layout')||'';
|
|
@@ -1642,6 +1642,18 @@ function routerSource() {
|
|
|
1642
1642
|
// swap() through pendingCtx, the same way pendingContainerAttrs is.
|
|
1643
1643
|
var navCtxNext=null;
|
|
1644
1644
|
var pendingCtx=null;
|
|
1645
|
+
// The page belongs to another origin: the server says so with
|
|
1646
|
+
// X-STX-Location (it cannot redirect a router fetch there, because a
|
|
1647
|
+
// cross-origin redirect of a request carrying X-STX-Router fails CORS), or
|
|
1648
|
+
// a plain fetch was redirected off-origin. Either way the browser goes
|
|
1649
|
+
// there itself; a fragment from another origin is never swapped in.
|
|
1650
|
+
function leaveOrigin(r){
|
|
1651
|
+
var away=r.headers.get('X-STX-Location');
|
|
1652
|
+
if(!away&&r.redirected&&r.url&&new URL(r.url,location.href).origin!==location.origin)away=r.url;
|
|
1653
|
+
if(!away)return false;
|
|
1654
|
+
location.href=away;
|
|
1655
|
+
return true;
|
|
1656
|
+
}
|
|
1645
1657
|
function routerFetch(url,headers){
|
|
1646
1658
|
var init={headers:headers};
|
|
1647
1659
|
if(navAbort)init.signal=navAbort.signal;
|
|
@@ -1733,10 +1745,11 @@ function routerSource() {
|
|
|
1733
1745
|
// Layout changed \u2014 fetch full page and do full document swap
|
|
1734
1746
|
log('[router] cache hit but layout changed \u2014 fetching full page');
|
|
1735
1747
|
return routerFetch(url,{'Accept':'text/html'}).then(function(r){
|
|
1748
|
+
if(leaveOrigin(r))return null;
|
|
1736
1749
|
if(!r.ok)throw httpError(r.status);
|
|
1737
1750
|
return r.text();
|
|
1738
1751
|
}).then(function(html){
|
|
1739
|
-
if(stale())return false;
|
|
1752
|
+
if(html===null||stale())return false;
|
|
1740
1753
|
log('[router] full page fetched from cache path, len:',html.length);
|
|
1741
1754
|
pendingLayoutDecl={layout:layoutCache[targetPath]||'',group:layoutGroupCache[targetPath]||''};
|
|
1742
1755
|
return swapWith(html,targetPath);
|
|
@@ -1761,6 +1774,7 @@ else {
|
|
|
1761
1774
|
var wantsFragment=shouldUseFragmentResponse();
|
|
1762
1775
|
return routerFetch(url,wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}).then(function(r){
|
|
1763
1776
|
if(stale())return null;
|
|
1777
|
+
if(leaveOrigin(r))return null;
|
|
1764
1778
|
if(!r.ok)throw httpError(r.status);
|
|
1765
1779
|
// A route guard answered with a redirect and fetch followed it
|
|
1766
1780
|
// transparently, so r.ok is the DESTINATION's 200 and this markup
|
|
@@ -1794,6 +1808,7 @@ else {
|
|
|
1794
1808
|
log('[router] layout change \u2014 fetching full page for document swap');
|
|
1795
1809
|
return routerFetch(url,{'Accept':'text/html'}).then(function(fullRes){
|
|
1796
1810
|
log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);
|
|
1811
|
+
if(leaveOrigin(fullRes))return null;
|
|
1797
1812
|
if(!fullRes.ok)throw httpError(fullRes.status);
|
|
1798
1813
|
return fullRes.text().then(function(html){
|
|
1799
1814
|
log('[router] full page html length:',html.length);
|
package/dist/domain-routes.d.ts
CHANGED
|
@@ -14,12 +14,23 @@ export declare function matchDomain(host: string, route: DomainRoute): Record<st
|
|
|
14
14
|
* the root).
|
|
15
15
|
*/
|
|
16
16
|
export declare function resolveDomainRoute(host: string, pathname: string, routes: DomainRoute[]): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Where a request for another page on a routed host belongs, or null to
|
|
19
|
+
* answer it in place.
|
|
20
|
+
*
|
|
21
|
+
* Only for a route that names `elsewhere`, only for a host that matches it,
|
|
22
|
+
* only for paths other than the root, and only for page requests: `isAsset`
|
|
23
|
+
* is the server's own test for scripts, styles, `/api` and `public/` files,
|
|
24
|
+
* which stay where the page at the root can load them.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveDomainElsewhere(host: string, pathname: string, search: string, routes: DomainRoute[], isAsset: (pathname: string) => boolean): string | null;
|
|
17
27
|
/** One line for boot output: `{username}.example.com/ → /{username}`. */
|
|
18
28
|
export declare function describeDomainRoutes(routes: DomainRoute[]): string;
|
|
19
29
|
export declare interface DomainRoute {
|
|
20
30
|
pattern: string
|
|
21
31
|
labels: Label[]
|
|
22
32
|
to: string
|
|
33
|
+
elsewhere?: string
|
|
23
34
|
literals: number
|
|
24
35
|
}
|
|
25
36
|
/**
|
|
@@ -45,6 +56,21 @@ export declare interface DomainRoute {
|
|
|
45
56
|
* site's own, answered as it would be on the main domain, so one page can be
|
|
46
57
|
* given a domain without the rest of the site breaking under it.
|
|
47
58
|
*
|
|
59
|
+
* Unless the route says where the rest of the site lives:
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* domains: {
|
|
63
|
+
* '{username}.example.com': { to: '/{username}', elsewhere: 'https://example.com' },
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* Then a request for any other PAGE on that host is redirected there
|
|
68
|
+
* (`chris.example.com/pricing` → `example.com/pricing`), while its assets,
|
|
69
|
+
* `/api` and `public/` files are still answered in place, because the page
|
|
70
|
+
* at its root needs them. A browser keeps a session per origin: without
|
|
71
|
+
* this, signing in from a profile's own domain signs in to that domain, and
|
|
72
|
+
* the visitor ends up using the whole app under someone else's name.
|
|
73
|
+
*
|
|
48
74
|
* A pattern is a hostname whose labels are either literal or `{name}`, which
|
|
49
75
|
* matches exactly one DNS label (`{username}.example.com` matches
|
|
50
76
|
* `chris.example.com`, not `a.b.example.com` and not the bare apex). Literal
|
|
@@ -59,4 +85,4 @@ export declare interface DomainRoute {
|
|
|
59
85
|
*/
|
|
60
86
|
/** One label of a compiled pattern. */
|
|
61
87
|
declare type Label = { literal: string } | { param: string }
|
|
62
|
-
export type DomainRoutesOption = Record<string, string>;
|
|
88
|
+
export type DomainRoutesOption = Record<string, string | { to: string, elsewhere?: string }>;
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { serve as bunServe } from 'bun';
|
|
2
|
-
import { compileDomainRoutes, describeDomainRoutes, requestHost, resolveDomainRoute } from './domain-routes';
|
|
2
|
+
import { compileDomainRoutes, describeDomainRoutes, requestHost, resolveDomainElsewhere, resolveDomainRoute } from './domain-routes';
|
|
3
3
|
import type { DomainRoutesOption } from './domain-routes';
|
|
4
4
|
import type { MiddlewareContext, MiddlewareHandler, MiddlewareRequest, PageMiddleware, PrepareMiddlewareRequest } from './page-middleware';
|
|
5
5
|
export type { DomainRoute, DomainRoutesOption } from './domain-routes';
|
|
@@ -392,4 +392,4 @@ export declare interface ServeOptions {
|
|
|
392
392
|
declare type CssEngine = Pick<typeof import('@stacksjs/ts-css/engine'), 'CSSGenerator' | 'config'>;
|
|
393
393
|
/** The Bun server `serve()` started - handed to `onRequest` beside each request. */
|
|
394
394
|
export type ServeServer = ReturnType<typeof bunServe>;
|
|
395
|
-
export { compileDomainRoutes, describeDomainRoutes, matchDomain, requestHost, resolveDomainRoute } from './domain-routes';
|
|
395
|
+
export { compileDomainRoutes, describeDomainRoutes, matchDomain, requestHost, resolveDomainElsewhere, resolveDomainRoute } from './domain-routes';
|
package/dist/serve.js
CHANGED
|
@@ -8728,9 +8728,22 @@ var LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
|
8728
8728
|
var PARAM = /^\{([A-Za-z_]\w*)\}$/;
|
|
8729
8729
|
function compileDomainRoutes(input, warn = () => {}) {
|
|
8730
8730
|
const routes = [];
|
|
8731
|
-
for (const [rawPattern,
|
|
8731
|
+
for (const [rawPattern, target] of Object.entries(input ?? {})) {
|
|
8732
8732
|
const pattern = String(rawPattern).trim().toLowerCase().replace(/\.$/, "");
|
|
8733
|
+
const rawTo = typeof target === "object" && target ? target.to : target;
|
|
8733
8734
|
const to = String(rawTo ?? "").trim();
|
|
8735
|
+
let elsewhere;
|
|
8736
|
+
if (typeof target === "object" && target?.elsewhere) {
|
|
8737
|
+
try {
|
|
8738
|
+
const url = new URL(String(target.elsewhere));
|
|
8739
|
+
if (url.protocol === "https:" || url.protocol === "http:")
|
|
8740
|
+
elsewhere = url.origin;
|
|
8741
|
+
} catch {}
|
|
8742
|
+
if (!elsewhere) {
|
|
8743
|
+
warn(`[stx] domain route "${rawPattern}" ignored: elsewhere "${target.elsewhere}" is not an http(s) origin`);
|
|
8744
|
+
continue;
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8734
8747
|
const parts = pattern.split(".");
|
|
8735
8748
|
if (parts.length < 2 || !to.startsWith("/")) {
|
|
8736
8749
|
warn(`[stx] domain route "${rawPattern}" -> "${rawTo}" ignored: a route is a hostname with a dot and a path starting with "/"`);
|
|
@@ -8755,7 +8768,7 @@ function compileDomainRoutes(input, warn = () => {}) {
|
|
|
8755
8768
|
warn(`[stx] domain route "${rawPattern}" -> "${rawTo}" ignored: ${!valid ? "a label is neither a hostname label nor {name}" : `the path uses {${missing.join("}, {")}} which the hostname does not capture`}`);
|
|
8756
8769
|
continue;
|
|
8757
8770
|
}
|
|
8758
|
-
routes.push({ pattern, labels, to, literals: labels.filter((label) => ("literal" in label)).length });
|
|
8771
|
+
routes.push({ pattern, labels, to, ...elsewhere ? { elsewhere } : {}, literals: labels.filter((label) => ("literal" in label)).length });
|
|
8759
8772
|
}
|
|
8760
8773
|
return routes.sort((a, b) => b.literals - a.literals || b.labels.length - a.labels.length);
|
|
8761
8774
|
}
|
|
@@ -8796,8 +8809,20 @@ function resolveDomainRoute(host, pathname, routes) {
|
|
|
8796
8809
|
}
|
|
8797
8810
|
return null;
|
|
8798
8811
|
}
|
|
8812
|
+
function resolveDomainElsewhere(host, pathname, search, routes, isAsset) {
|
|
8813
|
+
if (!routes.length || pathname === "/" || pathname === "" || pathname === "/index")
|
|
8814
|
+
return null;
|
|
8815
|
+
for (const route of routes) {
|
|
8816
|
+
if (!matchDomain(host, route))
|
|
8817
|
+
continue;
|
|
8818
|
+
if (!route.elsewhere || isAsset(pathname))
|
|
8819
|
+
return null;
|
|
8820
|
+
return `${route.elsewhere}${pathname}${search}`;
|
|
8821
|
+
}
|
|
8822
|
+
return null;
|
|
8823
|
+
}
|
|
8799
8824
|
function describeDomainRoutes(routes) {
|
|
8800
|
-
return routes.map((route) => `${route.pattern}/ \u2192 ${route.to}`).join(", ");
|
|
8825
|
+
return routes.map((route) => `${route.pattern}/ \u2192 ${route.to}${route.elsewhere ? ` (other pages: ${route.elsewhere})` : ""}`).join(", ");
|
|
8801
8826
|
}
|
|
8802
8827
|
// src/page-middleware.ts
|
|
8803
8828
|
var DEFAULT_PRIORITY = 10;
|
|
@@ -9904,7 +9929,7 @@ function __stxOverlay(errs){
|
|
|
9904
9929
|
rebuildDiscoveredPagePaths(kept);
|
|
9905
9930
|
await indexPageMiddleware(kept);
|
|
9906
9931
|
try {
|
|
9907
|
-
const { Router } = await import("./chunk-
|
|
9932
|
+
const { Router } = await import("./chunk-g7br5ehx.js");
|
|
9908
9933
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
9909
9934
|
const router = new Router(process19.cwd(), { pagesDirs, stateDir: stateDirName(), exclude: options.exclude });
|
|
9910
9935
|
if (!options.quiet)
|
|
@@ -10577,6 +10602,13 @@ ${block}`;
|
|
|
10577
10602
|
const headers = new Headers(req.headers);
|
|
10578
10603
|
headers.set("x-stx-domain-route", requestHost(req));
|
|
10579
10604
|
req = new Request(original, { headers, method: req.method, body: req.body, redirect: req.redirect, duplex: "half" });
|
|
10605
|
+
} else if (req.method === "GET" || req.method === "HEAD") {
|
|
10606
|
+
const location = resolveDomainElsewhere(requestHost(req), original.pathname, original.search, domainRoutes, (pathname) => pathname.startsWith("/api/") || pathname.startsWith("/_stx/") || pathname.startsWith("/.well-known/") || isStaticAssetPath(pathname) || publicFileExists(pathname, publicDir));
|
|
10607
|
+
if (location) {
|
|
10608
|
+
if (req.headers.get(SPA_NAV_HEADER) === "true")
|
|
10609
|
+
return new Response(null, { status: 200, headers: { "X-STX-Location": location, "Cache-Control": "no-store", Vary: SPA_NAV_HEADER } });
|
|
10610
|
+
return new Response(null, { status: 301, headers: { Location: location } });
|
|
10611
|
+
}
|
|
10580
10612
|
}
|
|
10581
10613
|
}
|
|
10582
10614
|
const url = new URL(req.url);
|
|
@@ -11468,6 +11500,7 @@ export {
|
|
|
11468
11500
|
resolveServeOpenUrl,
|
|
11469
11501
|
resolveImageWarmupBindBudget,
|
|
11470
11502
|
resolveDomainRoute,
|
|
11503
|
+
resolveDomainElsewhere,
|
|
11471
11504
|
requestHost,
|
|
11472
11505
|
render404Page,
|
|
11473
11506
|
publicFileExists,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-plugin-stx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.412",
|
|
5
5
|
"description": "A Bun plugin that allows for using Laravel Blade-like syntax.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"typecheck": "bun tsc --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@stacksjs/stx": "0.2.
|
|
78
|
-
"stx-router": "0.2.
|
|
77
|
+
"@stacksjs/stx": "0.2.412",
|
|
78
|
+
"stx-router": "0.2.412"
|
|
79
79
|
}
|
|
80
80
|
}
|