shell-dsl 0.0.34 → 0.0.36

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.
Files changed (92) hide show
  1. package/README.md +41 -5
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/cjs/src/fs/memfs-adapter.cjs +56 -2
  4. package/dist/cjs/src/fs/memfs-adapter.cjs.map +3 -3
  5. package/dist/cjs/src/fs/real-fs.cjs +134 -3
  6. package/dist/cjs/src/fs/real-fs.cjs.map +3 -3
  7. package/dist/cjs/src/fs/special-files.cjs +3 -2
  8. package/dist/cjs/src/fs/special-files.cjs.map +3 -3
  9. package/dist/cjs/src/fs/web-fs.cjs +72 -3
  10. package/dist/cjs/src/fs/web-fs.cjs.map +3 -3
  11. package/dist/cjs/src/index.cjs +2 -7
  12. package/dist/cjs/src/index.cjs.map +3 -3
  13. package/dist/cjs/src/interpreter/interpreter.cjs +186 -68
  14. package/dist/cjs/src/interpreter/interpreter.cjs.map +3 -3
  15. package/dist/cjs/src/parser/ast.cjs +5 -25
  16. package/dist/cjs/src/parser/ast.cjs.map +3 -3
  17. package/dist/cjs/src/parser/index.cjs +2 -7
  18. package/dist/cjs/src/parser/index.cjs.map +3 -3
  19. package/dist/cjs/src/parser/parser.cjs +132 -82
  20. package/dist/cjs/src/parser/parser.cjs.map +3 -3
  21. package/dist/cjs/src/types.cjs.map +2 -2
  22. package/dist/cjs/src/vcs/content.cjs +106 -0
  23. package/dist/cjs/src/vcs/content.cjs.map +10 -0
  24. package/dist/cjs/src/vcs/diff.cjs +72 -28
  25. package/dist/cjs/src/vcs/diff.cjs.map +3 -3
  26. package/dist/cjs/src/vcs/index.cjs.map +1 -1
  27. package/dist/cjs/src/vcs/objects.cjs +141 -0
  28. package/dist/cjs/src/vcs/objects.cjs.map +10 -0
  29. package/dist/cjs/src/vcs/rules.cjs +6 -3
  30. package/dist/cjs/src/vcs/rules.cjs.map +3 -3
  31. package/dist/cjs/src/vcs/snapshot.cjs +89 -39
  32. package/dist/cjs/src/vcs/snapshot.cjs.map +3 -3
  33. package/dist/cjs/src/vcs/storage.cjs +44 -3
  34. package/dist/cjs/src/vcs/storage.cjs.map +3 -3
  35. package/dist/cjs/src/vcs/text-diff.cjs +219 -0
  36. package/dist/cjs/src/vcs/text-diff.cjs.map +10 -0
  37. package/dist/cjs/src/vcs/vcs.cjs +108 -61
  38. package/dist/cjs/src/vcs/vcs.cjs.map +3 -3
  39. package/dist/mjs/package.json +1 -1
  40. package/dist/mjs/src/fs/memfs-adapter.mjs +57 -2
  41. package/dist/mjs/src/fs/memfs-adapter.mjs.map +3 -3
  42. package/dist/mjs/src/fs/real-fs.mjs +135 -3
  43. package/dist/mjs/src/fs/real-fs.mjs.map +3 -3
  44. package/dist/mjs/src/fs/special-files.mjs +3 -2
  45. package/dist/mjs/src/fs/special-files.mjs.map +3 -3
  46. package/dist/mjs/src/fs/web-fs.mjs +72 -3
  47. package/dist/mjs/src/fs/web-fs.mjs.map +3 -3
  48. package/dist/mjs/src/index.mjs +4 -14
  49. package/dist/mjs/src/index.mjs.map +3 -3
  50. package/dist/mjs/src/interpreter/interpreter.mjs +186 -68
  51. package/dist/mjs/src/interpreter/interpreter.mjs.map +3 -3
  52. package/dist/mjs/src/parser/ast.mjs +5 -25
  53. package/dist/mjs/src/parser/ast.mjs.map +3 -3
  54. package/dist/mjs/src/parser/index.mjs +4 -14
  55. package/dist/mjs/src/parser/index.mjs.map +3 -3
  56. package/dist/mjs/src/parser/parser.mjs +132 -82
  57. package/dist/mjs/src/parser/parser.mjs.map +3 -3
  58. package/dist/mjs/src/types.mjs.map +2 -2
  59. package/dist/mjs/src/vcs/content.mjs +66 -0
  60. package/dist/mjs/src/vcs/content.mjs.map +10 -0
  61. package/dist/mjs/src/vcs/diff.mjs +72 -28
  62. package/dist/mjs/src/vcs/diff.mjs.map +3 -3
  63. package/dist/mjs/src/vcs/index.mjs.map +1 -1
  64. package/dist/mjs/src/vcs/objects.mjs +106 -0
  65. package/dist/mjs/src/vcs/objects.mjs.map +10 -0
  66. package/dist/mjs/src/vcs/rules.mjs +6 -3
  67. package/dist/mjs/src/vcs/rules.mjs.map +3 -3
  68. package/dist/mjs/src/vcs/snapshot.mjs +89 -39
  69. package/dist/mjs/src/vcs/snapshot.mjs.map +3 -3
  70. package/dist/mjs/src/vcs/storage.mjs +45 -3
  71. package/dist/mjs/src/vcs/storage.mjs.map +3 -3
  72. package/dist/mjs/src/vcs/text-diff.mjs +179 -0
  73. package/dist/mjs/src/vcs/text-diff.mjs.map +10 -0
  74. package/dist/mjs/src/vcs/vcs.mjs +115 -63
  75. package/dist/mjs/src/vcs/vcs.mjs.map +3 -3
  76. package/dist/types/src/fs/real-fs.d.ts +12 -1
  77. package/dist/types/src/index.d.ts +4 -4
  78. package/dist/types/src/interpreter/interpreter.d.ts +15 -1
  79. package/dist/types/src/parser/ast.d.ts +34 -38
  80. package/dist/types/src/parser/index.d.ts +2 -2
  81. package/dist/types/src/parser/parser.d.ts +4 -1
  82. package/dist/types/src/types.d.ts +10 -0
  83. package/dist/types/src/vcs/content.d.ts +6 -0
  84. package/dist/types/src/vcs/diff.d.ts +10 -7
  85. package/dist/types/src/vcs/index.d.ts +1 -1
  86. package/dist/types/src/vcs/objects.d.ts +22 -0
  87. package/dist/types/src/vcs/snapshot.d.ts +13 -8
  88. package/dist/types/src/vcs/storage.d.ts +7 -1
  89. package/dist/types/src/vcs/text-diff.d.ts +1 -0
  90. package/dist/types/src/vcs/types.d.ts +20 -5
  91. package/dist/types/src/vcs/vcs.d.ts +6 -0
  92. package/package.json +7 -2
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../src/types.ts"],
4
4
  "sourcesContent": [
5
- "// Virtual Filesystem Interface\nexport interface VirtualFS {\n readFile(path: string): Promise<Buffer>;\n readFile(path: string, encoding: BufferEncoding): Promise<string>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<FileStat>;\n exists(path: string): Promise<boolean>;\n\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<void>;\n\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n\n resolve(...paths: string[]): string;\n dirname(path: string): string;\n basename(path: string): string;\n glob(pattern: string, opts?: { cwd?: string }): Promise<string[]>;\n}\n\nexport interface FileStat {\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n}\n\n// Command Interfaces\nexport type Command = (ctx: CommandContext) => Promise<number>;\n\nexport interface CommandContext {\n args: string[];\n stdin: Stdin;\n stdout: Stdout;\n stderr: Stderr;\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n setCwd: (path: string) => void;\n exec?: (name: string, args: string[]) => Promise<ExecResult>;\n}\n\nexport interface Stdin {\n stream(): AsyncIterable<Uint8Array>;\n buffer(): Promise<Buffer>;\n text(): Promise<string>;\n lines(): AsyncIterable<string>;\n}\n\nexport interface Stdout {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n isTTY: boolean;\n}\n\nexport interface Stderr {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n isTTY: boolean;\n}\n\nexport interface OutputCollector extends Stdout {\n close(): void;\n collect(): Promise<Buffer>;\n getReadableStream(): AsyncIterable<Uint8Array>;\n}\n\n// Execution Result\nexport interface ExecResult {\n stdout: Buffer;\n stderr: Buffer;\n exitCode: number;\n}\n\n// Shell Configuration\nexport interface ShellConfig {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n isTTY?: boolean;\n}\n\n// Raw escape hatch type\nexport interface RawValue {\n raw: string;\n}\n\nexport function isRawValue(value: unknown): value is RawValue {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"raw\" in value &&\n typeof (value as RawValue).raw === \"string\"\n );\n}\n\n// JS Object Redirection types\nexport type RedirectObject = Buffer | Blob | Response | string;\n\nexport interface RedirectObjectMap {\n [marker: string]: RedirectObject;\n}\n\nexport function isRedirectObject(value: unknown): value is RedirectObject {\n return (\n Buffer.isBuffer(value) ||\n value instanceof Blob ||\n value instanceof Response ||\n typeof value === \"string\"\n );\n}\n"
5
+ "// Virtual Filesystem Interface\nexport interface VirtualFSWritable {\n write(chunk: Uint8Array): Promise<void>;\n close(): Promise<void>;\n abort?(reason?: unknown): Promise<void>;\n}\n\nexport interface VirtualFS {\n readFile(path: string): Promise<Buffer>;\n readFile(path: string, encoding: BufferEncoding): Promise<string>;\n readStream(path: string): AsyncIterable<Uint8Array>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<FileStat>;\n exists(path: string): Promise<boolean>;\n\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n writeStream(path: string, opts?: { append?: boolean }): Promise<VirtualFSWritable>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<void>;\n\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n\n resolve(...paths: string[]): string;\n dirname(path: string): string;\n basename(path: string): string;\n glob(pattern: string, opts?: { cwd?: string }): Promise<string[]>;\n}\n\nexport interface FileStat {\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n mtimeMs: number;\n}\n\n// Command Interfaces\nexport type Command = (ctx: CommandContext) => Promise<number>;\n\nexport interface CommandContext {\n args: string[];\n stdin: Stdin;\n stdout: Stdout;\n stderr: Stderr;\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n setCwd: (path: string) => void;\n exec?: (name: string, args: string[]) => Promise<ExecResult>;\n}\n\nexport interface Stdin {\n stream(): AsyncIterable<Uint8Array>;\n buffer(): Promise<Buffer>;\n text(): Promise<string>;\n lines(): AsyncIterable<string>;\n}\n\nexport interface Stdout {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n isTTY: boolean;\n}\n\nexport interface Stderr {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n isTTY: boolean;\n}\n\nexport interface OutputCollector extends Stdout {\n close(): void;\n collect(): Promise<Buffer>;\n getReadableStream(): AsyncIterable<Uint8Array>;\n}\n\n// Execution Result\nexport interface ExecResult {\n stdout: Buffer;\n stderr: Buffer;\n exitCode: number;\n}\n\n// Shell Configuration\nexport interface ShellConfig {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n isTTY?: boolean;\n}\n\n// Raw escape hatch type\nexport interface RawValue {\n raw: string;\n}\n\nexport function isRawValue(value: unknown): value is RawValue {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"raw\" in value &&\n typeof (value as RawValue).raw === \"string\"\n );\n}\n\n// JS Object Redirection types\nexport type RedirectObject = Buffer | Blob | Response | string;\n\nexport interface RedirectObjectMap {\n [marker: string]: RedirectObject;\n}\n\nexport function isRedirectObject(value: unknown): value is RedirectObject {\n return (\n Buffer.isBuffer(value) ||\n value instanceof Blob ||\n value instanceof Response ||\n typeof value === \"string\"\n );\n}\n"
6
6
  ],
7
- "mappings": ";AAwFO,SAAS,UAAU,CAAC,OAAmC;AAAA,EAC5D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,SAAS,SACT,OAAQ,MAAmB,QAAQ;AAAA;AAWhC,SAAS,gBAAgB,CAAC,OAAyC;AAAA,EACxE,OACE,OAAO,SAAS,KAAK,KACrB,iBAAiB,QACjB,iBAAiB,YACjB,OAAO,UAAU;AAAA;",
7
+ "mappings": ";AAiGO,SAAS,UAAU,CAAC,OAAmC;AAAA,EAC5D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,SAAS,SACT,OAAQ,MAAmB,QAAQ;AAAA;AAWhC,SAAS,gBAAgB,CAAC,OAAyC;AAAA,EACxE,OACE,OAAO,SAAS,KAAK,KACrB,iBAAiB,QACjB,iBAAiB,YACjB,OAAO,UAAU;AAAA;",
8
8
  "debugId": "9C8E04A91CC7802764756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,66 @@
1
+ // src/vcs/content.ts
2
+ import { sha256 } from "@noble/hashes/sha2.js";
3
+ var BINARY_SAMPLE_BYTES = 64 * 1024;
4
+ var MAX_PATCH_BYTES = 1024 * 1024;
5
+ var textDecoder = new TextDecoder("utf-8", { fatal: true });
6
+ var ALLOWED_CONTROL_BYTES = new Set([8, 9, 10, 12, 13]);
7
+ function detectBinaryFromSample(sample) {
8
+ if (sample.length === 0) {
9
+ return false;
10
+ }
11
+ let controlBytes = 0;
12
+ for (const byte of sample) {
13
+ if (byte === 0) {
14
+ return true;
15
+ }
16
+ if (byte < 32 && !ALLOWED_CONTROL_BYTES.has(byte)) {
17
+ controlBytes++;
18
+ }
19
+ }
20
+ try {
21
+ textDecoder.decode(sample);
22
+ } catch {
23
+ return true;
24
+ }
25
+ return controlBytes / sample.length > 0.2;
26
+ }
27
+ function appendSample(sample, chunk, limit = BINARY_SAMPLE_BYTES) {
28
+ if (sample.length >= limit || chunk.length === 0) {
29
+ return sample;
30
+ }
31
+ const nextLength = Math.min(limit, sample.length + chunk.length);
32
+ const next = new Uint8Array(nextLength);
33
+ next.set(sample, 0);
34
+ next.set(chunk.subarray(0, nextLength - sample.length), sample.length);
35
+ return next;
36
+ }
37
+ async function readStreamSample(source, limit = BINARY_SAMPLE_BYTES) {
38
+ let sample = new Uint8Array;
39
+ for await (const chunk of source) {
40
+ sample = appendSample(sample, chunk, limit);
41
+ if (sample.length >= limit) {
42
+ break;
43
+ }
44
+ }
45
+ return sample;
46
+ }
47
+ function hashSample(sample) {
48
+ return toHex(sha256(sample));
49
+ }
50
+ function toHex(bytes) {
51
+ let out = "";
52
+ for (const byte of bytes) {
53
+ out += byte.toString(16).padStart(2, "0");
54
+ }
55
+ return out;
56
+ }
57
+ export {
58
+ readStreamSample,
59
+ hashSample,
60
+ detectBinaryFromSample,
61
+ appendSample,
62
+ MAX_PATCH_BYTES,
63
+ BINARY_SAMPLE_BYTES
64
+ };
65
+
66
+ //# debugId=DD419DBA8730B84964756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/vcs/content.ts"],
4
+ "sourcesContent": [
5
+ "import { sha256 } from \"@noble/hashes/sha2.js\";\n\nexport const BINARY_SAMPLE_BYTES = 64 * 1024;\nexport const MAX_PATCH_BYTES = 1024 * 1024;\n\nconst textDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nconst ALLOWED_CONTROL_BYTES = new Set([0x08, 0x09, 0x0a, 0x0c, 0x0d]);\n\nexport function detectBinaryFromSample(sample: Uint8Array<ArrayBufferLike>): boolean {\n if (sample.length === 0) {\n return false;\n }\n\n let controlBytes = 0;\n\n for (const byte of sample) {\n if (byte === 0x00) {\n return true;\n }\n if (byte < 0x20 && !ALLOWED_CONTROL_BYTES.has(byte)) {\n controlBytes++;\n }\n }\n\n try {\n textDecoder.decode(sample);\n } catch {\n return true;\n }\n\n return controlBytes / sample.length > 0.2;\n}\n\nexport function appendSample(\n sample: Uint8Array<ArrayBufferLike>,\n chunk: Uint8Array<ArrayBufferLike>,\n limit: number = BINARY_SAMPLE_BYTES,\n): Uint8Array<ArrayBufferLike> {\n if (sample.length >= limit || chunk.length === 0) {\n return sample;\n }\n\n const nextLength = Math.min(limit, sample.length + chunk.length);\n const next = new Uint8Array(nextLength);\n next.set(sample, 0);\n next.set(chunk.subarray(0, nextLength - sample.length), sample.length);\n return next;\n}\n\nexport async function readStreamSample(\n source: AsyncIterable<Uint8Array>,\n limit: number = BINARY_SAMPLE_BYTES,\n): Promise<Uint8Array<ArrayBufferLike>> {\n let sample: Uint8Array<ArrayBufferLike> = new Uint8Array();\n for await (const chunk of source) {\n sample = appendSample(sample, chunk, limit);\n if (sample.length >= limit) {\n break;\n }\n }\n return sample;\n}\n\nexport function hashSample(sample: Uint8Array<ArrayBufferLike>): string {\n return toHex(sha256(sample));\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let out = \"\";\n for (const byte of bytes) {\n out += byte.toString(16).padStart(2, \"0\");\n }\n return out;\n}\n"
6
+ ],
7
+ "mappings": ";AAAA;AAEO,IAAM,sBAAsB,KAAK;AACjC,IAAM,kBAAkB,OAAO;AAEtC,IAAM,cAAc,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC;AAC5D,IAAM,wBAAwB,IAAI,IAAI,CAAC,GAAM,GAAM,IAAM,IAAM,EAAI,CAAC;AAE7D,SAAS,sBAAsB,CAAC,QAA8C;AAAA,EACnF,IAAI,OAAO,WAAW,GAAG;AAAA,IACvB,OAAO;AAAA,EACT;AAAA,EAEA,IAAI,eAAe;AAAA,EAEnB,WAAW,QAAQ,QAAQ;AAAA,IACzB,IAAI,SAAS,GAAM;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IACA,IAAI,OAAO,MAAQ,CAAC,sBAAsB,IAAI,IAAI,GAAG;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI;AAAA,IACF,YAAY,OAAO,MAAM;AAAA,IACzB,MAAM;AAAA,IACN,OAAO;AAAA;AAAA,EAGT,OAAO,eAAe,OAAO,SAAS;AAAA;AAGjC,SAAS,YAAY,CAC1B,QACA,OACA,QAAgB,qBACa;AAAA,EAC7B,IAAI,OAAO,UAAU,SAAS,MAAM,WAAW,GAAG;AAAA,IAChD,OAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,KAAK,IAAI,OAAO,OAAO,SAAS,MAAM,MAAM;AAAA,EAC/D,MAAM,OAAO,IAAI,WAAW,UAAU;AAAA,EACtC,KAAK,IAAI,QAAQ,CAAC;AAAA,EAClB,KAAK,IAAI,MAAM,SAAS,GAAG,aAAa,OAAO,MAAM,GAAG,OAAO,MAAM;AAAA,EACrE,OAAO;AAAA;AAGT,eAAsB,gBAAgB,CACpC,QACA,QAAgB,qBACsB;AAAA,EACtC,IAAI,SAAsC,IAAI;AAAA,EAC9C,iBAAiB,SAAS,QAAQ;AAAA,IAChC,SAAS,aAAa,QAAQ,OAAO,KAAK;AAAA,IAC1C,IAAI,OAAO,UAAU,OAAO;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAGF,SAAS,UAAU,CAAC,QAA6C;AAAA,EACtE,OAAO,MAAM,OAAO,MAAM,CAAC;AAAA;AAG7B,SAAS,KAAK,CAAC,OAA2B;AAAA,EACxC,IAAI,MAAM;AAAA,EACV,WAAW,QAAQ,OAAO;AAAA,IACxB,OAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EAC1C;AAAA,EACA,OAAO;AAAA;",
8
+ "debugId": "DD419DBA8730B84964756E2164756E21",
9
+ "names": []
10
+ }
@@ -1,57 +1,102 @@
1
1
  // src/vcs/diff.ts
2
+ import { MAX_PATCH_BYTES } from "./content.mjs";
2
3
  import { VCSRules } from "./rules.mjs";
3
- import { buildTreeManifest } from "./snapshot.mjs";
4
- function diffManifests(before, after, rules = new VCSRules) {
4
+ import { createUnifiedPatch } from "./text-diff.mjs";
5
+ async function diffManifests(before, after, options) {
5
6
  const entries = [];
6
7
  const allPaths = new Set([...Object.keys(before), ...Object.keys(after)]);
7
- for (const path of allPaths) {
8
+ for (const path of [...allPaths].sort((a, b) => a.localeCompare(b))) {
8
9
  const prev = before[path];
9
10
  const curr = after[path];
10
11
  if (!prev && curr) {
11
- entries.push(createDiffEntry("add", path, curr, undefined, rules));
12
+ entries.push(await createDiffEntry("add", path, curr, undefined, options));
12
13
  } else if (prev && !curr) {
13
- entries.push(createDiffEntry("delete", path, undefined, prev, rules));
14
+ entries.push(await createDiffEntry("delete", path, undefined, prev, options));
14
15
  } else if (prev && curr && !entriesEqual(prev, curr)) {
15
- entries.push(createDiffEntry("modify", path, curr, prev, rules));
16
+ entries.push(await createDiffEntry("modify", path, curr, prev, options));
16
17
  }
17
18
  }
18
- return entries.sort((a, b) => a.path.localeCompare(b.path));
19
+ return entries;
19
20
  }
20
- async function diffWorkingTree(fs, rootPath, manifest, rules = new VCSRules({ internalDirName: ".vcs" })) {
21
- const workingManifest = await buildTreeManifest(fs, rootPath, {
22
- rules,
23
- trackedPaths: Object.keys(manifest)
24
- });
25
- return diffManifests(manifest, workingManifest, rules);
26
- }
27
- function createDiffEntry(type, path, current, previous, rules) {
21
+ async function createDiffEntry(type, path, current, previous, options) {
22
+ const rules = options.rules ?? new VCSRules;
28
23
  const attributes = rules.resolveAttributes(path);
29
24
  const entryKind = getEntryKind(current ?? previous);
30
25
  const previousEntryKind = previous ? getEntryKind(previous) : undefined;
26
+ const currentFile = isFileEntry(current) ? current : undefined;
27
+ const previousFile = isFileEntry(previous) ? previous : undefined;
28
+ const binary = await resolveBinary(path, currentFile, previousFile, attributes, options);
29
+ const diff = resolveDiffMode(attributes, binary);
31
30
  const entry = {
32
31
  type,
33
32
  path,
34
- binary: attributes.binary,
35
- diff: attributes.diff,
33
+ binary,
34
+ diff,
36
35
  entryKind,
37
- previousEntryKind
36
+ previousEntryKind,
37
+ blobId: currentFile?.blobId,
38
+ previousBlobId: previousFile?.blobId
38
39
  };
39
- if (attributes.diff !== "none") {
40
- if (isFileEntry(current)) {
41
- entry.content = current.content;
42
- }
43
- if (isFileEntry(previous)) {
44
- entry.previousContent = previous.content;
45
- }
40
+ if (!currentFile && !previousFile) {
41
+ return entry;
42
+ }
43
+ if (diff === "none") {
44
+ entry.patchSuppressedReason = "none";
45
+ return entry;
46
+ }
47
+ if (diff === "binary") {
48
+ entry.patchSuppressedReason = "binary";
49
+ return entry;
46
50
  }
51
+ const largestSize = Math.max(currentFile?.size ?? 0, previousFile?.size ?? 0);
52
+ if (largestSize > MAX_PATCH_BYTES) {
53
+ entry.patchSuppressedReason = "too-large";
54
+ return entry;
55
+ }
56
+ const previousText = previousFile ? await options.objectStore.readBlobText(previousFile.blobId) : "";
57
+ const currentText = currentFile ? await options.objectStore.readBlobText(currentFile.blobId) : "";
58
+ entry.patch = createUnifiedPatch(path, previousText, currentText);
47
59
  return entry;
48
60
  }
61
+ async function resolveBinary(path, current, previous, attributes, options) {
62
+ if (attributes.diff === "text") {
63
+ return false;
64
+ }
65
+ if (attributes.diff === "binary" || attributes.binary === true) {
66
+ return true;
67
+ }
68
+ const currentBinary = await resolveEntryBinary(path, current, options.afterIndex, options.objectStore);
69
+ const previousBinary = await resolveEntryBinary(path, previous, options.beforeIndex, options.objectStore);
70
+ return currentBinary || previousBinary;
71
+ }
72
+ async function resolveEntryBinary(path, entry, indexEntries, objectStore) {
73
+ if (!entry) {
74
+ return false;
75
+ }
76
+ const cached = indexEntries?.[path];
77
+ if (cached?.blobId === entry.blobId) {
78
+ return cached.binary;
79
+ }
80
+ return objectStore.isBinaryBlob(entry.blobId);
81
+ }
82
+ function resolveDiffMode(attributes, binary) {
83
+ if (attributes.diff === "none") {
84
+ return "none";
85
+ }
86
+ if (attributes.diff === "text") {
87
+ return "text";
88
+ }
89
+ if (attributes.diff === "binary" || binary) {
90
+ return "binary";
91
+ }
92
+ return "text";
93
+ }
49
94
  function entriesEqual(a, b) {
50
95
  if (getEntryKind(a) !== getEntryKind(b))
51
96
  return false;
52
97
  if (!isFileEntry(a) || !isFileEntry(b))
53
98
  return true;
54
- return a.content === b.content;
99
+ return a.blobId === b.blobId && a.size === b.size;
55
100
  }
56
101
  function getEntryKind(entry) {
57
102
  return entry?.kind === "directory" ? "directory" : "file";
@@ -60,8 +105,7 @@ function isFileEntry(entry) {
60
105
  return !!entry && entry.kind !== "directory";
61
106
  }
62
107
  export {
63
- diffWorkingTree,
64
108
  diffManifests
65
109
  };
66
110
 
67
- //# debugId=E1AC85757DDF65ED64756E2164756E21
111
+ //# debugId=72FD49BE2137E1FF64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/vcs/diff.ts"],
4
4
  "sourcesContent": [
5
- "import type { VirtualFS } from \"../types.mjs\";\nimport type { TreeManifest, DiffEntry, TreeEntry } from \"./types.mjs\";\nimport { VCSRules } from \"./rules.mjs\";\nimport { buildTreeManifest } from \"./snapshot.mjs\";\n\n/**\n * Compute diff entries between two tree manifests.\n */\nexport function diffManifests(\n before: TreeManifest,\n after: TreeManifest,\n rules: VCSRules = new VCSRules(),\n): DiffEntry[] {\n const entries: DiffEntry[] = [];\n const allPaths = new Set([...Object.keys(before), ...Object.keys(after)]);\n\n for (const path of allPaths) {\n const prev = before[path];\n const curr = after[path];\n\n if (!prev && curr) {\n entries.push(createDiffEntry(\"add\", path, curr, undefined, rules));\n } else if (prev && !curr) {\n entries.push(createDiffEntry(\"delete\", path, undefined, prev, rules));\n } else if (prev && curr && !entriesEqual(prev, curr)) {\n entries.push(createDiffEntry(\"modify\", path, curr, prev, rules));\n }\n }\n\n return entries.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/**\n * Compute diff between a tree manifest and the current working tree.\n */\nexport async function diffWorkingTree(\n fs: VirtualFS,\n rootPath: string,\n manifest: TreeManifest,\n rules: VCSRules = new VCSRules({ internalDirName: \".vcs\" }),\n): Promise<DiffEntry[]> {\n const workingManifest = await buildTreeManifest(fs, rootPath, {\n rules,\n trackedPaths: Object.keys(manifest),\n });\n return diffManifests(manifest, workingManifest, rules);\n}\n\nfunction createDiffEntry(\n type: DiffEntry[\"type\"],\n path: string,\n current: TreeEntry | undefined,\n previous: TreeEntry | undefined,\n rules: VCSRules,\n): DiffEntry {\n const attributes = rules.resolveAttributes(path);\n const entryKind = getEntryKind(current ?? previous);\n const previousEntryKind = previous ? getEntryKind(previous) : undefined;\n const entry: DiffEntry = {\n type,\n path,\n binary: attributes.binary,\n diff: attributes.diff,\n entryKind,\n previousEntryKind,\n };\n\n if (attributes.diff !== \"none\") {\n if (isFileEntry(current)) {\n entry.content = current.content;\n }\n if (isFileEntry(previous)) {\n entry.previousContent = previous.content;\n }\n }\n\n return entry;\n}\n\nfunction entriesEqual(a: TreeEntry, b: TreeEntry): boolean {\n if (getEntryKind(a) !== getEntryKind(b)) return false;\n if (!isFileEntry(a) || !isFileEntry(b)) return true;\n return a.content === b.content;\n}\n\nfunction getEntryKind(entry: TreeEntry | undefined): \"file\" | \"directory\" {\n return entry?.kind === \"directory\" ? \"directory\" : \"file\";\n}\n\nfunction isFileEntry(entry: TreeEntry | undefined): entry is Extract<TreeEntry, { kind?: \"file\" }> {\n return !!entry && entry.kind !== \"directory\";\n}\n"
5
+ "import type { TreeManifest, DiffEntry, TreeEntry, VCSIndexEntry } from \"./types.mjs\";\nimport { MAX_PATCH_BYTES } from \"./content.mjs\";\nimport { VCSObjectStore } from \"./objects.mjs\";\nimport { VCSRules } from \"./rules.mjs\";\nimport { createUnifiedPatch } from \"./text-diff.mjs\";\n\ninterface DiffOptions {\n rules?: VCSRules;\n objectStore: VCSObjectStore;\n beforeIndex?: Record<string, VCSIndexEntry>;\n afterIndex?: Record<string, VCSIndexEntry>;\n}\n\n/**\n * Compute diff entries between two tree manifests.\n */\nexport async function diffManifests(\n before: TreeManifest,\n after: TreeManifest,\n options: DiffOptions,\n): Promise<DiffEntry[]> {\n const entries: DiffEntry[] = [];\n const allPaths = new Set([...Object.keys(before), ...Object.keys(after)]);\n\n for (const path of [...allPaths].sort((a, b) => a.localeCompare(b))) {\n const prev = before[path];\n const curr = after[path];\n\n if (!prev && curr) {\n entries.push(await createDiffEntry(\"add\", path, curr, undefined, options));\n } else if (prev && !curr) {\n entries.push(await createDiffEntry(\"delete\", path, undefined, prev, options));\n } else if (prev && curr && !entriesEqual(prev, curr)) {\n entries.push(await createDiffEntry(\"modify\", path, curr, prev, options));\n }\n }\n\n return entries;\n}\n\nasync function createDiffEntry(\n type: DiffEntry[\"type\"],\n path: string,\n current: TreeEntry | undefined,\n previous: TreeEntry | undefined,\n options: DiffOptions,\n): Promise<DiffEntry> {\n const rules = options.rules ?? new VCSRules();\n const attributes = rules.resolveAttributes(path);\n const entryKind = getEntryKind(current ?? previous);\n const previousEntryKind = previous ? getEntryKind(previous) : undefined;\n const currentFile = isFileEntry(current) ? current : undefined;\n const previousFile = isFileEntry(previous) ? previous : undefined;\n const binary = await resolveBinary(path, currentFile, previousFile, attributes, options);\n const diff = resolveDiffMode(attributes, binary);\n const entry: DiffEntry = {\n type,\n path,\n binary,\n diff,\n entryKind,\n previousEntryKind,\n blobId: currentFile?.blobId,\n previousBlobId: previousFile?.blobId,\n };\n\n if (!currentFile && !previousFile) {\n return entry;\n }\n\n if (diff === \"none\") {\n entry.patchSuppressedReason = \"none\";\n return entry;\n }\n\n if (diff === \"binary\") {\n entry.patchSuppressedReason = \"binary\";\n return entry;\n }\n\n const largestSize = Math.max(currentFile?.size ?? 0, previousFile?.size ?? 0);\n if (largestSize > MAX_PATCH_BYTES) {\n entry.patchSuppressedReason = \"too-large\";\n return entry;\n }\n\n const previousText = previousFile\n ? await options.objectStore.readBlobText(previousFile.blobId)\n : \"\";\n const currentText = currentFile\n ? await options.objectStore.readBlobText(currentFile.blobId)\n : \"\";\n entry.patch = createUnifiedPatch(path, previousText, currentText);\n return entry;\n}\n\nasync function resolveBinary(\n path: string,\n current: Extract<TreeEntry, { kind?: \"file\" }> | undefined,\n previous: Extract<TreeEntry, { kind?: \"file\" }> | undefined,\n attributes: ReturnType<VCSRules[\"resolveAttributes\"]>,\n options: DiffOptions,\n): Promise<boolean> {\n if (attributes.diff === \"text\") {\n return false;\n }\n if (attributes.diff === \"binary\" || attributes.binary === true) {\n return true;\n }\n\n const currentBinary = await resolveEntryBinary(\n path,\n current,\n options.afterIndex,\n options.objectStore,\n );\n const previousBinary = await resolveEntryBinary(\n path,\n previous,\n options.beforeIndex,\n options.objectStore,\n );\n\n return currentBinary || previousBinary;\n}\n\nasync function resolveEntryBinary(\n path: string,\n entry: Extract<TreeEntry, { kind?: \"file\" }> | undefined,\n indexEntries: Record<string, VCSIndexEntry> | undefined,\n objectStore: VCSObjectStore,\n): Promise<boolean> {\n if (!entry) {\n return false;\n }\n const cached = indexEntries?.[path];\n if (cached?.blobId === entry.blobId) {\n return cached.binary;\n }\n return objectStore.isBinaryBlob(entry.blobId);\n}\n\nfunction resolveDiffMode(\n attributes: ReturnType<VCSRules[\"resolveAttributes\"]>,\n binary: boolean,\n): DiffEntry[\"diff\"] {\n if (attributes.diff === \"none\") {\n return \"none\";\n }\n if (attributes.diff === \"text\") {\n return \"text\";\n }\n if (attributes.diff === \"binary\" || binary) {\n return \"binary\";\n }\n return \"text\";\n}\n\nfunction entriesEqual(a: TreeEntry, b: TreeEntry): boolean {\n if (getEntryKind(a) !== getEntryKind(b)) return false;\n if (!isFileEntry(a) || !isFileEntry(b)) return true;\n return a.blobId === b.blobId && a.size === b.size;\n}\n\nfunction getEntryKind(entry: TreeEntry | undefined): \"file\" | \"directory\" {\n return entry?.kind === \"directory\" ? \"directory\" : \"file\";\n}\n\nfunction isFileEntry(entry: TreeEntry | undefined): entry is Extract<TreeEntry, { kind?: \"file\" }> {\n return !!entry && entry.kind !== \"directory\";\n}\n"
6
6
  ],
7
- "mappings": ";AAEA;AACA;AAKO,SAAS,aAAa,CAC3B,QACA,OACA,QAAkB,IAAI,UACT;AAAA,EACb,MAAM,UAAuB,CAAC;AAAA,EAC9B,MAAM,WAAW,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC;AAAA,EAExE,WAAW,QAAQ,UAAU;AAAA,IAC3B,MAAM,OAAO,OAAO;AAAA,IACpB,MAAM,OAAO,MAAM;AAAA,IAEnB,IAAI,CAAC,QAAQ,MAAM;AAAA,MACjB,QAAQ,KAAK,gBAAgB,OAAO,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,IACnE,EAAO,SAAI,QAAQ,CAAC,MAAM;AAAA,MACxB,QAAQ,KAAK,gBAAgB,UAAU,MAAM,WAAW,MAAM,KAAK,CAAC;AAAA,IACtE,EAAO,SAAI,QAAQ,QAAQ,CAAC,aAAa,MAAM,IAAI,GAAG;AAAA,MACpD,QAAQ,KAAK,gBAAgB,UAAU,MAAM,MAAM,MAAM,KAAK,CAAC;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA;AAM5D,eAAsB,eAAe,CACnC,IACA,UACA,UACA,QAAkB,IAAI,SAAS,EAAE,iBAAiB,OAAO,CAAC,GACpC;AAAA,EACtB,MAAM,kBAAkB,MAAM,kBAAkB,IAAI,UAAU;AAAA,IAC5D;AAAA,IACA,cAAc,OAAO,KAAK,QAAQ;AAAA,EACpC,CAAC;AAAA,EACD,OAAO,cAAc,UAAU,iBAAiB,KAAK;AAAA;AAGvD,SAAS,eAAe,CACtB,MACA,MACA,SACA,UACA,OACW;AAAA,EACX,MAAM,aAAa,MAAM,kBAAkB,IAAI;AAAA,EAC/C,MAAM,YAAY,aAAa,WAAW,QAAQ;AAAA,EAClD,MAAM,oBAAoB,WAAW,aAAa,QAAQ,IAAI;AAAA,EAC9D,MAAM,QAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB,MAAM,WAAW;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AAAA,EAEA,IAAI,WAAW,SAAS,QAAQ;AAAA,IAC9B,IAAI,YAAY,OAAO,GAAG;AAAA,MACxB,MAAM,UAAU,QAAQ;AAAA,IAC1B;AAAA,IACA,IAAI,YAAY,QAAQ,GAAG;AAAA,MACzB,MAAM,kBAAkB,SAAS;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,OAAO;AAAA;AAGT,SAAS,YAAY,CAAC,GAAc,GAAuB;AAAA,EACzD,IAAI,aAAa,CAAC,MAAM,aAAa,CAAC;AAAA,IAAG,OAAO;AAAA,EAChD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC;AAAA,IAAG,OAAO;AAAA,EAC/C,OAAO,EAAE,YAAY,EAAE;AAAA;AAGzB,SAAS,YAAY,CAAC,OAAoD;AAAA,EACxE,OAAO,OAAO,SAAS,cAAc,cAAc;AAAA;AAGrD,SAAS,WAAW,CAAC,OAA8E;AAAA,EACjG,OAAO,CAAC,CAAC,SAAS,MAAM,SAAS;AAAA;",
8
- "debugId": "E1AC85757DDF65ED64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "72FD49BE2137E1FF64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/vcs/index.ts"],
4
4
  "sourcesContent": [
5
- "export { VersionControlSystem } from \"./vcs.mjs\";\nexport type {\n VCSConfig,\n VCSAttributeRule,\n VCSResolvedAttributes,\n VCSDiffMode,\n Revision,\n DiffEntry,\n TreeManifest,\n TreeEntry,\n FileEntry,\n DirectoryEntry,\n CommitOptions,\n CheckoutOptions,\n LogOptions,\n LogEntry,\n BranchInfo,\n} from \"./types.mjs\";\n"
5
+ "export { VersionControlSystem } from \"./vcs.mjs\";\nexport type {\n VCSConfig,\n VCSAttributeRule,\n VCSResolvedAttributes,\n VCSDiffMode,\n VCSPatchSuppressionReason,\n Revision,\n DiffEntry,\n TreeManifest,\n TreeEntry,\n FileEntry,\n DirectoryEntry,\n VCSIndexEntry,\n VCSIndexFile,\n CommitOptions,\n CheckoutOptions,\n LogOptions,\n LogEntry,\n BranchInfo,\n} from \"./types.mjs\";\n"
6
6
  ],
7
7
  "mappings": ";AAAA;",
8
8
  "debugId": "D3267E53BCD7103264756E2164756E21",
@@ -0,0 +1,106 @@
1
+ // src/vcs/objects.ts
2
+ import { sha256 } from "@noble/hashes/sha2.js";
3
+ import {
4
+ appendSample,
5
+ detectBinaryFromSample,
6
+ hashSample,
7
+ readStreamSample
8
+ } from "./content.mjs";
9
+
10
+ class VCSObjectStore {
11
+ fs;
12
+ basePath;
13
+ constructor(fs, basePath) {
14
+ this.fs = fs;
15
+ this.basePath = basePath;
16
+ }
17
+ async initialize() {
18
+ await this.fs.mkdir(this.path("objects", "blobs"), { recursive: true });
19
+ await this.fs.mkdir(this.path("tmp"), { recursive: true });
20
+ }
21
+ async hasBlob(blobId) {
22
+ return this.fs.exists(this.blobPath(blobId));
23
+ }
24
+ async store(source) {
25
+ const tempPath = this.path("tmp", `${Date.now()}-${Math.random().toString(36).slice(2)}.blob`);
26
+ const writer = await this.fs.writeStream(tempPath);
27
+ const hash = sha256.create();
28
+ let size = 0;
29
+ let sample = new Uint8Array;
30
+ try {
31
+ for await (const chunk of source) {
32
+ const bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
33
+ hash.update(bytes);
34
+ size += bytes.byteLength;
35
+ sample = appendSample(sample, bytes);
36
+ await writer.write(bytes);
37
+ }
38
+ await writer.close();
39
+ const blobId = toHex(hash.digest());
40
+ const finalPath = this.blobPath(blobId);
41
+ await this.fs.mkdir(this.fs.dirname(finalPath), { recursive: true });
42
+ if (!await this.fs.exists(finalPath)) {
43
+ await copyStream(this.fs, tempPath, finalPath);
44
+ }
45
+ await this.fs.rm(tempPath, { force: true });
46
+ return {
47
+ blobId,
48
+ size,
49
+ binary: detectBinaryFromSample(sample),
50
+ sampleHash: hashSample(sample)
51
+ };
52
+ } catch (error) {
53
+ await writer.abort?.(error);
54
+ await this.fs.rm(tempPath, { force: true });
55
+ throw error;
56
+ }
57
+ }
58
+ async readBlob(blobId) {
59
+ return this.fs.readFile(this.blobPath(blobId));
60
+ }
61
+ async readBlobText(blobId) {
62
+ return this.fs.readFile(this.blobPath(blobId), "utf8");
63
+ }
64
+ readBlobStream(blobId) {
65
+ return this.fs.readStream(this.blobPath(blobId));
66
+ }
67
+ async isBinaryBlob(blobId) {
68
+ const sample = await readStreamSample(this.readBlobStream(blobId));
69
+ return detectBinaryFromSample(sample);
70
+ }
71
+ async deleteTempFiles() {
72
+ await this.fs.rm(this.path("tmp"), { recursive: true, force: true });
73
+ await this.fs.mkdir(this.path("tmp"), { recursive: true });
74
+ }
75
+ blobPath(blobId) {
76
+ return this.path("objects", "blobs", blobId.slice(0, 2), blobId.slice(2));
77
+ }
78
+ path(...segments) {
79
+ return this.fs.resolve(this.basePath, ...segments);
80
+ }
81
+ }
82
+ async function copyStream(fs, fromPath, toPath) {
83
+ const writer = await fs.writeStream(toPath);
84
+ try {
85
+ for await (const chunk of fs.readStream(fromPath)) {
86
+ await writer.write(chunk);
87
+ }
88
+ await writer.close();
89
+ } catch (error) {
90
+ await writer.abort?.(error);
91
+ await fs.rm(toPath, { force: true });
92
+ throw error;
93
+ }
94
+ }
95
+ function toHex(bytes) {
96
+ let out = "";
97
+ for (const byte of bytes) {
98
+ out += byte.toString(16).padStart(2, "0");
99
+ }
100
+ return out;
101
+ }
102
+ export {
103
+ VCSObjectStore
104
+ };
105
+
106
+ //# debugId=E1A8D2A19C59546964756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/vcs/objects.ts"],
4
+ "sourcesContent": [
5
+ "import { sha256 } from \"@noble/hashes/sha2.js\";\nimport type { VirtualFS } from \"../types.mjs\";\nimport {\n appendSample,\n detectBinaryFromSample,\n hashSample,\n readStreamSample,\n} from \"./content.mjs\";\n\nexport interface StoredBlob {\n blobId: string;\n size: number;\n binary: boolean;\n sampleHash: string;\n}\n\nexport class VCSObjectStore {\n constructor(\n private readonly fs: VirtualFS,\n private readonly basePath: string,\n ) {}\n\n async initialize(): Promise<void> {\n await this.fs.mkdir(this.path(\"objects\", \"blobs\"), { recursive: true });\n await this.fs.mkdir(this.path(\"tmp\"), { recursive: true });\n }\n\n async hasBlob(blobId: string): Promise<boolean> {\n return this.fs.exists(this.blobPath(blobId));\n }\n\n async store(source: AsyncIterable<Uint8Array>): Promise<StoredBlob> {\n const tempPath = this.path(\"tmp\", `${Date.now()}-${Math.random().toString(36).slice(2)}.blob`);\n const writer = await this.fs.writeStream(tempPath);\n const hash = sha256.create();\n let size = 0;\n let sample: Uint8Array<ArrayBufferLike> = new Uint8Array();\n\n try {\n for await (const chunk of source) {\n const bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n hash.update(bytes);\n size += bytes.byteLength;\n sample = appendSample(sample, bytes);\n await writer.write(bytes);\n }\n await writer.close();\n\n const blobId = toHex(hash.digest());\n const finalPath = this.blobPath(blobId);\n await this.fs.mkdir(this.fs.dirname(finalPath), { recursive: true });\n\n if (!(await this.fs.exists(finalPath))) {\n await copyStream(this.fs, tempPath, finalPath);\n }\n\n await this.fs.rm(tempPath, { force: true });\n return {\n blobId,\n size,\n binary: detectBinaryFromSample(sample),\n sampleHash: hashSample(sample),\n };\n } catch (error) {\n await writer.abort?.(error);\n await this.fs.rm(tempPath, { force: true });\n throw error;\n }\n }\n\n async readBlob(blobId: string): Promise<Buffer> {\n return this.fs.readFile(this.blobPath(blobId));\n }\n\n async readBlobText(blobId: string): Promise<string> {\n return this.fs.readFile(this.blobPath(blobId), \"utf8\");\n }\n\n readBlobStream(blobId: string): AsyncIterable<Uint8Array> {\n return this.fs.readStream(this.blobPath(blobId));\n }\n\n async isBinaryBlob(blobId: string): Promise<boolean> {\n const sample = await readStreamSample(this.readBlobStream(blobId));\n return detectBinaryFromSample(sample);\n }\n\n async deleteTempFiles(): Promise<void> {\n await this.fs.rm(this.path(\"tmp\"), { recursive: true, force: true });\n await this.fs.mkdir(this.path(\"tmp\"), { recursive: true });\n }\n\n private blobPath(blobId: string): string {\n return this.path(\"objects\", \"blobs\", blobId.slice(0, 2), blobId.slice(2));\n }\n\n private path(...segments: string[]): string {\n return this.fs.resolve(this.basePath, ...segments);\n }\n}\n\nasync function copyStream(fs: VirtualFS, fromPath: string, toPath: string): Promise<void> {\n const writer = await fs.writeStream(toPath);\n try {\n for await (const chunk of fs.readStream(fromPath)) {\n await writer.write(chunk);\n }\n await writer.close();\n } catch (error) {\n await writer.abort?.(error);\n await fs.rm(toPath, { force: true });\n throw error;\n }\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let out = \"\";\n for (const byte of bytes) {\n out += byte.toString(16).padStart(2, \"0\");\n }\n return out;\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "E1A8D2A19C59546964756E2164756E21",
9
+ "names": []
10
+ }
@@ -53,8 +53,8 @@ class VCSRules {
53
53
  return this.isIgnored(relPath);
54
54
  }
55
55
  resolveAttributes(relPath) {
56
- let binary = false;
57
- let diff = "text";
56
+ let binary;
57
+ let diff;
58
58
  for (const rule of this.attributeRules) {
59
59
  if (!matchVCSPath(rule.pattern, relPath))
60
60
  continue;
@@ -68,6 +68,9 @@ class VCSRules {
68
68
  if (diff === "binary") {
69
69
  binary = true;
70
70
  }
71
+ if (diff === "text") {
72
+ binary = false;
73
+ }
71
74
  return { binary, diff };
72
75
  }
73
76
  }
@@ -137,4 +140,4 @@ export {
137
140
  VCSRules
138
141
  };
139
142
 
140
- //# debugId=42FBA1E942F9F1FC64756E2164756E21
143
+ //# debugId=B359D27A071846E264756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/vcs/rules.ts"],
4
4
  "sourcesContent": [
5
- "import { matchGlobPath } from \"./match.mjs\";\nimport type { VCSAttributeRule, VCSResolvedAttributes } from \"./types.mjs\";\n\ninterface VCSRulesConfig {\n internalPath?: string;\n internalDirName?: string;\n ignore?: string[];\n attributes?: VCSAttributeRule[];\n}\n\nexport class VCSRules {\n private readonly internalPath: string;\n private readonly ignorePatterns: string[];\n private readonly attributeRules: VCSAttributeRule[];\n\n constructor(config: VCSRulesConfig = {}) {\n this.internalPath = normalizePath(config.internalPath ?? config.internalDirName ?? \"\");\n this.ignorePatterns = [...(config.ignore ?? [])];\n this.attributeRules = [...(config.attributes ?? [])];\n }\n\n isInternalPath(relPath: string): boolean {\n if (!this.internalPath) return false;\n const normalizedPath = normalizePath(relPath);\n if (!normalizedPath) return false;\n return normalizedPath === this.internalPath || normalizedPath.startsWith(`${this.internalPath}/`);\n }\n\n isIgnored(relPath: string): boolean {\n if (this.isInternalPath(relPath)) return true;\n return this.ignorePatterns.some((pattern) => matchVCSPath(pattern, relPath));\n }\n\n shouldEnterDirectory(relPath: string, trackedPaths: Iterable<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (!this.isIgnored(relPath)) return true;\n return hasTrackedPathAtOrBelow(relPath, trackedPaths);\n }\n\n shouldIncludeWorkingFile(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (trackedPaths.has(relPath)) return true;\n return !this.isIgnored(relPath);\n }\n\n shouldIncludeEmptyDirectory(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (trackedPaths.has(relPath)) return true;\n return !this.isIgnored(relPath);\n }\n\n shouldIncludeRestoreScanFile(relPath: string): boolean {\n return !this.isInternalPath(relPath);\n }\n\n shouldPreserveUntrackedIgnored(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (trackedPaths.has(relPath)) return false;\n return this.isIgnored(relPath);\n }\n\n resolveAttributes(relPath: string): VCSResolvedAttributes {\n let binary = false;\n let diff: VCSResolvedAttributes[\"diff\"] = \"text\";\n\n for (const rule of this.attributeRules) {\n if (!matchVCSPath(rule.pattern, relPath)) continue;\n if (rule.binary !== undefined) {\n binary = rule.binary;\n }\n if (rule.diff !== undefined) {\n diff = rule.diff;\n }\n }\n\n if (diff === \"binary\") {\n binary = true;\n }\n\n return { binary, diff };\n }\n}\n\nexport function matchVCSPath(pattern: string, relPath: string): boolean {\n const normalizedPattern = normalizePattern(pattern);\n const normalizedPath = normalizePath(relPath);\n\n if (!normalizedPattern || !normalizedPath) return false;\n\n if (normalizedPattern.mode === \"root-path\") {\n return matchGlobPath(normalizedPattern.pattern, normalizedPath);\n }\n\n if (normalizedPattern.mode === \"root-segment\") {\n const [firstSegment] = normalizedPath.split(\"/\");\n return firstSegment ? matchGlobPath(normalizedPattern.pattern, firstSegment) : false;\n }\n\n if (normalizedPattern.mode === \"root-prefix\") {\n return (\n matchGlobPath(normalizedPattern.pattern, normalizedPath) ||\n matchGlobPath(`${normalizedPattern.pattern}/**`, normalizedPath)\n );\n }\n\n const segments = normalizedPath.split(\"/\");\n return segments.some((segment) => matchGlobPath(normalizedPattern.pattern, segment));\n}\n\nfunction normalizePath(relPath: string): string {\n return relPath.replace(/\\\\/g, \"/\").replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction normalizePattern(pattern: string): {\n pattern: string;\n mode: \"root-path\" | \"root-prefix\" | \"root-segment\" | \"segment\";\n} | null {\n let normalized = pattern.trim();\n if (!normalized) return null;\n\n normalized = normalized.replace(/\\\\/g, \"/\");\n const anchored = normalized.startsWith(\"/\");\n const directoryOnly = normalized.endsWith(\"/\");\n normalized = normalized.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (!normalized) return null;\n\n if (normalized.includes(\"/\")) {\n return {\n pattern: normalized,\n mode: directoryOnly ? \"root-prefix\" : \"root-path\",\n };\n }\n\n if (anchored) {\n return {\n pattern: normalized,\n mode: \"root-segment\",\n };\n }\n\n return {\n pattern: normalized,\n mode: \"segment\",\n };\n}\n\nfunction hasTrackedPathAtOrBelow(relPath: string, trackedPaths: Iterable<string>): boolean {\n const normalizedPath = normalizePath(relPath);\n if (!normalizedPath) return false;\n\n const prefix = normalizedPath + \"/\";\n for (const trackedPath of trackedPaths) {\n const normalizedTrackedPath = normalizePath(trackedPath);\n if (\n normalizedTrackedPath === normalizedPath ||\n normalizedTrackedPath.startsWith(prefix)\n ) {\n return true;\n }\n }\n return false;\n}\n"
5
+ "import { matchGlobPath } from \"./match.mjs\";\nimport type { VCSAttributeRule, VCSResolvedAttributes } from \"./types.mjs\";\n\ninterface VCSRulesConfig {\n internalPath?: string;\n internalDirName?: string;\n ignore?: string[];\n attributes?: VCSAttributeRule[];\n}\n\nexport class VCSRules {\n private readonly internalPath: string;\n private readonly ignorePatterns: string[];\n private readonly attributeRules: VCSAttributeRule[];\n\n constructor(config: VCSRulesConfig = {}) {\n this.internalPath = normalizePath(config.internalPath ?? config.internalDirName ?? \"\");\n this.ignorePatterns = [...(config.ignore ?? [])];\n this.attributeRules = [...(config.attributes ?? [])];\n }\n\n isInternalPath(relPath: string): boolean {\n if (!this.internalPath) return false;\n const normalizedPath = normalizePath(relPath);\n if (!normalizedPath) return false;\n return normalizedPath === this.internalPath || normalizedPath.startsWith(`${this.internalPath}/`);\n }\n\n isIgnored(relPath: string): boolean {\n if (this.isInternalPath(relPath)) return true;\n return this.ignorePatterns.some((pattern) => matchVCSPath(pattern, relPath));\n }\n\n shouldEnterDirectory(relPath: string, trackedPaths: Iterable<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (!this.isIgnored(relPath)) return true;\n return hasTrackedPathAtOrBelow(relPath, trackedPaths);\n }\n\n shouldIncludeWorkingFile(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (trackedPaths.has(relPath)) return true;\n return !this.isIgnored(relPath);\n }\n\n shouldIncludeEmptyDirectory(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (trackedPaths.has(relPath)) return true;\n return !this.isIgnored(relPath);\n }\n\n shouldIncludeRestoreScanFile(relPath: string): boolean {\n return !this.isInternalPath(relPath);\n }\n\n shouldPreserveUntrackedIgnored(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (trackedPaths.has(relPath)) return false;\n return this.isIgnored(relPath);\n }\n\n resolveAttributes(relPath: string): VCSResolvedAttributes {\n let binary: boolean | undefined;\n let diff: VCSResolvedAttributes[\"diff\"];\n\n for (const rule of this.attributeRules) {\n if (!matchVCSPath(rule.pattern, relPath)) continue;\n if (rule.binary !== undefined) {\n binary = rule.binary;\n }\n if (rule.diff !== undefined) {\n diff = rule.diff;\n }\n }\n\n if (diff === \"binary\") {\n binary = true;\n }\n if (diff === \"text\") {\n binary = false;\n }\n\n return { binary, diff };\n }\n}\n\nexport function matchVCSPath(pattern: string, relPath: string): boolean {\n const normalizedPattern = normalizePattern(pattern);\n const normalizedPath = normalizePath(relPath);\n\n if (!normalizedPattern || !normalizedPath) return false;\n\n if (normalizedPattern.mode === \"root-path\") {\n return matchGlobPath(normalizedPattern.pattern, normalizedPath);\n }\n\n if (normalizedPattern.mode === \"root-segment\") {\n const [firstSegment] = normalizedPath.split(\"/\");\n return firstSegment ? matchGlobPath(normalizedPattern.pattern, firstSegment) : false;\n }\n\n if (normalizedPattern.mode === \"root-prefix\") {\n return (\n matchGlobPath(normalizedPattern.pattern, normalizedPath) ||\n matchGlobPath(`${normalizedPattern.pattern}/**`, normalizedPath)\n );\n }\n\n const segments = normalizedPath.split(\"/\");\n return segments.some((segment) => matchGlobPath(normalizedPattern.pattern, segment));\n}\n\nfunction normalizePath(relPath: string): string {\n return relPath.replace(/\\\\/g, \"/\").replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction normalizePattern(pattern: string): {\n pattern: string;\n mode: \"root-path\" | \"root-prefix\" | \"root-segment\" | \"segment\";\n} | null {\n let normalized = pattern.trim();\n if (!normalized) return null;\n\n normalized = normalized.replace(/\\\\/g, \"/\");\n const anchored = normalized.startsWith(\"/\");\n const directoryOnly = normalized.endsWith(\"/\");\n normalized = normalized.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (!normalized) return null;\n\n if (normalized.includes(\"/\")) {\n return {\n pattern: normalized,\n mode: directoryOnly ? \"root-prefix\" : \"root-path\",\n };\n }\n\n if (anchored) {\n return {\n pattern: normalized,\n mode: \"root-segment\",\n };\n }\n\n return {\n pattern: normalized,\n mode: \"segment\",\n };\n}\n\nfunction hasTrackedPathAtOrBelow(relPath: string, trackedPaths: Iterable<string>): boolean {\n const normalizedPath = normalizePath(relPath);\n if (!normalizedPath) return false;\n\n const prefix = normalizedPath + \"/\";\n for (const trackedPath of trackedPaths) {\n const normalizedTrackedPath = normalizePath(trackedPath);\n if (\n normalizedTrackedPath === normalizedPath ||\n normalizedTrackedPath.startsWith(prefix)\n ) {\n return true;\n }\n }\n return false;\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "42FBA1E942F9F1FC64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "B359D27A071846E264756E2164756E21",
9
9
  "names": []
10
10
  }