bun-plugin-stx 0.2.169 → 0.2.171
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-z66h8w4a.js → chunk-ebg5468e.js} +1 -1
- package/dist/serve.js +577 -613
- package/package.json +1 -1
package/dist/serve.js
CHANGED
|
@@ -8721,6 +8721,7 @@ var defaultGeneratedDir4 = /* @__PURE__ */ resolve12(process20.cwd(), "src/gener
|
|
|
8721
8721
|
import { BUILD_ID_HEADER, extractPageResponseStatus, findContainerRegion, getBuildId, mergeCrosswindConfig, stateDir, stateDirName } from "@stacksjs/stx";
|
|
8722
8722
|
import { buildCodeFrame, locateFailureLine } from "@stacksjs/stx/build-message";
|
|
8723
8723
|
import { clearBundleFailures, getBundleFailures } from "@stacksjs/stx/client-script-bundler";
|
|
8724
|
+
import { compressResponse, runPageAction as sharedRunPageAction } from "@stacksjs/stx";
|
|
8724
8725
|
var defaultStxModule = import("@stacksjs/stx");
|
|
8725
8726
|
var REACTIVE_TEMPLATE_DIRECTIVE_RE = /@for|:for|@if|:if|@else|:else|x-for|x-if|x-else/;
|
|
8726
8727
|
function extractServerScriptsFromTemplate(content) {
|
|
@@ -9379,6 +9380,7 @@ function __stxOverlay(errs){
|
|
|
9379
9380
|
const full = reqCtx ?? {
|
|
9380
9381
|
url: "",
|
|
9381
9382
|
path: "",
|
|
9383
|
+
method: "GET",
|
|
9382
9384
|
search,
|
|
9383
9385
|
host,
|
|
9384
9386
|
cookieHeader: activeServeCookieHeader,
|
|
@@ -9520,7 +9522,7 @@ function __stxOverlay(errs){
|
|
|
9520
9522
|
pageMiddlewarePatterns.length = 0;
|
|
9521
9523
|
await Promise.all(files.map((f) => detectPageMiddleware(f)));
|
|
9522
9524
|
try {
|
|
9523
|
-
const { Router } = await import("./chunk-
|
|
9525
|
+
const { Router } = await import("./chunk-ebg5468e.js");
|
|
9524
9526
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
9525
9527
|
const router = new Router(process19.cwd(), { pagesDirs, stateDir: stateDirName() });
|
|
9526
9528
|
if (!options.quiet)
|
|
@@ -9700,57 +9702,17 @@ function __stxOverlay(errs){
|
|
|
9700
9702
|
return "";
|
|
9701
9703
|
}
|
|
9702
9704
|
}
|
|
9703
|
-
async function parseFormBody(request) {
|
|
9704
|
-
const type = request.headers.get("content-type") || "";
|
|
9705
|
-
const form = {};
|
|
9706
|
-
const put = (key, value) => {
|
|
9707
|
-
const existing = form[key];
|
|
9708
|
-
if (existing === undefined)
|
|
9709
|
-
form[key] = value;
|
|
9710
|
-
else if (Array.isArray(existing))
|
|
9711
|
-
existing.push(value);
|
|
9712
|
-
else
|
|
9713
|
-
form[key] = [existing, value];
|
|
9714
|
-
};
|
|
9715
|
-
if (type.includes("multipart/form-data")) {
|
|
9716
|
-
const data = await request.formData();
|
|
9717
|
-
for (const [key, value] of data.entries()) {
|
|
9718
|
-
if (typeof value === "string")
|
|
9719
|
-
put(key, value);
|
|
9720
|
-
}
|
|
9721
|
-
return form;
|
|
9722
|
-
}
|
|
9723
|
-
if (type.includes("application/x-www-form-urlencoded")) {
|
|
9724
|
-
for (const [key, value] of new URLSearchParams(await request.text()).entries())
|
|
9725
|
-
put(key, value);
|
|
9726
|
-
return form;
|
|
9727
|
-
}
|
|
9728
|
-
return form;
|
|
9729
|
-
}
|
|
9730
9705
|
async function runPageAction(context, reqCtx) {
|
|
9731
|
-
if (!reqCtx
|
|
9732
|
-
return;
|
|
9733
|
-
if (typeof context.action !== "function" || !reqCtx.request)
|
|
9706
|
+
if (!reqCtx)
|
|
9734
9707
|
return;
|
|
9735
|
-
|
|
9736
|
-
try {
|
|
9737
|
-
form = await parseFormBody(reqCtx.request);
|
|
9738
|
-
} catch {}
|
|
9739
|
-
const result = await context.action({
|
|
9708
|
+
const result = await sharedRunPageAction(context, {
|
|
9740
9709
|
request: reqCtx.request,
|
|
9741
|
-
|
|
9710
|
+
method: reqCtx.method,
|
|
9742
9711
|
params: reqCtx.params ?? {},
|
|
9743
|
-
query: context.query ?? {},
|
|
9744
9712
|
cookies: reqCtx.cookies ?? {}
|
|
9745
9713
|
});
|
|
9746
|
-
if (result
|
|
9747
|
-
|
|
9748
|
-
if (typeof to === "string" && to) {
|
|
9749
|
-
reqCtx.actionRedirect = to;
|
|
9750
|
-
return;
|
|
9751
|
-
}
|
|
9752
|
-
Object.assign(context, result);
|
|
9753
|
-
}
|
|
9714
|
+
if (result.redirect)
|
|
9715
|
+
reqCtx.actionRedirect = result.redirect;
|
|
9754
9716
|
}
|
|
9755
9717
|
async function processTemplate(filePath, reqCtx) {
|
|
9756
9718
|
const content = await Bun.file(filePath).text();
|
|
@@ -10136,426 +10098,427 @@ ${html}`;
|
|
|
10136
10098
|
reusePort: options.reusePort ?? false,
|
|
10137
10099
|
idleTimeout: 0,
|
|
10138
10100
|
async fetch(req, server) {
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10101
|
+
return compressResponse(req, await (async () => {
|
|
10102
|
+
const url = new URL(req.url);
|
|
10103
|
+
let path = url.pathname;
|
|
10104
|
+
const proxyRules = stxConfig.server?.proxy;
|
|
10105
|
+
if (proxyRules) {
|
|
10106
|
+
for (const [prefix, rule] of Object.entries(proxyRules)) {
|
|
10107
|
+
if (path !== prefix && !path.startsWith(prefix))
|
|
10108
|
+
continue;
|
|
10109
|
+
const ruleCfg = typeof rule === "string" ? { target: rule } : rule;
|
|
10110
|
+
const outPath = ruleCfg.rewrite ? ruleCfg.rewrite(path) : path;
|
|
10111
|
+
const target = new URL(ruleCfg.target);
|
|
10112
|
+
const outUrl = new URL(outPath + url.search, target);
|
|
10113
|
+
const headers = new Headers(req.headers);
|
|
10114
|
+
if (ruleCfg.changeOrigin !== false)
|
|
10115
|
+
headers.set("host", target.host);
|
|
10116
|
+
try {
|
|
10117
|
+
return await fetch(outUrl, {
|
|
10118
|
+
method: req.method,
|
|
10119
|
+
headers,
|
|
10120
|
+
body: req.body,
|
|
10121
|
+
redirect: "manual",
|
|
10122
|
+
...req.body ? { duplex: "half" } : {}
|
|
10123
|
+
});
|
|
10124
|
+
} catch (err) {
|
|
10125
|
+
return new Response(`[stx dev proxy] ${prefix} \u2192 ${ruleCfg.target} failed: ${err instanceof Error ? err.message : String(err)}`, { status: 502 });
|
|
10126
|
+
}
|
|
10163
10127
|
}
|
|
10164
10128
|
}
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
req = new Request(req, { headers });
|
|
10181
|
-
}
|
|
10182
|
-
}
|
|
10183
|
-
let mintedCsrfToken = null;
|
|
10184
|
-
const _i18nResp = await (async () => {
|
|
10185
|
-
activeServeLocale = i18nLocale;
|
|
10186
|
-
activeServeSearch = url.search;
|
|
10187
|
-
activeServeHost = req.headers.get("host") || "";
|
|
10188
|
-
activeServeCookieHeader = req.headers.get("cookie") || "";
|
|
10189
|
-
activeServeCookies = parseCookies(req);
|
|
10190
|
-
activeServeIp = server.requestIP(req)?.address || (req.headers.get("x-forwarded-for") || "").split(",")[0].trim();
|
|
10191
|
-
mintedCsrfToken = csrfTokenToMint(req, activeServeCookies);
|
|
10192
|
-
if (mintedCsrfToken)
|
|
10193
|
-
activeServeCookies[CSRF_COOKIE] = mintedCsrfToken;
|
|
10194
|
-
const reqCtx = {
|
|
10195
|
-
url: req.url,
|
|
10196
|
-
path,
|
|
10197
|
-
search: activeServeSearch,
|
|
10198
|
-
host: activeServeHost,
|
|
10199
|
-
cookieHeader: activeServeCookieHeader,
|
|
10200
|
-
cookies: activeServeCookies,
|
|
10201
|
-
ip: activeServeIp,
|
|
10202
|
-
locale: activeServeLocale,
|
|
10203
|
-
params: {},
|
|
10204
|
-
method: (req.method || "GET").toUpperCase(),
|
|
10205
|
-
request: req
|
|
10206
|
-
};
|
|
10207
|
-
try {
|
|
10208
|
-
if (req.method === "OPTIONS") {
|
|
10209
|
-
return new Response(null, { headers: corsHeaders });
|
|
10129
|
+
let i18nLocale = null;
|
|
10130
|
+
if (i18nConfig && !url.pathname.startsWith("/api/")) {
|
|
10131
|
+
const stripped = localeFromPath(url.pathname);
|
|
10132
|
+
i18nLocale = stripped.locale;
|
|
10133
|
+
if (stripped.path !== url.pathname) {
|
|
10134
|
+
const rewritten = new URL(req.url);
|
|
10135
|
+
rewritten.pathname = stripped.path;
|
|
10136
|
+
const headers = new Headers(req.headers);
|
|
10137
|
+
headers.set("x-stx-locale", stripped.locale);
|
|
10138
|
+
req = new Request(rewritten, { headers, method: req.method, body: req.body, redirect: req.redirect, duplex: "half" });
|
|
10139
|
+
path = stripped.path;
|
|
10140
|
+
} else if (i18nLocale) {
|
|
10141
|
+
const headers = new Headers(req.headers);
|
|
10142
|
+
headers.set("x-stx-locale", i18nLocale);
|
|
10143
|
+
req = new Request(req, { headers });
|
|
10210
10144
|
}
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10145
|
+
}
|
|
10146
|
+
let mintedCsrfToken = null;
|
|
10147
|
+
const _i18nResp = await (async () => {
|
|
10148
|
+
activeServeLocale = i18nLocale;
|
|
10149
|
+
activeServeSearch = url.search;
|
|
10150
|
+
activeServeHost = req.headers.get("host") || "";
|
|
10151
|
+
activeServeCookieHeader = req.headers.get("cookie") || "";
|
|
10152
|
+
activeServeCookies = parseCookies(req);
|
|
10153
|
+
activeServeIp = server.requestIP(req)?.address || (req.headers.get("x-forwarded-for") || "").split(",")[0].trim();
|
|
10154
|
+
mintedCsrfToken = csrfTokenToMint(req, activeServeCookies);
|
|
10155
|
+
if (mintedCsrfToken)
|
|
10156
|
+
activeServeCookies[CSRF_COOKIE] = mintedCsrfToken;
|
|
10157
|
+
const reqCtx = {
|
|
10158
|
+
url: req.url,
|
|
10159
|
+
path,
|
|
10160
|
+
search: activeServeSearch,
|
|
10161
|
+
host: activeServeHost,
|
|
10162
|
+
cookieHeader: activeServeCookieHeader,
|
|
10163
|
+
cookies: activeServeCookies,
|
|
10164
|
+
ip: activeServeIp,
|
|
10165
|
+
locale: activeServeLocale,
|
|
10166
|
+
params: {},
|
|
10167
|
+
method: (req.method || "GET").toUpperCase(),
|
|
10168
|
+
request: req
|
|
10169
|
+
};
|
|
10170
|
+
try {
|
|
10171
|
+
if (req.method === "OPTIONS") {
|
|
10172
|
+
return new Response(null, { headers: corsHeaders });
|
|
10173
|
+
}
|
|
10174
|
+
if (path === "/_stx/hmr") {
|
|
10175
|
+
let controller;
|
|
10176
|
+
let keepalive;
|
|
10177
|
+
const stream = new ReadableStream({
|
|
10178
|
+
start(c) {
|
|
10179
|
+
controller = c;
|
|
10180
|
+
hmrClients.add(c);
|
|
10181
|
+
c.enqueue(hmrEncoder.encode(`data: {"type":"connected"}
|
|
10219
10182
|
|
|
10220
10183
|
`));
|
|
10221
|
-
|
|
10222
|
-
|
|
10184
|
+
if (currentBuildErrors.length > 0) {
|
|
10185
|
+
c.enqueue(hmrEncoder.encode(`data: ${JSON.stringify({ type: "build-error", errors: currentBuildErrors })}
|
|
10223
10186
|
|
|
10224
10187
|
`));
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10188
|
+
}
|
|
10189
|
+
keepalive = setInterval(() => {
|
|
10190
|
+
try {
|
|
10191
|
+
c.enqueue(hmrEncoder.encode(`: keepalive
|
|
10229
10192
|
|
|
10230
10193
|
`));
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
"Content-Type": "application/javascript; charset=utf-8",
|
|
10256
|
-
"Cache-Control": "public, max-age=0, must-revalidate",
|
|
10257
|
-
ETag: etag,
|
|
10258
|
-
...corsHeaders
|
|
10259
|
-
};
|
|
10260
|
-
if (req.headers.get("if-none-match") === etag)
|
|
10261
|
-
return new Response(null, { status: 304, headers });
|
|
10262
|
-
if (req.method === "HEAD")
|
|
10263
|
-
return new Response(null, { headers });
|
|
10264
|
-
return new Response(content2, { headers });
|
|
10265
|
-
}
|
|
10266
|
-
if (path === "/_stx/stores.js") {
|
|
10267
|
-
const stx = await stxModule;
|
|
10268
|
-
const code = typeof stx.getStoreScript === "function" ? await stx.getStoreScript() : null;
|
|
10269
|
-
return new Response(code ?? "", {
|
|
10270
|
-
status: code === null ? 404 : 200,
|
|
10271
|
-
headers: {
|
|
10194
|
+
} catch {}
|
|
10195
|
+
}, 5000);
|
|
10196
|
+
},
|
|
10197
|
+
cancel() {
|
|
10198
|
+
if (keepalive)
|
|
10199
|
+
clearInterval(keepalive);
|
|
10200
|
+
if (controller)
|
|
10201
|
+
hmrClients.delete(controller);
|
|
10202
|
+
}
|
|
10203
|
+
});
|
|
10204
|
+
return new Response(stream, {
|
|
10205
|
+
headers: {
|
|
10206
|
+
"Content-Type": "text/event-stream",
|
|
10207
|
+
"Cache-Control": "no-cache, no-transform",
|
|
10208
|
+
Connection: "keep-alive",
|
|
10209
|
+
"X-Accel-Buffering": "no"
|
|
10210
|
+
}
|
|
10211
|
+
});
|
|
10212
|
+
}
|
|
10213
|
+
if (path === "/_stx/runtime.js" || path === "/_stx/router.js") {
|
|
10214
|
+
const stx = await stxModule;
|
|
10215
|
+
const content2 = path === "/_stx/runtime.js" ? await stx.getCachedSignalsRuntime(stxConfig.debug === true) : await stx.getCachedRouterScript();
|
|
10216
|
+
const etag = `"${Bun.hash(content2).toString(16)}"`;
|
|
10217
|
+
const headers = {
|
|
10272
10218
|
"Content-Type": "application/javascript; charset=utf-8",
|
|
10273
|
-
"Cache-Control": "
|
|
10219
|
+
"Cache-Control": "public, max-age=0, must-revalidate",
|
|
10220
|
+
ETag: etag,
|
|
10274
10221
|
...corsHeaders
|
|
10275
|
-
}
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
const content2 = stx.getCrosswindServeAsset(crosswindAsset[1]);
|
|
10282
|
-
if (content2 === undefined)
|
|
10283
|
-
return new Response("Crosswind asset not found", { status: 404 });
|
|
10284
|
-
const headers = {
|
|
10285
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
10286
|
-
"Cache-Control": "public, max-age=31536000, immutable",
|
|
10287
|
-
...corsHeaders
|
|
10288
|
-
};
|
|
10289
|
-
if (req.method === "HEAD")
|
|
10290
|
-
return new Response(null, { headers });
|
|
10291
|
-
return new Response(content2, { headers });
|
|
10292
|
-
}
|
|
10293
|
-
if (options.onRequest) {
|
|
10294
|
-
const hookResult = await options.onRequest(req);
|
|
10295
|
-
if (hookResult instanceof Response)
|
|
10296
|
-
return hookResult;
|
|
10297
|
-
if (hookResult && typeof hookResult === "object")
|
|
10298
|
-
Object.assign(reqCtx, hookResult);
|
|
10299
|
-
}
|
|
10300
|
-
const route = resolveRouteMiddleware(path);
|
|
10301
|
-
const extraNames = extraProtectedPrefixes.some((p) => path === p || path.startsWith(p.endsWith("/") ? p : `${p}/`)) ? ["auth"] : [];
|
|
10302
|
-
const requested = [...globalMiddlewareNames, ...route.names, ...extraNames];
|
|
10303
|
-
if (requested.length > 0) {
|
|
10304
|
-
const expanded = expandMiddlewareNames(requested);
|
|
10305
|
-
const cookies = parseCookies(req);
|
|
10306
|
-
const ctx = {
|
|
10307
|
-
path,
|
|
10308
|
-
url,
|
|
10309
|
-
params: route.params,
|
|
10310
|
-
cookies,
|
|
10311
|
-
redirect: (to, status = 302) => {
|
|
10312
|
-
const sep = to.includes("?") ? "&" : "?";
|
|
10313
|
-
const next = encodeURIComponent(path + (url.search || ""));
|
|
10314
|
-
return new Response(null, {
|
|
10315
|
-
status,
|
|
10316
|
-
headers: { Location: `${to}${sep}next=${next}` }
|
|
10317
|
-
});
|
|
10318
|
-
}
|
|
10319
|
-
};
|
|
10320
|
-
for (const entry of expanded) {
|
|
10321
|
-
const colon = entry.indexOf(":");
|
|
10322
|
-
const name = colon === -1 ? entry : entry.slice(0, colon);
|
|
10323
|
-
const args = colon === -1 ? [] : entry.slice(colon + 1).split(",");
|
|
10324
|
-
const handler = middlewareRegistry[name];
|
|
10325
|
-
if (!handler) {
|
|
10326
|
-
console.warn(`[stx serve] unknown middleware "${name}" on ${path}`);
|
|
10327
|
-
continue;
|
|
10328
|
-
}
|
|
10329
|
-
const result = await handler(req, ctx, ...args);
|
|
10330
|
-
if (result instanceof Response)
|
|
10331
|
-
return result;
|
|
10222
|
+
};
|
|
10223
|
+
if (req.headers.get("if-none-match") === etag)
|
|
10224
|
+
return new Response(null, { status: 304, headers });
|
|
10225
|
+
if (req.method === "HEAD")
|
|
10226
|
+
return new Response(null, { headers });
|
|
10227
|
+
return new Response(content2, { headers });
|
|
10332
10228
|
}
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10229
|
+
if (path === "/_stx/stores.js") {
|
|
10230
|
+
const stx = await stxModule;
|
|
10231
|
+
const code = typeof stx.getStoreScript === "function" ? await stx.getStoreScript() : null;
|
|
10232
|
+
return new Response(code ?? "", {
|
|
10233
|
+
status: code === null ? 404 : 200,
|
|
10234
|
+
headers: {
|
|
10235
|
+
"Content-Type": "application/javascript; charset=utf-8",
|
|
10236
|
+
"Cache-Control": "no-store",
|
|
10237
|
+
...corsHeaders
|
|
10238
|
+
}
|
|
10239
|
+
});
|
|
10338
10240
|
}
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
|
|
10241
|
+
const crosswindAsset = path.match(/^\/_stx\/crosswind\.([a-f0-9]{16})\.css$/);
|
|
10242
|
+
if (crosswindAsset) {
|
|
10243
|
+
const stx = await stxModule;
|
|
10244
|
+
const content2 = stx.getCrosswindServeAsset(crosswindAsset[1]);
|
|
10245
|
+
if (content2 === undefined)
|
|
10246
|
+
return new Response("Crosswind asset not found", { status: 404 });
|
|
10247
|
+
const headers = {
|
|
10248
|
+
"Content-Type": "text/css; charset=utf-8",
|
|
10249
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
10250
|
+
...corsHeaders
|
|
10251
|
+
};
|
|
10252
|
+
if (req.method === "HEAD")
|
|
10253
|
+
return new Response(null, { headers });
|
|
10254
|
+
return new Response(content2, { headers });
|
|
10255
|
+
}
|
|
10256
|
+
if (options.onRequest) {
|
|
10257
|
+
const hookResult = await options.onRequest(req);
|
|
10258
|
+
if (hookResult instanceof Response)
|
|
10259
|
+
return hookResult;
|
|
10260
|
+
if (hookResult && typeof hookResult === "object")
|
|
10261
|
+
Object.assign(reqCtx, hookResult);
|
|
10262
|
+
}
|
|
10263
|
+
const route = resolveRouteMiddleware(path);
|
|
10264
|
+
const extraNames = extraProtectedPrefixes.some((p) => path === p || path.startsWith(p.endsWith("/") ? p : `${p}/`)) ? ["auth"] : [];
|
|
10265
|
+
const requested = [...globalMiddlewareNames, ...route.names, ...extraNames];
|
|
10266
|
+
if (requested.length > 0) {
|
|
10267
|
+
const expanded = expandMiddlewareNames(requested);
|
|
10268
|
+
const cookies = parseCookies(req);
|
|
10269
|
+
const ctx = {
|
|
10270
|
+
path,
|
|
10271
|
+
url,
|
|
10272
|
+
params: route.params,
|
|
10273
|
+
cookies,
|
|
10274
|
+
redirect: (to, status = 302) => {
|
|
10275
|
+
const sep = to.includes("?") ? "&" : "?";
|
|
10276
|
+
const next = encodeURIComponent(path + (url.search || ""));
|
|
10277
|
+
return new Response(null, {
|
|
10278
|
+
status,
|
|
10279
|
+
headers: { Location: `${to}${sep}next=${next}` }
|
|
10280
|
+
});
|
|
10281
|
+
}
|
|
10282
|
+
};
|
|
10283
|
+
for (const entry of expanded) {
|
|
10284
|
+
const colon = entry.indexOf(":");
|
|
10285
|
+
const name = colon === -1 ? entry : entry.slice(0, colon);
|
|
10286
|
+
const args = colon === -1 ? [] : entry.slice(colon + 1).split(",");
|
|
10287
|
+
const handler = middlewareRegistry[name];
|
|
10288
|
+
if (!handler) {
|
|
10289
|
+
console.warn(`[stx serve] unknown middleware "${name}" on ${path}`);
|
|
10290
|
+
continue;
|
|
10291
|
+
}
|
|
10292
|
+
const result = await handler(req, ctx, ...args);
|
|
10293
|
+
if (result instanceof Response)
|
|
10294
|
+
return result;
|
|
10362
10295
|
}
|
|
10363
10296
|
}
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
|
|
10367
|
-
|
|
10368
|
-
if (!tableName) {
|
|
10369
|
-
return new Response(JSON.stringify({ error: "Table name required" }), {
|
|
10370
|
-
status: 400,
|
|
10371
|
-
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
10372
|
-
});
|
|
10297
|
+
if (options.routes) {
|
|
10298
|
+
const routeHandler = options.routes[path];
|
|
10299
|
+
if (routeHandler) {
|
|
10300
|
+
return routeHandler(req);
|
|
10373
10301
|
}
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10302
|
+
}
|
|
10303
|
+
if (path.startsWith("/_stx/component/")) {
|
|
10304
|
+
const componentName = decodeURIComponent(path.slice("/_stx/component/".length));
|
|
10305
|
+
if (componentName) {
|
|
10306
|
+
try {
|
|
10307
|
+
const { processDirectives, defaultConfig: defaultConfig4 } = await stxModule;
|
|
10308
|
+
const componentTemplate = `<${componentName} />`;
|
|
10309
|
+
const componentOpts = {
|
|
10310
|
+
...defaultConfig4,
|
|
10311
|
+
...componentsDir && { componentsDir },
|
|
10312
|
+
...layoutsDir && { layoutsDir },
|
|
10313
|
+
...partialsDir && { partialsDir },
|
|
10314
|
+
autoShell: false
|
|
10315
|
+
};
|
|
10316
|
+
const html = await processDirectives(componentTemplate, {}, `${componentsDir}/${componentName}.stx`, componentOpts, new Set);
|
|
10317
|
+
return new Response(html, {
|
|
10318
|
+
headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders }
|
|
10319
|
+
});
|
|
10320
|
+
} catch (e) {
|
|
10321
|
+
return new Response(`<div class="stx-async-error">${e.message}</div>`, {
|
|
10322
|
+
status: 500,
|
|
10323
|
+
headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders }
|
|
10324
|
+
});
|
|
10325
|
+
}
|
|
10379
10326
|
}
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
const dbPath = nodePath.resolve(process19.cwd(), "database/stacks.sqlite");
|
|
10383
|
-
const db = new Database(dbPath);
|
|
10327
|
+
}
|
|
10328
|
+
if (path.startsWith("/api/data/") && req.method === "POST") {
|
|
10384
10329
|
try {
|
|
10385
|
-
const
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
if (body[col] !== undefined && body[col] !== "") {
|
|
10392
|
-
columns.push(col);
|
|
10393
|
-
placeholders.push("?");
|
|
10394
|
-
values.push(body[col]);
|
|
10395
|
-
}
|
|
10396
|
-
}
|
|
10397
|
-
const now = new Date().toISOString();
|
|
10398
|
-
if (tableInfo.some((c) => c.name === "created_at")) {
|
|
10399
|
-
columns.push("created_at");
|
|
10400
|
-
placeholders.push("?");
|
|
10401
|
-
values.push(now);
|
|
10402
|
-
}
|
|
10403
|
-
if (tableInfo.some((c) => c.name === "updated_at")) {
|
|
10404
|
-
columns.push("updated_at");
|
|
10405
|
-
placeholders.push("?");
|
|
10406
|
-
values.push(now);
|
|
10330
|
+
const tableName = path.replace("/api/data/", "").split("/")[0];
|
|
10331
|
+
if (!tableName) {
|
|
10332
|
+
return new Response(JSON.stringify({ error: "Table name required" }), {
|
|
10333
|
+
status: 400,
|
|
10334
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
10335
|
+
});
|
|
10407
10336
|
}
|
|
10408
|
-
if (
|
|
10409
|
-
return new Response(JSON.stringify({ error: "
|
|
10337
|
+
if (!/^[a-zA-Z_]\w*$/.test(tableName)) {
|
|
10338
|
+
return new Response(JSON.stringify({ error: "Invalid table name" }), {
|
|
10410
10339
|
status: 400,
|
|
10411
10340
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
10412
10341
|
});
|
|
10413
10342
|
}
|
|
10414
|
-
const
|
|
10415
|
-
const
|
|
10416
|
-
const
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
status: 500,
|
|
10431
|
-
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
10432
|
-
});
|
|
10433
|
-
} finally {
|
|
10434
|
-
db.close();
|
|
10435
|
-
}
|
|
10436
|
-
} catch {}
|
|
10437
|
-
}
|
|
10438
|
-
if (path === "/index")
|
|
10439
|
-
path = "/";
|
|
10440
|
-
if (path === "/") {
|
|
10441
|
-
const indexContent = await getRoute("/", reqCtx);
|
|
10442
|
-
if (!indexContent) {
|
|
10443
|
-
const homeContent = await getRoute("/home", reqCtx);
|
|
10444
|
-
if (homeContent) {
|
|
10445
|
-
return new Response(null, {
|
|
10446
|
-
status: 302,
|
|
10447
|
-
headers: {
|
|
10448
|
-
Location: "/home",
|
|
10449
|
-
...corsHeaders
|
|
10343
|
+
const body = await req.json();
|
|
10344
|
+
const { Database } = await import("bun:sqlite");
|
|
10345
|
+
const dbPath = nodePath.resolve(process19.cwd(), "database/stacks.sqlite");
|
|
10346
|
+
const db = new Database(dbPath);
|
|
10347
|
+
try {
|
|
10348
|
+
const tableInfo = db.query(`PRAGMA table_info("${tableName}")`).all();
|
|
10349
|
+
const validColumns = tableInfo.map((c) => c.name).filter((n) => n !== "id" && n !== "created_at" && n !== "updated_at");
|
|
10350
|
+
const columns = [];
|
|
10351
|
+
const placeholders = [];
|
|
10352
|
+
const values = [];
|
|
10353
|
+
for (const col of validColumns) {
|
|
10354
|
+
if (body[col] !== undefined && body[col] !== "") {
|
|
10355
|
+
columns.push(col);
|
|
10356
|
+
placeholders.push("?");
|
|
10357
|
+
values.push(body[col]);
|
|
10358
|
+
}
|
|
10450
10359
|
}
|
|
10451
|
-
|
|
10360
|
+
const now = new Date().toISOString();
|
|
10361
|
+
if (tableInfo.some((c) => c.name === "created_at")) {
|
|
10362
|
+
columns.push("created_at");
|
|
10363
|
+
placeholders.push("?");
|
|
10364
|
+
values.push(now);
|
|
10365
|
+
}
|
|
10366
|
+
if (tableInfo.some((c) => c.name === "updated_at")) {
|
|
10367
|
+
columns.push("updated_at");
|
|
10368
|
+
placeholders.push("?");
|
|
10369
|
+
values.push(now);
|
|
10370
|
+
}
|
|
10371
|
+
if (columns.length === 0) {
|
|
10372
|
+
return new Response(JSON.stringify({ error: "No valid fields provided" }), {
|
|
10373
|
+
status: 400,
|
|
10374
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
10375
|
+
});
|
|
10376
|
+
}
|
|
10377
|
+
const query = `INSERT INTO "${tableName}" (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`;
|
|
10378
|
+
const stmt = db.prepare(query);
|
|
10379
|
+
const result = stmt.run(...values);
|
|
10380
|
+
return new Response(JSON.stringify({
|
|
10381
|
+
success: true,
|
|
10382
|
+
id: result.lastInsertRowid,
|
|
10383
|
+
message: "Record created successfully"
|
|
10384
|
+
}), {
|
|
10385
|
+
status: 201,
|
|
10386
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
10387
|
+
});
|
|
10388
|
+
} catch (error) {
|
|
10389
|
+
console.error("API Error:", error);
|
|
10390
|
+
return new Response(JSON.stringify({
|
|
10391
|
+
error: error.message || "Failed to create record"
|
|
10392
|
+
}), {
|
|
10393
|
+
status: 500,
|
|
10394
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
10395
|
+
});
|
|
10396
|
+
} finally {
|
|
10397
|
+
db.close();
|
|
10398
|
+
}
|
|
10399
|
+
} catch {}
|
|
10400
|
+
}
|
|
10401
|
+
if (path === "/index")
|
|
10402
|
+
path = "/";
|
|
10403
|
+
if (path === "/") {
|
|
10404
|
+
const indexContent = await getRoute("/", reqCtx);
|
|
10405
|
+
if (!indexContent) {
|
|
10406
|
+
const homeContent = await getRoute("/home", reqCtx);
|
|
10407
|
+
if (homeContent) {
|
|
10408
|
+
return new Response(null, {
|
|
10409
|
+
status: 302,
|
|
10410
|
+
headers: {
|
|
10411
|
+
Location: "/home",
|
|
10412
|
+
...corsHeaders
|
|
10413
|
+
}
|
|
10414
|
+
});
|
|
10415
|
+
}
|
|
10452
10416
|
}
|
|
10453
10417
|
}
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10460
|
-
}
|
|
10461
|
-
|
|
10462
|
-
|
|
10463
|
-
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10418
|
+
const content = await getRoute(path, reqCtx);
|
|
10419
|
+
if (reqCtx.actionRedirect) {
|
|
10420
|
+
return new Response(null, {
|
|
10421
|
+
status: 303,
|
|
10422
|
+
headers: { ...corsHeaders, Location: reqCtx.actionRedirect }
|
|
10423
|
+
});
|
|
10424
|
+
}
|
|
10425
|
+
if (content) {
|
|
10426
|
+
const responseStatus = reqCtx.responseStatus ?? 200;
|
|
10427
|
+
const isSpaNav = req.headers.get("X-STX-Router") === "true";
|
|
10428
|
+
if (isSpaNav) {
|
|
10429
|
+
const layoutMatch = content.match(/<!-- stx-layout: ([^ ]+) -->/);
|
|
10430
|
+
const pageLayout = layoutMatch ? layoutMatch[1] : "default";
|
|
10431
|
+
const groupMetaMatch = content.match(/<meta\b[^>]*name=["']stx-layout-group["'][^>]*content=["']([^"']+)["'][^>]*>/i);
|
|
10432
|
+
const pageLayoutGroup = i18nConfig && i18nLocale ? `i18n:${i18nLocale}` : groupMetaMatch?.[1] || deriveLayoutGroup(pageLayout);
|
|
10433
|
+
let fragment = content;
|
|
10434
|
+
let containerAttrs = "";
|
|
10435
|
+
let mainContentStart = -1;
|
|
10436
|
+
let mainContentEnd = -1;
|
|
10437
|
+
const headStyles = [];
|
|
10438
|
+
const headMatch = content.match(/<head\b[^>]*>([\s\S]*?)<\/head>/i);
|
|
10439
|
+
if (headMatch) {
|
|
10440
|
+
const headContent = headMatch[1];
|
|
10441
|
+
let styleMatch;
|
|
10442
|
+
const styleRe = /<style\b[^>]*>[\s\S]*?<\/style>/gi;
|
|
10443
|
+
while ((styleMatch = styleRe.exec(headContent)) !== null) {
|
|
10444
|
+
if (styleMatch[0].includes("data-crosswind")) {
|
|
10445
|
+
const cssContent = styleMatch[0].replace(/<style[^>]*>/, "").replace(/<\/style>/, "");
|
|
10446
|
+
const hiddenRule = cssContent.indexOf("[hidden]");
|
|
10447
|
+
if (hiddenRule !== -1) {
|
|
10448
|
+
const afterPreflight = cssContent.indexOf("}", hiddenRule) + 1;
|
|
10449
|
+
const utilities = cssContent.slice(afterPreflight).trim();
|
|
10450
|
+
if (utilities) {
|
|
10451
|
+
headStyles.push(`<style data-crosswind="fragment">${utilities}</style>`);
|
|
10452
|
+
}
|
|
10489
10453
|
}
|
|
10454
|
+
continue;
|
|
10490
10455
|
}
|
|
10491
|
-
|
|
10456
|
+
headStyles.push(styleMatch[0]);
|
|
10457
|
+
}
|
|
10458
|
+
const crosswindLinkRe = /<link\b[^>]*\bdata-crosswind=(?:"generated"|'generated')[^>]*>/gi;
|
|
10459
|
+
let crosswindLinkMatch;
|
|
10460
|
+
while ((crosswindLinkMatch = crosswindLinkRe.exec(headContent)) !== null) {
|
|
10461
|
+
headStyles.push(crosswindLinkMatch[0]);
|
|
10492
10462
|
}
|
|
10493
|
-
headStyles.push(styleMatch[0]);
|
|
10494
10463
|
}
|
|
10495
|
-
const
|
|
10496
|
-
|
|
10497
|
-
|
|
10498
|
-
|
|
10464
|
+
const containerSelector = stxConfig?.router?.container || "main";
|
|
10465
|
+
const containerRegion = findContainerRegion(fragment, containerSelector);
|
|
10466
|
+
if (!containerRegion) {
|
|
10467
|
+
console.warn(`[stx] No SPA swap container matching "${containerSelector}" in ${new URL(req.url).pathname}. SPA navigation is disabled for links on this page; the full document will load instead. Add a matching element, or point router.container at one this page has.`);
|
|
10499
10468
|
}
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
const beforeMain = content.slice(bodyStart, mainIdx);
|
|
10514
|
-
let bodyStyleMatch;
|
|
10515
|
-
const bodyStyleRe = /<style\b[^>]*>[\s\S]*?<\/style>/gi;
|
|
10516
|
-
while ((bodyStyleMatch = bodyStyleRe.exec(beforeMain)) !== null) {
|
|
10517
|
-
headStyles.push(bodyStyleMatch[0]);
|
|
10469
|
+
const mainOpenMatch = containerRegion ? { 0: containerRegion.openTag, index: containerRegion.openIndex } : null;
|
|
10470
|
+
const mainCloseIdx = containerRegion ? containerRegion.end : -1;
|
|
10471
|
+
if (mainOpenMatch && mainCloseIdx !== -1) {
|
|
10472
|
+
const bodyMatch = content.match(/<body\b[^>]*>/i);
|
|
10473
|
+
if (bodyMatch) {
|
|
10474
|
+
const bodyStart = bodyMatch.index + bodyMatch[0].length;
|
|
10475
|
+
const mainIdx = mainOpenMatch.index;
|
|
10476
|
+
const beforeMain = content.slice(bodyStart, mainIdx);
|
|
10477
|
+
let bodyStyleMatch;
|
|
10478
|
+
const bodyStyleRe = /<style\b[^>]*>[\s\S]*?<\/style>/gi;
|
|
10479
|
+
while ((bodyStyleMatch = bodyStyleRe.exec(beforeMain)) !== null) {
|
|
10480
|
+
headStyles.push(bodyStyleMatch[0]);
|
|
10481
|
+
}
|
|
10518
10482
|
}
|
|
10483
|
+
containerAttrs = mainOpenMatch[0].replace(new RegExp(`^<${containerRegion.tagName}\\b`, "i"), "").replace(/\/?>$/, "").trim();
|
|
10484
|
+
const mainStart = mainOpenMatch.index + mainOpenMatch[0].length;
|
|
10485
|
+
mainContentStart = mainStart;
|
|
10486
|
+
mainContentEnd = mainCloseIdx;
|
|
10487
|
+
fragment = fragment.slice(mainStart, mainCloseIdx).trim();
|
|
10519
10488
|
}
|
|
10520
|
-
|
|
10521
|
-
const
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
const
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
}
|
|
10554
|
-
fragment = fragment.replace(/<script data-stx-scoped>\s*;?\(function\(\)\s*\{[\s\S]*?<\/script>/g, "");
|
|
10555
|
-
const clearStale = "<script data-stx-page>if(window.stx)window.stx._latestSetup=null;</script>";
|
|
10556
|
-
const componentFactoryScripts = pageSetupScripts.filter((script) => script.includes("data-stx-component-factories"));
|
|
10557
|
-
const trailingPageScripts = pageSetupScripts.filter((script) => !script.includes("data-stx-component-factories"));
|
|
10558
|
-
fragment = `${headStyles.join(`
|
|
10489
|
+
const pageSetupScripts = [];
|
|
10490
|
+
const pageSetupScriptOffsets = new Set;
|
|
10491
|
+
const appendOutsideMainScript = (match) => {
|
|
10492
|
+
const offset = match.index;
|
|
10493
|
+
const insideMain = mainContentStart !== -1 && offset >= mainContentStart && offset < mainContentEnd;
|
|
10494
|
+
if (insideMain || pageSetupScriptOffsets.has(offset))
|
|
10495
|
+
return;
|
|
10496
|
+
pageSetupScriptOffsets.add(offset);
|
|
10497
|
+
pageSetupScripts.push(match[0]);
|
|
10498
|
+
};
|
|
10499
|
+
const routeParamsRe = /<script\b[^>]*data-stx-route-params[^>]*>[\s\S]*?<\/script>/gi;
|
|
10500
|
+
let setupMatch;
|
|
10501
|
+
while ((setupMatch = routeParamsRe.exec(content)) !== null) {
|
|
10502
|
+
appendOutsideMainScript(setupMatch);
|
|
10503
|
+
}
|
|
10504
|
+
const allScriptRe = /<script\b[^>]*data-stx-scoped[^>]*>[\s\S]*?<\/script>/gi;
|
|
10505
|
+
while ((setupMatch = allScriptRe.exec(content)) !== null) {
|
|
10506
|
+
const scriptContent = setupMatch[0];
|
|
10507
|
+
if (scriptContent.includes("__stx_early_mounts"))
|
|
10508
|
+
continue;
|
|
10509
|
+
if (scriptContent.includes("data-stx-reactive") && scriptContent.includes("window.__stx_reactive"))
|
|
10510
|
+
continue;
|
|
10511
|
+
appendOutsideMainScript(setupMatch);
|
|
10512
|
+
}
|
|
10513
|
+
const bridgeInitRe = /<script\b[^>]*data-stx-reactive[^>]*>(?![\s\S]*window\.__stx_reactive)[\s\S]*?<\/script>/gi;
|
|
10514
|
+
while ((setupMatch = bridgeInitRe.exec(content)) !== null) {
|
|
10515
|
+
appendOutsideMainScript(setupMatch);
|
|
10516
|
+
}
|
|
10517
|
+
fragment = fragment.replace(/<script data-stx-scoped>\s*;?\(function\(\)\s*\{[\s\S]*?<\/script>/g, "");
|
|
10518
|
+
const clearStale = "<script data-stx-page>if(window.stx)window.stx._latestSetup=null;</script>";
|
|
10519
|
+
const componentFactoryScripts = pageSetupScripts.filter((script) => script.includes("data-stx-component-factories"));
|
|
10520
|
+
const trailingPageScripts = pageSetupScripts.filter((script) => !script.includes("data-stx-component-factories"));
|
|
10521
|
+
fragment = `${headStyles.join(`
|
|
10559
10522
|
`)}
|
|
10560
10523
|
${componentFactoryScripts.join(`
|
|
10561
10524
|
`)}
|
|
@@ -10563,215 +10526,216 @@ ${fragment}
|
|
|
10563
10526
|
${clearStale}
|
|
10564
10527
|
${trailingPageScripts.join(`
|
|
10565
10528
|
`)}`;
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10529
|
+
const titleMatch = content.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
10530
|
+
const pageTitle = titleMatch ? titleMatch[1].trim() : "";
|
|
10531
|
+
const shipsRuntime = content.includes("data-stx-runtime");
|
|
10532
|
+
return new Response(fragment, {
|
|
10533
|
+
status: isRenderFailure(content) ? 500 : responseStatus,
|
|
10534
|
+
headers: {
|
|
10535
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
10536
|
+
"X-STX-Fragment": "true",
|
|
10537
|
+
"X-STX-Layout": pageLayout,
|
|
10538
|
+
"X-STX-Layout-Group": pageLayoutGroup,
|
|
10539
|
+
"X-STX-Runtime": shipsRuntime ? "true" : "false",
|
|
10540
|
+
...pageTitle && { "X-STX-Title": encodeURIComponent(pageTitle) },
|
|
10541
|
+
[BUILD_ID_HEADER]: getBuildId(),
|
|
10542
|
+
...containerAttrs && { "X-STX-Container-Attrs": encodeURIComponent(containerAttrs) },
|
|
10543
|
+
"Cache-Control": "no-store",
|
|
10544
|
+
...corsHeaders
|
|
10545
|
+
}
|
|
10546
|
+
});
|
|
10547
|
+
}
|
|
10548
|
+
let cleaned = content;
|
|
10549
|
+
let runtimeCount = 0;
|
|
10550
|
+
cleaned = content.replace(/<script data-stx-scoped>\(function\(\)\{'use strict';var cloakStyle[\s\S]*?<\/script>/g, (match) => {
|
|
10551
|
+
runtimeCount++;
|
|
10552
|
+
return runtimeCount === 1 ? match : "";
|
|
10553
|
+
});
|
|
10554
|
+
const pageRenderFailed = isRenderFailure(cleaned);
|
|
10555
|
+
if (pageRenderFailed) {
|
|
10556
|
+
console.error(`[stx] render failed for ${new URL(req.url).pathname} \u2014 serving 500`);
|
|
10557
|
+
for (const custom500 of ["/500", "/errors/500"]) {
|
|
10558
|
+
try {
|
|
10559
|
+
const errorPage = usableErrorPage(await getRoute(custom500, reqCtx));
|
|
10560
|
+
if (errorPage) {
|
|
10561
|
+
const isProd2 = isProductionServe();
|
|
10562
|
+
return new Response(isProd2 ? errorPage : injectHmrClient(errorPage), {
|
|
10563
|
+
status: 500,
|
|
10564
|
+
headers: {
|
|
10565
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
10566
|
+
"Cache-Control": "no-store",
|
|
10567
|
+
...corsHeaders
|
|
10568
|
+
}
|
|
10569
|
+
});
|
|
10570
|
+
}
|
|
10571
|
+
} catch {}
|
|
10572
|
+
}
|
|
10573
|
+
}
|
|
10574
|
+
return new Response(injectHmrClient(cleaned), {
|
|
10575
|
+
status: pageRenderFailed ? 500 : responseStatus,
|
|
10571
10576
|
headers: {
|
|
10572
10577
|
"Content-Type": "text/html; charset=utf-8",
|
|
10573
|
-
"X-STX-Fragment": "true",
|
|
10574
|
-
"X-STX-Layout": pageLayout,
|
|
10575
|
-
"X-STX-Layout-Group": pageLayoutGroup,
|
|
10576
|
-
"X-STX-Runtime": shipsRuntime ? "true" : "false",
|
|
10577
|
-
...pageTitle && { "X-STX-Title": encodeURIComponent(pageTitle) },
|
|
10578
|
-
[BUILD_ID_HEADER]: getBuildId(),
|
|
10579
|
-
...containerAttrs && { "X-STX-Container-Attrs": encodeURIComponent(containerAttrs) },
|
|
10580
10578
|
"Cache-Control": "no-store",
|
|
10581
10579
|
...corsHeaders
|
|
10582
10580
|
}
|
|
10583
10581
|
});
|
|
10584
10582
|
}
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
try {
|
|
10596
|
-
const errorPage = usableErrorPage(await getRoute(custom500, reqCtx));
|
|
10597
|
-
if (errorPage) {
|
|
10598
|
-
const isProd2 = isProductionServe();
|
|
10599
|
-
return new Response(isProd2 ? errorPage : injectHmrClient(errorPage), {
|
|
10600
|
-
status: 500,
|
|
10601
|
-
headers: {
|
|
10602
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
10603
|
-
"Cache-Control": "no-store",
|
|
10604
|
-
...corsHeaders
|
|
10605
|
-
}
|
|
10606
|
-
});
|
|
10607
|
-
}
|
|
10608
|
-
} catch {}
|
|
10609
|
-
}
|
|
10610
|
-
}
|
|
10611
|
-
return new Response(injectHmrClient(cleaned), {
|
|
10612
|
-
status: pageRenderFailed ? 500 : responseStatus,
|
|
10613
|
-
headers: {
|
|
10614
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
10615
|
-
"Cache-Control": "no-store",
|
|
10616
|
-
...corsHeaders
|
|
10617
|
-
}
|
|
10618
|
-
});
|
|
10619
|
-
}
|
|
10620
|
-
const contentWithExt = await getRoute(`${path}.html`, reqCtx);
|
|
10621
|
-
if (contentWithExt) {
|
|
10622
|
-
return new Response(injectHmrClient(contentWithExt), {
|
|
10623
|
-
status: reqCtx.responseStatus ?? 200,
|
|
10624
|
-
headers: {
|
|
10625
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
10626
|
-
"Cache-Control": "no-store",
|
|
10627
|
-
...corsHeaders
|
|
10628
|
-
}
|
|
10629
|
-
});
|
|
10630
|
-
}
|
|
10631
|
-
if (path.startsWith("/chunk-") || path.endsWith(".js") || path.endsWith(".css")) {
|
|
10632
|
-
try {
|
|
10633
|
-
const buildFile = Bun.file(stateDir(process19.cwd(), path));
|
|
10634
|
-
if (await buildFile.exists()) {
|
|
10635
|
-
const ext = path.split(".").pop()?.toLowerCase();
|
|
10636
|
-
const contentType = ext === "css" ? "text/css" : ext === "js" ? "application/javascript" : "text/plain";
|
|
10637
|
-
return new Response(buildFile, {
|
|
10638
|
-
headers: {
|
|
10639
|
-
"Content-Type": contentType,
|
|
10640
|
-
"Cache-Control": "no-store"
|
|
10641
|
-
}
|
|
10642
|
-
});
|
|
10643
|
-
}
|
|
10644
|
-
} catch {}
|
|
10645
|
-
}
|
|
10646
|
-
if (path.startsWith("/assets/") || path.startsWith("/resources/assets/")) {
|
|
10647
|
-
await ensureAssets();
|
|
10648
|
-
let assetPathname;
|
|
10649
|
-
try {
|
|
10650
|
-
assetPathname = decodeURIComponent(path);
|
|
10651
|
-
} catch {
|
|
10652
|
-
return new Response("Invalid asset path", { status: 400 });
|
|
10583
|
+
const contentWithExt = await getRoute(`${path}.html`, reqCtx);
|
|
10584
|
+
if (contentWithExt) {
|
|
10585
|
+
return new Response(injectHmrClient(contentWithExt), {
|
|
10586
|
+
status: reqCtx.responseStatus ?? 200,
|
|
10587
|
+
headers: {
|
|
10588
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
10589
|
+
"Cache-Control": "no-store",
|
|
10590
|
+
...corsHeaders
|
|
10591
|
+
}
|
|
10592
|
+
});
|
|
10653
10593
|
}
|
|
10654
|
-
if (
|
|
10655
|
-
return new Response("Invalid asset path", { status: 400 });
|
|
10656
|
-
for (const assetPath of assetRequestPaths(assetPathname)) {
|
|
10594
|
+
if (path.startsWith("/chunk-") || path.endsWith(".js") || path.endsWith(".css")) {
|
|
10657
10595
|
try {
|
|
10658
|
-
const
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
const
|
|
10662
|
-
|
|
10663
|
-
return bundleBrowserAsset(filePath);
|
|
10664
|
-
return new Response(file, {
|
|
10596
|
+
const buildFile = Bun.file(stateDir(process19.cwd(), path));
|
|
10597
|
+
if (await buildFile.exists()) {
|
|
10598
|
+
const ext = path.split(".").pop()?.toLowerCase();
|
|
10599
|
+
const contentType = ext === "css" ? "text/css" : ext === "js" ? "application/javascript" : "text/plain";
|
|
10600
|
+
return new Response(buildFile, {
|
|
10665
10601
|
headers: {
|
|
10666
|
-
"Content-Type":
|
|
10602
|
+
"Content-Type": contentType,
|
|
10667
10603
|
"Cache-Control": "no-store"
|
|
10668
10604
|
}
|
|
10669
10605
|
});
|
|
10670
10606
|
}
|
|
10607
|
+
} catch {}
|
|
10608
|
+
}
|
|
10609
|
+
if (path.startsWith("/assets/") || path.startsWith("/resources/assets/")) {
|
|
10610
|
+
await ensureAssets();
|
|
10611
|
+
let assetPathname;
|
|
10612
|
+
try {
|
|
10613
|
+
assetPathname = decodeURIComponent(path);
|
|
10671
10614
|
} catch {
|
|
10672
|
-
|
|
10615
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
10673
10616
|
}
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
const publicRoot = nodePath.resolve(process19.cwd(), publicDir);
|
|
10678
|
-
let safePathname;
|
|
10679
|
-
try {
|
|
10680
|
-
safePathname = decodeURIComponent(path);
|
|
10681
|
-
} catch {
|
|
10682
|
-
safePathname = path;
|
|
10683
|
-
}
|
|
10684
|
-
if (!safePathname.includes("\x00")) {
|
|
10685
|
-
const resolvedPath = nodePath.resolve(publicRoot, `.${safePathname}`);
|
|
10686
|
-
const isInsidePublicRoot = resolvedPath === publicRoot || resolvedPath.startsWith(`${publicRoot}${nodePath.sep}`);
|
|
10687
|
-
if (isInsidePublicRoot) {
|
|
10617
|
+
if (!isSafeAssetPath(assetPathname))
|
|
10618
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
10619
|
+
for (const assetPath of assetRequestPaths(assetPathname)) {
|
|
10688
10620
|
try {
|
|
10689
|
-
const
|
|
10621
|
+
const filePath = nodePath.resolve(process19.cwd(), `.${assetPath}`);
|
|
10622
|
+
const file = Bun.file(filePath);
|
|
10690
10623
|
if (await file.exists()) {
|
|
10691
|
-
const
|
|
10692
|
-
if (
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
}
|
|
10624
|
+
const ext = getAssetExtension(assetPath);
|
|
10625
|
+
if (isBundledAssetExtension(ext))
|
|
10626
|
+
return bundleBrowserAsset(filePath);
|
|
10627
|
+
return new Response(file, {
|
|
10628
|
+
headers: {
|
|
10629
|
+
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
10630
|
+
"Cache-Control": "no-store"
|
|
10631
|
+
}
|
|
10632
|
+
});
|
|
10701
10633
|
}
|
|
10702
|
-
} catch {
|
|
10634
|
+
} catch {
|
|
10635
|
+
continue;
|
|
10636
|
+
}
|
|
10703
10637
|
}
|
|
10704
10638
|
}
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
const customContent = await getRoute(custom404, reqCtx);
|
|
10713
|
-
if (customContent) {
|
|
10714
|
-
return new Response(isProd ? customContent : injectHmrClient(customContent), {
|
|
10715
|
-
status: 404,
|
|
10716
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
10717
|
-
});
|
|
10639
|
+
if ((req.method === "GET" || req.method === "HEAD") && path !== "/") {
|
|
10640
|
+
const publicRoot = nodePath.resolve(process19.cwd(), publicDir);
|
|
10641
|
+
let safePathname;
|
|
10642
|
+
try {
|
|
10643
|
+
safePathname = decodeURIComponent(path);
|
|
10644
|
+
} catch {
|
|
10645
|
+
safePathname = path;
|
|
10718
10646
|
}
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10647
|
+
if (!safePathname.includes("\x00")) {
|
|
10648
|
+
const resolvedPath = nodePath.resolve(publicRoot, `.${safePathname}`);
|
|
10649
|
+
const isInsidePublicRoot = resolvedPath === publicRoot || resolvedPath.startsWith(`${publicRoot}${nodePath.sep}`);
|
|
10650
|
+
if (isInsidePublicRoot) {
|
|
10651
|
+
try {
|
|
10652
|
+
const file = Bun.file(resolvedPath);
|
|
10653
|
+
if (await file.exists()) {
|
|
10654
|
+
const stat4 = await file.stat().catch(() => null);
|
|
10655
|
+
if (stat4 && !stat4.isDirectory()) {
|
|
10656
|
+
const ext = resolvedPath.split(".").pop()?.toLowerCase();
|
|
10657
|
+
return new Response(file, {
|
|
10658
|
+
headers: {
|
|
10659
|
+
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
10660
|
+
"Cache-Control": staticCacheControl(resolvedPath)
|
|
10661
|
+
}
|
|
10662
|
+
});
|
|
10663
|
+
}
|
|
10664
|
+
}
|
|
10665
|
+
} catch {}
|
|
10732
10666
|
}
|
|
10733
10667
|
}
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10668
|
+
}
|
|
10669
|
+
if (path === "/favicon.ico") {
|
|
10670
|
+
return new Response(null, { status: 204 });
|
|
10671
|
+
}
|
|
10672
|
+
const isProd = isProductionServe();
|
|
10673
|
+
for (const custom404 of ["/404", "/errors/404"]) {
|
|
10674
|
+
try {
|
|
10675
|
+
const customContent = await getRoute(custom404, reqCtx);
|
|
10676
|
+
if (customContent) {
|
|
10677
|
+
return new Response(isProd ? customContent : injectHmrClient(customContent), {
|
|
10678
|
+
status: 404,
|
|
10679
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
10680
|
+
});
|
|
10681
|
+
}
|
|
10682
|
+
} catch {}
|
|
10683
|
+
}
|
|
10684
|
+
let availableRoutes;
|
|
10685
|
+
if (!isProd) {
|
|
10686
|
+
const files = await discoverFiles();
|
|
10687
|
+
availableRoutes = [];
|
|
10688
|
+
for (const filePath of files) {
|
|
10689
|
+
let normalizedPath = filePath.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
10690
|
+
for (const pattern of patterns) {
|
|
10691
|
+
const normalizedPattern = pattern.replace(/\\/g, "/").replace(/\/$/, "");
|
|
10692
|
+
if (normalizedPath.startsWith(`${normalizedPattern}/`)) {
|
|
10693
|
+
normalizedPath = normalizedPath.slice(normalizedPattern.length + 1);
|
|
10694
|
+
break;
|
|
10695
|
+
}
|
|
10696
|
+
}
|
|
10697
|
+
let route2 = normalizedPath.replace(/\.(stx|md|html)$/, "");
|
|
10698
|
+
if (route2.startsWith("pages/")) {
|
|
10699
|
+
route2 = route2.slice(6);
|
|
10700
|
+
}
|
|
10701
|
+
availableRoutes.push(route2);
|
|
10737
10702
|
}
|
|
10738
|
-
availableRoutes.push(route2);
|
|
10739
10703
|
}
|
|
10704
|
+
const notFoundHtml = render404Page({ path, routes: availableRoutes, isProduction: isProd });
|
|
10705
|
+
return new Response(isProd ? notFoundHtml : injectHmrClient(notFoundHtml), {
|
|
10706
|
+
status: 404,
|
|
10707
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
10708
|
+
});
|
|
10709
|
+
} finally {
|
|
10710
|
+
activeServeLocale = null;
|
|
10711
|
+
activeServeSearch = "";
|
|
10712
|
+
activeServeHost = "";
|
|
10713
|
+
activeServeCookieHeader = "";
|
|
10714
|
+
activeServeCookies = {};
|
|
10715
|
+
activeServeIp = "";
|
|
10716
|
+
}
|
|
10717
|
+
})();
|
|
10718
|
+
let _finalResp = await applyI18nToResponse(_i18nResp, i18nLocale ?? (i18nConfig?.defaultLocale ?? "en"), path);
|
|
10719
|
+
if (mintedCsrfToken) {
|
|
10720
|
+
try {
|
|
10721
|
+
_finalResp.headers.append("Set-Cookie", csrfCookieHeader(mintedCsrfToken, req.url.startsWith("https://")));
|
|
10722
|
+
} catch {
|
|
10723
|
+
const headers = new Headers(_finalResp.headers);
|
|
10724
|
+
headers.append("Set-Cookie", csrfCookieHeader(mintedCsrfToken, req.url.startsWith("https://")));
|
|
10725
|
+
_finalResp = new Response(_finalResp.body, {
|
|
10726
|
+
status: _finalResp.status,
|
|
10727
|
+
statusText: _finalResp.statusText,
|
|
10728
|
+
headers
|
|
10729
|
+
});
|
|
10740
10730
|
}
|
|
10741
|
-
const notFoundHtml = render404Page({ path, routes: availableRoutes, isProduction: isProd });
|
|
10742
|
-
return new Response(isProd ? notFoundHtml : injectHmrClient(notFoundHtml), {
|
|
10743
|
-
status: 404,
|
|
10744
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
10745
|
-
});
|
|
10746
|
-
} finally {
|
|
10747
|
-
activeServeLocale = null;
|
|
10748
|
-
activeServeSearch = "";
|
|
10749
|
-
activeServeHost = "";
|
|
10750
|
-
activeServeCookieHeader = "";
|
|
10751
|
-
activeServeCookies = {};
|
|
10752
|
-
activeServeIp = "";
|
|
10753
10731
|
}
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
_finalResp.headers.append("Set-Cookie", csrfCookieHeader(mintedCsrfToken, req.url.startsWith("https://")));
|
|
10759
|
-
} catch {
|
|
10760
|
-
const headers = new Headers(_finalResp.headers);
|
|
10761
|
-
headers.append("Set-Cookie", csrfCookieHeader(mintedCsrfToken, req.url.startsWith("https://")));
|
|
10762
|
-
_finalResp = new Response(_finalResp.body, {
|
|
10763
|
-
status: _finalResp.status,
|
|
10764
|
-
statusText: _finalResp.statusText,
|
|
10765
|
-
headers
|
|
10766
|
-
});
|
|
10732
|
+
if (options.onResponse) {
|
|
10733
|
+
const hooked = await options.onResponse(req, _finalResp);
|
|
10734
|
+
if (hooked instanceof Response)
|
|
10735
|
+
return hooked;
|
|
10767
10736
|
}
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
const hooked = await options.onResponse(req, _finalResp);
|
|
10771
|
-
if (hooked instanceof Response)
|
|
10772
|
-
return hooked;
|
|
10773
|
-
}
|
|
10774
|
-
return _finalResp;
|
|
10737
|
+
return _finalResp;
|
|
10738
|
+
})());
|
|
10775
10739
|
}
|
|
10776
10740
|
});
|
|
10777
10741
|
break;
|