@opencode-ai/util 0.0.0-dev-17545 → 0.0.0-dev-17603
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/binary.d.ts +7 -0
- package/dist/binary.js +34 -0
- package/dist/binary.test.d.ts +1 -0
- package/dist/binary.test.js +16 -0
- package/dist/effect-flock.js +4 -4
- package/dist/encode.d.ts +4 -0
- package/dist/encode.js +38 -0
- package/dist/encode.test.d.ts +1 -0
- package/dist/encode.test.js +23 -0
- package/dist/fs-util.js +1 -1
- package/dist/npm.js +1 -1
- package/dist/observability/otlp.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/patch.js +2 -2
- package/dist/path.d.ts +4 -0
- package/dist/path.js +33 -0
- package/dist/path.test.d.ts +1 -0
- package/dist/path.test.js +22 -0
- package/dist/process.js +1 -1
- package/dist/retry.d.ts +8 -0
- package/dist/retry.js +37 -0
- package/dist/retry.test.d.ts +1 -0
- package/dist/retry.test.js +31 -0
- package/package.json +4 -4
package/dist/binary.d.ts
ADDED
package/dist/binary.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export var Binary;
|
|
2
|
+
(function (Binary) {
|
|
3
|
+
function search(array, id, compare) {
|
|
4
|
+
let left = 0;
|
|
5
|
+
let right = array.length - 1;
|
|
6
|
+
while (left <= right) {
|
|
7
|
+
const middle = Math.floor((left + right) / 2);
|
|
8
|
+
const value = compare(array[middle]);
|
|
9
|
+
if (value === id)
|
|
10
|
+
return { found: true, index: middle };
|
|
11
|
+
if (value < id)
|
|
12
|
+
left = middle + 1;
|
|
13
|
+
else
|
|
14
|
+
right = middle - 1;
|
|
15
|
+
}
|
|
16
|
+
return { found: false, index: left };
|
|
17
|
+
}
|
|
18
|
+
Binary.search = search;
|
|
19
|
+
function insert(array, item, compare) {
|
|
20
|
+
const id = compare(item);
|
|
21
|
+
let left = 0;
|
|
22
|
+
let right = array.length;
|
|
23
|
+
while (left < right) {
|
|
24
|
+
const middle = Math.floor((left + right) / 2);
|
|
25
|
+
if (compare(array[middle]) < id)
|
|
26
|
+
left = middle + 1;
|
|
27
|
+
else
|
|
28
|
+
right = middle;
|
|
29
|
+
}
|
|
30
|
+
array.splice(left, 0, item);
|
|
31
|
+
return array;
|
|
32
|
+
}
|
|
33
|
+
Binary.insert = insert;
|
|
34
|
+
})(Binary || (Binary = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
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/effect-flock.js
CHANGED
|
@@ -11,17 +11,17 @@ export var EffectFlock;
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
// Errors
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
|
-
class LockTimeoutError extends Schema.
|
|
14
|
+
class LockTimeoutError extends Schema.TaggedError()("LockTimeoutError", {
|
|
15
15
|
key: Schema.String,
|
|
16
16
|
}) {
|
|
17
17
|
}
|
|
18
18
|
EffectFlock.LockTimeoutError = LockTimeoutError;
|
|
19
|
-
class LockCompromisedError extends Schema.
|
|
19
|
+
class LockCompromisedError extends Schema.TaggedError()("LockCompromisedError", {
|
|
20
20
|
detail: Schema.String,
|
|
21
21
|
}) {
|
|
22
22
|
}
|
|
23
23
|
EffectFlock.LockCompromisedError = LockCompromisedError;
|
|
24
|
-
class ReleaseError extends Schema.
|
|
24
|
+
class ReleaseError extends Schema.TaggedError()("ReleaseError", {
|
|
25
25
|
detail: Schema.String,
|
|
26
26
|
cause: Schema.optional(Schema.Defect()),
|
|
27
27
|
}) {
|
|
@@ -30,7 +30,7 @@ export var EffectFlock;
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
/** Internal: signals "lock is held, retry later". Never leaks to callers. */
|
|
33
|
-
class NotAcquired extends Schema.
|
|
33
|
+
class NotAcquired extends Schema.TaggedError()("NotAcquired", {}) {
|
|
34
34
|
}
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
36
36
|
// Timing defaults
|
package/dist/encode.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function base64Encode(value: string): string;
|
|
2
|
+
export declare function base64Decode(value: string): string;
|
|
3
|
+
export declare function checksum(content: string): string | undefined;
|
|
4
|
+
export declare function sampledChecksum(content: string, limit?: number): string | undefined;
|
package/dist/encode.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function base64Encode(value) {
|
|
2
|
+
const bytes = new TextEncoder().encode(value);
|
|
3
|
+
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
4
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
5
|
+
}
|
|
6
|
+
export function base64Decode(value) {
|
|
7
|
+
const binary = atob(value.replace(/-/g, "+").replace(/_/g, "/"));
|
|
8
|
+
return new TextDecoder().decode(Uint8Array.from(binary, (character) => character.charCodeAt(0)));
|
|
9
|
+
}
|
|
10
|
+
export function checksum(content) {
|
|
11
|
+
if (!content)
|
|
12
|
+
return;
|
|
13
|
+
let hash = 0x811c9dc5;
|
|
14
|
+
for (let index = 0; index < content.length; index++) {
|
|
15
|
+
hash ^= content.charCodeAt(index);
|
|
16
|
+
hash = Math.imul(hash, 0x01000193);
|
|
17
|
+
}
|
|
18
|
+
return (hash >>> 0).toString(36);
|
|
19
|
+
}
|
|
20
|
+
export function sampledChecksum(content, limit = 500_000) {
|
|
21
|
+
if (!content)
|
|
22
|
+
return;
|
|
23
|
+
if (content.length <= limit)
|
|
24
|
+
return checksum(content);
|
|
25
|
+
const size = 4096;
|
|
26
|
+
return `${content.length}:${[
|
|
27
|
+
0,
|
|
28
|
+
Math.floor(content.length * 0.25),
|
|
29
|
+
Math.floor(content.length * 0.5),
|
|
30
|
+
Math.floor(content.length * 0.75),
|
|
31
|
+
content.length - size,
|
|
32
|
+
]
|
|
33
|
+
.map((point) => {
|
|
34
|
+
const start = Math.max(0, Math.min(content.length - size, point - Math.floor(size / 2)));
|
|
35
|
+
return checksum(content.slice(start, start + size)) ?? "";
|
|
36
|
+
})
|
|
37
|
+
.join(":")}`;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
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/fs-util.js
CHANGED
|
@@ -10,7 +10,7 @@ import { makeGlobalNode } from "./effect/app-node.js";
|
|
|
10
10
|
import { filesystem } from "./effect/app-node-platform.js";
|
|
11
11
|
export var FSUtil;
|
|
12
12
|
(function (FSUtil) {
|
|
13
|
-
class FileSystemError extends Schema.
|
|
13
|
+
class FileSystemError extends Schema.TaggedError()("FileSystemError", {
|
|
14
14
|
method: Schema.String,
|
|
15
15
|
cause: Schema.optional(Schema.Defect()),
|
|
16
16
|
}) {
|
package/dist/npm.js
CHANGED
|
@@ -11,7 +11,7 @@ import { LayerNode } from "./effect/layer-node.js";
|
|
|
11
11
|
import { makeRuntime } from "./effect/runtime.js";
|
|
12
12
|
import { NpmConfig } from "./npm-config.js";
|
|
13
13
|
import { resolveModule } from "#runtime-import";
|
|
14
|
-
export class InstallFailedError extends Schema.
|
|
14
|
+
export class InstallFailedError extends Schema.TaggedError()("NpmInstallFailedError", {
|
|
15
15
|
add: Schema.Array(Schema.String).pipe(Schema.optional),
|
|
16
16
|
dir: Schema.String,
|
|
17
17
|
cause: Schema.optional(Schema.Defect()),
|
|
@@ -13,6 +13,6 @@ export declare function resource(app?: App): {
|
|
|
13
13
|
serviceVersion: string;
|
|
14
14
|
attributes: Record<string, string>;
|
|
15
15
|
};
|
|
16
|
-
export declare function loggers(options: Options | undefined, app: App): Effect.Effect<import("effect/Logger").Logger<unknown, void>, never, import("effect/unstable/http/HttpClient").HttpClient | Scope.Scope | import("effect/unstable/observability/OtlpSerialization").OtlpSerialization>[];
|
|
16
|
+
export declare function loggers(options: Options | undefined, app: App): Effect.Effect<import("effect/Logger").Logger<unknown, void>, never, import("effect/unstable/http/HttpClient").HttpClient | Scope.Scope | import("effect/unstable/observability/OtlpExporter").Flusher | import("effect/unstable/observability/OtlpSerialization").OtlpSerialization>[];
|
|
17
17
|
export declare const tracingLayer: (options: Options | undefined, app: App) => Effect.Effect<Layer.Layer<never, never, never>, never, never>;
|
|
18
18
|
export * as Otlp from "./otlp.js";
|
package/dist/observability.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
|
3
3
|
import { LayerNode } from "./effect/layer-node.js";
|
|
4
4
|
import { Effect, Layer, Logger, References, Schema } from "effect";
|
|
5
5
|
import { FetchHttpClient } from "effect/unstable/http";
|
|
6
|
-
import { OtlpSerialization } from "effect/unstable/observability";
|
|
6
|
+
import { OtlpExporter, OtlpSerialization } from "effect/unstable/observability";
|
|
7
7
|
import { Logging } from "./observability/logging.js";
|
|
8
8
|
import { Otlp } from "./observability/otlp.js";
|
|
9
9
|
export const Options = Schema.Struct({
|
|
@@ -24,7 +24,7 @@ export function layer(options = {
|
|
|
24
24
|
};
|
|
25
25
|
const local = Logger.layer(Logging.loggers(app.channel === "local", app.channel), { mergeWithExisting: false }).pipe(Layer.provide(NodeFileSystem.layer), Layer.orDie, Layer.merge(Layer.succeed(References.MinimumLogLevel, Logging.minimumLogLevel())));
|
|
26
26
|
return Layer.unwrap(Effect.gen(function* () {
|
|
27
|
-
const logs = Logger.layer([...Logging.loggers(app.channel === "local", app.channel), ...Otlp.loggers(options, app)], { mergeWithExisting: false }).pipe(Layer.provide(NodeFileSystem.layer), Layer.provide(OtlpSerialization.layerJson), Layer.provide(FetchHttpClient.layer), Layer.orDie, Layer.merge(Layer.succeed(References.MinimumLogLevel, Logging.minimumLogLevel())));
|
|
27
|
+
const logs = Logger.layer([...Logging.loggers(app.channel === "local", app.channel), ...Otlp.loggers(options, app)], { mergeWithExisting: false }).pipe(Layer.provide(NodeFileSystem.layer), Layer.provide(OtlpSerialization.layerJson), Layer.provide(FetchHttpClient.layer), Layer.provide(OtlpExporter.layerFlusher), Layer.orDie, Layer.merge(Layer.succeed(References.MinimumLogLevel, Logging.minimumLogLevel())));
|
|
28
28
|
return Layer.merge(logs, yield* Otlp.tracingLayer(options, app));
|
|
29
29
|
})).pipe(Layer.catchCause(() => local));
|
|
30
30
|
}
|
package/dist/patch.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export * as Patch from "./patch.js";
|
|
2
2
|
import { Result, Schema } from "effect";
|
|
3
3
|
import { Bom } from "./bom.js";
|
|
4
|
-
export class BoundaryError extends Schema.
|
|
4
|
+
export class BoundaryError extends Schema.TaggedError()("Patch.BoundaryError", {
|
|
5
5
|
boundary: Schema.Literals(["first", "last"]),
|
|
6
6
|
}) {
|
|
7
7
|
get message() {
|
|
8
8
|
return `The ${this.boundary} line of the patch must be '${this.boundary === "first" ? "*** Begin Patch" : "*** End Patch"}'`;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
export class InvalidHunkError extends Schema.
|
|
11
|
+
export class InvalidHunkError extends Schema.TaggedError()("Patch.InvalidHunkError", {
|
|
12
12
|
line: Schema.String,
|
|
13
13
|
lineNumber: Schema.Number,
|
|
14
14
|
reason: Schema.optional(Schema.String),
|
package/dist/path.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function getFilename(path: string | undefined): string;
|
|
2
|
+
export declare function getDirectory(path: string | undefined): string;
|
|
3
|
+
export declare function getFilenameTruncated(path: string | undefined, maxLength?: number): string;
|
|
4
|
+
export declare function truncateMiddle(text: string, maxLength?: number): string;
|
package/dist/path.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function getFilename(path) {
|
|
2
|
+
if (!path)
|
|
3
|
+
return "";
|
|
4
|
+
const trimmed = path.replace(/[/\\]+$/, "");
|
|
5
|
+
const parts = trimmed.split(/[/\\]/);
|
|
6
|
+
return parts[parts.length - 1] ?? "";
|
|
7
|
+
}
|
|
8
|
+
export function getDirectory(path) {
|
|
9
|
+
if (!path)
|
|
10
|
+
return "";
|
|
11
|
+
const trimmed = path.replace(/[/\\]+$/, "");
|
|
12
|
+
const parts = trimmed.split(/[/\\]/);
|
|
13
|
+
return parts.slice(0, parts.length - 1).join("/") + "/";
|
|
14
|
+
}
|
|
15
|
+
export function getFilenameTruncated(path, maxLength = 20) {
|
|
16
|
+
const filename = getFilename(path);
|
|
17
|
+
if (filename.length <= maxLength)
|
|
18
|
+
return filename;
|
|
19
|
+
const lastDot = filename.lastIndexOf(".");
|
|
20
|
+
const extension = lastDot <= 0 ? "" : filename.slice(lastDot);
|
|
21
|
+
const available = maxLength - extension.length - 1;
|
|
22
|
+
if (available <= 0)
|
|
23
|
+
return filename.slice(0, maxLength - 1) + "…";
|
|
24
|
+
return filename.slice(0, available) + "…" + extension;
|
|
25
|
+
}
|
|
26
|
+
export function truncateMiddle(text, maxLength = 20) {
|
|
27
|
+
if (text.length <= maxLength)
|
|
28
|
+
return text;
|
|
29
|
+
const available = maxLength - 1;
|
|
30
|
+
const start = Math.ceil(available / 2);
|
|
31
|
+
const end = Math.floor(available / 2);
|
|
32
|
+
return text.slice(0, start) + "…" + text.slice(-end);
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { 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("keeps filename truncation stable", () => {
|
|
19
|
+
expect(getFilenameTruncated("/repo/long-component-name.tsx", 16)).toBe("long-compon….tsx");
|
|
20
|
+
expect(truncateMiddle("abcdefghijklmnop", 9)).toBe("abcd…mnop");
|
|
21
|
+
});
|
|
22
|
+
});
|
package/dist/process.js
CHANGED
|
@@ -3,7 +3,7 @@ import { ChildProcess } from "effect/unstable/process";
|
|
|
3
3
|
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
4
4
|
import { CrossSpawnSpawner } from "./cross-spawn-spawner.js";
|
|
5
5
|
import { makeGlobalNode } from "./effect/app-node.js";
|
|
6
|
-
export class AppProcessError extends Schema.
|
|
6
|
+
export class AppProcessError extends Schema.TaggedError()("AppProcessError", {
|
|
7
7
|
command: Schema.String,
|
|
8
8
|
exitCode: Schema.optional(Schema.Number),
|
|
9
9
|
stderr: Schema.optional(Schema.String),
|
package/dist/retry.d.ts
ADDED
package/dist/retry.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const transientMessages = [
|
|
2
|
+
"load failed",
|
|
3
|
+
"network connection was lost",
|
|
4
|
+
"network request failed",
|
|
5
|
+
"failed to fetch",
|
|
6
|
+
"econnreset",
|
|
7
|
+
"econnrefused",
|
|
8
|
+
"etimedout",
|
|
9
|
+
"socket hang up",
|
|
10
|
+
];
|
|
11
|
+
function isTransientError(error) {
|
|
12
|
+
if (!error)
|
|
13
|
+
return false;
|
|
14
|
+
// oxlint-disable-next-line no-base-to-string -- Error input is intentionally normalized for message matching.
|
|
15
|
+
const message = String(error instanceof Error ? error.message : error).toLowerCase();
|
|
16
|
+
return transientMessages.some((item) => message.includes(item));
|
|
17
|
+
}
|
|
18
|
+
export async function retry(operation, options = {}) {
|
|
19
|
+
const attempts = options.attempts ?? 3;
|
|
20
|
+
const delay = options.delay ?? 500;
|
|
21
|
+
const factor = options.factor ?? 2;
|
|
22
|
+
const maxDelay = options.maxDelay ?? 10_000;
|
|
23
|
+
const retryIf = options.retryIf ?? isTransientError;
|
|
24
|
+
let lastError;
|
|
25
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
26
|
+
try {
|
|
27
|
+
return await operation();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
lastError = error;
|
|
31
|
+
if (attempt === attempts - 1 || !retryIf(error))
|
|
32
|
+
throw error;
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(delay * Math.pow(factor, attempt), maxDelay)));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw lastError;
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
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("does not retry other failures", async () => {
|
|
16
|
+
const failures = [];
|
|
17
|
+
await expect(retry(async () => {
|
|
18
|
+
failures.push("attempt");
|
|
19
|
+
throw new Error("invalid response");
|
|
20
|
+
})).rejects.toThrow("invalid response");
|
|
21
|
+
expect(failures).toHaveLength(1);
|
|
22
|
+
});
|
|
23
|
+
test("uses a caller-owned retry condition", async () => {
|
|
24
|
+
const failures = [];
|
|
25
|
+
await expect(retry(async () => {
|
|
26
|
+
failures.push("attempt");
|
|
27
|
+
throw new Error("retry me");
|
|
28
|
+
}, { attempts: 2, delay: 0, retryIf: () => true })).rejects.toThrow("retry me");
|
|
29
|
+
expect(failures).toHaveLength(2);
|
|
30
|
+
});
|
|
31
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@opencode-ai/util",
|
|
4
|
-
"version": "0.0.0-dev-
|
|
4
|
+
"version": "0.0.0-dev-17603",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"typecheck": "tsgo --noEmit"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@effect/opentelemetry": "4.0.0-beta.
|
|
47
|
-
"@effect/platform-node": "4.0.0-beta.
|
|
46
|
+
"@effect/opentelemetry": "4.0.0-beta.107",
|
|
47
|
+
"@effect/platform-node": "4.0.0-beta.107",
|
|
48
48
|
"@npmcli/arborist": "9.4.0",
|
|
49
49
|
"@npmcli/config": "10.8.1",
|
|
50
50
|
"@opentelemetry/api": "1.9.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@opentelemetry/sdk-trace-base": "2.6.1",
|
|
54
54
|
"@opentelemetry/sdk-trace-node": "2.6.1",
|
|
55
55
|
"cross-spawn": "7.0.6",
|
|
56
|
-
"effect": "4.0.0-beta.
|
|
56
|
+
"effect": "4.0.0-beta.107",
|
|
57
57
|
"glob": "13.0.5",
|
|
58
58
|
"mime-types": "3.0.2",
|
|
59
59
|
"minimatch": "10.2.5",
|