tux-review 0.1.6 → 0.1.7
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/package.json +1 -1
- package/src/server.js +27 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tux-review",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "TUX - TUX UIX review system: attach structured, machine-readable feedback directly to running web UIs (pages, routes, components, elements, states) with a deterministic CLI, canonical JSON and LLM-friendly skills.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
package/src/server.js
CHANGED
|
@@ -287,6 +287,22 @@ function static_(state, res, path) {
|
|
|
287
287
|
res.end(body);
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
// Transparent proxy response headers (SPC 34): forward every upstream header
|
|
291
|
+
// except the hop-by-hop / length headers this server manages itself. Must
|
|
292
|
+
// produce the same header set as py/tux/server.py::_proxy.
|
|
293
|
+
function proxyHeaders(upHeaders, { html, bodyLength }) {
|
|
294
|
+
const headers = {};
|
|
295
|
+
for (const [k, v] of Object.entries(upHeaders)) {
|
|
296
|
+
const lk = k.toLowerCase();
|
|
297
|
+
if (lk === 'connection' || lk === 'transfer-encoding' || lk === 'content-length') continue;
|
|
298
|
+
if (html && lk === 'cache-control') continue;
|
|
299
|
+
headers[k] = v;
|
|
300
|
+
}
|
|
301
|
+
if (html) headers['cache-control'] = 'no-store';
|
|
302
|
+
headers['content-length'] = String(bodyLength);
|
|
303
|
+
return headers;
|
|
304
|
+
}
|
|
305
|
+
|
|
290
306
|
function proxy(state, req, res) {
|
|
291
307
|
const target = new URL(state.target);
|
|
292
308
|
const options = {
|
|
@@ -298,25 +314,17 @@ function proxy(state, req, res) {
|
|
|
298
314
|
headers: { ...req.headers, host: target.host },
|
|
299
315
|
};
|
|
300
316
|
const upstream = httpRequest(options, (up) => {
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
headers['cache-control'] = 'no-store';
|
|
313
|
-
res.writeHead(up.statusCode, headers);
|
|
314
|
-
res.end(body);
|
|
315
|
-
});
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
res.writeHead(up.statusCode, up.headers);
|
|
319
|
-
up.pipe(res);
|
|
317
|
+
const isHtml = (up.headers['content-type'] ?? '').startsWith('text/html');
|
|
318
|
+
const chunks = [];
|
|
319
|
+
up.on('data', (c) => chunks.push(c));
|
|
320
|
+
up.on('end', () => {
|
|
321
|
+
const raw = Buffer.concat(chunks);
|
|
322
|
+
// Injection lengthens the body, so the upstream Content-Length must be
|
|
323
|
+
// recomputed; non-HTML passes through as raw bytes (no text round-trip).
|
|
324
|
+
const out = isHtml ? Buffer.from(inject(raw.toString('utf8')), 'utf8') : raw;
|
|
325
|
+
res.writeHead(up.statusCode, proxyHeaders(up.headers, { html: isHtml, bodyLength: out.length }));
|
|
326
|
+
res.end(out);
|
|
327
|
+
});
|
|
320
328
|
});
|
|
321
329
|
upstream.on('error', (err) => {
|
|
322
330
|
sendJson(res, 502, { error: { code: 'bad_gateway', message: `target unreachable: ${err.message}` } });
|