@lova/mem-vfs 0.1.0

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 (58) hide show
  1. package/README.md +222 -0
  2. package/dist/core/vfs-directory.d.ts +35 -0
  3. package/dist/core/vfs-directory.d.ts.map +1 -0
  4. package/dist/core/vfs-directory.js +78 -0
  5. package/dist/core/vfs-directory.js.map +1 -0
  6. package/dist/core/vfs-file.d.ts +25 -0
  7. package/dist/core/vfs-file.d.ts.map +1 -0
  8. package/dist/core/vfs-file.js +60 -0
  9. package/dist/core/vfs-file.js.map +1 -0
  10. package/dist/core/vfs-node.d.ts +42 -0
  11. package/dist/core/vfs-node.d.ts.map +1 -0
  12. package/dist/core/vfs-node.js +69 -0
  13. package/dist/core/vfs-node.js.map +1 -0
  14. package/dist/core/vfs-symlink.d.ts +21 -0
  15. package/dist/core/vfs-symlink.d.ts.map +1 -0
  16. package/dist/core/vfs-symlink.js +41 -0
  17. package/dist/core/vfs-symlink.js.map +1 -0
  18. package/dist/core/vfs.d.ts +107 -0
  19. package/dist/core/vfs.d.ts.map +1 -0
  20. package/dist/core/vfs.js +775 -0
  21. package/dist/core/vfs.js.map +1 -0
  22. package/dist/errors/file-system-errors.d.ts +79 -0
  23. package/dist/errors/file-system-errors.d.ts.map +1 -0
  24. package/dist/errors/file-system-errors.js +127 -0
  25. package/dist/errors/file-system-errors.js.map +1 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +23 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/path/path-normalizer.d.ts +23 -0
  31. package/dist/path/path-normalizer.d.ts.map +1 -0
  32. package/dist/path/path-normalizer.js +159 -0
  33. package/dist/path/path-normalizer.js.map +1 -0
  34. package/dist/path/path-resolver.d.ts +31 -0
  35. package/dist/path/path-resolver.d.ts.map +1 -0
  36. package/dist/path/path-resolver.js +68 -0
  37. package/dist/path/path-resolver.js.map +1 -0
  38. package/dist/path/path-validator.d.ts +12 -0
  39. package/dist/path/path-validator.d.ts.map +1 -0
  40. package/dist/path/path-validator.js +87 -0
  41. package/dist/path/path-validator.js.map +1 -0
  42. package/dist/types/index.d.ts +171 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/types/index.js +29 -0
  45. package/dist/types/index.js.map +1 -0
  46. package/dist/watcher/debouncer.d.ts +31 -0
  47. package/dist/watcher/debouncer.d.ts.map +1 -0
  48. package/dist/watcher/debouncer.js +66 -0
  49. package/dist/watcher/debouncer.js.map +1 -0
  50. package/dist/watcher/watcher-events.d.ts +30 -0
  51. package/dist/watcher/watcher-events.d.ts.map +1 -0
  52. package/dist/watcher/watcher-events.js +52 -0
  53. package/dist/watcher/watcher-events.js.map +1 -0
  54. package/dist/watcher/watcher.d.ts +57 -0
  55. package/dist/watcher/watcher.d.ts.map +1 -0
  56. package/dist/watcher/watcher.js +194 -0
  57. package/dist/watcher/watcher.js.map +1 -0
  58. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # mem-vfs
2
+
3
+ High-performance in-memory virtual file system for Node.js with symlink support, file watching, and snapshot/rollback capabilities.
4
+
5
+ ## Features
6
+
7
+ - **Complete File System API**: readFile, writeFile, appendFile, deleteFile, copyFile, moveFile
8
+ - **Directory Operations**: createDirectory, readDirectory, deleteDirectory (with recursive option)
9
+ - **Symbolic Links**: createSymlink, readSymlink, isSymlink with loop detection
10
+ - **Glob Pattern Matching**: Full glob support with maxDepth, followSymlinks, ignore patterns
11
+ - **Snapshots & Rollback**: Create snapshots, restore state, compute diffs
12
+ - **JSON Import/Export**: Load from and export to JSON structure
13
+ - **Path Utilities**: normalize, resolve, relative, join, dirname, basename
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install mem-vfs
19
+ # or
20
+ pnpm add mem-vfs
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { createVFS } from 'mem-vfs';
27
+
28
+ const vfs = createVFS();
29
+
30
+ // Write and read files
31
+ await vfs.writeFile('/src/index.ts', 'export const hello = "world";');
32
+ const content = await vfs.readFile('/src/index.ts', 'utf-8');
33
+
34
+ // Create directories
35
+ await vfs.createDirectory('/src/utils', true);
36
+
37
+ // Glob search
38
+ const tsFiles = await vfs.glob('**/*.ts');
39
+
40
+ // Symbolic links
41
+ await vfs.createSymlink('/src', '/link-to-src');
42
+
43
+ // Snapshots
44
+ const snapshotId = vfs.createSnapshot('before-refactor');
45
+ // ... make changes ...
46
+ vfs.restoreSnapshot(snapshotId);
47
+ ```
48
+
49
+ ## API Reference
50
+
51
+ ### File Operations
52
+
53
+ ```typescript
54
+ // Read file (returns string with encoding, Buffer without)
55
+ await vfs.readFile(path: string, encoding?: BufferEncoding): Promise<string | Buffer>
56
+
57
+ // Write file (auto-creates parent directories)
58
+ await vfs.writeFile(path: string, content: string | Buffer): Promise<void>
59
+
60
+ // Append to file
61
+ await vfs.appendFile(path: string, content: string | Buffer): Promise<void>
62
+
63
+ // Delete file
64
+ await vfs.deleteFile(path: string): Promise<void>
65
+
66
+ // Copy file
67
+ await vfs.copyFile(src: string, dest: string): Promise<void>
68
+
69
+ // Move file
70
+ await vfs.moveFile(src: string, dest: string): Promise<void>
71
+ ```
72
+
73
+ ### Directory Operations
74
+
75
+ ```typescript
76
+ // Create directory (recursive option creates parent directories)
77
+ await vfs.createDirectory(path: string, recursive?: boolean): Promise<void>
78
+
79
+ // Read directory contents
80
+ await vfs.readDirectory(path: string): Promise<DirectoryEntry[]>
81
+
82
+ // Delete directory (recursive option deletes contents)
83
+ await vfs.deleteDirectory(path: string, recursive?: boolean): Promise<void>
84
+ ```
85
+
86
+ ### Status Queries
87
+
88
+ ```typescript
89
+ await vfs.exists(path: string): Promise<boolean>
90
+ await vfs.isFile(path: string): Promise<boolean>
91
+ await vfs.isDirectory(path: string): Promise<boolean>
92
+ await vfs.isSymlink(path: string): Promise<boolean>
93
+ await vfs.getStats(path: string): Promise<FileStats>
94
+ ```
95
+
96
+ ### Symbolic Links
97
+
98
+ ```typescript
99
+ // Create symlink
100
+ await vfs.createSymlink(target: string, linkPath: string): Promise<void>
101
+
102
+ // Read symlink target
103
+ await vfs.readSymlink(linkPath: string): Promise<string>
104
+ ```
105
+
106
+ ### Glob
107
+
108
+ ```typescript
109
+ const files = await vfs.glob(pattern: string, options?: GlobOptions): Promise<string[]>
110
+
111
+ // Options:
112
+ interface GlobOptions {
113
+ cwd?: string; // Working directory
114
+ ignore?: string[]; // Ignore patterns
115
+ dot?: boolean; // Include dotfiles
116
+ absolute?: boolean; // Return absolute paths
117
+ onlyFiles?: boolean; // Only return files
118
+ onlyDirectories?: boolean; // Only return directories
119
+ followSymlinks?: boolean; // Follow symbolic links
120
+ maxDepth?: number; // Maximum traversal depth
121
+ }
122
+ ```
123
+
124
+ ### Snapshots
125
+
126
+ ```typescript
127
+ // Create snapshot
128
+ const id = vfs.createSnapshot(name?: string): SnapshotId
129
+
130
+ // Restore snapshot
131
+ vfs.restoreSnapshot(id: SnapshotId): void
132
+
133
+ // Get snapshot info
134
+ vfs.getSnapshotInfo(id: SnapshotId): SnapshotInfo | undefined
135
+
136
+ // List all snapshots
137
+ vfs.listSnapshots(): SnapshotInfo[]
138
+
139
+ // Delete snapshot
140
+ vfs.deleteSnapshot(id: SnapshotId): boolean
141
+
142
+ // Compute diff between snapshots
143
+ vfs.diff(fromId?: SnapshotId, toId?: SnapshotId): FileDiff[]
144
+ ```
145
+
146
+ ### JSON Import/Export
147
+
148
+ ```typescript
149
+ // Load from JSON structure
150
+ await vfs.fromJSON({
151
+ 'src': {
152
+ 'index.ts': 'export {}',
153
+ 'utils': {
154
+ 'helper.ts': 'export function help() {}'
155
+ }
156
+ },
157
+ 'README.md': '# Project'
158
+ });
159
+
160
+ // Export to JSON
161
+ const json = vfs.toJSON(): DirectoryJSON
162
+
163
+ // Reset file system
164
+ vfs.reset(): void
165
+ ```
166
+
167
+ ## Configuration
168
+
169
+ ```typescript
170
+ const vfs = createVFS({
171
+ caseSensitive: true, // Case-sensitive paths (default: true)
172
+ defaultFileMode: 0o644, // Default file permissions
173
+ defaultDirectoryMode: 0o755, // Default directory permissions
174
+ maxSymlinkDepth: 40, // Maximum symlink resolution depth
175
+ });
176
+ ```
177
+
178
+ ## Error Handling
179
+
180
+ ```typescript
181
+ import {
182
+ FileNotFoundError,
183
+ DirectoryNotFoundError,
184
+ DirectoryNotEmptyError,
185
+ NotAFileError,
186
+ NotADirectoryError,
187
+ SymlinkLoopError,
188
+ InvalidPathError
189
+ } from 'mem-vfs';
190
+
191
+ try {
192
+ await vfs.readFile('/nonexistent');
193
+ } catch (error) {
194
+ if (error instanceof FileNotFoundError) {
195
+ console.log('File not found:', error.path);
196
+ }
197
+ }
198
+ ```
199
+
200
+ ## Path Utilities
201
+
202
+ ```typescript
203
+ import {
204
+ normalizePath,
205
+ dirname,
206
+ basename,
207
+ extname,
208
+ join,
209
+ resolve,
210
+ relative,
211
+ isAbsolute
212
+ } from 'mem-vfs';
213
+
214
+ normalizePath('/a/b/../c/./d') // '/a/c/d'
215
+ dirname('/path/to/file.txt') // '/path/to'
216
+ basename('/path/to/file.txt') // 'file.txt'
217
+ join('/a', 'b', 'c') // '/a/b/c'
218
+ ```
219
+
220
+ ## License
221
+
222
+ MIT
@@ -0,0 +1,35 @@
1
+ /**
2
+ * VFS 目錄節點
3
+ */
4
+ import { VFSNode } from './vfs-node.js';
5
+ import { VFSNodeType } from '../types/index.js';
6
+ /** VFS 目錄節點 */
7
+ export declare class VFSDirectory extends VFSNode {
8
+ readonly type = VFSNodeType.Directory;
9
+ /** 子節點 */
10
+ private readonly children;
11
+ constructor(name: string, mode?: number);
12
+ /** 取得大小(子節點數量 * 4096,模擬目錄大小) */
13
+ get size(): number;
14
+ /** 取得子節點數量 */
15
+ get childCount(): number;
16
+ /** 是否為空目錄 */
17
+ get isEmpty(): boolean;
18
+ /** 取得子節點 */
19
+ getChild(name: string): VFSNode | undefined;
20
+ /** 檢查子節點是否存在 */
21
+ hasChild(name: string): boolean;
22
+ /** 新增子節點 */
23
+ addChild(node: VFSNode): void;
24
+ /** 移除子節點 */
25
+ removeChild(name: string): boolean;
26
+ /** 取得所有子節點名稱 */
27
+ getChildNames(): string[];
28
+ /** 取得所有子節點 */
29
+ getChildren(): VFSNode[];
30
+ /** 迭代子節點 */
31
+ entries(): IterableIterator<[string, VFSNode]>;
32
+ /** 深拷貝 */
33
+ clone(): VFSDirectory;
34
+ }
35
+ //# sourceMappingURL=vfs-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-directory.d.ts","sourceRoot":"","sources":["../../src/core/vfs-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,eAAe;AACf,qBAAa,YAAa,SAAQ,OAAO;IACvC,QAAQ,CAAC,IAAI,yBAAyB;IAEtC,UAAU;IACV,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;gBAEhD,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAA+B;IAI/D,gCAAgC;IAChC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,cAAc;IACd,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,aAAa;IACb,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,YAAY;IACZ,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAK3C,gBAAgB;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI/B,YAAY;IACZ,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAK7B,YAAY;IACZ,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAUlC,gBAAgB;IAChB,aAAa,IAAI,MAAM,EAAE;IAIzB,cAAc;IACd,WAAW,IAAI,OAAO,EAAE;IAKxB,YAAY;IACX,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAI/C,UAAU;IACV,KAAK,IAAI,YAAY;CAetB"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * VFS 目錄節點
3
+ */
4
+ import { VFSNode } from './vfs-node.js';
5
+ import { VFSNodeType } from '../types/index.js';
6
+ /** 預設目錄模式 */
7
+ const DEFAULT_DIRECTORY_MODE = 0o755;
8
+ /** VFS 目錄節點 */
9
+ export class VFSDirectory extends VFSNode {
10
+ type = VFSNodeType.Directory;
11
+ /** 子節點 */
12
+ children = new Map();
13
+ constructor(name, mode = DEFAULT_DIRECTORY_MODE) {
14
+ super(name, mode);
15
+ }
16
+ /** 取得大小(子節點數量 * 4096,模擬目錄大小) */
17
+ get size() {
18
+ return 4096;
19
+ }
20
+ /** 取得子節點數量 */
21
+ get childCount() {
22
+ return this.children.size;
23
+ }
24
+ /** 是否為空目錄 */
25
+ get isEmpty() {
26
+ return this.children.size === 0;
27
+ }
28
+ /** 取得子節點 */
29
+ getChild(name) {
30
+ this.touch();
31
+ return this.children.get(name);
32
+ }
33
+ /** 檢查子節點是否存在 */
34
+ hasChild(name) {
35
+ return this.children.has(name);
36
+ }
37
+ /** 新增子節點 */
38
+ addChild(node) {
39
+ this.children.set(node.name, node);
40
+ this.markModified();
41
+ }
42
+ /** 移除子節點 */
43
+ removeChild(name) {
44
+ const result = this.children.delete(name);
45
+ if (result) {
46
+ this.markModified();
47
+ }
48
+ return result;
49
+ }
50
+ /** 取得所有子節點名稱 */
51
+ getChildNames() {
52
+ return Array.from(this.children.keys());
53
+ }
54
+ /** 取得所有子節點 */
55
+ getChildren() {
56
+ this.touch();
57
+ return Array.from(this.children.values());
58
+ }
59
+ /** 迭代子節點 */
60
+ *entries() {
61
+ yield* this.children.entries();
62
+ }
63
+ /** 深拷貝 */
64
+ clone() {
65
+ const cloned = new VFSDirectory(this.name, this.mode);
66
+ cloned.uid = this.uid;
67
+ cloned.gid = this.gid;
68
+ cloned.createdTime = new Date(this.createdTime);
69
+ cloned.modifiedTime = new Date(this.modifiedTime);
70
+ cloned.accessedTime = new Date(this.accessedTime);
71
+ // 深拷貝子節點
72
+ for (const [name, node] of this.children) {
73
+ cloned.children.set(name, node.clone());
74
+ }
75
+ return cloned;
76
+ }
77
+ }
78
+ //# sourceMappingURL=vfs-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-directory.js","sourceRoot":"","sources":["../../src/core/vfs-directory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * VFS 檔案節點
3
+ */
4
+ import { VFSNode } from './vfs-node.js';
5
+ import { VFSNodeType } from '../types/index.js';
6
+ /** VFS 檔案節點 */
7
+ export declare class VFSFile extends VFSNode {
8
+ readonly type = VFSNodeType.File;
9
+ /** 檔案內容 */
10
+ private content;
11
+ constructor(name: string, content?: string | Buffer, mode?: number);
12
+ /** 取得大小 */
13
+ get size(): number;
14
+ /** 讀取內容 */
15
+ read(encoding?: BufferEncoding): string | Buffer;
16
+ /** 寫入內容 */
17
+ write(data: string | Buffer): void;
18
+ /** 追加內容 */
19
+ append(data: string | Buffer): void;
20
+ /** 截斷內容 */
21
+ truncate(length?: number): void;
22
+ /** 深拷貝 */
23
+ clone(): VFSFile;
24
+ }
25
+ //# sourceMappingURL=vfs-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-file.d.ts","sourceRoot":"","sources":["../../src/core/vfs-file.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,eAAe;AACf,qBAAa,OAAQ,SAAQ,OAAO;IAClC,QAAQ,CAAC,IAAI,oBAAoB;IAEjC,WAAW;IACX,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,GAAG,MAAwB,EAAE,IAAI,GAAE,MAA0B;IAKtG,WAAW;IACX,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,WAAW;IACX,IAAI,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,MAAM;IAUhD,WAAW;IACX,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKlC,WAAW;IACX,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAMnC,WAAW;IACX,QAAQ,CAAC,MAAM,GAAE,MAAU,GAAG,IAAI;IASlC,UAAU;IACV,KAAK,IAAI,OAAO;CAUjB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * VFS 檔案節點
3
+ */
4
+ import { VFSNode } from './vfs-node.js';
5
+ import { VFSNodeType } from '../types/index.js';
6
+ /** 預設檔案模式 */
7
+ const DEFAULT_FILE_MODE = 0o644;
8
+ /** VFS 檔案節點 */
9
+ export class VFSFile extends VFSNode {
10
+ type = VFSNodeType.File;
11
+ /** 檔案內容 */
12
+ content;
13
+ constructor(name, content = Buffer.alloc(0), mode = DEFAULT_FILE_MODE) {
14
+ super(name, mode);
15
+ this.content = typeof content === 'string' ? Buffer.from(content, 'utf-8') : content;
16
+ }
17
+ /** 取得大小 */
18
+ get size() {
19
+ return this.content.length;
20
+ }
21
+ /** 讀取內容 */
22
+ read(encoding) {
23
+ this.touch();
24
+ if (encoding) {
25
+ return this.content.toString(encoding);
26
+ }
27
+ return Buffer.from(this.content);
28
+ }
29
+ /** 寫入內容 */
30
+ write(data) {
31
+ this.content = typeof data === 'string' ? Buffer.from(data, 'utf-8') : Buffer.from(data);
32
+ this.markModified();
33
+ }
34
+ /** 追加內容 */
35
+ append(data) {
36
+ const appendBuffer = typeof data === 'string' ? Buffer.from(data, 'utf-8') : data;
37
+ this.content = Buffer.concat([this.content, appendBuffer]);
38
+ this.markModified();
39
+ }
40
+ /** 截斷內容 */
41
+ truncate(length = 0) {
42
+ if (length >= this.content.length) {
43
+ return;
44
+ }
45
+ this.content = this.content.subarray(0, length);
46
+ this.markModified();
47
+ }
48
+ /** 深拷貝 */
49
+ clone() {
50
+ const cloned = new VFSFile(this.name, Buffer.from(this.content), this.mode);
51
+ cloned.uid = this.uid;
52
+ cloned.gid = this.gid;
53
+ // 時間屬性需要重新賦值(因為 readonly 是在 constructor 設定的)
54
+ cloned.createdTime = new Date(this.createdTime);
55
+ cloned.modifiedTime = new Date(this.modifiedTime);
56
+ cloned.accessedTime = new Date(this.accessedTime);
57
+ return cloned;
58
+ }
59
+ }
60
+ //# sourceMappingURL=vfs-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-file.js","sourceRoot":"","sources":["../../src/core/vfs-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * VFS 節點基礎類別
3
+ */
4
+ import type { FileStats } from '../types/index.js';
5
+ import { VFSNodeType } from '../types/index.js';
6
+ /** VFS 節點基礎類別 */
7
+ export declare abstract class VFSNode {
8
+ /** 節點名稱 */
9
+ readonly name: string;
10
+ /** 節點類型 */
11
+ abstract readonly type: VFSNodeType;
12
+ /** 建立時間 */
13
+ readonly createdTime: Date;
14
+ /** 修改時間 */
15
+ modifiedTime: Date;
16
+ /** 存取時間 */
17
+ accessedTime: Date;
18
+ /** 檔案模式 */
19
+ mode: number;
20
+ /** 使用者 ID */
21
+ uid: number;
22
+ /** 群組 ID */
23
+ gid: number;
24
+ constructor(name: string, mode?: number);
25
+ /** 是否為檔案 */
26
+ get isFile(): boolean;
27
+ /** 是否為目錄 */
28
+ get isDirectory(): boolean;
29
+ /** 是否為符號連結 */
30
+ get isSymlink(): boolean;
31
+ /** 取得大小(由子類別實作) */
32
+ abstract get size(): number;
33
+ /** 取得統計資訊 */
34
+ getStats(): FileStats;
35
+ /** 更新存取時間 */
36
+ touch(): void;
37
+ /** 更新修改時間 */
38
+ markModified(): void;
39
+ /** 深拷貝(由子類別實作) */
40
+ abstract clone(): VFSNode;
41
+ }
42
+ //# sourceMappingURL=vfs-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-node.d.ts","sourceRoot":"","sources":["../../src/core/vfs-node.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,iBAAiB;AACjB,8BAAsB,OAAO;IAC3B,WAAW;IACX,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,WAAW;IACX,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAEpC,WAAW;IACX,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAE3B,WAAW;IACX,YAAY,EAAE,IAAI,CAAC;IAEnB,WAAW;IACX,YAAY,EAAE,IAAI,CAAC;IAEnB,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IAEb,aAAa;IACb,GAAG,EAAE,MAAM,CAAC;IAEZ,YAAY;IACZ,GAAG,EAAE,MAAM,CAAC;gBAEA,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAc;IAY9C,YAAY;IACZ,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,YAAY;IACZ,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,cAAc;IACd,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,mBAAmB;IACnB,QAAQ,KAAK,IAAI,IAAI,MAAM,CAAC;IAE5B,aAAa;IACb,QAAQ,IAAI,SAAS;IAerB,aAAa;IACb,KAAK,IAAI,IAAI;IAIb,aAAa;IACb,YAAY,IAAI,IAAI;IAMpB,kBAAkB;IAClB,QAAQ,CAAC,KAAK,IAAI,OAAO;CAC1B"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * VFS 節點基礎類別
3
+ */
4
+ import { VFSNodeType } from '../types/index.js';
5
+ /** VFS 節點基礎類別 */
6
+ export class VFSNode {
7
+ /** 節點名稱 */
8
+ name;
9
+ /** 建立時間 */
10
+ createdTime;
11
+ /** 修改時間 */
12
+ modifiedTime;
13
+ /** 存取時間 */
14
+ accessedTime;
15
+ /** 檔案模式 */
16
+ mode;
17
+ /** 使用者 ID */
18
+ uid;
19
+ /** 群組 ID */
20
+ gid;
21
+ constructor(name, mode = 0o644) {
22
+ this.name = name;
23
+ this.mode = mode;
24
+ this.uid = 0;
25
+ this.gid = 0;
26
+ const now = new Date();
27
+ this.createdTime = now;
28
+ this.modifiedTime = now;
29
+ this.accessedTime = now;
30
+ }
31
+ /** 是否為檔案 */
32
+ get isFile() {
33
+ return this.type === VFSNodeType.File;
34
+ }
35
+ /** 是否為目錄 */
36
+ get isDirectory() {
37
+ return this.type === VFSNodeType.Directory;
38
+ }
39
+ /** 是否為符號連結 */
40
+ get isSymlink() {
41
+ return this.type === VFSNodeType.Symlink;
42
+ }
43
+ /** 取得統計資訊 */
44
+ getStats() {
45
+ return {
46
+ isFile: this.isFile,
47
+ isDirectory: this.isDirectory,
48
+ isSymlink: this.isSymlink,
49
+ size: this.size,
50
+ createdTime: this.createdTime,
51
+ modifiedTime: this.modifiedTime,
52
+ accessedTime: this.accessedTime,
53
+ mode: this.mode,
54
+ uid: this.uid,
55
+ gid: this.gid,
56
+ };
57
+ }
58
+ /** 更新存取時間 */
59
+ touch() {
60
+ this.accessedTime = new Date();
61
+ }
62
+ /** 更新修改時間 */
63
+ markModified() {
64
+ const now = new Date();
65
+ this.modifiedTime = now;
66
+ this.accessedTime = now;
67
+ }
68
+ }
69
+ //# sourceMappingURL=vfs-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-node.js","sourceRoot":"","sources":["../../src/core/vfs-node.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,iBAAiB;AACjB,MAAM,OAAgB,OAAO;IAC3B,WAAW;IACF,IAAI,CAAS;IAKtB,WAAW;IACF,WAAW,CAAO;IAE3B,WAAW;IACX,YAAY,CAAO;IAEnB,WAAW;IACX,YAAY,CAAO;IAEnB,WAAW;IACX,IAAI,CAAS;IAEb,aAAa;IACb,GAAG,CAAS;IAEZ,YAAY;IACZ,GAAG,CAAS;IAEZ,YAAY,IAAY,EAAE,OAAe,KAAK;QAC5C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QACb,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QAEb,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC;QACxB,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC;IAC1B,CAAC;IAED,YAAY;IACZ,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,CAAC;IACxC,CAAC;IAED,YAAY;IACZ,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC,SAAS,CAAC;IAC7C,CAAC;IAED,cAAc;IACd,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC,OAAO,CAAC;IAC3C,CAAC;IAKD,aAAa;IACb,QAAQ;QACN,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC;IACJ,CAAC;IAED,aAAa;IACb,KAAK;QACH,IAAI,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,aAAa;IACb,YAAY;QACV,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC;QACxB,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC;IAC1B,CAAC;CAIF"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * VFS 符號連結節點
3
+ */
4
+ import { VFSNode } from './vfs-node.js';
5
+ import { VFSNodeType } from '../types/index.js';
6
+ /** VFS 符號連結節點 */
7
+ export declare class VFSSymlink extends VFSNode {
8
+ readonly type = VFSNodeType.Symlink;
9
+ /** 目標路徑 */
10
+ private targetPath;
11
+ constructor(name: string, target: string, mode?: number);
12
+ /** 取得大小(目標路徑的長度) */
13
+ get size(): number;
14
+ /** 取得目標路徑 */
15
+ get target(): string;
16
+ /** 設定目標路徑 */
17
+ setTarget(target: string): void;
18
+ /** 深拷貝 */
19
+ clone(): VFSSymlink;
20
+ }
21
+ //# sourceMappingURL=vfs-symlink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-symlink.d.ts","sourceRoot":"","sources":["../../src/core/vfs-symlink.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,iBAAiB;AACjB,qBAAa,UAAW,SAAQ,OAAO;IACrC,QAAQ,CAAC,IAAI,uBAAuB;IAEpC,WAAW;IACX,OAAO,CAAC,UAAU,CAAS;gBAEf,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,MAA6B;IAK7E,oBAAoB;IACpB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,aAAa;IACb,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,aAAa;IACb,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK/B,UAAU;IACV,KAAK,IAAI,UAAU;CASpB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * VFS 符號連結節點
3
+ */
4
+ import { VFSNode } from './vfs-node.js';
5
+ import { VFSNodeType } from '../types/index.js';
6
+ /** 預設符號連結模式 */
7
+ const DEFAULT_SYMLINK_MODE = 0o777;
8
+ /** VFS 符號連結節點 */
9
+ export class VFSSymlink extends VFSNode {
10
+ type = VFSNodeType.Symlink;
11
+ /** 目標路徑 */
12
+ targetPath;
13
+ constructor(name, target, mode = DEFAULT_SYMLINK_MODE) {
14
+ super(name, mode);
15
+ this.targetPath = target;
16
+ }
17
+ /** 取得大小(目標路徑的長度) */
18
+ get size() {
19
+ return Buffer.byteLength(this.targetPath, 'utf-8');
20
+ }
21
+ /** 取得目標路徑 */
22
+ get target() {
23
+ return this.targetPath;
24
+ }
25
+ /** 設定目標路徑 */
26
+ setTarget(target) {
27
+ this.targetPath = target;
28
+ this.markModified();
29
+ }
30
+ /** 深拷貝 */
31
+ clone() {
32
+ const cloned = new VFSSymlink(this.name, this.targetPath, this.mode);
33
+ cloned.uid = this.uid;
34
+ cloned.gid = this.gid;
35
+ cloned.createdTime = new Date(this.createdTime);
36
+ cloned.modifiedTime = new Date(this.modifiedTime);
37
+ cloned.accessedTime = new Date(this.accessedTime);
38
+ return cloned;
39
+ }
40
+ }
41
+ //# sourceMappingURL=vfs-symlink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs-symlink.js","sourceRoot":"","sources":["../../src/core/vfs-symlink.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,eAAe;AACf,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAEnC,iBAAiB;AACjB,MAAM,OAAO,UAAW,SAAQ,OAAO;IAC5B,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC;IAEpC,WAAW;IACH,UAAU,CAAS;IAE3B,YAAY,IAAY,EAAE,MAAc,EAAE,OAAe,oBAAoB;QAC3E,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;IAC3B,CAAC;IAED,oBAAoB;IACpB,IAAI,IAAI;QACN,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAED,aAAa;IACb,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,aAAa;IACb,SAAS,CAAC,MAAc;QACtB,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;QACzB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED,UAAU;IACV,KAAK;QACH,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACtB,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,MAAgC,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3E,MAAM,CAAC,YAAY,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAClD,MAAM,CAAC,YAAY,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAClD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}