Skip to content

Commit f3ab48d

Browse files
rrodrigu3zclaude
andcommitted
refactor: simplify TextDecoder allocation, O(N) string concat, consolidate imports
- Hoist TextDecoder to module-level constant in client.ts and stream.ts - Replace O(N^2) string concatenation in execCollect with array + join - Consolidate three imports from generated/openshell.js into one - Reuse module-level EMPTY_BYTES constant for stdin default - Remove vitest globals: true (test files import explicitly) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 427956c commit f3ab48d

3 files changed

Lines changed: 16 additions & 11 deletions

File tree

src/client.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ import { createChannel, createClient, type Channel } from "nice-grpc";
55
import { credentials as grpcCredentials } from "@grpc/grpc-js";
66
import {
77
OpenShellDefinition,
8+
serviceStatusToJSON,
89
type OpenShellClient as GeneratedClient,
10+
type ExecSandboxEvent,
11+
type CreateSandboxRequest,
912
} from "./generated/openshell.js";
10-
import type { ExecSandboxEvent, CreateSandboxRequest } from "./generated/openshell.js";
1113
import { SandboxPhase, type Sandbox as SandboxModel } from "./generated/datamodel.js";
12-
import { serviceStatusToJSON } from "./generated/openshell.js";
14+
15+
const decoder = new TextDecoder();
16+
const EMPTY_BYTES = new Uint8Array();
1317

1418
export interface OpenShellClientOpts {
1519
/** gRPC gateway endpoint (e.g., "localhost:8080") */
@@ -82,7 +86,7 @@ export class OpenShellClient {
8286
workdir: request.workdir ?? "",
8387
environment: request.environment ?? {},
8488
timeoutSeconds: request.timeoutSeconds ?? 0,
85-
stdin: request.stdin ?? new Uint8Array(),
89+
stdin: request.stdin ?? EMPTY_BYTES,
8690
});
8791
}
8892

@@ -114,8 +118,8 @@ export class OpenShellClient {
114118
command: string[],
115119
opts?: { workdir?: string; environment?: Record<string, string>; timeoutSeconds?: number },
116120
): Promise<ExecCollectResult> {
117-
let stdout = "";
118-
let stderr = "";
121+
const stdoutParts: string[] = [];
122+
const stderrParts: string[] = [];
119123
let exitCode = 0;
120124

121125
for await (const event of this.execSandbox({
@@ -124,15 +128,15 @@ export class OpenShellClient {
124128
...opts,
125129
})) {
126130
if (event.stdout) {
127-
stdout += new TextDecoder().decode(event.stdout.data);
131+
stdoutParts.push(decoder.decode(event.stdout.data));
128132
} else if (event.stderr) {
129-
stderr += new TextDecoder().decode(event.stderr.data);
133+
stderrParts.push(decoder.decode(event.stderr.data));
130134
} else if (event.exit) {
131135
exitCode = event.exit.exitCode;
132136
}
133137
}
134138

135-
return { stdout, stderr, exitCode };
139+
return { stdout: stdoutParts.join(""), stderr: stderrParts.join(""), exitCode };
136140
}
137141

138142
close(): void {

src/stream.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { ExecSandboxEvent } from "./generated/openshell.js";
22

3+
const decoder = new TextDecoder();
4+
35
export interface ExecLine {
46
type: "stdout" | "stderr";
57
line: string;
@@ -27,14 +29,14 @@ export async function* streamExecLines(
2729

2830
for await (const event of grpcStream) {
2931
if (event.stdout) {
30-
stdoutBuf += new TextDecoder().decode(event.stdout.data);
32+
stdoutBuf += decoder.decode(event.stdout.data);
3133
const lines = stdoutBuf.split("\n");
3234
stdoutBuf = lines.pop() ?? "";
3335
for (const line of lines) {
3436
if (line) yield { type: "stdout", line };
3537
}
3638
} else if (event.stderr) {
37-
stderrBuf += new TextDecoder().decode(event.stderr.data);
39+
stderrBuf += decoder.decode(event.stderr.data);
3840
const lines = stderrBuf.split("\n");
3941
stderrBuf = lines.pop() ?? "";
4042
for (const line of lines) {

vitest.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { defineConfig } from "vitest/config";
22

33
export default defineConfig({
44
test: {
5-
globals: true,
65
environment: "node",
76
include: ["src/__tests__/**/*.test.ts"],
87
exclude: ["src/__tests__/integration.test.ts"],

0 commit comments

Comments
 (0)