buninu 0.3.0 → 0.4.0
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/CHANGELOG.md +71 -0
- package/README.md +334 -37
- package/apps/bunmsh/CHANGELOG.md +262 -0
- package/apps/bunmsh/LICENSE +21 -0
- package/apps/bunmsh/LICENSE-MKSH +295 -0
- package/apps/bunmsh/PORTING.md +192 -0
- package/apps/bunmsh/README.md +801 -0
- package/apps/bunmsh/bunmsh +7 -0
- package/apps/bunmsh/bunshell.md +61 -0
- package/apps/bunmsh/help/README.md +1945 -0
- package/apps/bunmsh/help/alias.md +22 -0
- package/apps/bunmsh/help/basename.md +38 -0
- package/apps/bunmsh/help/builtin.md +25 -0
- package/apps/bunmsh/help/bun.md +27 -0
- package/apps/bunmsh/help/bunmsh.md +47 -0
- package/apps/bunmsh/help/cat.md +31 -0
- package/apps/bunmsh/help/catfancy.md +35 -0
- package/apps/bunmsh/help/cd.md +22 -0
- package/apps/bunmsh/help/chmod.md +27 -0
- package/apps/bunmsh/help/clear.md +21 -0
- package/apps/bunmsh/help/colon.md +22 -0
- package/apps/bunmsh/help/command.md +27 -0
- package/apps/bunmsh/help/cp.md +28 -0
- package/apps/bunmsh/help/cut.md +25 -0
- package/apps/bunmsh/help/date.md +25 -0
- package/apps/bunmsh/help/dirname.md +25 -0
- package/apps/bunmsh/help/echo.md +25 -0
- package/apps/bunmsh/help/env.md +27 -0
- package/apps/bunmsh/help/eval.md +22 -0
- package/apps/bunmsh/help/exec.md +22 -0
- package/apps/bunmsh/help/exit.md +21 -0
- package/apps/bunmsh/help/export.md +22 -0
- package/apps/bunmsh/help/false.md +22 -0
- package/apps/bunmsh/help/find.md +33 -0
- package/apps/bunmsh/help/getopts.md +28 -0
- package/apps/bunmsh/help/grep.md +34 -0
- package/apps/bunmsh/help/head.md +27 -0
- package/apps/bunmsh/help/kill.md +27 -0
- package/apps/bunmsh/help/ln.md +28 -0
- package/apps/bunmsh/help/ls.md +37 -0
- package/apps/bunmsh/help/lsbun.md +29 -0
- package/apps/bunmsh/help/lsfancy.md +37 -0
- package/apps/bunmsh/help/md5sum.md +21 -0
- package/apps/bunmsh/help/mkdir.md +27 -0
- package/apps/bunmsh/help/mktemp.md +26 -0
- package/apps/bunmsh/help/mv.md +26 -0
- package/apps/bunmsh/help/print.md +30 -0
- package/apps/bunmsh/help/printf.md +25 -0
- package/apps/bunmsh/help/pwd.md +21 -0
- package/apps/bunmsh/help/read.md +27 -0
- package/apps/bunmsh/help/readonly.md +27 -0
- package/apps/bunmsh/help/realpath.md +21 -0
- package/apps/bunmsh/help/rm.md +33 -0
- package/apps/bunmsh/help/rmdir.md +27 -0
- package/apps/bunmsh/help/sed.md +29 -0
- package/apps/bunmsh/help/seq.md +29 -0
- package/apps/bunmsh/help/serve.md +50 -0
- package/apps/bunmsh/help/set.md +26 -0
- package/apps/bunmsh/help/sha256sum.md +21 -0
- package/apps/bunmsh/help/shift.md +23 -0
- package/apps/bunmsh/help/sleep.md +25 -0
- package/apps/bunmsh/help/sort.md +29 -0
- package/apps/bunmsh/help/source.md +23 -0
- package/apps/bunmsh/help/tab.md +30 -0
- package/apps/bunmsh/help/tac.md +29 -0
- package/apps/bunmsh/help/tail.md +27 -0
- package/apps/bunmsh/help/tee.md +25 -0
- package/apps/bunmsh/help/test.md +30 -0
- package/apps/bunmsh/help/time.md +29 -0
- package/apps/bunmsh/help/touch.md +26 -0
- package/apps/bunmsh/help/tr.md +26 -0
- package/apps/bunmsh/help/true.md +22 -0
- package/apps/bunmsh/help/type.md +21 -0
- package/apps/bunmsh/help/umask.md +22 -0
- package/apps/bunmsh/help/unalias.md +28 -0
- package/apps/bunmsh/help/uname.md +31 -0
- package/apps/bunmsh/help/unset.md +28 -0
- package/apps/bunmsh/help/wc.md +27 -0
- package/apps/bunmsh/help/whence.md +26 -0
- package/apps/bunmsh/help/which.md +41 -0
- package/apps/bunmsh/help/yes.md +23 -0
- package/apps/bunmsh/package.json +26 -0
- package/apps/bunmsh/serve.js +468 -0
- package/apps/bunmsh/single-exe/README.md +402 -0
- package/apps/bunmsh/single-exe/assetsHelper.js +371 -0
- package/apps/bunmsh/single-exe/assetsLoader.mjs +85 -0
- package/apps/bunmsh/single-exe/assetsPacker.js +218 -0
- package/apps/bunmsh/single-exe/compiled.js +527 -0
- package/apps/bunmsh/single-exe/entry.mjs +17 -0
- package/apps/bunmsh/src/cat-fancy.js +117 -0
- package/apps/bunmsh/src/cat-operands.js +31 -0
- package/apps/bunmsh/src/completion.js +247 -0
- package/apps/bunmsh/src/environment.js +61 -0
- package/apps/bunmsh/src/fancy-ls.js +186 -0
- package/apps/bunmsh/src/find.js +119 -0
- package/apps/bunmsh/src/history.js +198 -0
- package/apps/bunmsh/src/main.js +776 -0
- package/apps/bunmsh/src/mouse.js +103 -0
- package/apps/bunmsh/src/shell.js +3557 -0
- package/apps/bunmsh/test/catfancy.test.js +102 -0
- package/apps/bunmsh/test/reference-source.sh +1 -0
- package/apps/bunmsh/test/reference.test.js +677 -0
- package/apps/bunmsh/test/serve/README.md +3 -0
- package/apps/bunmsh/test/serve/bad.json +1 -0
- package/apps/bunmsh/test/serve/data.json +1 -0
- package/apps/bunmsh/test/serve/data.json5 +1 -0
- package/apps/bunmsh/test/serve/data.jsonc +2 -0
- package/apps/bunmsh/test/serve/data.jsonl +2 -0
- package/apps/bunmsh/test/serve/data.ndjson +1 -0
- package/apps/bunmsh/test/serve/data.toml +3 -0
- package/apps/bunmsh/test/serve/data.ts +15 -0
- package/apps/bunmsh/test/serve/data.xml +1 -0
- package/apps/bunmsh/test/serve/data.yaml +3 -0
- package/apps/bunmsh/test/serve/data.yml +1 -0
- package/apps/bunmsh/test/serve.test.js +241 -0
- package/apps/bunmsh/test/shell.test.js +1778 -0
- package/apps/bunmsh/test/t.sh +12 -0
- package/apps/cmdlist +6 -1
- package/apps/jsgotty/gotty.js +65 -76
- package/apps/jsgotty/package.json +6 -1
- package/apps/jsgotty/single-exe/README.md +358 -54
- package/apps/jsgotty/single-exe/assetsHelper.js +293 -12
- package/apps/jsgotty/single-exe/assetsLoader.mjs +1 -1
- package/apps/jsgotty/single-exe/assetsPacker.js +218 -0
- package/apps/jsgotty/single-exe/compiled.js +379 -19
- package/apps/jsgotty/single-exe/entry.mjs +16 -2
- package/apps/jsgotty/zmodem-node.js +14 -12
- package/apps/jsmdcui/CHANGELOG.md +56 -0
- package/apps/jsmdcui/package.json +12 -2
- package/apps/jsmdcui/single-exe/README.md +298 -375
- package/apps/jsmdcui/single-exe/assetsHelper.js +198 -18
- package/apps/jsmdcui/single-exe/assetsLoader.mjs +1 -1
- package/apps/jsmdcui/single-exe/assetsPacker.js +218 -0
- package/apps/jsmdcui/single-exe/compiled.js +291 -5
- package/apps/jsmdcui/single-exe/entry.mjs +7 -0
- package/apps/jsmdcui/src/index.js +11 -8
- package/apps/jsmdcui/tests/jmigt.js +16 -0
- package/bin/bunmsh.bat +3 -0
- package/bin/bunmsh.sh +5 -0
- package/bin/init.js +48 -5
- package/bin/install.js +541 -0
- package/package.json +4 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"line":3}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface Item {
|
|
2
|
+
name: string;
|
|
3
|
+
count: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function total<T extends Item>(items: T[]): number {
|
|
7
|
+
return items.reduce((sum, item) => sum + item.count, 0);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const items: Item[] = [
|
|
11
|
+
{ name: "ts", count: 1 },
|
|
12
|
+
{ name: "js", count: 2 },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export const grandTotal: number = total(items);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<root><item id="1">xml</item></root>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
name: yml
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { afterAll, beforeAll, expect, test } from "bun:test";
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
highlightJson,
|
|
6
|
+
main as startFileServer,
|
|
7
|
+
openTargetUrl,
|
|
8
|
+
parseServeArguments,
|
|
9
|
+
randomServeRoute,
|
|
10
|
+
resolveBunfsPath,
|
|
11
|
+
} from "../serve.js";
|
|
12
|
+
|
|
13
|
+
const cwd = join(import.meta.dir, "serve");
|
|
14
|
+
const servePath = join(import.meta.dir, "..", "serve.js");
|
|
15
|
+
let previousPort;
|
|
16
|
+
let server;
|
|
17
|
+
|
|
18
|
+
// SERVE_AUTO_OPEN/SERVE_MINAPK_WEBVIEW/SERVE_RANDOM_URL are read straight off
|
|
19
|
+
// process.env at module load (not passed through parseServeArguments'
|
|
20
|
+
// `env` parameter) so a bundler can inline them as build-time constants.
|
|
21
|
+
// That means their defaults can only be observed from a fresh process
|
|
22
|
+
// started with those variables already set.
|
|
23
|
+
function parseServeArgumentsWithEnv(args, env) {
|
|
24
|
+
const script = `
|
|
25
|
+
const { parseServeArguments } = await import(${JSON.stringify(servePath)});
|
|
26
|
+
const result = parseServeArguments(${JSON.stringify(args)}, undefined, "/cwd");
|
|
27
|
+
delete result.env;
|
|
28
|
+
console.log(JSON.stringify(result));
|
|
29
|
+
`;
|
|
30
|
+
const proc = Bun.spawnSync({
|
|
31
|
+
cmd: [process.execPath, "-e", script],
|
|
32
|
+
env: { ...process.env, SERVE_AUTO_OPEN: "", SERVE_MINAPK_WEBVIEW: "", SERVE_RANDOM_URL: "", ...env },
|
|
33
|
+
});
|
|
34
|
+
if (proc.exitCode !== 0) throw new Error(proc.stderr.toString());
|
|
35
|
+
return JSON.parse(proc.stdout.toString());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
test("serve highlights pretty JSON using jsmdcui json syntax groups", () => {
|
|
39
|
+
const html = highlightJson('{"key":"line\\n <tag> & \\"quoted\\" \'apostrophe\'","number":12,"ok":true,"empty":null}');
|
|
40
|
+
expect(html).toContain('<span class="json-statement">"key":</span>');
|
|
41
|
+
expect(html).toContain('<span class="json-special">\\n</span>');
|
|
42
|
+
expect(html).toContain('<span class="json-number">12</span>');
|
|
43
|
+
expect(html).toContain('<span class="json-constant">true</span>');
|
|
44
|
+
expect(html).toContain('<span class="json-constant">null</span>');
|
|
45
|
+
expect(html).toContain("<tag> &");
|
|
46
|
+
expect(html).toContain("'apostrophe'");
|
|
47
|
+
expect(html).not.toContain("<tag>");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("serve accepts either bunfs spelling and derives the platform path", () => {
|
|
51
|
+
expect(resolveBunfsPath("/$bunfs", "/$bunfs/root")).toBe("/$bunfs/root");
|
|
52
|
+
expect(resolveBunfsPath("/$bunfs/assets/file.txt", "/$bunfs/root"))
|
|
53
|
+
.toBe("/$bunfs/root/assets/file.txt");
|
|
54
|
+
expect(resolveBunfsPath("B:/~BUN/root/assets/file.txt", "/$bunfs/root"))
|
|
55
|
+
.toBe("/$bunfs/root/assets/file.txt");
|
|
56
|
+
expect(resolveBunfsPath("/$bunfs", "B:\\~BUN\\root")).toBe("B:/~BUN/root");
|
|
57
|
+
expect(resolveBunfsPath("/$bunfs/assets/file.txt", "B:\\~BUN\\root"))
|
|
58
|
+
.toBe("B:/~BUN/root/assets/file.txt");
|
|
59
|
+
expect(resolveBunfsPath("B:/~BUN/root/assets/file.txt", "B:\\~BUN\\root"))
|
|
60
|
+
.toBe("B:/~BUN/root/assets/file.txt");
|
|
61
|
+
expect(resolveBunfsPath("/$bunfs", "/source/bunmsh")).toBeNull();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("serve CLI flags force options on even over an inline environment default left off", () => {
|
|
65
|
+
expect(parseServeArgumentsWithEnv(["--auto-open", "--minapk-webview", "--random-url", "public"], {
|
|
66
|
+
SERVE_AUTO_OPEN: "0",
|
|
67
|
+
SERVE_MINAPK_WEBVIEW: "0",
|
|
68
|
+
SERVE_RANDOM_URL: "false",
|
|
69
|
+
})).toMatchObject({
|
|
70
|
+
directory: "public",
|
|
71
|
+
autoOpen: true,
|
|
72
|
+
minapkWebview: "1",
|
|
73
|
+
randomUrl: true,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("serve options default to off/not-passed with no flags or environment", () => {
|
|
78
|
+
expect(parseServeArgumentsWithEnv([], {})).toMatchObject({
|
|
79
|
+
directory: "/cwd",
|
|
80
|
+
autoOpen: false,
|
|
81
|
+
minapkWebview: null,
|
|
82
|
+
randomUrl: false,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("serve inline environment defaults carry through unless overridden", () => {
|
|
87
|
+
const env = { SERVE_AUTO_OPEN: "1", SERVE_MINAPK_WEBVIEW: "yes", SERVE_RANDOM_URL: "on" };
|
|
88
|
+
expect(parseServeArgumentsWithEnv([], env)).toMatchObject({
|
|
89
|
+
autoOpen: true,
|
|
90
|
+
minapkWebview: "1",
|
|
91
|
+
randomUrl: true,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("serve CLI =off/no/false/empty explicitly disables an environment default turned on", () => {
|
|
96
|
+
const env = { SERVE_AUTO_OPEN: "1", SERVE_MINAPK_WEBVIEW: "1", SERVE_RANDOM_URL: "true" };
|
|
97
|
+
expect(parseServeArgumentsWithEnv(
|
|
98
|
+
["--auto-open=off", "--minapk-webview=no", "--random-url=false"], env,
|
|
99
|
+
)).toMatchObject({ autoOpen: false, minapkWebview: null, randomUrl: false });
|
|
100
|
+
expect(parseServeArgumentsWithEnv(["--auto-open=", "--random-url="], env))
|
|
101
|
+
.toMatchObject({ autoOpen: false, randomUrl: false });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("serve --auto-open=/path sets a suffix path instead of plain on/off", () => {
|
|
105
|
+
expect(parseServeArguments(["--auto-open=/index.html"], {}, "/cwd"))
|
|
106
|
+
.toMatchObject({ autoOpen: "/index.html" });
|
|
107
|
+
expect(parseServeArguments(["--auto-open=/nested/page.html"], {}, "/cwd"))
|
|
108
|
+
.toMatchObject({ autoOpen: "/nested/page.html" });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("serve SERVE_AUTO_OPEN=/path sets the same suffix as a default, CLI still overrides it", () => {
|
|
112
|
+
expect(parseServeArgumentsWithEnv([], { SERVE_AUTO_OPEN: "/default.html" }))
|
|
113
|
+
.toMatchObject({ autoOpen: "/default.html" });
|
|
114
|
+
expect(parseServeArgumentsWithEnv(["--auto-open=off"], { SERVE_AUTO_OPEN: "/default.html" }))
|
|
115
|
+
.toMatchObject({ autoOpen: false });
|
|
116
|
+
expect(parseServeArgumentsWithEnv(["--auto-open=/override.html"], { SERVE_AUTO_OPEN: "/default.html" }))
|
|
117
|
+
.toMatchObject({ autoOpen: "/override.html" });
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("openTargetUrl resolves a /path suffix relative to the served URL, including any random-url prefix", () => {
|
|
121
|
+
const base = new URL("http://localhost:3000/");
|
|
122
|
+
expect(openTargetUrl(base, true).href).toBe(base.href);
|
|
123
|
+
expect(openTargetUrl(base, false).href).toBe(base.href);
|
|
124
|
+
expect(openTargetUrl(base, "/index.html").href).toBe("http://localhost:3000/index.html");
|
|
125
|
+
expect(openTargetUrl(base, "/nested/page.html").href).toBe("http://localhost:3000/nested/page.html");
|
|
126
|
+
|
|
127
|
+
const prefixed = new URL("http://localhost:3000/AaBIsecretprefix/");
|
|
128
|
+
expect(openTargetUrl(prefixed, "/index.html").href)
|
|
129
|
+
.toBe("http://localhost:3000/AaBIsecretprefix/index.html");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("serve --minapk-webview=N passes the literal digit string", () => {
|
|
133
|
+
expect(parseServeArguments(["--minapk-webview=42"], {}, "/cwd"))
|
|
134
|
+
.toMatchObject({ minapkWebview: "42" });
|
|
135
|
+
expect(parseServeArguments(["--minapk-webview=bogus"], {}, "/cwd"))
|
|
136
|
+
.toMatchObject({ error: "invalid value for --minapk-webview: bogus" });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("serve --minapk-webview never consumes a following bare argument as its value", () => {
|
|
140
|
+
// Only `--minapk-webview=N` sets a value; a space-separated "1" must stay a
|
|
141
|
+
// directory operand, or a real directory named "1" would collide with it.
|
|
142
|
+
expect(parseServeArguments(["--minapk-webview", "1"], {}, "/cwd"))
|
|
143
|
+
.toMatchObject({ minapkWebview: "1", directory: "1" });
|
|
144
|
+
expect(parseServeArguments(["--minapk-webview", "1", "dir"], {}, "/cwd"))
|
|
145
|
+
.toMatchObject({ error: "too many directory operands" });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("serve random routes have high entropy and are URL-safe", () => {
|
|
149
|
+
const first = randomServeRoute();
|
|
150
|
+
const second = randomServeRoute();
|
|
151
|
+
expect(first).not.toBe(second);
|
|
152
|
+
expect(first).toMatch(/^[A-Za-z0-9_-]{80,}\/$/);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
beforeAll(() => {
|
|
156
|
+
previousPort = process.env.PORT;
|
|
157
|
+
process.env.PORT = "0";
|
|
158
|
+
server = startFileServer(cwd);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
afterAll(() => {
|
|
162
|
+
server?.stop(true);
|
|
163
|
+
if (previousPort === undefined) delete process.env.PORT;
|
|
164
|
+
else process.env.PORT = previousPort;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("serve exposes linked Markdown and parser previews", async () => {
|
|
168
|
+
const index = await fetch(server.url).then((response) => response.text());
|
|
169
|
+
expect(index).toContain('href="README.md-bun.markdown.html"');
|
|
170
|
+
expect(index).toContain('href="data.json-json.parse"');
|
|
171
|
+
expect(index).toContain('href="data.json5-bun.json5.parse"');
|
|
172
|
+
expect(index).toContain("🔍");
|
|
173
|
+
if (typeof Bun.XML?.parse === "function") {
|
|
174
|
+
expect(index).toContain('href="data.xml-bun.xml.parse"');
|
|
175
|
+
const xml = await fetch(new URL("data.xml-bun.xml.parse", server.url));
|
|
176
|
+
expect(xml.status).toBe(200);
|
|
177
|
+
expect(await xml.text()).toContain("xml");
|
|
178
|
+
} else {
|
|
179
|
+
expect(index).not.toContain('href="data.xml-bun.xml.parse"');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const markdown = await fetch(new URL("README.md-bun.markdown.html", server.url));
|
|
183
|
+
expect(markdown.status).toBe(200);
|
|
184
|
+
expect(await markdown.text()).toContain("<strong>Bun</strong>");
|
|
185
|
+
|
|
186
|
+
const previews = [
|
|
187
|
+
["data.json-json.parse", '"name": "json"'],
|
|
188
|
+
["data.json5-bun.json5.parse", '"name": "json5"'],
|
|
189
|
+
["data.jsonc-bun.jsonc.parse", '"name": "jsonc"'],
|
|
190
|
+
["data.jsonl-bun.jsonl.parse", '"line": 2'],
|
|
191
|
+
["data.ndjson-bun.jsonl.parse", '"line": 3'],
|
|
192
|
+
["data.yaml-bun.yaml.parse", '"name": "yaml"'],
|
|
193
|
+
["data.yml-bun.yaml.parse", '"name": "yml"'],
|
|
194
|
+
["data.toml-bun.toml.parse", '"name": "toml"'],
|
|
195
|
+
];
|
|
196
|
+
for (const [path, expected] of previews) {
|
|
197
|
+
const response = await fetch(new URL(path, server.url));
|
|
198
|
+
expect(response.status).toBe(200);
|
|
199
|
+
const html = await response.text();
|
|
200
|
+
expect(html.replace(/<\/?span(?: [^>]*)?>/g, ""))
|
|
201
|
+
.toContain(expected.replaceAll('"', """));
|
|
202
|
+
expect(html).toContain('class="json-statement"');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
expect((await fetch(new URL("bad.json-json.parse", server.url))).status).toBe(422);
|
|
206
|
+
const afterParserError = await fetch(server.url);
|
|
207
|
+
expect(afterParserError.status).toBe(200);
|
|
208
|
+
expect(await afterParserError.text()).toContain("README.md");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("serve keeps native HTTP Range handling on original files", async () => {
|
|
212
|
+
const response = await fetch(new URL("README.md", server.url), {
|
|
213
|
+
headers: { range: "bytes=0-8" },
|
|
214
|
+
});
|
|
215
|
+
expect(response.status).toBe(206);
|
|
216
|
+
expect(response.headers.get("content-range")).toBe("bytes 0-8/26");
|
|
217
|
+
expect(await response.text()).toBe("# Preview");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("serve discovers request-time files without rebuilding routes", async () => {
|
|
221
|
+
const path = join(cwd, "created-after-start.txt");
|
|
222
|
+
try {
|
|
223
|
+
await Bun.write(path, "dynamic file");
|
|
224
|
+
const index = await fetch(server.url).then((response) => response.text());
|
|
225
|
+
expect(index).toContain("created-after-start.txt");
|
|
226
|
+
const response = await fetch(new URL("created-after-start.txt", server.url), {
|
|
227
|
+
headers: { range: "bytes=0-6" },
|
|
228
|
+
});
|
|
229
|
+
expect(response.status).toBe(206);
|
|
230
|
+
expect(response.headers.get("content-range")).toBe("bytes 0-6/12");
|
|
231
|
+
expect(await response.text()).toBe("dynamic");
|
|
232
|
+
} finally {
|
|
233
|
+
rmSync(path, { force: true });
|
|
234
|
+
}
|
|
235
|
+
expect((await fetch(new URL("created-after-start.txt", server.url))).status).toBe(404);
|
|
236
|
+
expect((await fetch(server.url)).status).toBe(200);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("serve rejects paths escaping the served root", async () => {
|
|
240
|
+
expect((await fetch(new URL("/%5c..%5cpackage.json", server.url))).status).toBe(400);
|
|
241
|
+
});
|