From ceb5f3554edbfb31b11dec9b96995dc7bfb2758d Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Mon, 6 Jul 2026 18:21:43 +0530 Subject: [PATCH] feat(plugin): bound vcs adapter diffs --- packages/core/src/vcs.ts | 6 +++++- packages/core/src/vcs/backends.ts | 12 ++++++++++++ packages/core/test/vcs-backends.test.ts | 17 +++++++++++++++++ packages/plugin/src/v2/effect/vcs.ts | 10 ++++++---- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/core/src/vcs.ts b/packages/core/src/vcs.ts index 4b30ea285e..f0e519ba47 100644 --- a/packages/core/src/vcs.ts +++ b/packages/core/src/vcs.ts @@ -10,6 +10,7 @@ import { AppProcess } from "./process" import { VcsBackends } from "./vcs/backends" import { VcsGit } from "./vcs/git" import { VcsHg } from "./vcs/hg" +import { MAX_TOTAL_PATCH_BYTES, PATCH_CONTEXT_LINES } from "./vcs/patch" export { FileStatus, Mode } @@ -60,7 +61,10 @@ const layer = Layer.effect( diff: Effect.fn("Vcs.diff")(function* (mode: Mode, options?: DiffOptions) { const impl = yield* adapter() if (!impl) return [] - return yield* impl.diff(mode, options) + return yield* impl.diff(mode, { + context: options?.context ?? PATCH_CONTEXT_LINES, + maxOutputBytes: MAX_TOTAL_PATCH_BYTES, + }) }), }) }), diff --git a/packages/core/src/vcs/backends.ts b/packages/core/src/vcs/backends.ts index dde3585ff7..ececbc37fd 100644 --- a/packages/core/src/vcs/backends.ts +++ b/packages/core/src/vcs/backends.ts @@ -81,10 +81,22 @@ function guard(type: string, make: () => Vcs.Adapter): Vcs.Adapter { adapter.pipe( Effect.flatMap((impl) => impl.diff(mode, options)), sanitize(type, "diff", decodeDiff), + Effect.map((rows) => boundDiff(rows, options.maxOutputBytes)), ), } } +function boundDiff(rows: readonly FileDiff.Info[], maxOutputBytes: number) { + let total = 0 + return rows.map((row) => { + if (row.patch === undefined) return row + const bytes = Buffer.byteLength(row.patch) + if (total + bytes > maxOutputBytes) return { ...row, patch: undefined } + total += bytes + return row + }) +} + function sanitize(type: string, operation: string, decode: (input: unknown) => Option.Option) { return (effect: Effect.Effect) => effect.pipe( diff --git a/packages/core/test/vcs-backends.test.ts b/packages/core/test/vcs-backends.test.ts index a0210ed307..7d56a85503 100644 --- a/packages/core/test/vcs-backends.test.ts +++ b/packages/core/test/vcs-backends.test.ts @@ -53,6 +53,23 @@ describe("VcsBackends", () => { }).pipe(Effect.scoped, provide), ) + it.live("passes normalized options and bounds plugin diffs", () => + Effect.gen(function* () { + let received: Parameters[1] | undefined + yield* register( + backend({ + diff: (_mode, options) => { + received = options + return Effect.succeed([{ ...diff[0], patch: "x".repeat(options.maxOutputBytes + 1) }]) + }, + }), + ) + const vcs = yield* Vcs.Service + expect(yield* vcs.diff("working", { context: 3 })).toEqual([{ ...diff[0], patch: undefined }]) + expect(received).toEqual({ context: 3, maxOutputBytes: 10_000_000 }) + }).pipe(Effect.scoped, provide), + ) + it.live("passes the location scope to the adapter factory", () => Effect.gen(function* () { let scope: PluginVcs.AdapterScope | undefined diff --git a/packages/plugin/src/v2/effect/vcs.ts b/packages/plugin/src/v2/effect/vcs.ts index 0012575cd8..6476e36b50 100644 --- a/packages/plugin/src/v2/effect/vcs.ts +++ b/packages/plugin/src/v2/effect/vcs.ts @@ -15,12 +15,14 @@ export interface AdapterScope { readonly store: string } +export interface DiffOptions { + readonly context: number + readonly maxOutputBytes: number +} + export interface Adapter { readonly status: () => Effect.Effect - readonly diff: ( - mode: Mode, - options?: { readonly context?: number }, - ) => Effect.Effect + readonly diff: (mode: Mode, options: DiffOptions) => Effect.Effect } export interface Backend {