@stacksjs/stx 0.2.168 → 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/cli.js +624 -624
- package/dist/compression.d.ts +23 -0
- package/dist/compression.js +68 -0
- package/dist/dev-server/serve-file.js +226 -223
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/production-server.js +105 -102
- package/dist/serve.js +2 -1
- package/package.json +5 -5
|
@@ -6,6 +6,7 @@ import { hydrateTemplateStream, hydrateFragment } from "./template-hydrator.js";
|
|
|
6
6
|
import { extractLayoutMetadata } from "./app-shell.js";
|
|
7
7
|
import { pageShipsSignalsRuntime } from "./runtime-injection.js";
|
|
8
8
|
import { patternToRegex } from "stx-router";
|
|
9
|
+
import { compressResponse } from "./compression.js";
|
|
9
10
|
const MIME_TYPES = {
|
|
10
11
|
".js": "application/javascript",
|
|
11
12
|
".mjs": "application/javascript",
|
|
@@ -65,122 +66,124 @@ export async function startProductionServer(options = {}) {
|
|
|
65
66
|
const server = serve({
|
|
66
67
|
port,
|
|
67
68
|
async fetch(request) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (options.apiRoutes?.[pathname])
|
|
75
|
-
return options.apiRoutes[pathname](request);
|
|
76
|
-
if ((request.method === "GET" || request.method === "HEAD") && pathname !== "/") {
|
|
77
|
-
const publicRoot = path.resolve(outputDir, "public");
|
|
78
|
-
let safePathname;
|
|
79
|
-
try {
|
|
80
|
-
safePathname = decodeURIComponent(pathname);
|
|
81
|
-
} catch {
|
|
82
|
-
safePathname = pathname;
|
|
69
|
+
return compressResponse(request, await (async () => {
|
|
70
|
+
const pathname = new URL(request.url).pathname;
|
|
71
|
+
if (options.onRequest) {
|
|
72
|
+
const response = await options.onRequest(request);
|
|
73
|
+
if (response)
|
|
74
|
+
return response;
|
|
83
75
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
76
|
+
if (options.apiRoutes?.[pathname])
|
|
77
|
+
return options.apiRoutes[pathname](request);
|
|
78
|
+
if ((request.method === "GET" || request.method === "HEAD") && pathname !== "/") {
|
|
79
|
+
const publicRoot = path.resolve(outputDir, "public");
|
|
80
|
+
let safePathname;
|
|
81
|
+
try {
|
|
82
|
+
safePathname = decodeURIComponent(pathname);
|
|
83
|
+
} catch {
|
|
84
|
+
safePathname = pathname;
|
|
85
|
+
}
|
|
86
|
+
if (!safePathname.includes("\x00")) {
|
|
87
|
+
const resolvedPath = path.resolve(publicRoot, `.${safePathname}`);
|
|
88
|
+
if (resolvedPath === publicRoot || resolvedPath.startsWith(`${publicRoot}${path.sep}`))
|
|
89
|
+
try {
|
|
90
|
+
if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) {
|
|
91
|
+
const ext = path.extname(resolvedPath).toLowerCase(), contentType = MIME_TYPES[ext] || "application/octet-stream", isFingerprinted = pathname.startsWith("/__stx/") || /\.[0-9a-f]{8}\./.test(pathname);
|
|
92
|
+
return new Response(Bun.file(resolvedPath), {
|
|
93
|
+
headers: {
|
|
94
|
+
"Content-Type": contentType,
|
|
95
|
+
"Cache-Control": isFingerprinted ? "public, max-age=31536000, immutable" : "public, max-age=3600"
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
98
101
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
const isSpaNav = request.headers.get("X-STX-Router") === "true";
|
|
103
|
+
let matchedRoute = null, params = {};
|
|
104
|
+
matchedRoute = exactRoutes.get(pathname) || null;
|
|
105
|
+
if (!matchedRoute)
|
|
106
|
+
for (const { regex, route, paramNames } of paramRoutes) {
|
|
107
|
+
const match = pathname.match(regex);
|
|
108
|
+
if (match) {
|
|
109
|
+
matchedRoute = route;
|
|
110
|
+
paramNames.forEach((name, i) => {
|
|
111
|
+
params[name] = match[i + 1];
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
112
115
|
}
|
|
116
|
+
if (!matchedRoute) {
|
|
117
|
+
const errorPage = exactRoutes.get("/404") || null;
|
|
118
|
+
if (errorPage) {
|
|
119
|
+
const compiled = compiledTemplates.get(errorPage.pattern);
|
|
120
|
+
if (compiled)
|
|
121
|
+
return new Response(compiled.html, {
|
|
122
|
+
status: 404,
|
|
123
|
+
headers: { "Content-Type": "text/html" }
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return new Response("Not Found", { status: 404 });
|
|
113
127
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
if (isSpaNav) {
|
|
129
|
+
const fragment = fragmentCache.get(matchedRoute.pattern);
|
|
130
|
+
if (fragment) {
|
|
131
|
+
const layoutMetadata = layoutMetadataCache.get(matchedRoute.pattern);
|
|
132
|
+
return new Response(fragment, {
|
|
133
|
+
headers: {
|
|
134
|
+
"Content-Type": "text/html",
|
|
135
|
+
"X-STX-Fragment": "true",
|
|
136
|
+
...runtimeCache.has(matchedRoute.pattern) ? { "X-STX-Runtime": runtimeCache.get(matchedRoute.pattern) ? "true" : "false" } : {},
|
|
137
|
+
...layoutMetadata ? {
|
|
138
|
+
"X-STX-Layout": layoutMetadata.layout,
|
|
139
|
+
"X-STX-Layout-Group": layoutMetadata.group
|
|
140
|
+
} : {},
|
|
141
|
+
"Cache-Control": "no-cache"
|
|
142
|
+
}
|
|
122
143
|
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const compiled = compiledTemplates.get(matchedRoute.pattern);
|
|
147
|
+
if (!compiled) {
|
|
148
|
+
const errorPage = exactRoutes.get("/500") || null;
|
|
149
|
+
if (errorPage) {
|
|
150
|
+
const compiledError = compiledTemplates.get(errorPage.pattern);
|
|
151
|
+
if (compiledError)
|
|
152
|
+
return new Response(compiledError.html, {
|
|
153
|
+
status: 500,
|
|
154
|
+
headers: { "Content-Type": "text/html" }
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
123
158
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (isSpaNav) {
|
|
127
|
-
const fragment = fragmentCache.get(matchedRoute.pattern);
|
|
128
|
-
if (fragment) {
|
|
129
|
-
const layoutMetadata = layoutMetadataCache.get(matchedRoute.pattern);
|
|
130
|
-
return new Response(fragment, {
|
|
159
|
+
if (!compiled.hasServerScripts && Object.keys(compiled.placeholders).length === 0)
|
|
160
|
+
return new Response(compiled.html, {
|
|
131
161
|
headers: {
|
|
132
162
|
"Content-Type": "text/html",
|
|
133
|
-
"
|
|
134
|
-
...runtimeCache.has(matchedRoute.pattern) ? { "X-STX-Runtime": runtimeCache.get(matchedRoute.pattern) ? "true" : "false" } : {},
|
|
135
|
-
...layoutMetadata ? {
|
|
136
|
-
"X-STX-Layout": layoutMetadata.layout,
|
|
137
|
-
"X-STX-Layout-Group": layoutMetadata.group
|
|
138
|
-
} : {},
|
|
139
|
-
"Cache-Control": "no-cache"
|
|
163
|
+
"Cache-Control": "public, max-age=60"
|
|
140
164
|
}
|
|
141
165
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const compiledError = compiledTemplates.get(errorPage.pattern);
|
|
149
|
-
if (compiledError)
|
|
150
|
-
return new Response(compiledError.html, {
|
|
151
|
-
status: 500,
|
|
152
|
-
headers: { "Content-Type": "text/html" }
|
|
166
|
+
try {
|
|
167
|
+
const { html, boundaries } = await hydrateTemplateStream(compiled, { params, request });
|
|
168
|
+
if (boundaries && boundaries.length > 0) {
|
|
169
|
+
const { renderStreamingPage, streamToResponse } = await import("./streaming.js");
|
|
170
|
+
return streamToResponse(renderStreamingPage(html, boundaries, { timeoutMs: 30000 }), {
|
|
171
|
+
headers: { "Cache-Control": "no-cache" }
|
|
153
172
|
});
|
|
154
|
-
}
|
|
155
|
-
return new Response("Internal Server Error", { status: 500 });
|
|
156
|
-
}
|
|
157
|
-
if (!compiled.hasServerScripts && Object.keys(compiled.placeholders).length === 0)
|
|
158
|
-
return new Response(compiled.html, {
|
|
159
|
-
headers: {
|
|
160
|
-
"Content-Type": "text/html",
|
|
161
|
-
"Cache-Control": "public, max-age=60"
|
|
162
173
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
return new Response(html, {
|
|
175
|
+
headers: {
|
|
176
|
+
"Content-Type": "text/html",
|
|
177
|
+
"Cache-Control": "no-cache"
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error(`[stx] Hydration error for ${pathname}:`, error);
|
|
182
|
+
return new Response(compiled.html, {
|
|
183
|
+
headers: { "Content-Type": "text/html" }
|
|
170
184
|
});
|
|
171
185
|
}
|
|
172
|
-
|
|
173
|
-
headers: {
|
|
174
|
-
"Content-Type": "text/html",
|
|
175
|
-
"Cache-Control": "no-cache"
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error(`[stx] Hydration error for ${pathname}:`, error);
|
|
180
|
-
return new Response(compiled.html, {
|
|
181
|
-
headers: { "Content-Type": "text/html" }
|
|
182
|
-
});
|
|
183
|
-
}
|
|
186
|
+
})());
|
|
184
187
|
}
|
|
185
188
|
});
|
|
186
189
|
console.log(`[stx] Production server running at http://localhost:${port}`);
|
package/dist/serve.js
CHANGED
|
@@ -5,6 +5,7 @@ import { findSfcTemplateBlock } from "./sfc-template.js";
|
|
|
5
5
|
import { readMarkdownFile } from "./assets.js";
|
|
6
6
|
import { processDirectives } from "./process.js";
|
|
7
7
|
import { extractVariables } from "./utils.js";
|
|
8
|
+
import { compressResponse } from "./compression.js";
|
|
8
9
|
export function htmlResponse(html, status = 200, headers = {}) {
|
|
9
10
|
return new Response(html, {
|
|
10
11
|
status,
|
|
@@ -157,7 +158,7 @@ ${error instanceof Error ? error.stack : ""}</pre>` : "<h1>Internal Server Error
|
|
|
157
158
|
}
|
|
158
159
|
const server = bunServe({
|
|
159
160
|
port,
|
|
160
|
-
fetch: handleRequest
|
|
161
|
+
fetch: async (request) => compressResponse(request, await handleRequest(request))
|
|
161
162
|
});
|
|
162
163
|
let watcher = null;
|
|
163
164
|
if (watch)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/stx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.170",
|
|
5
5
|
"description": "A performant UI Framework. Powered by Bun.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -117,12 +117,12 @@
|
|
|
117
117
|
"dependencies": {
|
|
118
118
|
"@cwcss/crosswind": "^0.2.14",
|
|
119
119
|
"@stacksjs/clapp": "^0.2.0",
|
|
120
|
-
"@stacksjs/desktop": "0.2.
|
|
121
|
-
"@stacksjs/sanitizer": "0.2.
|
|
120
|
+
"@stacksjs/desktop": "0.2.170",
|
|
121
|
+
"@stacksjs/sanitizer": "0.2.170",
|
|
122
122
|
"@stacksjs/ts-i18n": "^0.1.11",
|
|
123
|
-
"bun-plugin-stx": "0.2.
|
|
123
|
+
"bun-plugin-stx": "0.2.170",
|
|
124
124
|
"bunfig": "^0.15.17",
|
|
125
|
-
"stx-router": "0.2.
|
|
125
|
+
"stx-router": "0.2.170",
|
|
126
126
|
"ts-broadcasting": "^0.0.9",
|
|
127
127
|
"ts-images": "^0.1.9",
|
|
128
128
|
"ts-syntax-highlighter": "^0.2.1"
|