@opencode/util 0.0.0-dev-21018 → 0.0.0-dev-21022
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/npm.js +9 -1
- package/package.json +1 -1
- package/dist/activity-calendar.test.d.ts +0 -1
- package/dist/activity-calendar.test.js +0 -119
- package/dist/artifact.test.d.ts +0 -1
- package/dist/artifact.test.js +0 -28
- package/dist/base64.test.d.ts +0 -1
- package/dist/base64.test.js +0 -10
- package/dist/binary.test.d.ts +0 -1
- package/dist/binary.test.js +0 -16
- package/dist/bom.test.d.ts +0 -1
- package/dist/bom.test.js +0 -19
- package/dist/encode.test.d.ts +0 -1
- package/dist/encode.test.js +0 -23
- package/dist/open.test.d.ts +0 -1
- package/dist/open.test.js +0 -14
- package/dist/path.test.d.ts +0 -1
- package/dist/path.test.js +0 -71
- package/dist/retry.test.d.ts +0 -1
- package/dist/retry.test.js +0 -63
package/dist/npm.js
CHANGED
|
@@ -160,6 +160,7 @@ const layer = Layer.effect(Service, Effect.gen(function* () {
|
|
|
160
160
|
...(input.update ? { preferOnline: true, noGitRevCache: true } : {}),
|
|
161
161
|
// Audit reports are unused here, but Arborist waits for them before completing an install.
|
|
162
162
|
audit: false,
|
|
163
|
+
windowsHide: true,
|
|
163
164
|
};
|
|
164
165
|
const arborist = new Arborist({
|
|
165
166
|
...options,
|
|
@@ -284,7 +285,14 @@ const layer = Layer.effect(Service, Effect.gen(function* () {
|
|
|
284
285
|
if (!installed)
|
|
285
286
|
return yield* new InstallFailedError({ dir: root, cause: new Error(`Package is not installed: ${pkg}`) });
|
|
286
287
|
const { manifest, resolve } = yield* Effect.promise(() => import("pacote"));
|
|
287
|
-
const options = {
|
|
288
|
+
const options = {
|
|
289
|
+
...(yield* NpmConfig.load(root)),
|
|
290
|
+
preferOnline: true,
|
|
291
|
+
noGitRevCache: true,
|
|
292
|
+
ignoreScripts: true,
|
|
293
|
+
// Pacote spawns Git directly rather than through AppProcess.
|
|
294
|
+
windowsHide: true,
|
|
295
|
+
};
|
|
288
296
|
const available = yield* Effect.tryPromise({
|
|
289
297
|
try: async () => target.type === "git" ? gitRevision(await resolve(pkg, options)) : (await manifest(pkg, options)).version,
|
|
290
298
|
catch: (cause) => new InstallFailedError({ dir: root, cause }),
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { activityCalendar } from "./activity-calendar.js";
|
|
3
|
-
const input = {
|
|
4
|
-
from: new Date(2026, 0, 1).getTime(),
|
|
5
|
-
to: new Date(2026, 0, 8).getTime(),
|
|
6
|
-
maxWeeks: 53,
|
|
7
|
-
activity: [
|
|
8
|
-
{ date: "2026-01-01", steps: 1 },
|
|
9
|
-
{ date: "2026-01-02", steps: 50 },
|
|
10
|
-
],
|
|
11
|
-
};
|
|
12
|
-
describe("activity calendar", () => {
|
|
13
|
-
test("aligns Monday-first weeks and excludes cells outside the end-exclusive range", () => {
|
|
14
|
-
const calendar = activityCalendar(input);
|
|
15
|
-
expect(calendar.clipped).toBe(false);
|
|
16
|
-
expect(calendar.weeks).toHaveLength(2);
|
|
17
|
-
expect(calendar.weeks[0][0]).toEqual({ date: "2025-12-29", steps: 0, level: -1 });
|
|
18
|
-
expect(calendar.weeks[0][3]).toEqual({ date: "2026-01-01", steps: 1, level: 2 });
|
|
19
|
-
expect(calendar.weeks[0][4]).toEqual({ date: "2026-01-02", steps: 50, level: 4 });
|
|
20
|
-
expect(calendar.weeks[1][2]).toEqual({ date: "2026-01-07", steps: 0, level: 0 });
|
|
21
|
-
expect(calendar.weeks[1][3].level).toBe(-1);
|
|
22
|
-
expect(calendar.months).toEqual([{ week: 0, label: "Jan", span: 2 }]);
|
|
23
|
-
});
|
|
24
|
-
test("clips to the latest weeks and keeps empty days distinct from out-of-range cells", () => {
|
|
25
|
-
const calendar = activityCalendar({ ...input, activity: [], maxWeeks: 1 });
|
|
26
|
-
expect(calendar.clipped).toBe(true);
|
|
27
|
-
expect(calendar.weeks).toHaveLength(1);
|
|
28
|
-
expect(calendar.weeks[0][0]).toEqual({ date: "2026-01-05", steps: 0, level: 0 });
|
|
29
|
-
expect(calendar.weeks[0][3].level).toBe(-1);
|
|
30
|
-
});
|
|
31
|
-
test("caps long histories at 53 weeks", () => {
|
|
32
|
-
const calendar = activityCalendar({ ...input, from: new Date(2020, 0, 1).getTime(), maxWeeks: 200 });
|
|
33
|
-
expect(calendar.clipped).toBe(true);
|
|
34
|
-
expect(calendar.weeks).toHaveLength(53);
|
|
35
|
-
expect(calendar.weeks.at(-1)?.[2].date).toBe("2026-01-07");
|
|
36
|
-
expect(calendar.months.reduce((sum, month) => sum + month.span, 0)).toBe(53);
|
|
37
|
-
});
|
|
38
|
-
test("returns month positions and spans for partial first and last months", () => {
|
|
39
|
-
const calendar = activityCalendar({
|
|
40
|
-
...input,
|
|
41
|
-
from: new Date(2026, 3, 29).getTime(),
|
|
42
|
-
to: new Date(2026, 4, 20).getTime(),
|
|
43
|
-
});
|
|
44
|
-
expect(calendar.months).toEqual([
|
|
45
|
-
{ week: 0, label: "Apr", span: 1 },
|
|
46
|
-
{ week: 1, label: "May", span: 3 },
|
|
47
|
-
]);
|
|
48
|
-
const partial = activityCalendar({
|
|
49
|
-
...input,
|
|
50
|
-
from: new Date(2026, 3, 1).getTime(),
|
|
51
|
-
to: new Date(2026, 3, 2).getTime(),
|
|
52
|
-
});
|
|
53
|
-
expect(partial.months).toEqual([{ week: 0, label: "Apr", span: 1 }]);
|
|
54
|
-
});
|
|
55
|
-
test("places month labels across a year boundary", () => {
|
|
56
|
-
const calendar = activityCalendar({
|
|
57
|
-
...input,
|
|
58
|
-
from: new Date(2025, 11, 22).getTime(),
|
|
59
|
-
to: new Date(2026, 0, 12).getTime(),
|
|
60
|
-
});
|
|
61
|
-
expect(calendar.months).toEqual([
|
|
62
|
-
{ week: 0, label: "Dec", span: 1 },
|
|
63
|
-
{ week: 1, label: "Jan", span: 2 },
|
|
64
|
-
]);
|
|
65
|
-
});
|
|
66
|
-
test("keeps exactly seven distinct cells per week across DST and leap days", () => {
|
|
67
|
-
const calendar = activityCalendar({
|
|
68
|
-
from: new Date(2024, 1, 26).getTime(),
|
|
69
|
-
to: new Date(2024, 2, 12).getTime(),
|
|
70
|
-
maxWeeks: 53,
|
|
71
|
-
activity: [
|
|
72
|
-
{ date: "2024-02-29", steps: 1 },
|
|
73
|
-
{ date: "2024-03-10", steps: 1 },
|
|
74
|
-
],
|
|
75
|
-
});
|
|
76
|
-
expect(calendar.weeks).toHaveLength(3);
|
|
77
|
-
expect(calendar.weeks
|
|
78
|
-
.flat()
|
|
79
|
-
.filter((day) => day.steps > 0)
|
|
80
|
-
.map((day) => day.date)).toEqual(["2024-02-29", "2024-03-10"]);
|
|
81
|
-
expect(new Set(calendar.weeks.flat().map((day) => day.date)).size).toBe(21);
|
|
82
|
-
});
|
|
83
|
-
test("uses the highest intensity for a single distinct positive count", () => {
|
|
84
|
-
const calendar = activityCalendar({ ...input, activity: input.activity.map((day) => ({ ...day, steps: 10 })) });
|
|
85
|
-
expect(calendar.weeks
|
|
86
|
-
.flat()
|
|
87
|
-
.filter((day) => day.steps > 0)
|
|
88
|
-
.map((day) => day.level)).toEqual([4, 4]);
|
|
89
|
-
});
|
|
90
|
-
test("ranks distinct activity counts in four levels", () => {
|
|
91
|
-
const calendar = activityCalendar({
|
|
92
|
-
...input,
|
|
93
|
-
to: new Date(2026, 0, 10).getTime(),
|
|
94
|
-
activity: Array.from({ length: 8 }, (_, index) => ({ date: `2026-01-0${index + 1}`, steps: index + 1 })),
|
|
95
|
-
});
|
|
96
|
-
expect(calendar.weeks
|
|
97
|
-
.flat()
|
|
98
|
-
.filter((day) => day.steps > 0)
|
|
99
|
-
.map((day) => day.level)).toEqual([1, 1, 2, 2, 3, 3, 4, 4]);
|
|
100
|
-
});
|
|
101
|
-
test("keeps intensity relative to the requested range when older weeks are clipped", () => {
|
|
102
|
-
const calendar = activityCalendar({
|
|
103
|
-
...input,
|
|
104
|
-
maxWeeks: 1,
|
|
105
|
-
activity: [
|
|
106
|
-
{ date: "2026-01-01", steps: 50 },
|
|
107
|
-
{ date: "2026-01-05", steps: 1 },
|
|
108
|
-
],
|
|
109
|
-
});
|
|
110
|
-
expect(calendar.weeks[0][0].level).toBe(2);
|
|
111
|
-
});
|
|
112
|
-
test("returns an empty calendar for an empty range", () => {
|
|
113
|
-
expect(activityCalendar({ ...input, to: input.from, activity: [] })).toEqual({
|
|
114
|
-
weeks: [],
|
|
115
|
-
months: [],
|
|
116
|
-
clipped: false,
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
});
|
package/dist/artifact.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/artifact.test.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { artifactKind } from "./artifact.js";
|
|
3
|
-
describe("artifactKind", () => {
|
|
4
|
-
test.each([
|
|
5
|
-
["shot.PNG", "image"],
|
|
6
|
-
["logo.svg", "svg"],
|
|
7
|
-
["song.mp3", "audio"],
|
|
8
|
-
["demo.mp4", "video"],
|
|
9
|
-
["clip.webm", "video"],
|
|
10
|
-
["paper.pdf", "pdf"],
|
|
11
|
-
["out/index.html", "html"],
|
|
12
|
-
["README.md", "markdown"],
|
|
13
|
-
["flow.mmd", "mermaid"],
|
|
14
|
-
["data.csv", "table"],
|
|
15
|
-
["data.tsv", "table"],
|
|
16
|
-
["Inter.woff2", "font"],
|
|
17
|
-
["report.docx", "document"],
|
|
18
|
-
["budget.XLSX", "spreadsheet"],
|
|
19
|
-
["deck.pptx", "presentation"],
|
|
20
|
-
["legacy.doc", "text"],
|
|
21
|
-
["src/app.ts", "text"],
|
|
22
|
-
["Makefile", "text"],
|
|
23
|
-
[".env", "text"],
|
|
24
|
-
["archive.tar.gz", "text"],
|
|
25
|
-
])("classifies %s as %s", (path, kind) => {
|
|
26
|
-
expect(artifactKind(path)).toBe(kind);
|
|
27
|
-
});
|
|
28
|
-
});
|
package/dist/base64.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/base64.test.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { base64ToBytes, bytesToBase64 } from "./base64.js";
|
|
3
|
-
describe("base64", () => {
|
|
4
|
-
test("encodes as standard base64 and decodes back to the same bytes", () => {
|
|
5
|
-
const bytes = Uint8Array.from({ length: 70_000 }, (_, index) => index % 256);
|
|
6
|
-
const encoded = bytesToBase64(bytes);
|
|
7
|
-
expect(encoded).toBe(Buffer.from(bytes).toString("base64"));
|
|
8
|
-
expect(base64ToBytes(encoded)).toEqual(bytes);
|
|
9
|
-
});
|
|
10
|
-
});
|
package/dist/binary.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/binary.test.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { Binary } from "./binary.js";
|
|
3
|
-
describe("binary search", () => {
|
|
4
|
-
test("finds sorted values and their insertion points", () => {
|
|
5
|
-
const values = [{ id: "a" }, { id: "c" }, { id: "e" }];
|
|
6
|
-
expect(Binary.search(values, "c", (item) => item.id)).toEqual({ found: true, index: 1 });
|
|
7
|
-
expect(Binary.search(values, "d", (item) => item.id)).toEqual({ found: false, index: 2 });
|
|
8
|
-
expect(Binary.search(values, "0", (item) => item.id)).toEqual({ found: false, index: 0 });
|
|
9
|
-
});
|
|
10
|
-
test("inserts before the first matching sorted value", () => {
|
|
11
|
-
const values = [{ id: "a" }, { id: "c" }, { id: "c" }, { id: "e" }];
|
|
12
|
-
const item = { id: "c" };
|
|
13
|
-
expect(Binary.insert(values, item, (value) => value.id)).toBe(values);
|
|
14
|
-
expect(values).toEqual([{ id: "a" }, item, { id: "c" }, { id: "c" }, { id: "e" }]);
|
|
15
|
-
});
|
|
16
|
-
});
|
package/dist/bom.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/bom.test.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "bun:test";
|
|
2
|
-
import { Bom } from "./bom.js";
|
|
3
|
-
test.each([
|
|
4
|
-
{ prefix: "", bom: false, expected: undefined },
|
|
5
|
-
{ prefix: "", bom: true, expected: "\uFEFF" },
|
|
6
|
-
{ prefix: "\uFEFF", bom: false, expected: "" },
|
|
7
|
-
{ prefix: "\uFEFF", bom: true, expected: undefined },
|
|
8
|
-
{ prefix: "\uFEFF\uFEFF", bom: false, expected: "" },
|
|
9
|
-
{ prefix: "\uFEFF\uFEFF", bom: true, expected: "\uFEFF" },
|
|
10
|
-
])("syncBytes(%j)", (row) => {
|
|
11
|
-
const encoder = new TextEncoder();
|
|
12
|
-
const text = "a\uFEFF\u00e9";
|
|
13
|
-
const input = encoder.encode(row.prefix + text);
|
|
14
|
-
expect(Bom.syncBytes(input, row.bom)).toEqual({
|
|
15
|
-
text,
|
|
16
|
-
bytes: row.expected === undefined ? undefined : encoder.encode(row.expected + text),
|
|
17
|
-
});
|
|
18
|
-
expect(input).toEqual(encoder.encode(row.prefix + text));
|
|
19
|
-
});
|
package/dist/encode.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/encode.test.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { base64Decode, base64Encode, checksum, sampledChecksum } from "./encode.js";
|
|
3
|
-
describe("frontend encoding", () => {
|
|
4
|
-
test("uses unpadded URL-safe UTF-8 base64", () => {
|
|
5
|
-
expect(base64Encode("hello")).toBe("aGVsbG8");
|
|
6
|
-
expect(base64Encode("✓ à la mode")).toBe("4pyTIMOgIGxhIG1vZGU");
|
|
7
|
-
expect(base64Decode("4pyTIMOgIGxhIG1vZGU")).toBe("✓ à la mode");
|
|
8
|
-
expect(base64Decode("dXNlcjpwYXNz")).toBe("user:pass");
|
|
9
|
-
});
|
|
10
|
-
test("rejects invalid base64", () => {
|
|
11
|
-
expect(() => base64Decode("%%%")).toThrow();
|
|
12
|
-
});
|
|
13
|
-
test("keeps stable FNV checksums", () => {
|
|
14
|
-
expect(checksum("")).toBeUndefined();
|
|
15
|
-
expect(checksum("hello")).toBe("m3bicr");
|
|
16
|
-
expect(checksum("✓ à la mode")).toBe("jmczk0");
|
|
17
|
-
});
|
|
18
|
-
test("samples large values without changing the size boundary", () => {
|
|
19
|
-
const value = "abcdef".repeat(100);
|
|
20
|
-
expect(sampledChecksum(value, value.length)).toBe(checksum(value));
|
|
21
|
-
expect(sampledChecksum(value, value.length - 1)).toBe("600:1isj1k5:1isj1k5:1isj1k5:1isj1k5:1isj1k5");
|
|
22
|
-
});
|
|
23
|
-
});
|
package/dist/open.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/open.test.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { openUrl } from "./open.js";
|
|
3
|
-
describe("openUrl", () => {
|
|
4
|
-
test("rejects values that are not URLs", async () => {
|
|
5
|
-
await expect(openUrl("not a url")).rejects.toThrow("Only http and https links");
|
|
6
|
-
await expect(openUrl("")).rejects.toThrow("Only http and https links");
|
|
7
|
-
});
|
|
8
|
-
test("rejects non-http schemes", async () => {
|
|
9
|
-
await expect(openUrl("file:///etc/hosts")).rejects.toThrow("Only http and https links");
|
|
10
|
-
await expect(openUrl("javascript:alert(1)")).rejects.toThrow("Only http and https links");
|
|
11
|
-
await expect(openUrl("ms-msdt:/id PCWDiagnostic")).rejects.toThrow("Only http and https links");
|
|
12
|
-
await expect(openUrl("\\\\server\\share\\file.html")).rejects.toThrow("Only http and https links");
|
|
13
|
-
});
|
|
14
|
-
});
|
package/dist/path.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/path.test.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { encodeFilePath, getDirectory, getFilename, getFilenameTruncated, truncateMiddle } from "./path.js";
|
|
3
|
-
describe("client paths", () => {
|
|
4
|
-
test("reads POSIX and Windows paths with the same rules", () => {
|
|
5
|
-
expect(getFilename("/repo/src/index.ts")).toBe("index.ts");
|
|
6
|
-
expect(getFilename("C:\\repo\\src\\index.ts\\")).toBe("index.ts");
|
|
7
|
-
expect(getDirectory("/repo/src/index.ts")).toBe("/repo/src/");
|
|
8
|
-
expect(getDirectory("C:\\repo\\src\\index.ts")).toBe("C:/repo/src/");
|
|
9
|
-
});
|
|
10
|
-
test("preserves root, UNC, mixed, and single-segment behavior", () => {
|
|
11
|
-
expect(getFilename("\\\\server\\share\\file")).toBe("file");
|
|
12
|
-
expect(getDirectory("\\\\server\\share\\file")).toBe("//server/share/");
|
|
13
|
-
expect(getDirectory("C:\\repo/src\\file")).toBe("C:/repo/src/");
|
|
14
|
-
expect(getDirectory("file")).toBe("/");
|
|
15
|
-
expect(getFilename(undefined)).toBe("");
|
|
16
|
-
expect(getDirectory("")).toBe("");
|
|
17
|
-
});
|
|
18
|
-
test.each([
|
|
19
|
-
["/repo/src/index.ts///", "index.ts"],
|
|
20
|
-
["C:\\repo\\src\\index.ts", "index.ts"],
|
|
21
|
-
["C:\\repo/src\\file", "file"],
|
|
22
|
-
["C:/repo\\src/file/\\", "file"],
|
|
23
|
-
["/", ""],
|
|
24
|
-
["\\", ""],
|
|
25
|
-
["/\\/\\", ""],
|
|
26
|
-
["C:\\", "C:"],
|
|
27
|
-
["file", "file"],
|
|
28
|
-
["", ""],
|
|
29
|
-
])("reads the filename from %j", (path, filename) => {
|
|
30
|
-
expect(getFilename(path)).toBe(filename);
|
|
31
|
-
});
|
|
32
|
-
test("keeps filename truncation stable", () => {
|
|
33
|
-
expect(getFilenameTruncated("/repo/long-component-name.tsx", 16)).toBe("long-compon….tsx");
|
|
34
|
-
expect(truncateMiddle("abcdefghijklmnop", 9)).toBe("abcd…mnop");
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
describe("encodeFilePath", () => {
|
|
38
|
-
const rows = [
|
|
39
|
-
["/home/user/project/README.md", "/home/user/project/README.md"],
|
|
40
|
-
["/home/user/file#name with spaces.txt", "/home/user/file%23name%20with%20spaces.txt"],
|
|
41
|
-
["/path/to/file#with?special%chars&more.txt", "/path/to/file%23with%3Fspecial%25chars%26more.txt"],
|
|
42
|
-
["/path/to/file?name.txt", "/path/to/file%3Fname.txt"],
|
|
43
|
-
["/path/to/file%name.txt", "/path/to/file%25name.txt"],
|
|
44
|
-
["/home/user/文档/README.md", "/home/user/%E6%96%87%E6%A1%A3/README.md"],
|
|
45
|
-
["/", "/"],
|
|
46
|
-
["", ""],
|
|
47
|
-
["//path//to///file.txt", "//path//to///file.txt"],
|
|
48
|
-
["src/components/App.tsx", "src/components/App.tsx"],
|
|
49
|
-
["src\\components\\App.tsx", "src/components/App.tsx"],
|
|
50
|
-
["D:\\dev\\projects\\opencode\\README.bs.md", "/D:/dev/projects/opencode/README.bs.md"],
|
|
51
|
-
["D:\\dev\\projects\\opencode/README.bs.md", "/D:/dev/projects/opencode/README.bs.md"],
|
|
52
|
-
["C:\\Program Files\\MyApp\\file with spaces.txt", "/C:/Program%20Files/MyApp/file%20with%20spaces.txt"],
|
|
53
|
-
["D:\\projects\\file#name with ?marks.txt", "/D:/projects/file%23name%20with%20%3Fmarks.txt"],
|
|
54
|
-
["C:\\", "/C:/"],
|
|
55
|
-
["c:\\users\\test\\file.txt", "/c:/users/test/file.txt"],
|
|
56
|
-
["D:", "/D:"],
|
|
57
|
-
["C:\\Users\\test\\", "/C:/Users/test/"],
|
|
58
|
-
["C:\\Users\\..\\test\\.\\file.txt", "/C:/Users/../test/./file.txt"],
|
|
59
|
-
["/D:/path/file.txt", "/D:/path/file.txt"],
|
|
60
|
-
];
|
|
61
|
-
test.each(rows)("encodes %p as %p", (input, expected) => {
|
|
62
|
-
expect(encodeFilePath(input)).toBe(expected);
|
|
63
|
-
});
|
|
64
|
-
test("every encoded path forms a file URL that keeps its query", () => {
|
|
65
|
-
rows.forEach(([input]) => {
|
|
66
|
-
const url = new URL(`file://${encodeFilePath(input)}?start=10`);
|
|
67
|
-
expect(url.protocol).toBe("file:");
|
|
68
|
-
expect(url.searchParams.get("start")).toBe("10");
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
});
|
package/dist/retry.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/retry.test.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { retry } from "./retry.js";
|
|
3
|
-
describe("client retry", () => {
|
|
4
|
-
test("retries transient failures up to the configured attempt count", async () => {
|
|
5
|
-
const failures = [];
|
|
6
|
-
const value = await retry(async () => {
|
|
7
|
-
failures.push("attempt");
|
|
8
|
-
if (failures.length < 3)
|
|
9
|
-
throw new Error("Failed to fetch");
|
|
10
|
-
return "ready";
|
|
11
|
-
}, { delay: 0 });
|
|
12
|
-
expect(value).toBe("ready");
|
|
13
|
-
expect(failures).toHaveLength(3);
|
|
14
|
-
});
|
|
15
|
-
test("retries a real network failure wrapped as the cause of a transport error", async () => {
|
|
16
|
-
const server = Bun.listen({
|
|
17
|
-
hostname: "127.0.0.1",
|
|
18
|
-
port: 0,
|
|
19
|
-
socket: {
|
|
20
|
-
open(socket) {
|
|
21
|
-
socket.terminate();
|
|
22
|
-
},
|
|
23
|
-
data() { },
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
const failures = [];
|
|
27
|
-
try {
|
|
28
|
-
await expect(retry(() => fetch(`http://127.0.0.1:${server.port}/api/project`).catch((cause) => {
|
|
29
|
-
failures.push(cause);
|
|
30
|
-
throw new Error("Transport", { cause });
|
|
31
|
-
}), { delay: 0 })).rejects.toThrow("Transport");
|
|
32
|
-
}
|
|
33
|
-
finally {
|
|
34
|
-
server.stop(true);
|
|
35
|
-
}
|
|
36
|
-
expect(failures).toHaveLength(3);
|
|
37
|
-
expect(failures.every((cause) => cause instanceof TypeError)).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
test("does not retry a transport error whose cause is not transient", async () => {
|
|
40
|
-
const failures = [];
|
|
41
|
-
await expect(retry(async () => {
|
|
42
|
-
failures.push("attempt");
|
|
43
|
-
throw new Error("Transport", { cause: new Error("invalid response") });
|
|
44
|
-
})).rejects.toThrow("Transport");
|
|
45
|
-
expect(failures).toHaveLength(1);
|
|
46
|
-
});
|
|
47
|
-
test("does not retry other failures", async () => {
|
|
48
|
-
const failures = [];
|
|
49
|
-
await expect(retry(async () => {
|
|
50
|
-
failures.push("attempt");
|
|
51
|
-
throw new Error("invalid response");
|
|
52
|
-
})).rejects.toThrow("invalid response");
|
|
53
|
-
expect(failures).toHaveLength(1);
|
|
54
|
-
});
|
|
55
|
-
test("uses a caller-owned retry condition", async () => {
|
|
56
|
-
const failures = [];
|
|
57
|
-
await expect(retry(async () => {
|
|
58
|
-
failures.push("attempt");
|
|
59
|
-
throw new Error("retry me");
|
|
60
|
-
}, { attempts: 2, delay: 0, retryIf: () => true })).rejects.toThrow("retry me");
|
|
61
|
-
expect(failures).toHaveLength(2);
|
|
62
|
-
});
|
|
63
|
-
});
|