@sleep2agi/commhub-server 0.9.0-preview.21 → 0.9.0-preview.22
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/README.md +1 -1
- package/bin/commhub.ts +6 -2
- package/package.json +1 -1
- package/src/api-host-supervisors-fallback.test.ts +15 -5
- package/src/avatar-validate.test.ts +113 -0
- package/src/avatar-validate.ts +69 -0
- package/src/boot-side-effect.test.ts +124 -0
- package/src/db.ts +10 -0
- package/src/health-redaction.test.ts +176 -0
- package/src/index.ts +11 -2385
- package/src/observer-avatar-http.test.ts +349 -0
- package/src/observer-push.test.ts +176 -0
- package/src/push.ts +87 -7
- package/src/response-charset.test.ts +130 -0
- package/src/server.ts +2642 -0
- package/src/tools.ts +8 -1
- package/src/uploads-http.test.ts +11 -8
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// #426 unit tests for the withUtf8CharsetContentType helper — the
|
|
2
|
+
// piece that rewrites text-y Content-Type headers to include
|
|
3
|
+
// charset=utf-8 so legacy Windows clients don't fall back to
|
|
4
|
+
// ISO-8859-1 decoding. The helper lives in server/src/index.ts;
|
|
5
|
+
// to keep the test standalone we re-implement its pure semantics here
|
|
6
|
+
// and assert those. Any drift between this pure copy and the
|
|
7
|
+
// production helper is caught by the "identical semantics" test at
|
|
8
|
+
// the bottom via a source grep.
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
|
|
14
|
+
// Pure re-implementation — MUST match server/src/index.ts:withUtf8CharsetContentType.
|
|
15
|
+
function withUtf8CharsetContentType(res: Response): Response {
|
|
16
|
+
const ct = res.headers.get("content-type");
|
|
17
|
+
if (!ct) return res;
|
|
18
|
+
if (/;\s*charset=/i.test(ct)) return res;
|
|
19
|
+
const trimmed = ct.trim();
|
|
20
|
+
if (
|
|
21
|
+
trimmed === "application/json" ||
|
|
22
|
+
trimmed.startsWith("text/") ||
|
|
23
|
+
trimmed.startsWith("application/json;") ||
|
|
24
|
+
trimmed === "application/ld+json" ||
|
|
25
|
+
trimmed === "application/xml"
|
|
26
|
+
) {
|
|
27
|
+
res.headers.set("content-type", `${trimmed}; charset=utf-8`);
|
|
28
|
+
}
|
|
29
|
+
return res;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function makeRes(contentType: string | null, body = ""): Response {
|
|
33
|
+
return new Response(body, { headers: contentType ? { "content-type": contentType } : {} });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("withUtf8CharsetContentType — #426", () => {
|
|
37
|
+
test("appends charset to text/event-stream", () => {
|
|
38
|
+
const res = withUtf8CharsetContentType(makeRes("text/event-stream"));
|
|
39
|
+
expect(res.headers.get("content-type")).toBe("text/event-stream; charset=utf-8");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("appends charset to application/json (bare)", () => {
|
|
43
|
+
const res = withUtf8CharsetContentType(makeRes("application/json"));
|
|
44
|
+
expect(res.headers.get("content-type")).toBe("application/json; charset=utf-8");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("appends charset to text/plain", () => {
|
|
48
|
+
const res = withUtf8CharsetContentType(makeRes("text/plain"));
|
|
49
|
+
expect(res.headers.get("content-type")).toBe("text/plain; charset=utf-8");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("appends charset to text/html", () => {
|
|
53
|
+
const res = withUtf8CharsetContentType(makeRes("text/html"));
|
|
54
|
+
expect(res.headers.get("content-type")).toBe("text/html; charset=utf-8");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("no-op when charset already present (case-insensitive)", () => {
|
|
58
|
+
for (const ct of [
|
|
59
|
+
"application/json; charset=utf-8",
|
|
60
|
+
"application/json;charset=UTF-8",
|
|
61
|
+
"text/event-stream;Charset=us-ascii",
|
|
62
|
+
"text/plain; CHARSET=iso-8859-1",
|
|
63
|
+
]) {
|
|
64
|
+
const res = withUtf8CharsetContentType(makeRes(ct));
|
|
65
|
+
expect(res.headers.get("content-type")).toBe(ct);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("no-op on binary types (opaque)", () => {
|
|
70
|
+
for (const ct of [
|
|
71
|
+
"application/octet-stream",
|
|
72
|
+
"image/png",
|
|
73
|
+
"image/jpeg",
|
|
74
|
+
"video/mp4",
|
|
75
|
+
"application/pdf",
|
|
76
|
+
"font/woff2",
|
|
77
|
+
]) {
|
|
78
|
+
const res = withUtf8CharsetContentType(makeRes(ct));
|
|
79
|
+
expect(res.headers.get("content-type")).toBe(ct);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("no-op when Content-Type header is absent entirely (returns as-is)", () => {
|
|
84
|
+
const res = withUtf8CharsetContentType(makeRes(null));
|
|
85
|
+
expect(res.headers.get("content-type")).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("handles application/ld+json / application/xml", () => {
|
|
89
|
+
expect(withUtf8CharsetContentType(makeRes("application/ld+json")).headers.get("content-type"))
|
|
90
|
+
.toBe("application/ld+json; charset=utf-8");
|
|
91
|
+
expect(withUtf8CharsetContentType(makeRes("application/xml")).headers.get("content-type"))
|
|
92
|
+
.toBe("application/xml; charset=utf-8");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("preserves non-charset parameters and appends charset (application/json; boundary=…)", () => {
|
|
96
|
+
// Bun's Response.json emits `application/json;charset=utf-8`; the test
|
|
97
|
+
// above covers that no-op path. When a caller sends a parametrised
|
|
98
|
+
// header like `application/json; profile=…`, the helper still recognises
|
|
99
|
+
// it via the `application/json;` prefix and appends the charset.
|
|
100
|
+
const res = withUtf8CharsetContentType(makeRes("application/json; profile=example"));
|
|
101
|
+
expect(res.headers.get("content-type")).toBe("application/json; profile=example; charset=utf-8");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("returns the same Response instance (mutates in place)", () => {
|
|
105
|
+
const input = makeRes("text/event-stream");
|
|
106
|
+
const output = withUtf8CharsetContentType(input);
|
|
107
|
+
expect(output).toBe(input);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Drift guard: the pure copy in this file MUST match the production
|
|
111
|
+
// helper's branching set. If someone edits one without touching the
|
|
112
|
+
// other, this test fails visibly instead of the helper going silently
|
|
113
|
+
// out of sync. Kept as fragment-level greps (not whole-body match) so
|
|
114
|
+
// added comments don't break the assertion.
|
|
115
|
+
test("production helper covers the same content-type branches", () => {
|
|
116
|
+
const src = readFileSync(join(import.meta.dir, "server.ts"), "utf-8");
|
|
117
|
+
expect(src).toContain("function withUtf8CharsetContentType(res: Response): Response {");
|
|
118
|
+
expect(src).toContain("if (/;\\s*charset=/i.test(ct)) return res;");
|
|
119
|
+
for (const branch of [
|
|
120
|
+
'trimmed === "application/json"',
|
|
121
|
+
'trimmed.startsWith("text/")',
|
|
122
|
+
'trimmed.startsWith("application/json;")',
|
|
123
|
+
'trimmed === "application/ld+json"',
|
|
124
|
+
'trimmed === "application/xml"',
|
|
125
|
+
]) {
|
|
126
|
+
expect(src).toContain(branch);
|
|
127
|
+
}
|
|
128
|
+
expect(src).toContain('res.headers.set("content-type", `${trimmed}; charset=utf-8`);');
|
|
129
|
+
});
|
|
130
|
+
});
|