shell-dsl 0.0.34 → 0.0.35

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 (70) hide show
  1. package/README.md +16 -3
  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.map +2 -2
  12. package/dist/cjs/src/types.cjs.map +2 -2
  13. package/dist/cjs/src/vcs/content.cjs +106 -0
  14. package/dist/cjs/src/vcs/content.cjs.map +10 -0
  15. package/dist/cjs/src/vcs/diff.cjs +72 -28
  16. package/dist/cjs/src/vcs/diff.cjs.map +3 -3
  17. package/dist/cjs/src/vcs/index.cjs.map +1 -1
  18. package/dist/cjs/src/vcs/objects.cjs +141 -0
  19. package/dist/cjs/src/vcs/objects.cjs.map +10 -0
  20. package/dist/cjs/src/vcs/rules.cjs +6 -3
  21. package/dist/cjs/src/vcs/rules.cjs.map +3 -3
  22. package/dist/cjs/src/vcs/snapshot.cjs +89 -39
  23. package/dist/cjs/src/vcs/snapshot.cjs.map +3 -3
  24. package/dist/cjs/src/vcs/storage.cjs +44 -3
  25. package/dist/cjs/src/vcs/storage.cjs.map +3 -3
  26. package/dist/cjs/src/vcs/text-diff.cjs +219 -0
  27. package/dist/cjs/src/vcs/text-diff.cjs.map +10 -0
  28. package/dist/cjs/src/vcs/vcs.cjs +108 -61
  29. package/dist/cjs/src/vcs/vcs.cjs.map +3 -3
  30. package/dist/mjs/package.json +1 -1
  31. package/dist/mjs/src/fs/memfs-adapter.mjs +57 -2
  32. package/dist/mjs/src/fs/memfs-adapter.mjs.map +3 -3
  33. package/dist/mjs/src/fs/real-fs.mjs +135 -3
  34. package/dist/mjs/src/fs/real-fs.mjs.map +3 -3
  35. package/dist/mjs/src/fs/special-files.mjs +3 -2
  36. package/dist/mjs/src/fs/special-files.mjs.map +3 -3
  37. package/dist/mjs/src/fs/web-fs.mjs +72 -3
  38. package/dist/mjs/src/fs/web-fs.mjs.map +3 -3
  39. package/dist/mjs/src/index.mjs.map +2 -2
  40. package/dist/mjs/src/types.mjs.map +2 -2
  41. package/dist/mjs/src/vcs/content.mjs +66 -0
  42. package/dist/mjs/src/vcs/content.mjs.map +10 -0
  43. package/dist/mjs/src/vcs/diff.mjs +72 -28
  44. package/dist/mjs/src/vcs/diff.mjs.map +3 -3
  45. package/dist/mjs/src/vcs/index.mjs.map +1 -1
  46. package/dist/mjs/src/vcs/objects.mjs +106 -0
  47. package/dist/mjs/src/vcs/objects.mjs.map +10 -0
  48. package/dist/mjs/src/vcs/rules.mjs +6 -3
  49. package/dist/mjs/src/vcs/rules.mjs.map +3 -3
  50. package/dist/mjs/src/vcs/snapshot.mjs +89 -39
  51. package/dist/mjs/src/vcs/snapshot.mjs.map +3 -3
  52. package/dist/mjs/src/vcs/storage.mjs +45 -3
  53. package/dist/mjs/src/vcs/storage.mjs.map +3 -3
  54. package/dist/mjs/src/vcs/text-diff.mjs +179 -0
  55. package/dist/mjs/src/vcs/text-diff.mjs.map +10 -0
  56. package/dist/mjs/src/vcs/vcs.mjs +115 -63
  57. package/dist/mjs/src/vcs/vcs.mjs.map +3 -3
  58. package/dist/types/src/fs/real-fs.d.ts +12 -1
  59. package/dist/types/src/index.d.ts +2 -2
  60. package/dist/types/src/types.d.ts +10 -0
  61. package/dist/types/src/vcs/content.d.ts +6 -0
  62. package/dist/types/src/vcs/diff.d.ts +10 -7
  63. package/dist/types/src/vcs/index.d.ts +1 -1
  64. package/dist/types/src/vcs/objects.d.ts +22 -0
  65. package/dist/types/src/vcs/snapshot.d.ts +13 -8
  66. package/dist/types/src/vcs/storage.d.ts +7 -1
  67. package/dist/types/src/vcs/text-diff.d.ts +1 -0
  68. package/dist/types/src/vcs/types.d.ts +20 -5
  69. package/dist/types/src/vcs/vcs.d.ts +6 -0
  70. package/package.json +7 -2
package/README.md CHANGED
@@ -748,7 +748,7 @@ const fs = new FileSystem("/", {}, createWebUnderlyingFS(root));
748
748
 
749
749
  ## Version Control
750
750
 
751
- `VersionControlSystem` adds git-like version control to any `VirtualFS`. It tracks changes as diffs, supports branching, and stores metadata in a `.vcs` directory.
751
+ `VersionControlSystem` adds git-like version control to any `VirtualFS`. It tracks revisions as lightweight tree manifests, stores file bytes in a content-addressed blob store under `.vcs`, and supports branching, checkout, and metadata-plus-patch diffs.
752
752
 
753
753
  Ignore and attribute rules are configured directly on the constructor:
754
754
 
@@ -780,7 +780,7 @@ Ignore patterns apply only to untracked paths:
780
780
  - Files already tracked by VCS remain tracked even if they later match an ignore rule
781
781
  - Full `checkout()` preserves ignored untracked files
782
782
 
783
- Attribute rules are applied in declaration order, with later matches winning. Supported properties:
783
+ Attribute rules are applied in declaration order, with later matches winning. By default, VCS auto-detects text vs binary content from the file bytes and only generates unified text patches for text files up to 1 MiB. Supported properties:
784
784
 
785
785
  - `binary?: boolean`
786
786
  - `diff?: "text" | "binary" | "none"`
@@ -804,10 +804,17 @@ const changes = await vcs.status();
804
804
  for (const entry of changes) {
805
805
  console.log(entry.type, entry.path, entry.diff, entry.binary);
806
806
  // "add" | "modify" | "delete", "text" | "binary" | "none", boolean
807
+
808
+ if (entry.patch) {
809
+ console.log(entry.patch);
810
+ } else {
811
+ console.log(entry.patchSuppressedReason);
812
+ // "binary" | "none" | "too-large"
813
+ }
807
814
  }
808
815
  ```
809
816
 
810
- When `diff` is `"none"`, the entry still reports the path and change type, but omits `content` and `previousContent`.
817
+ Each file entry includes `blobId` and `previousBlobId` when applicable. Text diffs are returned as unified patches in `patch`; binary files, `diff: "none"` paths, and oversized text files return metadata only with `patchSuppressedReason`.
811
818
 
812
819
  ### Checkout
813
820
 
@@ -850,11 +857,17 @@ for (const entry of diff) {
850
857
  console.log(entry.type, entry.path, entry.diff);
851
858
  }
852
859
 
860
+ // Read stored file bytes lazily
861
+ const readme = await vcs.readRevisionFile(2, "README.md", "utf8");
862
+ const blob = await vcs.readBlob(diff[0]!.blobId!);
863
+
853
864
  // Current HEAD info
854
865
  const head = await vcs.head();
855
866
  // { branch: "main", revision: 2 }
856
867
  ```
857
868
 
869
+ Blob objects are deduplicated by SHA-256, so replacing a large file stores a new blob only when the content changes instead of embedding the full file in every revision record.
870
+
858
871
  ### Separate VCS Storage
859
872
 
860
873
  By default, metadata lives in `{path}/.vcs`. You can store it on a different filesystem:
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "shell-dsl",
3
- "version": "0.0.34",
3
+ "version": "0.0.35",
4
4
  "type": "commonjs"
5
5
  }
@@ -80,6 +80,17 @@ function createVirtualFS(memfs) {
80
80
  const buf = Buffer.from(content);
81
81
  return encoding ? buf.toString(encoding) : buf;
82
82
  },
83
+ readStream(path) {
84
+ if (import_special_files.isDevNullPath(path)) {
85
+ return emptyIterable();
86
+ }
87
+ return {
88
+ async* [Symbol.asyncIterator]() {
89
+ const content = await fs.readFile(path);
90
+ yield Buffer.from(content);
91
+ }
92
+ };
93
+ },
83
94
  async readdir(path) {
84
95
  const specialError = import_special_files.getSpecialPathError(path, "readdir");
85
96
  if (specialError)
@@ -96,7 +107,8 @@ function createVirtualFS(memfs) {
96
107
  isFile: () => stats.isFile(),
97
108
  isDirectory: () => stats.isDirectory(),
98
109
  size: Number(stats.size),
99
- mtime: new Date(stats.mtime)
110
+ mtime: new Date(stats.mtime),
111
+ mtimeMs: Number(stats.mtimeMs ?? stats.mtime)
100
112
  };
101
113
  },
102
114
  async exists(path) {
@@ -122,6 +134,36 @@ function createVirtualFS(memfs) {
122
134
  }
123
135
  await fs.appendFile(path, data);
124
136
  },
137
+ async writeStream(path, opts) {
138
+ if (import_special_files.discardsSpecialFileWrites(path)) {
139
+ return createDiscardingWritable();
140
+ }
141
+ const chunks = [];
142
+ let closed = false;
143
+ return {
144
+ async write(chunk) {
145
+ if (closed) {
146
+ throw new Error("stream is closed");
147
+ }
148
+ chunks.push(Buffer.from(chunk));
149
+ },
150
+ async close() {
151
+ if (closed)
152
+ return;
153
+ closed = true;
154
+ const data = Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));
155
+ if (opts?.append) {
156
+ await fs.appendFile(path, data);
157
+ } else {
158
+ await fs.writeFile(path, data);
159
+ }
160
+ },
161
+ async abort(_reason) {
162
+ closed = true;
163
+ chunks.length = 0;
164
+ }
165
+ };
166
+ },
125
167
  async mkdir(path, opts) {
126
168
  const specialError = import_special_files.getSpecialPathError(path, "mkdir");
127
169
  if (specialError)
@@ -163,5 +205,17 @@ function createVirtualFS(memfs) {
163
205
  }
164
206
  };
165
207
  }
208
+ function emptyIterable() {
209
+ return {
210
+ async* [Symbol.asyncIterator]() {}
211
+ };
212
+ }
213
+ function createDiscardingWritable() {
214
+ return {
215
+ async write(_chunk) {},
216
+ async close() {},
217
+ async abort(_reason) {}
218
+ };
219
+ }
166
220
 
167
- //# debugId=F25975C12E3475B564756E2164756E21
221
+ //# debugId=238F7B043514ECF264756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/fs/memfs-adapter.ts"],
4
4
  "sourcesContent": [
5
- "import type { IFs } from \"memfs\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\nimport * as pathModule from \"path\";\nimport { globVirtualFS } from \"../utils/glob.cjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n readSpecialFile,\n statSpecialFile,\n} from \"./special-files.cjs\";\n\nexport function createVirtualFS(memfs: IFs): VirtualFS {\n const { promises: fs } = memfs;\n\n return {\n readFile: (async (path: string, encoding?: BufferEncoding): Promise<Buffer | string> => {\n const specialContent = readSpecialFile(path, encoding);\n if (specialContent !== undefined) return specialContent;\n const content = await fs.readFile(path);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }) as VirtualFS[\"readFile\"],\n\n async readdir(path: string): Promise<string[]> {\n const specialError = getSpecialPathError(path, \"readdir\");\n if (specialError) throw specialError;\n const entries = await fs.readdir(path);\n return entries.map(String);\n },\n\n async stat(path: string): Promise<FileStat> {\n const specialStat = statSpecialFile(path);\n if (specialStat) return specialStat;\n const stats = await fs.stat(path);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: Number(stats.size),\n mtime: new Date(stats.mtime),\n };\n },\n\n async exists(path: string): Promise<boolean> {\n const specialExists = existsSpecialFile(path);\n if (specialExists !== undefined) return specialExists;\n try {\n await fs.stat(path);\n return true;\n } catch {\n return false;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.writeFile(path, data);\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.appendFile(path, data);\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"mkdir\");\n if (specialError) throw specialError;\n await fs.mkdir(path, opts);\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"rm\");\n if (specialError) throw specialError;\n try {\n const stats = await fs.stat(path);\n if (stats.isDirectory()) {\n await fs.rmdir(path, { recursive: opts?.recursive });\n } else {\n await fs.unlink(path);\n }\n } catch (err) {\n if (!opts?.force) throw err;\n }\n },\n\n resolve(...paths: string[]): string {\n return pathModule.resolve(...paths);\n },\n\n dirname(path: string): string {\n return pathModule.dirname(path);\n },\n\n basename(path: string): string {\n return pathModule.basename(path);\n },\n\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n return globVirtualFS(\n {\n readdir: (filePath: string) => this.readdir(filePath),\n stat: (filePath: string) => this.stat(filePath),\n resolve: (...paths: string[]) => this.resolve(...paths),\n },\n pattern,\n { cwd }\n );\n },\n };\n}\n"
5
+ "import type { IFs } from \"memfs\";\nimport type { VirtualFS, VirtualFSWritable, FileStat } from \"../types.cjs\";\nimport * as pathModule from \"path\";\nimport { globVirtualFS } from \"../utils/glob.cjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n isDevNullPath,\n readSpecialFile,\n statSpecialFile,\n} from \"./special-files.cjs\";\n\nexport function createVirtualFS(memfs: IFs): VirtualFS {\n const { promises: fs } = memfs;\n\n return {\n readFile: (async (path: string, encoding?: BufferEncoding): Promise<Buffer | string> => {\n const specialContent = readSpecialFile(path, encoding);\n if (specialContent !== undefined) return specialContent;\n const content = await fs.readFile(path);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }) as VirtualFS[\"readFile\"],\n\n readStream(path: string): AsyncIterable<Uint8Array> {\n if (isDevNullPath(path)) {\n return emptyIterable();\n }\n return {\n async *[Symbol.asyncIterator]() {\n const content = await fs.readFile(path);\n yield Buffer.from(content);\n },\n };\n },\n\n async readdir(path: string): Promise<string[]> {\n const specialError = getSpecialPathError(path, \"readdir\");\n if (specialError) throw specialError;\n const entries = await fs.readdir(path);\n return entries.map(String);\n },\n\n async stat(path: string): Promise<FileStat> {\n const specialStat = statSpecialFile(path);\n if (specialStat) return specialStat;\n const stats = await fs.stat(path);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: Number(stats.size),\n mtime: new Date(stats.mtime),\n mtimeMs: Number((stats as { mtimeMs?: number }).mtimeMs ?? stats.mtime),\n };\n },\n\n async exists(path: string): Promise<boolean> {\n const specialExists = existsSpecialFile(path);\n if (specialExists !== undefined) return specialExists;\n try {\n await fs.stat(path);\n return true;\n } catch {\n return false;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.writeFile(path, data);\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.appendFile(path, data);\n },\n\n async writeStream(path: string, opts?: { append?: boolean }): Promise<VirtualFSWritable> {\n if (discardsSpecialFileWrites(path)) {\n return createDiscardingWritable();\n }\n\n const chunks: Uint8Array[] = [];\n let closed = false;\n\n return {\n async write(chunk: Uint8Array): Promise<void> {\n if (closed) {\n throw new Error(\"stream is closed\");\n }\n chunks.push(Buffer.from(chunk));\n },\n async close(): Promise<void> {\n if (closed) return;\n closed = true;\n const data = Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));\n if (opts?.append) {\n await fs.appendFile(path, data);\n } else {\n await fs.writeFile(path, data);\n }\n },\n async abort(_reason?: unknown): Promise<void> {\n closed = true;\n chunks.length = 0;\n },\n };\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"mkdir\");\n if (specialError) throw specialError;\n await fs.mkdir(path, opts);\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"rm\");\n if (specialError) throw specialError;\n try {\n const stats = await fs.stat(path);\n if (stats.isDirectory()) {\n await fs.rmdir(path, { recursive: opts?.recursive });\n } else {\n await fs.unlink(path);\n }\n } catch (err) {\n if (!opts?.force) throw err;\n }\n },\n\n resolve(...paths: string[]): string {\n return pathModule.resolve(...paths);\n },\n\n dirname(path: string): string {\n return pathModule.dirname(path);\n },\n\n basename(path: string): string {\n return pathModule.basename(path);\n },\n\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n return globVirtualFS(\n {\n readdir: (filePath: string) => this.readdir(filePath),\n stat: (filePath: string) => this.stat(filePath),\n resolve: (...paths: string[]) => this.resolve(...paths),\n },\n pattern,\n { cwd }\n );\n },\n };\n}\n\nfunction emptyIterable(): AsyncIterable<Uint8Array> {\n return {\n async *[Symbol.asyncIterator]() {},\n };\n}\n\nfunction createDiscardingWritable(): VirtualFSWritable {\n return {\n async write(_chunk: Uint8Array): Promise<void> {},\n async close(): Promise<void> {},\n async abort(_reason?: unknown): Promise<void> {},\n };\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE4B,IAA5B;AAC8B,IAA9B;AAOO,IANP;AAQO,SAAS,eAAe,CAAC,OAAuB;AAAA,EACrD,QAAQ,UAAU,OAAO;AAAA,EAEzB,OAAO;AAAA,IACL,UAAW,OAAO,MAAc,aAAwD;AAAA,MACtF,MAAM,iBAAiB,qCAAgB,MAAM,QAAQ;AAAA,MACrD,IAAI,mBAAmB;AAAA,QAAW,OAAO;AAAA,MACzC,MAAM,UAAU,MAAM,GAAG,SAAS,IAAI;AAAA,MACtC,MAAM,MAAM,OAAO,KAAK,OAAO;AAAA,MAC/B,OAAO,WAAW,IAAI,SAAS,QAAQ,IAAI;AAAA;AAAA,SAGvC,QAAO,CAAC,MAAiC;AAAA,MAC7C,MAAM,eAAe,yCAAoB,MAAM,SAAS;AAAA,MACxD,IAAI;AAAA,QAAc,MAAM;AAAA,MACxB,MAAM,UAAU,MAAM,GAAG,QAAQ,IAAI;AAAA,MACrC,OAAO,QAAQ,IAAI,MAAM;AAAA;AAAA,SAGrB,KAAI,CAAC,MAAiC;AAAA,MAC1C,MAAM,cAAc,qCAAgB,IAAI;AAAA,MACxC,IAAI;AAAA,QAAa,OAAO;AAAA,MACxB,MAAM,QAAQ,MAAM,GAAG,KAAK,IAAI;AAAA,MAChC,OAAO;AAAA,QACL,QAAQ,MAAM,MAAM,OAAO;AAAA,QAC3B,aAAa,MAAM,MAAM,YAAY;AAAA,QACrC,MAAM,OAAO,MAAM,IAAI;AAAA,QACvB,OAAO,IAAI,KAAK,MAAM,KAAK;AAAA,MAC7B;AAAA;AAAA,SAGI,OAAM,CAAC,MAAgC;AAAA,MAC3C,MAAM,gBAAgB,uCAAkB,IAAI;AAAA,MAC5C,IAAI,kBAAkB;AAAA,QAAW,OAAO;AAAA,MACxC,IAAI;AAAA,QACF,MAAM,GAAG,KAAK,IAAI;AAAA,QAClB,OAAO;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA;AAAA;AAAA,SAIL,UAAS,CAAC,MAAc,MAAsC;AAAA,MAClE,IAAI,+CAA0B,IAAI,GAAG;AAAA,QACnC;AAAA,MACF;AAAA,MACA,MAAM,GAAG,UAAU,MAAM,IAAI;AAAA;AAAA,SAGzB,WAAU,CAAC,MAAc,MAAsC;AAAA,MACnE,IAAI,+CAA0B,IAAI,GAAG;AAAA,QACnC;AAAA,MACF;AAAA,MACA,MAAM,GAAG,WAAW,MAAM,IAAI;AAAA;AAAA,SAG1B,MAAK,CAAC,MAAc,MAA+C;AAAA,MACvE,MAAM,eAAe,yCAAoB,MAAM,OAAO;AAAA,MACtD,IAAI;AAAA,QAAc,MAAM;AAAA,MACxB,MAAM,GAAG,MAAM,MAAM,IAAI;AAAA;AAAA,SAGrB,GAAE,CAAC,MAAc,MAAgE;AAAA,MACrF,MAAM,eAAe,yCAAoB,MAAM,IAAI;AAAA,MACnD,IAAI;AAAA,QAAc,MAAM;AAAA,MACxB,IAAI;AAAA,QACF,MAAM,QAAQ,MAAM,GAAG,KAAK,IAAI;AAAA,QAChC,IAAI,MAAM,YAAY,GAAG;AAAA,UACvB,MAAM,GAAG,MAAM,MAAM,EAAE,WAAW,MAAM,UAAU,CAAC;AAAA,QACrD,EAAO;AAAA,UACL,MAAM,GAAG,OAAO,IAAI;AAAA;AAAA,QAEtB,OAAO,KAAK;AAAA,QACZ,IAAI,CAAC,MAAM;AAAA,UAAO,MAAM;AAAA;AAAA;AAAA,IAI5B,OAAO,IAAI,OAAyB;AAAA,MAClC,OAAkB,mBAAQ,GAAG,KAAK;AAAA;AAAA,IAGpC,OAAO,CAAC,MAAsB;AAAA,MAC5B,OAAkB,mBAAQ,IAAI;AAAA;AAAA,IAGhC,QAAQ,CAAC,MAAsB;AAAA,MAC7B,OAAkB,oBAAS,IAAI;AAAA;AAAA,SAG3B,KAAI,CAAC,SAAiB,MAA4C;AAAA,MACtE,MAAM,MAAM,MAAM,OAAO;AAAA,MACzB,OAAO,0BACL;AAAA,QACE,SAAS,CAAC,aAAqB,KAAK,QAAQ,QAAQ;AAAA,QACpD,MAAM,CAAC,aAAqB,KAAK,KAAK,QAAQ;AAAA,QAC9C,SAAS,IAAI,UAAoB,KAAK,QAAQ,GAAG,KAAK;AAAA,MACxD,GACA,SACA,EAAE,IAAI,CACR;AAAA;AAAA,EAEJ;AAAA;",
8
- "debugId": "F25975C12E3475B564756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "238F7B043514ECF264756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -67,10 +67,21 @@ __export(exports_real_fs, {
67
67
  });
68
68
  module.exports = __toCommonJS(exports_real_fs);
69
69
  var path = __toESM(require("path"));
70
+ var nodeStreamFs = __toESM(require("node:fs"));
70
71
  var nodeFs = __toESM(require("node:fs/promises"));
71
72
  var import_glob = require("../utils/glob.cjs");
72
73
  var import_special_files = require("./special-files.cjs");
73
- var defaultFS = { promises: nodeFs };
74
+ var defaultFS = {
75
+ promises: nodeFs,
76
+ streams: {
77
+ createReadStream(realPath) {
78
+ return createNodeReadIterable(realPath);
79
+ },
80
+ createWriteStream(realPath, opts) {
81
+ return createNodeWritable(realPath, opts);
82
+ }
83
+ }
84
+ };
74
85
  var nodePathOps = {
75
86
  separator: path.sep,
76
87
  resolve: (...paths) => path.resolve(...paths),
@@ -178,6 +189,18 @@ class FileSystem {
178
189
  const buf = Buffer.from(content);
179
190
  return encoding ? buf.toString(encoding) : buf;
180
191
  }
192
+ readStream(filePath) {
193
+ if (import_special_files.isDevNullPath(filePath)) {
194
+ return emptyIterable();
195
+ }
196
+ this.checkPermission(filePath, "read");
197
+ const realPath = this.resolveSafePath(filePath);
198
+ const nativeStream = this.underlyingFs.streams?.createReadStream?.(realPath);
199
+ if (nativeStream) {
200
+ return normalizeChunks(nativeStream);
201
+ }
202
+ return bufferToIterable(this.readFile(filePath));
203
+ }
181
204
  async readdir(dirPath) {
182
205
  const specialError = import_special_files.getSpecialPathError(dirPath, "readdir");
183
206
  if (specialError) {
@@ -200,7 +223,8 @@ class FileSystem {
200
223
  isFile: () => stats.isFile(),
201
224
  isDirectory: () => stats.isDirectory(),
202
225
  size: stats.size,
203
- mtime: stats.mtime
226
+ mtime: stats.mtime,
227
+ mtimeMs: stats.mtimeMs ?? stats.mtime.getTime()
204
228
  };
205
229
  }
206
230
  async exists(filePath) {
@@ -233,6 +257,24 @@ class FileSystem {
233
257
  const realPath = this.resolveSafePath(filePath);
234
258
  await this.underlyingFs.promises.appendFile(realPath, data);
235
259
  }
260
+ async writeStream(filePath, opts) {
261
+ if (import_special_files.discardsSpecialFileWrites(filePath)) {
262
+ return createDiscardingWritable();
263
+ }
264
+ this.checkPermission(filePath, "write");
265
+ const realPath = this.resolveSafePath(filePath);
266
+ const nativeWriter = this.underlyingFs.streams?.createWriteStream?.(realPath, opts);
267
+ if (nativeWriter) {
268
+ return nativeWriter;
269
+ }
270
+ return createBufferedWritable(async (buffer) => {
271
+ if (opts?.append) {
272
+ await this.underlyingFs.promises.appendFile(realPath, buffer);
273
+ } else {
274
+ await this.underlyingFs.promises.writeFile(realPath, buffer);
275
+ }
276
+ });
277
+ }
236
278
  async mkdir(dirPath, opts) {
237
279
  const specialError = import_special_files.getSpecialPathError(dirPath, "mkdir");
238
280
  if (specialError) {
@@ -266,5 +308,94 @@ class FileSystem {
266
308
  return import_glob.globVirtualFS(this, pattern, { cwd });
267
309
  }
268
310
  }
311
+ function emptyIterable() {
312
+ return {
313
+ async* [Symbol.asyncIterator]() {}
314
+ };
315
+ }
316
+ function bufferToIterable(bufferPromise) {
317
+ return {
318
+ async* [Symbol.asyncIterator]() {
319
+ const buffer = await bufferPromise;
320
+ yield buffer;
321
+ }
322
+ };
323
+ }
324
+ async function* normalizeChunks(source) {
325
+ for await (const chunk of source) {
326
+ yield chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
327
+ }
328
+ }
329
+ function createDiscardingWritable() {
330
+ return {
331
+ async write(_chunk) {},
332
+ async close() {},
333
+ async abort(_reason) {}
334
+ };
335
+ }
336
+ function createBufferedWritable(onClose) {
337
+ const chunks = [];
338
+ let closed = false;
339
+ return {
340
+ async write(chunk) {
341
+ if (closed) {
342
+ throw new Error("stream is closed");
343
+ }
344
+ chunks.push(Buffer.from(chunk));
345
+ },
346
+ async close() {
347
+ if (closed)
348
+ return;
349
+ closed = true;
350
+ await onClose(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))));
351
+ },
352
+ async abort(_reason) {
353
+ closed = true;
354
+ chunks.length = 0;
355
+ }
356
+ };
357
+ }
358
+ async function* createNodeReadIterable(realPath) {
359
+ const stream = nodeStreamFs.createReadStream(realPath);
360
+ for await (const chunk of stream) {
361
+ yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
362
+ }
363
+ }
364
+ function createNodeWritable(realPath, opts) {
365
+ const stream = nodeStreamFs.createWriteStream(realPath, {
366
+ flags: opts?.append ? "a" : "w"
367
+ });
368
+ const ready = new Promise((resolve2, reject) => {
369
+ stream.once("open", () => resolve2());
370
+ stream.once("error", reject);
371
+ });
372
+ return {
373
+ async write(chunk) {
374
+ await ready;
375
+ await new Promise((resolve2, reject) => {
376
+ stream.write(chunk, (error) => {
377
+ if (error)
378
+ reject(error);
379
+ else
380
+ resolve2();
381
+ });
382
+ });
383
+ },
384
+ async close() {
385
+ await ready;
386
+ await new Promise((resolve2, reject) => {
387
+ stream.end((error) => {
388
+ if (error)
389
+ reject(error);
390
+ else
391
+ resolve2();
392
+ });
393
+ });
394
+ },
395
+ async abort(reason) {
396
+ stream.destroy(reason instanceof Error ? reason : undefined);
397
+ }
398
+ };
399
+ }
269
400
 
270
- //# debugId=58FDFA4CFF76BA5364756E2164756E21
401
+ //# debugId=39354A6D64DEDEC164756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/fs/real-fs.ts"],
4
4
  "sourcesContent": [
5
- "import * as path from \"path\";\nimport * as nodeFs from \"node:fs/promises\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\nimport { globVirtualFS } from \"../utils/glob.cjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n readSpecialFile,\n statSpecialFile,\n} from \"./special-files.cjs\";\n\nexport type Permission = \"read-write\" | \"read-only\" | \"excluded\";\nexport type PermissionRules = Record<string, Permission>;\nexport interface PathOps {\n readonly separator: string;\n resolve(...paths: string[]): string;\n normalize(path: string): string;\n join(...paths: string[]): string;\n relative(from: string, to: string): string;\n isAbsolute(path: string): boolean;\n dirname(path: string): string;\n basename(path: string): string;\n}\n\n// Minimal interface for the underlying fs (compatible with node:fs and memfs)\nexport interface UnderlyingFS {\n pathOps?: PathOps;\n promises: {\n readFile(path: string): Promise<Buffer | Uint8Array | string>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<{\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\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<string | undefined | void>;\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n };\n}\n\n// Default: use real node:fs\nconst defaultFS: UnderlyingFS = { promises: nodeFs };\nconst nodePathOps: PathOps = {\n separator: path.sep,\n resolve: (...paths) => path.resolve(...paths),\n normalize: (filePath) => path.normalize(filePath),\n join: (...paths) => path.join(...paths),\n relative: (from, to) => path.relative(from, to),\n isAbsolute: (filePath) => path.isAbsolute(filePath),\n dirname: (filePath) => path.dirname(filePath),\n basename: (filePath) => path.basename(filePath),\n};\n\ninterface CompiledRule {\n pattern: string;\n permission: Permission;\n specificity: number;\n}\n\nexport class FileSystem implements VirtualFS {\n private readonly mountBase: string | null;\n private readonly rules: CompiledRule[];\n private readonly pathOps: PathOps;\n protected readonly underlyingFs: UnderlyingFS;\n\n constructor(mountPath?: string, permissions?: PermissionRules, fs?: UnderlyingFS) {\n const underlyingFs = fs ?? defaultFS;\n this.pathOps = underlyingFs.pathOps ?? nodePathOps;\n this.mountBase = mountPath ? this.pathOps.resolve(mountPath) : null;\n this.rules = this.compileRules(permissions ?? {});\n this.underlyingFs = underlyingFs;\n }\n\n private compileRules(permissions: PermissionRules): CompiledRule[] {\n return Object.entries(permissions)\n .map(([pattern, permission]) => ({\n pattern,\n permission,\n specificity: this.calculateSpecificity(pattern),\n }))\n .sort((a, b) => b.specificity - a.specificity); // highest first\n }\n\n private calculateSpecificity(pattern: string): number {\n const segments = pattern.split(\"/\").filter(Boolean);\n let score = segments.length * 1000; // segment count is primary\n\n for (const seg of segments) {\n if (seg === \"**\") score += 0;\n else if (seg.includes(\"*\")) score += 1;\n else score += 10; // literal segment\n }\n return score;\n }\n\n public getPermission(virtualPath: string): Permission {\n const normalized = virtualPath.replace(/^\\/+/, \"\"); // strip leading slashes\n\n for (const rule of this.rules) {\n if (this.matchGlob(rule.pattern, normalized)) {\n return rule.permission;\n }\n }\n return \"read-write\"; // default\n }\n\n private matchGlob(pattern: string, filePath: string): boolean {\n // Convert glob to regex\n // ** matches any path segments, * matches within segment\n const regex = pattern\n .split(\"/\")\n .map((seg) => {\n if (seg === \"**\") return \".*\";\n return seg.replace(/\\*/g, \"[^/]*\").replace(/\\?/g, \"[^/]\");\n })\n .join(\"/\");\n return new RegExp(`^${regex}$`).test(filePath);\n }\n\n public checkPermission(virtualPath: string, operation: \"read\" | \"write\"): void {\n const perm = this.getPermission(virtualPath);\n\n if (perm === \"excluded\") {\n throw new Error(`Access denied: \"${virtualPath}\" is excluded`);\n }\n if (operation === \"write\" && perm === \"read-only\") {\n throw new Error(`Access denied: \"${virtualPath}\" is read-only`);\n }\n }\n\n private resolveSafePath(virtualPath: string): string {\n if (this.mountBase === null) {\n return this.pathOps.resolve(virtualPath);\n }\n\n // Check for path traversal by tracking depth\n const segments = virtualPath.split(\"/\").filter(Boolean);\n let depth = 0;\n for (const seg of segments) {\n if (seg === \"..\") {\n depth--;\n if (depth < 0) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n } else if (seg !== \".\") {\n depth++;\n }\n }\n\n const normalized = this.pathOps.normalize(virtualPath);\n const relativePath = normalized.startsWith(\"/\") ? normalized.slice(1) : normalized;\n const realPath = this.pathOps.join(this.mountBase, relativePath);\n const resolved = this.pathOps.resolve(realPath);\n\n // Double-check containment (defense in depth), including root mounts.\n const relativeFromMount = this.pathOps.relative(this.mountBase, resolved);\n const escapesMount =\n relativeFromMount === \"..\" ||\n relativeFromMount.startsWith(`..${this.pathOps.separator}`) ||\n this.pathOps.isAbsolute(relativeFromMount);\n if (escapesMount) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n\n return resolved;\n }\n\n // Read operations\n async readFile(filePath: string): Promise<Buffer>;\n async readFile(filePath: string, encoding: BufferEncoding): Promise<string>;\n async readFile(filePath: string, encoding?: BufferEncoding): Promise<Buffer | string> {\n const specialContent = readSpecialFile(filePath, encoding);\n if (specialContent !== undefined) {\n return specialContent;\n }\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const content = await this.underlyingFs.promises.readFile(realPath);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }\n\n async readdir(dirPath: string): Promise<string[]> {\n const specialError = getSpecialPathError(dirPath, \"readdir\");\n if (specialError) {\n throw specialError;\n }\n this.checkPermission(dirPath, \"read\");\n const realPath = this.resolveSafePath(dirPath);\n const entries = await this.underlyingFs.promises.readdir(realPath);\n return entries.map(String);\n }\n\n async stat(filePath: string): Promise<FileStat> {\n const specialStat = statSpecialFile(filePath);\n if (specialStat) {\n return specialStat;\n }\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const stats = await this.underlyingFs.promises.stat(realPath);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: stats.size,\n mtime: stats.mtime,\n };\n }\n\n async exists(filePath: string): Promise<boolean> {\n const specialExists = existsSpecialFile(filePath);\n if (specialExists !== undefined) {\n return specialExists;\n }\n try {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.stat(realPath);\n return true;\n } catch {\n return false;\n }\n }\n\n // Write operations\n async writeFile(filePath: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(filePath)) {\n return;\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.writeFile(realPath, data);\n }\n\n async appendFile(filePath: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(filePath)) {\n return;\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.appendFile(realPath, data);\n }\n\n async mkdir(dirPath: string, opts?: { recursive?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(dirPath, \"mkdir\");\n if (specialError) {\n throw specialError;\n }\n this.checkPermission(dirPath, \"write\");\n const realPath = this.resolveSafePath(dirPath);\n await this.underlyingFs.promises.mkdir(realPath, opts);\n }\n\n async rm(filePath: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(filePath, \"rm\");\n if (specialError) {\n throw specialError;\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.rm(realPath, opts);\n }\n\n // Path utilities (no permission check needed)\n resolve(...paths: string[]): string {\n return this.pathOps.resolve(\"/\", ...paths);\n }\n\n dirname(filePath: string): string {\n return this.pathOps.dirname(filePath);\n }\n\n basename(filePath: string): string {\n return this.pathOps.basename(filePath);\n }\n\n // Glob expansion\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n this.checkPermission(cwd, \"read\");\n return globVirtualFS(this, pattern, { cwd });\n }\n}\n"
5
+ "import * as path from \"path\";\nimport * as nodeStreamFs from \"node:fs\";\nimport * as nodeFs from \"node:fs/promises\";\nimport type { VirtualFS, VirtualFSWritable, FileStat } from \"../types.cjs\";\nimport { globVirtualFS } from \"../utils/glob.cjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n isDevNullPath,\n readSpecialFile,\n statSpecialFile,\n} from \"./special-files.cjs\";\n\nexport type Permission = \"read-write\" | \"read-only\" | \"excluded\";\nexport type PermissionRules = Record<string, Permission>;\nexport interface PathOps {\n readonly separator: string;\n resolve(...paths: string[]): string;\n normalize(path: string): string;\n join(...paths: string[]): string;\n relative(from: string, to: string): string;\n isAbsolute(path: string): boolean;\n dirname(path: string): string;\n basename(path: string): string;\n}\n\n// Minimal interface for the underlying fs (compatible with node:fs and memfs)\nexport interface UnderlyingFS {\n pathOps?: PathOps;\n promises: {\n readFile(path: string): Promise<Buffer | Uint8Array | string>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<{\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n mtimeMs?: number;\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<string | undefined | void>;\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n };\n streams?: {\n createReadStream?(path: string): AsyncIterable<Uint8Array>;\n createWriteStream?(\n path: string,\n opts?: { append?: boolean },\n ): Promise<VirtualFSWritable> | VirtualFSWritable;\n };\n}\n\n// Default: use real node:fs\nconst defaultFS: UnderlyingFS = {\n promises: nodeFs,\n streams: {\n createReadStream(realPath: string): AsyncIterable<Uint8Array> {\n return createNodeReadIterable(realPath);\n },\n createWriteStream(realPath: string, opts?: { append?: boolean }): VirtualFSWritable {\n return createNodeWritable(realPath, opts);\n },\n },\n};\nconst nodePathOps: PathOps = {\n separator: path.sep,\n resolve: (...paths) => path.resolve(...paths),\n normalize: (filePath) => path.normalize(filePath),\n join: (...paths) => path.join(...paths),\n relative: (from, to) => path.relative(from, to),\n isAbsolute: (filePath) => path.isAbsolute(filePath),\n dirname: (filePath) => path.dirname(filePath),\n basename: (filePath) => path.basename(filePath),\n};\n\ninterface CompiledRule {\n pattern: string;\n permission: Permission;\n specificity: number;\n}\n\nexport class FileSystem implements VirtualFS {\n private readonly mountBase: string | null;\n private readonly rules: CompiledRule[];\n private readonly pathOps: PathOps;\n protected readonly underlyingFs: UnderlyingFS;\n\n constructor(mountPath?: string, permissions?: PermissionRules, fs?: UnderlyingFS) {\n const underlyingFs = fs ?? defaultFS;\n this.pathOps = underlyingFs.pathOps ?? nodePathOps;\n this.mountBase = mountPath ? this.pathOps.resolve(mountPath) : null;\n this.rules = this.compileRules(permissions ?? {});\n this.underlyingFs = underlyingFs;\n }\n\n private compileRules(permissions: PermissionRules): CompiledRule[] {\n return Object.entries(permissions)\n .map(([pattern, permission]) => ({\n pattern,\n permission,\n specificity: this.calculateSpecificity(pattern),\n }))\n .sort((a, b) => b.specificity - a.specificity); // highest first\n }\n\n private calculateSpecificity(pattern: string): number {\n const segments = pattern.split(\"/\").filter(Boolean);\n let score = segments.length * 1000; // segment count is primary\n\n for (const seg of segments) {\n if (seg === \"**\") score += 0;\n else if (seg.includes(\"*\")) score += 1;\n else score += 10; // literal segment\n }\n return score;\n }\n\n public getPermission(virtualPath: string): Permission {\n const normalized = virtualPath.replace(/^\\/+/, \"\"); // strip leading slashes\n\n for (const rule of this.rules) {\n if (this.matchGlob(rule.pattern, normalized)) {\n return rule.permission;\n }\n }\n return \"read-write\"; // default\n }\n\n private matchGlob(pattern: string, filePath: string): boolean {\n // Convert glob to regex\n // ** matches any path segments, * matches within segment\n const regex = pattern\n .split(\"/\")\n .map((seg) => {\n if (seg === \"**\") return \".*\";\n return seg.replace(/\\*/g, \"[^/]*\").replace(/\\?/g, \"[^/]\");\n })\n .join(\"/\");\n return new RegExp(`^${regex}$`).test(filePath);\n }\n\n public checkPermission(virtualPath: string, operation: \"read\" | \"write\"): void {\n const perm = this.getPermission(virtualPath);\n\n if (perm === \"excluded\") {\n throw new Error(`Access denied: \"${virtualPath}\" is excluded`);\n }\n if (operation === \"write\" && perm === \"read-only\") {\n throw new Error(`Access denied: \"${virtualPath}\" is read-only`);\n }\n }\n\n private resolveSafePath(virtualPath: string): string {\n if (this.mountBase === null) {\n return this.pathOps.resolve(virtualPath);\n }\n\n // Check for path traversal by tracking depth\n const segments = virtualPath.split(\"/\").filter(Boolean);\n let depth = 0;\n for (const seg of segments) {\n if (seg === \"..\") {\n depth--;\n if (depth < 0) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n } else if (seg !== \".\") {\n depth++;\n }\n }\n\n const normalized = this.pathOps.normalize(virtualPath);\n const relativePath = normalized.startsWith(\"/\") ? normalized.slice(1) : normalized;\n const realPath = this.pathOps.join(this.mountBase, relativePath);\n const resolved = this.pathOps.resolve(realPath);\n\n // Double-check containment (defense in depth), including root mounts.\n const relativeFromMount = this.pathOps.relative(this.mountBase, resolved);\n const escapesMount =\n relativeFromMount === \"..\" ||\n relativeFromMount.startsWith(`..${this.pathOps.separator}`) ||\n this.pathOps.isAbsolute(relativeFromMount);\n if (escapesMount) {\n throw new Error(`Path traversal blocked: \"${virtualPath}\" escapes mount point`);\n }\n\n return resolved;\n }\n\n // Read operations\n async readFile(filePath: string): Promise<Buffer>;\n async readFile(filePath: string, encoding: BufferEncoding): Promise<string>;\n async readFile(filePath: string, encoding?: BufferEncoding): Promise<Buffer | string> {\n const specialContent = readSpecialFile(filePath, encoding);\n if (specialContent !== undefined) {\n return specialContent;\n }\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const content = await this.underlyingFs.promises.readFile(realPath);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }\n\n readStream(filePath: string): AsyncIterable<Uint8Array> {\n if (isDevNullPath(filePath)) {\n return emptyIterable();\n }\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const nativeStream = this.underlyingFs.streams?.createReadStream?.(realPath);\n if (nativeStream) {\n return normalizeChunks(nativeStream);\n }\n return bufferToIterable(this.readFile(filePath) as Promise<Buffer>);\n }\n\n async readdir(dirPath: string): Promise<string[]> {\n const specialError = getSpecialPathError(dirPath, \"readdir\");\n if (specialError) {\n throw specialError;\n }\n this.checkPermission(dirPath, \"read\");\n const realPath = this.resolveSafePath(dirPath);\n const entries = await this.underlyingFs.promises.readdir(realPath);\n return entries.map(String);\n }\n\n async stat(filePath: string): Promise<FileStat> {\n const specialStat = statSpecialFile(filePath);\n if (specialStat) {\n return specialStat;\n }\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n const stats = await this.underlyingFs.promises.stat(realPath);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: stats.size,\n mtime: stats.mtime,\n mtimeMs: stats.mtimeMs ?? stats.mtime.getTime(),\n };\n }\n\n async exists(filePath: string): Promise<boolean> {\n const specialExists = existsSpecialFile(filePath);\n if (specialExists !== undefined) {\n return specialExists;\n }\n try {\n this.checkPermission(filePath, \"read\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.stat(realPath);\n return true;\n } catch {\n return false;\n }\n }\n\n // Write operations\n async writeFile(filePath: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(filePath)) {\n return;\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.writeFile(realPath, data);\n }\n\n async appendFile(filePath: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(filePath)) {\n return;\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.appendFile(realPath, data);\n }\n\n async writeStream(filePath: string, opts?: { append?: boolean }): Promise<VirtualFSWritable> {\n if (discardsSpecialFileWrites(filePath)) {\n return createDiscardingWritable();\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n const nativeWriter = this.underlyingFs.streams?.createWriteStream?.(realPath, opts);\n if (nativeWriter) {\n return nativeWriter;\n }\n return createBufferedWritable(async (buffer) => {\n if (opts?.append) {\n await this.underlyingFs.promises.appendFile(realPath, buffer);\n } else {\n await this.underlyingFs.promises.writeFile(realPath, buffer);\n }\n });\n }\n\n async mkdir(dirPath: string, opts?: { recursive?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(dirPath, \"mkdir\");\n if (specialError) {\n throw specialError;\n }\n this.checkPermission(dirPath, \"write\");\n const realPath = this.resolveSafePath(dirPath);\n await this.underlyingFs.promises.mkdir(realPath, opts);\n }\n\n async rm(filePath: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(filePath, \"rm\");\n if (specialError) {\n throw specialError;\n }\n this.checkPermission(filePath, \"write\");\n const realPath = this.resolveSafePath(filePath);\n await this.underlyingFs.promises.rm(realPath, opts);\n }\n\n // Path utilities (no permission check needed)\n resolve(...paths: string[]): string {\n return this.pathOps.resolve(\"/\", ...paths);\n }\n\n dirname(filePath: string): string {\n return this.pathOps.dirname(filePath);\n }\n\n basename(filePath: string): string {\n return this.pathOps.basename(filePath);\n }\n\n // Glob expansion\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n this.checkPermission(cwd, \"read\");\n return globVirtualFS(this, pattern, { cwd });\n }\n}\n\nfunction emptyIterable(): AsyncIterable<Uint8Array> {\n return {\n async *[Symbol.asyncIterator]() {},\n };\n}\n\nfunction bufferToIterable(bufferPromise: Promise<Buffer>): AsyncIterable<Uint8Array> {\n return {\n async *[Symbol.asyncIterator]() {\n const buffer = await bufferPromise;\n yield buffer;\n },\n };\n}\n\nasync function* normalizeChunks(source: AsyncIterable<Uint8Array>): AsyncIterable<Uint8Array> {\n for await (const chunk of source) {\n yield chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n }\n}\n\nfunction createDiscardingWritable(): VirtualFSWritable {\n return {\n async write(_chunk: Uint8Array): Promise<void> {},\n async close(): Promise<void> {},\n async abort(_reason?: unknown): Promise<void> {},\n };\n}\n\nfunction createBufferedWritable(\n onClose: (buffer: Buffer) => Promise<void>,\n): VirtualFSWritable {\n const chunks: Uint8Array[] = [];\n let closed = false;\n\n return {\n async write(chunk: Uint8Array): Promise<void> {\n if (closed) {\n throw new Error(\"stream is closed\");\n }\n chunks.push(Buffer.from(chunk));\n },\n async close(): Promise<void> {\n if (closed) return;\n closed = true;\n await onClose(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))));\n },\n async abort(_reason?: unknown): Promise<void> {\n closed = true;\n chunks.length = 0;\n },\n };\n}\n\nasync function* createNodeReadIterable(realPath: string): AsyncIterable<Uint8Array> {\n const stream = nodeStreamFs.createReadStream(realPath);\n for await (const chunk of stream) {\n yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n }\n}\n\nfunction createNodeWritable(\n realPath: string,\n opts?: { append?: boolean },\n): VirtualFSWritable {\n const stream = nodeStreamFs.createWriteStream(realPath, {\n flags: opts?.append ? \"a\" : \"w\",\n });\n const ready = new Promise<void>((resolve, reject) => {\n stream.once(\"open\", () => resolve());\n stream.once(\"error\", reject);\n });\n\n return {\n async write(chunk: Uint8Array): Promise<void> {\n await ready;\n await new Promise<void>((resolve, reject) => {\n stream.write(chunk, (error) => {\n if (error) reject(error);\n else resolve();\n });\n });\n },\n async close(): Promise<void> {\n await ready;\n await new Promise<void>((resolve, reject) => {\n stream.end((error?: Error | null) => {\n if (error) reject(error);\n else resolve();\n });\n });\n },\n async abort(reason?: unknown): Promise<void> {\n stream.destroy(reason instanceof Error ? reason : undefined);\n },\n };\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "58FDFA4CFF76BA5364756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "39354A6D64DEDEC164756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -69,7 +69,8 @@ function statSpecialFile(path) {
69
69
  isFile: () => true,
70
70
  isDirectory: () => false,
71
71
  size: 0,
72
- mtime: new Date(0)
72
+ mtime: new Date(0),
73
+ mtimeMs: 0
73
74
  };
74
75
  }
75
76
  function existsSpecialFile(path) {
@@ -95,4 +96,4 @@ function getSpecialPathError(path, operation) {
95
96
  }
96
97
  }
97
98
 
98
- //# debugId=674232080F989C1164756E2164756E21
99
+ //# debugId=C4DAB79D54BC633764756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/fs/special-files.ts"],
4
4
  "sourcesContent": [
5
- "import type { FileStat } from \"../types.cjs\";\n\nexport const DEV_NULL_PATH = \"/dev/null\";\n\nfunction normalizeSpecialPath(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n}\n\nexport function isDevNullPath(path: string): boolean {\n return normalizeSpecialPath(path) === DEV_NULL_PATH;\n}\n\nexport function readSpecialFile(\n path: string,\n encoding?: BufferEncoding\n): Buffer | string | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return encoding ? \"\" : Buffer.alloc(0);\n}\n\nexport function statSpecialFile(path: string): FileStat | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return {\n isFile: () => true,\n isDirectory: () => false,\n size: 0,\n mtime: new Date(0),\n };\n}\n\nexport function existsSpecialFile(path: string): boolean | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return true;\n}\n\nexport function discardsSpecialFileWrites(path: string): boolean {\n return isDevNullPath(path);\n}\n\nexport function getSpecialPathError(\n path: string,\n operation: \"mkdir\" | \"readdir\" | \"rm\"\n): Error | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n switch (operation) {\n case \"mkdir\":\n return new Error(`EEXIST: file already exists, mkdir '${DEV_NULL_PATH}'`);\n case \"readdir\":\n return new Error(`ENOTDIR: not a directory, scandir '${DEV_NULL_PATH}'`);\n case \"rm\":\n return new Error(`EPERM: operation not permitted, rm '${DEV_NULL_PATH}'`);\n }\n}\n"
5
+ "import type { FileStat } from \"../types.cjs\";\n\nexport const DEV_NULL_PATH = \"/dev/null\";\n\nfunction normalizeSpecialPath(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n}\n\nexport function isDevNullPath(path: string): boolean {\n return normalizeSpecialPath(path) === DEV_NULL_PATH;\n}\n\nexport function readSpecialFile(\n path: string,\n encoding?: BufferEncoding\n): Buffer | string | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return encoding ? \"\" : Buffer.alloc(0);\n}\n\nexport function statSpecialFile(path: string): FileStat | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return {\n isFile: () => true,\n isDirectory: () => false,\n size: 0,\n mtime: new Date(0),\n mtimeMs: 0,\n };\n}\n\nexport function existsSpecialFile(path: string): boolean | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n return true;\n}\n\nexport function discardsSpecialFileWrites(path: string): boolean {\n return isDevNullPath(path);\n}\n\nexport function getSpecialPathError(\n path: string,\n operation: \"mkdir\" | \"readdir\" | \"rm\"\n): Error | undefined {\n if (!isDevNullPath(path)) {\n return undefined;\n }\n\n switch (operation) {\n case \"mkdir\":\n return new Error(`EEXIST: file already exists, mkdir '${DEV_NULL_PATH}'`);\n case \"readdir\":\n return new Error(`ENOTDIR: not a directory, scandir '${DEV_NULL_PATH}'`);\n case \"rm\":\n return new Error(`EPERM: operation not permitted, rm '${DEV_NULL_PATH}'`);\n }\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,IAAM,gBAAgB;AAE7B,SAAS,oBAAoB,CAAC,MAAsB;AAAA,EAClD,OAAO,KAAK,QAAQ,OAAO,GAAG;AAAA;AAGzB,SAAS,aAAa,CAAC,MAAuB;AAAA,EACnD,OAAO,qBAAqB,IAAI,MAAM;AAAA;AAGjC,SAAS,eAAe,CAC7B,MACA,UAC6B;AAAA,EAC7B,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC;AAAA;AAGhC,SAAS,eAAe,CAAC,MAAoC;AAAA,EAClE,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,MAAM;AAAA,IACN,OAAO,IAAI,KAAK,CAAC;AAAA,EACnB;AAAA;AAGK,SAAS,iBAAiB,CAAC,MAAmC;AAAA,EACnE,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA;AAGF,SAAS,yBAAyB,CAAC,MAAuB;AAAA,EAC/D,OAAO,cAAc,IAAI;AAAA;AAGpB,SAAS,mBAAmB,CACjC,MACA,WACmB;AAAA,EACnB,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,QAAQ;AAAA,SACD;AAAA,MACH,OAAO,IAAI,MAAM,uCAAuC,gBAAgB;AAAA,SACrE;AAAA,MACH,OAAO,IAAI,MAAM,sCAAsC,gBAAgB;AAAA,SACpE;AAAA,MACH,OAAO,IAAI,MAAM,uCAAuC,gBAAgB;AAAA;AAAA;",
8
- "debugId": "674232080F989C1164756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,IAAM,gBAAgB;AAE7B,SAAS,oBAAoB,CAAC,MAAsB;AAAA,EAClD,OAAO,KAAK,QAAQ,OAAO,GAAG;AAAA;AAGzB,SAAS,aAAa,CAAC,MAAuB;AAAA,EACnD,OAAO,qBAAqB,IAAI,MAAM;AAAA;AAGjC,SAAS,eAAe,CAC7B,MACA,UAC6B;AAAA,EAC7B,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC;AAAA;AAGhC,SAAS,eAAe,CAAC,MAAoC;AAAA,EAClE,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,MAAM;AAAA,IACN,OAAO,IAAI,KAAK,CAAC;AAAA,IACjB,SAAS;AAAA,EACX;AAAA;AAGK,SAAS,iBAAiB,CAAC,MAAmC;AAAA,EACnE,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO;AAAA;AAGF,SAAS,yBAAyB,CAAC,MAAuB;AAAA,EAC/D,OAAO,cAAc,IAAI;AAAA;AAGpB,SAAS,mBAAmB,CACjC,MACA,WACmB;AAAA,EACnB,IAAI,CAAC,cAAc,IAAI,GAAG;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,QAAQ;AAAA,SACD;AAAA,MACH,OAAO,IAAI,MAAM,uCAAuC,gBAAgB;AAAA,SACrE;AAAA,MACH,OAAO,IAAI,MAAM,sCAAsC,gBAAgB;AAAA,SACpE;AAAA,MACH,OAAO,IAAI,MAAM,uCAAuC,gBAAgB;AAAA;AAAA;",
8
+ "debugId": "C4DAB79D54BC633764756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -125,7 +125,8 @@ function createWebUnderlyingFS(root) {
125
125
  isFile: () => true,
126
126
  isDirectory: () => false,
127
127
  size: file.size,
128
- mtime: new Date(file.lastModified ?? 0)
128
+ mtime: new Date(file.lastModified ?? 0),
129
+ mtimeMs: file.lastModified ?? 0
129
130
  };
130
131
  } catch (error) {
131
132
  if (!isNotFoundOrTypeMismatch(error))
@@ -194,6 +195,57 @@ function createWebUnderlyingFS(root) {
194
195
  throw error;
195
196
  }
196
197
  }
198
+ },
199
+ streams: {
200
+ createReadStream(path) {
201
+ return {
202
+ async* [Symbol.asyncIterator]() {
203
+ const { parentSegments, name } = splitParent(path);
204
+ const parent = await walkDirectory(root, parentSegments, false);
205
+ const fileHandle = await parent.getFileHandle(name, { create: false });
206
+ const file = await fileHandle.getFile();
207
+ for await (const chunk of streamToAsyncIterable(file.stream())) {
208
+ yield chunk;
209
+ }
210
+ }
211
+ };
212
+ },
213
+ async createWriteStream(path, opts) {
214
+ const { parentSegments, name } = splitParent(path);
215
+ const parent = await walkDirectory(root, parentSegments, false);
216
+ const fileHandle = await parent.getFileHandle(name, { create: true });
217
+ const existingFile = opts?.append ? await fileHandle.getFile() : null;
218
+ const writable = await fileHandle.createWritable({ keepExistingData: !!opts?.append });
219
+ let position = opts?.append ? existingFile?.size ?? 0 : 0;
220
+ let closed = false;
221
+ return {
222
+ async write(chunk) {
223
+ if (closed) {
224
+ throw new Error("stream is closed");
225
+ }
226
+ const bytes = new Uint8Array(chunk.byteLength);
227
+ bytes.set(chunk, 0);
228
+ await writable.write({
229
+ type: "write",
230
+ position,
231
+ data: bytes
232
+ });
233
+ position += bytes.byteLength;
234
+ },
235
+ async close() {
236
+ if (closed)
237
+ return;
238
+ closed = true;
239
+ await writable.close();
240
+ },
241
+ async abort(_reason) {
242
+ if (closed)
243
+ return;
244
+ closed = true;
245
+ await writable.abort?.();
246
+ }
247
+ };
248
+ }
197
249
  }
198
250
  };
199
251
  }
@@ -208,7 +260,8 @@ function createDirectoryStat() {
208
260
  isFile: () => false,
209
261
  isDirectory: () => true,
210
262
  size: 0,
211
- mtime: DIRECTORY_MTIME
263
+ mtime: DIRECTORY_MTIME,
264
+ mtimeMs: DIRECTORY_MTIME.getTime()
212
265
  };
213
266
  }
214
267
  function normalizeWebPath(path) {
@@ -268,6 +321,22 @@ async function entryExists(dir, name) {
268
321
  return false;
269
322
  }
270
323
  }
324
+ async function* streamToAsyncIterable(stream) {
325
+ const reader = stream.getReader();
326
+ try {
327
+ while (true) {
328
+ const { done, value } = await reader.read();
329
+ if (done) {
330
+ return;
331
+ }
332
+ if (value) {
333
+ yield value;
334
+ }
335
+ }
336
+ } finally {
337
+ reader.releaseLock();
338
+ }
339
+ }
271
340
  function isNotFoundOrTypeMismatch(error) {
272
341
  return isNotFoundError(error) || isTypeMismatchError(error);
273
342
  }
@@ -292,4 +361,4 @@ function toWritableData(data) {
292
361
  return out.buffer;
293
362
  }
294
363
 
295
- //# debugId=63C0A71C805BC57D64756E2164756E21
364
+ //# debugId=87CE1021E696ED8364756E2164756E21