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