bun-plugin-stx 0.2.83 → 0.2.85
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-sm4gbwrd.js → chunk-394anrmt.js} +12 -0
- package/dist/serve.d.ts +38 -14
- package/dist/serve.js +70 -34
- package/package.json +1 -1
|
@@ -889,9 +889,21 @@ else {
|
|
|
889
889
|
}
|
|
890
890
|
}
|
|
891
891
|
|
|
892
|
+
// A full document is only safe to partial-swap into the stx shell if it is
|
|
893
|
+
// itself stx-rendered. Pages served by another engine mounted on the same
|
|
894
|
+
// origin (e.g. a docs/blog generator) carry none of these markers; swapping
|
|
895
|
+
// their <main> + styles into the stx shell corrupts the layout. Detect them
|
|
896
|
+
// and hand off to a native full navigation instead.
|
|
897
|
+
function isStxDocument(html){
|
|
898
|
+
return html.indexOf('name="stx-layout"')!==-1
|
|
899
|
+
||html.indexOf('__stxRouterConfig')!==-1
|
|
900
|
+
||html.indexOf('data-stx-content')!==-1;
|
|
901
|
+
}
|
|
902
|
+
|
|
892
903
|
function swap(html,url,pushState,hash){
|
|
893
904
|
var isFragment=html.indexOf('<!--stx-fragment-->')===0;
|
|
894
905
|
if(isFragment)html=html.slice('<!--stx-fragment-->'.length);
|
|
906
|
+
if(!isFragment&&!isStxDocument(html)){log('[router] non-stx document \u2014 full navigation to:',url);location.href=url;return}
|
|
895
907
|
var currentContent=getContainer();
|
|
896
908
|
log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&¤tContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);
|
|
897
909
|
if(!currentContent){log('[router] no container \u2014 falling back');location.href=url;return}
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build the candidate regexes for a dynamic route file (stacksjs/stx#1927).
|
|
3
|
-
*
|
|
4
|
-
* `fileRouteBase` is the route path with the file extension already
|
|
5
|
-
* stripped (e.g. `judges/[id]/profile/index`). Returns one regex per
|
|
6
|
-
* acceptable URL shape:
|
|
7
|
-
* - the literal path with `[param]` → `([^/]+)`
|
|
8
|
-
* - the same with a trailing `/index` removed, so
|
|
9
|
-
* `judges/[id]/profile/index` also matches `judges/35/profile`
|
|
10
|
-
* (without this, nested dynamic routes 404 on hard reload)
|
|
11
|
-
* - both of the above again with a leading `pages/` stripped, for
|
|
12
|
-
* projects that nest routable views under `pages/`
|
|
13
|
-
*/
|
|
14
1
|
export declare function buildDynamicRouteRegexes(fileRouteBase: string): RegExp[];
|
|
15
2
|
/**
|
|
16
3
|
* Are we serving in production? In production the built-in 404 must not
|
|
@@ -55,6 +42,42 @@ export declare function bundleBrowserAsset(entrypoint: string): Promise<Response
|
|
|
55
42
|
* @param options Server options with patterns and port
|
|
56
43
|
*/
|
|
57
44
|
export declare function serve(options: ServeOptions): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Build the candidate regexes for a dynamic route file (stacksjs/stx#1927).
|
|
47
|
+
*
|
|
48
|
+
* `fileRouteBase` is the route path with the file extension already
|
|
49
|
+
* stripped (e.g. `judges/[id]/profile/index`). Returns one regex per
|
|
50
|
+
* acceptable URL shape:
|
|
51
|
+
* - the literal path with `[param]` → `([^/]+)`
|
|
52
|
+
* - the same with a trailing `/index` removed, so
|
|
53
|
+
* `judges/[id]/profile/index` also matches `judges/35/profile`
|
|
54
|
+
* (without this, nested dynamic routes 404 on hard reload)
|
|
55
|
+
* - both of the above again with a leading `pages/` stripped, for
|
|
56
|
+
* projects that nest routable views under `pages/`
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* Snapshot of the in-flight request, threaded from the fetch handler down to
|
|
60
|
+
* the `<script server>` render (stacksjs/stacks#1967).
|
|
61
|
+
*
|
|
62
|
+
* The module-level `activeServe*` singletons are NOT safe to read at render
|
|
63
|
+
* time: a render can suspend at an `await` (onRequest hooks, DB queries in
|
|
64
|
+
* server scripts) while a concurrent request — the HMR EventSource opens on
|
|
65
|
+
* every page load — runs its own handler and resets the singletons in its
|
|
66
|
+
* `finally`. Threading an immutable per-request snapshot removes that race;
|
|
67
|
+
* the singletons remain only as a fallback for legacy call sites.
|
|
68
|
+
*/
|
|
69
|
+
export declare interface ServeRequestContext {
|
|
70
|
+
url: string
|
|
71
|
+
path: string
|
|
72
|
+
search: string
|
|
73
|
+
host: string
|
|
74
|
+
cookieHeader: string
|
|
75
|
+
cookies: Record<string, string>
|
|
76
|
+
ip: string
|
|
77
|
+
locale: string | null
|
|
78
|
+
params: Record<string, string>
|
|
79
|
+
[key: string]: unknown
|
|
80
|
+
}
|
|
58
81
|
export declare interface ServeOptions {
|
|
59
82
|
patterns: string[]
|
|
60
83
|
port?: number
|
|
@@ -71,9 +94,10 @@ export declare interface ServeOptions {
|
|
|
71
94
|
routes?: Record<string, (req: Request) => Response | Promise<Response>>
|
|
72
95
|
onRequest?: (req: Request) =>
|
|
73
96
|
| Response
|
|
97
|
+
| Record<string, unknown>
|
|
74
98
|
| null
|
|
75
99
|
| undefined
|
|
76
|
-
| Promise<Response | null | undefined>
|
|
100
|
+
| Promise<Response | Record<string, unknown> | null | undefined>
|
|
77
101
|
middleware?: Record<string, MiddlewareHandler>
|
|
78
102
|
globalMiddleware?: string[]
|
|
79
103
|
middlewareGroups?: Record<string, string[]>
|
package/dist/serve.js
CHANGED
|
@@ -9133,16 +9133,31 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9133
9133
|
return typeof translate === "function" ? translate(key, values, loc) : key;
|
|
9134
9134
|
};
|
|
9135
9135
|
}
|
|
9136
|
-
function injectServeRequestContext(context) {
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9136
|
+
function injectServeRequestContext(context, reqCtx) {
|
|
9137
|
+
const search = reqCtx ? reqCtx.search : activeServeSearch;
|
|
9138
|
+
const host = reqCtx ? reqCtx.host : activeServeHost;
|
|
9139
|
+
const cookies = reqCtx ? reqCtx.cookies : activeServeCookies;
|
|
9140
|
+
const ip = reqCtx ? reqCtx.ip : activeServeIp;
|
|
9141
|
+
context.__stxServeSearch = search;
|
|
9142
|
+
globalThis.__stxServeSearch = search;
|
|
9143
|
+
if (host)
|
|
9144
|
+
context.host = host;
|
|
9145
|
+
context.cookies = cookies;
|
|
9146
|
+
if (ip)
|
|
9147
|
+
context.ip = ip;
|
|
9148
|
+
const full = reqCtx ?? {
|
|
9149
|
+
url: "",
|
|
9150
|
+
path: "",
|
|
9151
|
+
search,
|
|
9152
|
+
host,
|
|
9153
|
+
cookieHeader: activeServeCookieHeader,
|
|
9154
|
+
cookies,
|
|
9155
|
+
ip,
|
|
9156
|
+
locale: activeServeLocale,
|
|
9157
|
+
params: {}
|
|
9158
|
+
};
|
|
9159
|
+
context.__stxServeContext = full;
|
|
9160
|
+
globalThis.__stxServeContext = full;
|
|
9146
9161
|
}
|
|
9147
9162
|
function htmlCacheKey(filePath) {
|
|
9148
9163
|
const loc = i18nConfig ? activeServeLocale ?? i18nConfig.defaultLocale : "";
|
|
@@ -9260,7 +9275,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9260
9275
|
pageMiddlewarePatterns.length = 0;
|
|
9261
9276
|
await Promise.all(files.map((f) => detectPageMiddleware(f)));
|
|
9262
9277
|
try {
|
|
9263
|
-
const { Router } = await import("./chunk-
|
|
9278
|
+
const { Router } = await import("./chunk-394anrmt.js");
|
|
9264
9279
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
9265
9280
|
const router = new Router(process19.cwd(), { pagesDirs });
|
|
9266
9281
|
if (!options.quiet)
|
|
@@ -9428,7 +9443,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9428
9443
|
return "";
|
|
9429
9444
|
}
|
|
9430
9445
|
}
|
|
9431
|
-
async function processTemplate(filePath) {
|
|
9446
|
+
async function processTemplate(filePath, reqCtx) {
|
|
9432
9447
|
const content = await Bun.file(filePath).text();
|
|
9433
9448
|
const skipCacheHint = /(?:^|[^\w$])__stx_skip_cache\s*=\s*true/.test(content);
|
|
9434
9449
|
if (ENABLE_HTML_CACHE && !skipCacheHint) {
|
|
@@ -9451,7 +9466,8 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9451
9466
|
const { processDirectives, extractVariables, defaultConfig: defaultConfig4, generateSpaShell, injectRouterScript, resetHead } = await stxModule;
|
|
9452
9467
|
resetHead();
|
|
9453
9468
|
injectServeLocaleContext(context);
|
|
9454
|
-
injectServeRequestContext(context);
|
|
9469
|
+
injectServeRequestContext(context, reqCtx);
|
|
9470
|
+
context.params = reqCtx?.params ?? {};
|
|
9455
9471
|
for (const scriptBody of serverScripts) {
|
|
9456
9472
|
await extractVariables(scriptBody, context, filePath);
|
|
9457
9473
|
}
|
|
@@ -9507,7 +9523,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9507
9523
|
}
|
|
9508
9524
|
return output;
|
|
9509
9525
|
}
|
|
9510
|
-
async function getRoute(requestPath) {
|
|
9526
|
+
async function getRoute(requestPath, reqCtx) {
|
|
9511
9527
|
const files = await discoverFiles();
|
|
9512
9528
|
let normalizedPath = requestPath.startsWith("/") ? requestPath.slice(1) : requestPath;
|
|
9513
9529
|
const possibleFiles = [];
|
|
@@ -9542,7 +9558,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9542
9558
|
}
|
|
9543
9559
|
}
|
|
9544
9560
|
if (normalizedFilePath === possible || relativeFilePath === possible) {
|
|
9545
|
-
const output = await processTemplate(filePath);
|
|
9561
|
+
const output = await processTemplate(filePath, reqCtx);
|
|
9546
9562
|
return output;
|
|
9547
9563
|
}
|
|
9548
9564
|
}
|
|
@@ -9561,14 +9577,14 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9561
9577
|
const isIndexFile = fileRoute === "index" || fileRoute.endsWith("/index");
|
|
9562
9578
|
const isRootRequest = requestPath === "/" || normalizedPath === "";
|
|
9563
9579
|
if (`/${fileRoute}` === requestPath || fileRoute === normalizedPath || `/${fileRoute}.stx` === requestPath || `/${fileRoute}.md` === requestPath || `/${fileRoute}.html` === requestPath || isIndexFile && isRootRequest) {
|
|
9564
|
-
const output = await processTemplate(filePath);
|
|
9580
|
+
const output = await processTemplate(filePath, reqCtx);
|
|
9565
9581
|
routes.set(requestPath, output);
|
|
9566
9582
|
return output;
|
|
9567
9583
|
}
|
|
9568
9584
|
if (fileRoute.startsWith("pages/")) {
|
|
9569
9585
|
const prettyRoute = fileRoute.slice(6);
|
|
9570
9586
|
if (`/${prettyRoute}` === requestPath || prettyRoute === normalizedPath) {
|
|
9571
|
-
const output = await processTemplate(filePath);
|
|
9587
|
+
const output = await processTemplate(filePath, reqCtx);
|
|
9572
9588
|
routes.set(requestPath, output);
|
|
9573
9589
|
return output;
|
|
9574
9590
|
}
|
|
@@ -9593,7 +9609,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9593
9609
|
if (match) {
|
|
9594
9610
|
const paramNames = [...fileRouteBase.matchAll(/\[([^\]]+)\]/g)].map((m) => m[1]);
|
|
9595
9611
|
const paramValues = match.slice(1);
|
|
9596
|
-
const output = await processTemplateDynamic(filePath, paramNames, paramValues, normalizedPath);
|
|
9612
|
+
const output = await processTemplateDynamic(filePath, paramNames, paramValues, normalizedPath, reqCtx);
|
|
9597
9613
|
routes.set(requestPath, output);
|
|
9598
9614
|
return output;
|
|
9599
9615
|
}
|
|
@@ -9601,7 +9617,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9601
9617
|
}
|
|
9602
9618
|
return null;
|
|
9603
9619
|
}
|
|
9604
|
-
async function processTemplateDynamic(filePath, paramNames, paramValues, routePath) {
|
|
9620
|
+
async function processTemplateDynamic(filePath, paramNames, paramValues, routePath, reqCtx) {
|
|
9605
9621
|
const content = await Bun.file(filePath).text();
|
|
9606
9622
|
const serverScriptRegex = /<script\b([^>]*)\bserver\b[^>]*>([\s\S]*?)<\/script>/gi;
|
|
9607
9623
|
const dynServerScripts = [];
|
|
@@ -9615,13 +9631,24 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9615
9631
|
__dirname: nodePath.dirname(filePath),
|
|
9616
9632
|
__route: routePath
|
|
9617
9633
|
};
|
|
9634
|
+
const paramsObj = {};
|
|
9618
9635
|
for (let i = 0;i < paramNames.length; i++) {
|
|
9619
|
-
|
|
9636
|
+
let value = paramValues[i] ?? "";
|
|
9637
|
+
try {
|
|
9638
|
+
value = decodeURIComponent(value);
|
|
9639
|
+
} catch {}
|
|
9640
|
+
paramsObj[paramNames[i]] = value;
|
|
9620
9641
|
}
|
|
9642
|
+
for (const name of paramNames) {
|
|
9643
|
+
context[name] = paramsObj[name];
|
|
9644
|
+
}
|
|
9645
|
+
context.params = paramsObj;
|
|
9646
|
+
if (reqCtx)
|
|
9647
|
+
reqCtx.params = paramsObj;
|
|
9621
9648
|
const { processDirectives, extractVariables, defaultConfig: defaultConfig4, injectRouterScript: injectRouter, resetHead: resetHeadDyn } = await stxModule;
|
|
9622
9649
|
resetHeadDyn();
|
|
9623
9650
|
injectServeLocaleContext(context);
|
|
9624
|
-
injectServeRequestContext(context);
|
|
9651
|
+
injectServeRequestContext(context, reqCtx);
|
|
9625
9652
|
for (const scriptBody of dynServerScripts) {
|
|
9626
9653
|
await extractVariables(scriptBody, context, filePath);
|
|
9627
9654
|
}
|
|
@@ -9651,10 +9678,6 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9651
9678
|
const dependencies = new Set;
|
|
9652
9679
|
output = await processDirectives(output, context, filePath, config5, dependencies);
|
|
9653
9680
|
if (paramNames.length > 0) {
|
|
9654
|
-
const paramsObj = {};
|
|
9655
|
-
for (let i = 0;i < paramNames.length; i++) {
|
|
9656
|
-
paramsObj[paramNames[i]] = paramValues[i];
|
|
9657
|
-
}
|
|
9658
9681
|
const paramsScript = `<script>if(window.stx)window.stx._rp=${JSON.stringify(paramsObj)};else window.__stx_rp=${JSON.stringify(paramsObj)};</script>`;
|
|
9659
9682
|
if (output.includes("</head>")) {
|
|
9660
9683
|
output = output.replace("</head>", `${paramsScript}
|
|
@@ -9846,6 +9869,17 @@ ${html}`;
|
|
|
9846
9869
|
activeServeCookieHeader = req.headers.get("cookie") || "";
|
|
9847
9870
|
activeServeCookies = parseCookies(req);
|
|
9848
9871
|
activeServeIp = server.requestIP(req)?.address || (req.headers.get("x-forwarded-for") || "").split(",")[0].trim();
|
|
9872
|
+
const reqCtx = {
|
|
9873
|
+
url: req.url,
|
|
9874
|
+
path,
|
|
9875
|
+
search: activeServeSearch,
|
|
9876
|
+
host: activeServeHost,
|
|
9877
|
+
cookieHeader: activeServeCookieHeader,
|
|
9878
|
+
cookies: activeServeCookies,
|
|
9879
|
+
ip: activeServeIp,
|
|
9880
|
+
locale: activeServeLocale,
|
|
9881
|
+
params: {}
|
|
9882
|
+
};
|
|
9849
9883
|
try {
|
|
9850
9884
|
if (req.method === "OPTIONS") {
|
|
9851
9885
|
return new Response(null, { headers: corsHeaders });
|
|
@@ -9866,7 +9900,7 @@ ${html}`;
|
|
|
9866
9900
|
|
|
9867
9901
|
`));
|
|
9868
9902
|
} catch {}
|
|
9869
|
-
},
|
|
9903
|
+
}, 5000);
|
|
9870
9904
|
},
|
|
9871
9905
|
cancel() {
|
|
9872
9906
|
if (keepalive)
|
|
@@ -9885,9 +9919,11 @@ ${html}`;
|
|
|
9885
9919
|
});
|
|
9886
9920
|
}
|
|
9887
9921
|
if (options.onRequest) {
|
|
9888
|
-
const
|
|
9889
|
-
if (
|
|
9890
|
-
return
|
|
9922
|
+
const hookResult = await options.onRequest(req);
|
|
9923
|
+
if (hookResult instanceof Response)
|
|
9924
|
+
return hookResult;
|
|
9925
|
+
if (hookResult && typeof hookResult === "object")
|
|
9926
|
+
Object.assign(reqCtx, hookResult);
|
|
9891
9927
|
}
|
|
9892
9928
|
const route = resolveRouteMiddleware(path);
|
|
9893
9929
|
const extraNames = extraProtectedPrefixes.some((p) => path === p || path.startsWith(p.endsWith("/") ? p : `${p}/`)) ? ["auth"] : [];
|
|
@@ -10030,9 +10066,9 @@ ${html}`;
|
|
|
10030
10066
|
if (path === "/index")
|
|
10031
10067
|
path = "/";
|
|
10032
10068
|
if (path === "/") {
|
|
10033
|
-
const indexContent = await getRoute("/");
|
|
10069
|
+
const indexContent = await getRoute("/", reqCtx);
|
|
10034
10070
|
if (!indexContent) {
|
|
10035
|
-
const homeContent = await getRoute("/home");
|
|
10071
|
+
const homeContent = await getRoute("/home", reqCtx);
|
|
10036
10072
|
if (homeContent) {
|
|
10037
10073
|
return new Response(null, {
|
|
10038
10074
|
status: 302,
|
|
@@ -10044,7 +10080,7 @@ ${html}`;
|
|
|
10044
10080
|
}
|
|
10045
10081
|
}
|
|
10046
10082
|
}
|
|
10047
|
-
const content = await getRoute(path);
|
|
10083
|
+
const content = await getRoute(path, reqCtx);
|
|
10048
10084
|
if (content) {
|
|
10049
10085
|
const isSpaNav = req.headers.get("X-STX-Router") === "true";
|
|
10050
10086
|
if (isSpaNav) {
|
|
@@ -10140,7 +10176,7 @@ ${pageSetupScripts.join(`
|
|
|
10140
10176
|
}
|
|
10141
10177
|
});
|
|
10142
10178
|
}
|
|
10143
|
-
const contentWithExt = await getRoute(`${path}.html
|
|
10179
|
+
const contentWithExt = await getRoute(`${path}.html`, reqCtx);
|
|
10144
10180
|
if (contentWithExt) {
|
|
10145
10181
|
return new Response(injectHmrClient(contentWithExt), {
|
|
10146
10182
|
headers: {
|
|
@@ -10231,7 +10267,7 @@ ${pageSetupScripts.join(`
|
|
|
10231
10267
|
const isProd = isProductionServe();
|
|
10232
10268
|
for (const custom404 of ["/404", "/errors/404"]) {
|
|
10233
10269
|
try {
|
|
10234
|
-
const customContent = await getRoute(custom404);
|
|
10270
|
+
const customContent = await getRoute(custom404, reqCtx);
|
|
10235
10271
|
if (customContent) {
|
|
10236
10272
|
return new Response(isProd ? customContent : injectHmrClient(customContent), {
|
|
10237
10273
|
status: 404,
|
package/package.json
CHANGED