critique 0.1.123 → 0.1.129

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.
@@ -1 +1 @@
1
- {"version":3,"file":"diff-utils.d.ts","sourceRoot":"","sources":["../src/diff-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"diff-utils.d.ts","sourceRoot":"","sources":["../src/diff-utils.ts"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAchE;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAC/C,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACpC,CA6FA;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,EAAE,GAChC,CAAC,CAAC,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAiBzE;AAED,eAAO,MAAM,aAAa,UASzB,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,mBAAmB,CAAC,GAC/D,MAAM,EAAE,CAQV;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAsBhF;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,UAAU,EAC9D,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,mBAAmB,CAAC,GAC/D,CAAC,EAAE,CAKL;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAyDlE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAiBjD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EAAE,EACxB,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAC1C,MAAM,CAMR;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAW/C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAYT;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,GAAG,SAAS,CAQrB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GAAG;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAYA;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,MAAY,GAC3B,OAAO,GAAG,SAAS,CAQrB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAC/C,KAAK,EAAE,CAAC,EAAE,EACV,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAC/B,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAyD7B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqGnE"}
@@ -2,6 +2,7 @@
2
2
  // Builds git commands, parses diff files, detects filetypes for syntax highlighting,
3
3
  // and provides helpers for unified/split view mode selection.
4
4
  import { execSync } from "child_process";
5
+ import { buildDirectoryTree } from "./directory-tree.js";
5
6
  /**
6
7
  * Strip submodule status lines from git diff output.
7
8
  * git diff --submodule=diff adds various status lines that the diff parser doesn't understand:
@@ -414,11 +415,41 @@ export function processFiles(files, formatPatch) {
414
415
  const totalLines = file.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0);
415
416
  return totalLines <= 6000;
416
417
  });
417
- const sortedFiles = filteredFiles.sort((a, b) => {
418
- const aSize = a.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0);
419
- const bSize = b.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0);
420
- return aSize - bSize;
418
+ const treeFiles = filteredFiles.map((file, index) => {
419
+ const { additions, deletions } = countChanges(file.hunks);
420
+ return {
421
+ path: getFileName(file),
422
+ status: getFileStatus(file),
423
+ additions,
424
+ deletions,
425
+ fileIndex: index,
426
+ };
421
427
  });
428
+ const treeFileOrder = buildDirectoryTree(treeFiles)
429
+ .filter((node) => node.isFile && node.fileIndex !== undefined)
430
+ .map((node) => node.fileIndex);
431
+ const seenIndexes = new Set();
432
+ const sortedFiles = [];
433
+ for (const index of treeFileOrder) {
434
+ if (seenIndexes.has(index))
435
+ continue;
436
+ const file = filteredFiles[index];
437
+ if (!file)
438
+ continue;
439
+ seenIndexes.add(index);
440
+ sortedFiles.push(file);
441
+ }
442
+ // Defensive fallback: keep any unmatched files in original order.
443
+ // This should be rare, but avoids dropping files if tree metadata and
444
+ // parsed file list ever diverge.
445
+ for (let index = 0; index < filteredFiles.length; index++) {
446
+ if (seenIndexes.has(index))
447
+ continue;
448
+ const file = filteredFiles[index];
449
+ if (!file)
450
+ continue;
451
+ sortedFiles.push(file);
452
+ }
422
453
  // Add rawDiff for each file
423
454
  return sortedFiles.map((file) => ({
424
455
  ...file,
@@ -4,7 +4,70 @@
4
4
  // extracts rename metadata for all rename/copy sections.
5
5
  import { describe, expect, it } from "bun:test";
6
6
  import { parsePatch, formatPatch } from "diff";
7
- import { preprocessDiff, parseGitDiffFiles, getFileStatus, getFileName, getOldFileName, buildGitCommand, buildSubmoduleDiffCommand, filterParsedFilesByPatterns, getFilterPatterns, matchesFileFilters, detectFiletype, } from "./diff-utils.js";
7
+ import { preprocessDiff, parseGitDiffFiles, processFiles, getFileStatus, getFileName, getOldFileName, buildGitCommand, buildSubmoduleDiffCommand, filterParsedFilesByPatterns, getFilterPatterns, matchesFileFilters, detectFiletype, } from "./diff-utils.js";
8
+ // ============================================================================
9
+ // processFiles ordering
10
+ // ============================================================================
11
+ describe("processFiles ordering", () => {
12
+ it("should order output files to match directory tree traversal", () => {
13
+ const files = [
14
+ {
15
+ oldFileName: "src/components/button.tsx",
16
+ newFileName: "src/components/button.tsx",
17
+ hunks: [{ lines: Array.from({ length: 90 }, () => "+line") }],
18
+ },
19
+ {
20
+ oldFileName: "src/index.ts",
21
+ newFileName: "src/index.ts",
22
+ hunks: [{ lines: ["+line"] }],
23
+ },
24
+ {
25
+ oldFileName: "README.md",
26
+ newFileName: "README.md",
27
+ hunks: [{ lines: ["+line", "+line", "+line"] }],
28
+ },
29
+ ];
30
+ const processed = processFiles(files, (file) => `diff --git ${file.oldFileName} ${file.newFileName}`);
31
+ expect(processed.map((file) => getFileName(file))).toEqual([
32
+ "README.md",
33
+ "src/components/button.tsx",
34
+ "src/index.ts",
35
+ ]);
36
+ });
37
+ it("should keep tree order with renamed, added, and deleted files", () => {
38
+ const files = [
39
+ {
40
+ oldFileName: "src/alpha.ts",
41
+ newFileName: "src/alpha.ts",
42
+ hunks: [{ lines: Array.from({ length: 30 }, () => "+line") }],
43
+ },
44
+ {
45
+ oldFileName: "docs/old-name.md",
46
+ newFileName: "docs/new-name.md",
47
+ renameFrom: "docs/old-name.md",
48
+ renameTo: "docs/new-name.md",
49
+ hunks: [{ lines: ["+line"] }],
50
+ },
51
+ {
52
+ oldFileName: "/dev/null",
53
+ newFileName: "docs/guide.md",
54
+ hunks: [{ lines: ["+line", "+line"] }],
55
+ },
56
+ {
57
+ oldFileName: "src/remove.ts",
58
+ newFileName: "/dev/null",
59
+ hunks: [{ lines: ["-line"] }],
60
+ },
61
+ ];
62
+ const processed = processFiles(files, (file) => `diff --git ${file.oldFileName} ${file.newFileName}`);
63
+ expect(processed.map((file) => getFileName(file))).toEqual([
64
+ "docs/guide.md",
65
+ "docs/new-name.md",
66
+ "src/alpha.ts",
67
+ "src/remove.ts",
68
+ ]);
69
+ });
70
+ });
8
71
  // ============================================================================
9
72
  // preprocessDiff
10
73
  // ============================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"directory-tree.d.ts","sourceRoot":"","sources":["../src/directory-tree.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;AAErE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,yDAAyD;IACzD,MAAM,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;CACf;AAYD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE,CAOpE"}
1
+ {"version":3,"file":"directory-tree.d.ts","sourceRoot":"","sources":["../src/directory-tree.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;AAErE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,yDAAyD;IACzD,MAAM,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;CACf;AAYD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE,CAQpE"}
@@ -11,6 +11,7 @@ export function buildDirectoryTree(files) {
11
11
  return [];
12
12
  }
13
13
  const tree = buildInternalTree(files);
14
+ sortInternalTree(tree);
14
15
  return flattenTree(tree);
15
16
  }
16
17
  /**
@@ -55,6 +56,18 @@ function getName(node) {
55
56
  const parts = node.path.split("/");
56
57
  return parts[parts.length - 1] || node.path;
57
58
  }
59
+ /**
60
+ * Sort tree nodes by name at every level so file ordering is deterministic
61
+ * and independent from incoming git diff section order.
62
+ */
63
+ function sortInternalTree(nodes) {
64
+ nodes.sort((a, b) => getName(a).toLowerCase().localeCompare(getName(b).toLowerCase()));
65
+ for (const node of nodes) {
66
+ if (node.children.length > 0) {
67
+ sortInternalTree(node.children);
68
+ }
69
+ }
70
+ }
58
71
  /**
59
72
  * Collapse directories that only contain a single subdirectory (no files)
60
73
  */
@@ -44,6 +44,28 @@ describe("buildDirectoryTree", () => {
44
44
  expect(result[1].displayPath).toBe("Button.tsx");
45
45
  expect(result[1].isFile).toBe(true);
46
46
  });
47
+ it("should sort nodes alphabetically regardless of input order", () => {
48
+ const files = [
49
+ { path: "website/src/index.ts", status: "modified", additions: 1, deletions: 0 },
50
+ { path: "db/schema.prisma", status: "modified", additions: 1, deletions: 0 },
51
+ { path: "discord/src/utils.ts", status: "modified", additions: 1, deletions: 0 },
52
+ { path: "discord/src/cli.ts", status: "modified", additions: 1, deletions: 0 },
53
+ { path: "gateway-proxy/src/main.rs", status: "modified", additions: 1, deletions: 0 },
54
+ ];
55
+ const result = buildDirectoryTree(files);
56
+ const rendered = result.map((node) => `${node.prefix}${node.connector}${node.displayPath}`);
57
+ expect(rendered).toEqual([
58
+ "├── db",
59
+ "│ └── schema.prisma",
60
+ "├── discord/src",
61
+ "│ ├── cli.ts",
62
+ "│ └── utils.ts",
63
+ "├── gateway-proxy/src",
64
+ "│ └── main.rs",
65
+ "└── website/src",
66
+ " └── index.ts",
67
+ ]);
68
+ });
47
69
  });
48
70
  describe("TreeRenderer visual tests", () => {
49
71
  let testSetup;
@@ -166,10 +188,10 @@ describe("TreeRenderer visual tests", () => {
166
188
  expect(frame).toMatchInlineSnapshot(`
167
189
  "├── package.json (+2,-1)
168
190
  ├── src
169
- │ ├── index.ts (+10,-5)
170
191
  │ ├── components
171
192
  │ │ ├── Button.tsx (+50,-0)
172
193
  │ │ └── Input.tsx (+15,-8)
194
+ │ ├── index.ts (+10,-5)
173
195
  │ └── utils
174
196
  │ └── helpers.ts (+0,-30)
175
197
  └── tests
@@ -197,8 +219,8 @@ describe("TreeRenderer visual tests", () => {
197
219
  const frame = testSetup.captureCharFrame();
198
220
  expect(frame).toMatchInlineSnapshot(`
199
221
  "└── packages/core/src/lib/utils
200
- ├── helpers.ts (+5,-3)
201
- └── format.ts (+20,-0)
222
+ ├── format.ts (+20,-0)
223
+ └── helpers.ts (+5,-3)
202
224
 
203
225
 
204
226
 
@@ -222,14 +244,14 @@ describe("TreeRenderer visual tests", () => {
222
244
  await testSetup.renderOnce();
223
245
  const frame = testSetup.captureCharFrame();
224
246
  expect(frame).toMatchInlineSnapshot(`
225
- "├── src
226
- ├── api
227
- │ │ ├── routes.ts (+10,-5)
228
- │ │ └── handlers.ts (+30,-0)
229
- └── db
230
- └── models.ts (+8,-2)
231
- └── lib
232
- └── utils.ts (+15,-0)
247
+ "├── lib
248
+ └── utils.ts (+15,-0)
249
+ └── src
250
+ ├── api
251
+ ├── handlers.ts (+30,-0)
252
+ └── routes.ts (+10,-5)
253
+ └── db
254
+ └── models.ts (+8,-2)
233
255
 
234
256
 
235
257
 
@@ -256,10 +278,10 @@ describe("DirectoryTreeView component", () => {
256
278
  await testSetup.renderOnce();
257
279
  const frame = testSetup.captureCharFrame();
258
280
  expect(frame).toMatchInlineSnapshot(`
259
- " ├── src
260
- │ ├── index.ts (+5,-2)
261
- │ └── utils.ts (+30)
262
- └── README.md (-15)
281
+ " ├── README.md (-15)
282
+ └── src
283
+ ├── index.ts (+5,-2)
284
+ └── utils.ts (+30)
263
285
 
264
286
 
265
287
 
@@ -0,0 +1,16 @@
1
+ export type KvValueMetadata = {
2
+ contentType?: string;
3
+ contentEncoding?: "gzip";
4
+ schemaVersion?: 1;
5
+ };
6
+ export declare const KV_SCHEMA_VERSION: 1;
7
+ export declare function buildKvPutOptions(ttlSeconds?: number, metadata?: KvValueMetadata): {
8
+ expirationTtl?: number;
9
+ metadata?: KvValueMetadata;
10
+ } | undefined;
11
+ export declare function gzipArrayBuffer(buffer: ArrayBuffer): Promise<ArrayBuffer>;
12
+ export declare function gunzipArrayBuffer(buffer: ArrayBuffer): Promise<ArrayBuffer>;
13
+ export declare function gzipText(value: string): Promise<ArrayBuffer>;
14
+ export declare function decodeTextFromKv(value: ArrayBuffer, metadata: KvValueMetadata | null): Promise<string>;
15
+ export declare function decodeBinaryFromKv(value: ArrayBuffer, metadata: KvValueMetadata | null): Promise<ArrayBuffer>;
16
+ //# sourceMappingURL=kv-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv-codec.d.ts","sourceRoot":"","sources":["../src/kv-codec.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,CAAC,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,iBAAiB,EAAG,CAAU,CAAA;AAE3C,wBAAgB,iBAAiB,CAC/B,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,eAAe,GACzB;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,SAAS,CASpE;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAG/E;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAGjF;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAGlE;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,eAAe,GAAG,IAAI,GAC/B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,eAAe,GAAG,IAAI,GAC/B,OAAO,CAAC,WAAW,CAAC,CAKtB"}
@@ -0,0 +1,36 @@
1
+ // KV value compression/decompression helpers for Cloudflare Worker storage.
2
+ // Provides metadata-aware gzip encoding and decoding for text and binary payloads.
3
+ export const KV_SCHEMA_VERSION = 1;
4
+ export function buildKvPutOptions(ttlSeconds, metadata) {
5
+ if (ttlSeconds === undefined && metadata === undefined) {
6
+ return undefined;
7
+ }
8
+ return {
9
+ ...(ttlSeconds !== undefined ? { expirationTtl: ttlSeconds } : {}),
10
+ ...(metadata !== undefined ? { metadata } : {}),
11
+ };
12
+ }
13
+ export async function gzipArrayBuffer(buffer) {
14
+ const stream = new Blob([buffer]).stream().pipeThrough(new CompressionStream("gzip"));
15
+ return await new Response(stream).arrayBuffer();
16
+ }
17
+ export async function gunzipArrayBuffer(buffer) {
18
+ const stream = new Blob([buffer]).stream().pipeThrough(new DecompressionStream("gzip"));
19
+ return await new Response(stream).arrayBuffer();
20
+ }
21
+ export async function gzipText(value) {
22
+ const bytes = new TextEncoder().encode(value);
23
+ return gzipArrayBuffer(bytes.buffer);
24
+ }
25
+ export async function decodeTextFromKv(value, metadata) {
26
+ const buffer = metadata?.contentEncoding === "gzip"
27
+ ? await gunzipArrayBuffer(value)
28
+ : value;
29
+ return new TextDecoder().decode(buffer);
30
+ }
31
+ export async function decodeBinaryFromKv(value, metadata) {
32
+ if (metadata?.contentEncoding === "gzip") {
33
+ return await gunzipArrayBuffer(value);
34
+ }
35
+ return value;
36
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=kv-codec.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv-codec.test.d.ts","sourceRoot":"","sources":["../src/kv-codec.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ // Tests for KV gzip codec helpers used by the Cloudflare worker.
2
+ import { describe, expect, test } from "bun:test";
3
+ import { buildKvPutOptions, decodeBinaryFromKv, decodeTextFromKv, gzipArrayBuffer, gzipText, } from "./kv-codec.js";
4
+ describe("kv-codec", () => {
5
+ test("round-trips gzip text payloads", async () => {
6
+ const input = "<html><body>diff line +1 -0</body></html>\n".repeat(200);
7
+ const compressed = await gzipText(input);
8
+ const decoded = await decodeTextFromKv(compressed, {
9
+ contentType: "text/html; charset=utf-8",
10
+ contentEncoding: "gzip",
11
+ schemaVersion: 1,
12
+ });
13
+ expect(decoded).toBe(input);
14
+ });
15
+ test("decodes legacy uncompressed text payloads", async () => {
16
+ const input = "plain html without kv metadata";
17
+ const bytes = new TextEncoder().encode(input);
18
+ const decoded = await decodeTextFromKv(bytes.buffer, null);
19
+ expect(decoded).toBe(input);
20
+ });
21
+ test("round-trips gzip binary payloads", async () => {
22
+ const bytes = Uint8Array.from({ length: 1024 }, (_, index) => index % 256);
23
+ const compressed = await gzipArrayBuffer(bytes.buffer);
24
+ const decoded = await decodeBinaryFromKv(compressed, {
25
+ contentType: "image/png",
26
+ contentEncoding: "gzip",
27
+ schemaVersion: 1,
28
+ });
29
+ expect(new Uint8Array(decoded)).toEqual(bytes);
30
+ });
31
+ test("returns undefined put options when empty", () => {
32
+ expect(buildKvPutOptions()).toBeUndefined();
33
+ });
34
+ test("builds put options with ttl and metadata", () => {
35
+ expect(buildKvPutOptions(3600, {
36
+ contentType: "text/html; charset=utf-8",
37
+ contentEncoding: "gzip",
38
+ schemaVersion: 1,
39
+ })).toEqual({
40
+ expirationTtl: 3600,
41
+ metadata: {
42
+ contentType: "text/html; charset=utf-8",
43
+ contentEncoding: "gzip",
44
+ schemaVersion: 1,
45
+ },
46
+ });
47
+ });
48
+ });
@@ -176,6 +176,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
176
176
  const trimmed = await session.text({ trimEnd: true });
177
177
  expect(trimmed).toMatchInlineSnapshot(`
178
178
  "
179
+ └── b/src
180
+ └── hello.ts (+1,-1)
181
+
182
+
179
183
  a/src/hello.ts → b/src/hello.ts +1-1
180
184
 
181
185
  1 const greeting = 'hello'
@@ -209,6 +213,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
209
213
  const trimmed = await session.text({ trimEnd: true, immediate: true });
210
214
  expect(trimmed).toMatchInlineSnapshot(`
211
215
  "
216
+ └── b
217
+ └── readme.md ()
218
+
219
+
212
220
  a/readme.md → b/readme.md +0-0
213
221
 
214
222
  1 # My Project
@@ -224,6 +232,11 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
224
232
  const trimmed = await session.text({ trimEnd: true });
225
233
  expect(trimmed).toMatchInlineSnapshot(`
226
234
  "
235
+ └── b/src
236
+ ├── logger.ts (+5)
237
+ └── index.ts (+2)
238
+
239
+
227
240
  b/src/logger.ts +5-0
228
241
 
229
242
  1 + export class Logger {
@@ -258,6 +271,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
258
271
  const trimmed = await session.text({ trimEnd: true });
259
272
  expect(trimmed).toMatchInlineSnapshot(`
260
273
  "
274
+ └── a/src
275
+ └── deprecated.ts (-4)
276
+
277
+
261
278
  a/src/deprecated.ts +0-4
262
279
 
263
280
  1 - // This module is no longer needed
@@ -276,6 +293,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
276
293
  const trimmed = await session.text({ trimEnd: true });
277
294
  expect(trimmed).toMatchInlineSnapshot(`
278
295
  "
296
+ └── b/src
297
+ └── utils.ts (+7)
298
+
299
+
279
300
  b/src/utils.ts +7-0
280
301
 
281
302
  1 + export function clamp(value: number, min: number, max: number): number {
@@ -298,6 +319,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
298
319
  const trimmed = await session.text({ trimEnd: true });
299
320
  expect(trimmed).toMatchInlineSnapshot(`
300
321
  "
322
+ └── b
323
+ └── config.json (+6,-4)
324
+
325
+
301
326
  a/config.json → b/config.json +6-4
302
327
 
303
328
  1 {
@@ -328,6 +353,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
328
353
  const trimmed = await session.text({ trimEnd: true });
329
354
  expect(trimmed).toMatchInlineSnapshot(`
330
355
  "
356
+ └── src
357
+ └── new-name.ts (+1,-1)
358
+
359
+
331
360
  src/old-name.ts → src/new-name.ts +1-1
332
361
 
333
362
  1 - export const name = 'old'
@@ -349,6 +378,9 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
349
378
  const trimmed = await session.text({ trimEnd: true, immediate: true });
350
379
  expect(trimmed).toMatchInlineSnapshot(`
351
380
  "
381
+ └── unknown ()
382
+
383
+
352
384
  unknown +0-0"
353
385
  `);
354
386
  expect(trimmed).not.toContain("URL is private");
@@ -365,6 +397,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
365
397
  expect(trimmed).not.toContain("unknown");
366
398
  expect(trimmed).toMatchInlineSnapshot(`
367
399
  "
400
+ └── b/src
401
+ └── hello.ts (+1,-1)
402
+
403
+
368
404
  a/src/hello.ts → b/src/hello.ts +1-1
369
405
 
370
406
  1 const greeting = 'hello'
@@ -381,6 +417,10 @@ describe("--stdin pager mode (lazygit issue #25)", () => {
381
417
  const trimmed = await session.text({ trimEnd: true });
382
418
  expect(trimmed).toMatchInlineSnapshot(`
383
419
  "
420
+ └── b/src
421
+ └── hello.ts (+1,-1)
422
+
423
+
384
424
  a/src/hello.ts → b/src/hello.ts +1-1
385
425
 
386
426
  1 const greeting = 'hello'
@@ -39,6 +39,13 @@ export declare function slugifyFileName(name: string): string;
39
39
  * Returns null for non-diff lines (headers, hunk markers, etc.).
40
40
  */
41
41
  export declare function extractLineNumber(line: CapturedLine): string | null;
42
+ /**
43
+ * Match a rendered tree file row and extract the file path label.
44
+ * Examples:
45
+ * "│ ├── index.ts (+5,-2)"
46
+ * "└── README.md (-15)"
47
+ */
48
+ export declare function extractTreeFilePath(lineText: string): string | null;
42
49
  /**
43
50
  * Build line-indexed anchors from file section layout positions.
44
51
  * This avoids regex detection on rendered text, which can produce
@@ -1 +1 @@
1
- {"version":3,"file":"web-utils.d.ts","sourceRoot":"","sources":["../src/web-utils.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"web-utils.d.ts","sourceRoot":"","sources":["../src/web-utils.tsx"],"names":[],"mappings":"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"}