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.
- package/dist/diff-utils.d.ts.map +1 -1
- package/dist/diff-utils.js +35 -4
- package/dist/diff-utils.test.js +64 -1
- package/dist/directory-tree.d.ts.map +1 -1
- package/dist/directory-tree.js +13 -0
- package/dist/directory-tree.test.js +37 -15
- package/dist/kv-codec.d.ts +16 -0
- package/dist/kv-codec.d.ts.map +1 -0
- package/dist/kv-codec.js +36 -0
- package/dist/kv-codec.test.d.ts +2 -0
- package/dist/kv-codec.test.d.ts.map +1 -0
- package/dist/kv-codec.test.js +48 -0
- package/dist/stdin-pager.test.js +40 -0
- package/dist/web-utils.d.ts +7 -0
- package/dist/web-utils.d.ts.map +1 -1
- package/dist/web-utils.js +100 -84
- package/dist/web-utils.test.d.ts.map +1 -1
- package/dist/web-utils.test.js +55 -6
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +46 -9
- package/dist/worker.preview-upload.e2e.test.d.ts +2 -0
- package/dist/worker.preview-upload.e2e.test.d.ts.map +1 -0
- package/dist/worker.preview-upload.e2e.test.js +106 -0
- package/package.json +3 -2
- package/src/diff-utils.test.ts +77 -0
- package/src/diff-utils.ts +36 -5
- package/src/directory-tree.test.tsx +40 -15
- package/src/directory-tree.ts +14 -0
- package/src/kv-codec.test.ts +67 -0
- package/src/kv-codec.ts +59 -0
- package/src/stdin-pager.test.ts +40 -0
- package/src/{web-utils.test.ts → web-utils.test.tsx} +71 -9
- package/src/web-utils.tsx +161 -101
- package/src/worker.preview-upload.e2e.test.ts +121 -0
- package/src/worker.tsx +79 -9
package/dist/diff-utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-utils.d.ts","sourceRoot":"","sources":["../src/diff-utils.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/diff-utils.js
CHANGED
|
@@ -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
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
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,
|
package/dist/diff-utils.test.js
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/directory-tree.js
CHANGED
|
@@ -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
|
-
├──
|
|
201
|
-
└──
|
|
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
|
-
"├──
|
|
226
|
-
│
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
" ├──
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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"}
|
package/dist/kv-codec.js
ADDED
|
@@ -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 @@
|
|
|
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
|
+
});
|
package/dist/stdin-pager.test.js
CHANGED
|
@@ -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'
|
package/dist/web-utils.d.ts
CHANGED
|
@@ -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
|
package/dist/web-utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-utils.d.ts","sourceRoot":"","sources":["../src/web-utils.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"web-utils.d.ts","sourceRoot":"","sources":["../src/web-utils.tsx"],"names":[],"mappings":"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"}
|