@bendyline/docblocks 2.0.0 → 2.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.
- package/dist/{chunk-J32GU3F3.js → chunk-6IYMTYMQ.js} +249 -221
- package/dist/chunk-6IYMTYMQ.js.map +1 -0
- package/dist/{chunk-75UUF6C4.js → chunk-B56KWCSE.js} +2 -2
- package/dist/{chunk-RVQHY3XU.js → chunk-BNKV7MTO.js} +1 -1
- package/dist/{chunk-RVQHY3XU.js.map → chunk-BNKV7MTO.js.map} +1 -1
- package/dist/chunk-BXMF5YQJ.js +149 -0
- package/dist/chunk-BXMF5YQJ.js.map +1 -0
- package/dist/{chunk-PGR7LK6D.js → chunk-COYQXQK3.js} +14 -5
- package/dist/chunk-COYQXQK3.js.map +1 -0
- package/dist/{chunk-RQJRQF46.js → chunk-DN7A33LL.js} +42 -49
- package/dist/chunk-DN7A33LL.js.map +1 -0
- package/dist/chunk-FA7UNOXK.js +20 -0
- package/dist/chunk-FA7UNOXK.js.map +1 -0
- package/dist/chunk-GM5YXU4L.js +29 -0
- package/dist/chunk-GM5YXU4L.js.map +1 -0
- package/dist/{chunk-SI2LU2RP.js → chunk-JS6XFWQK.js} +2 -1
- package/dist/chunk-JS6XFWQK.js.map +1 -0
- package/dist/{chunk-EGNDADXB.js → chunk-MNSZ7EZN.js} +2 -2
- package/dist/chunk-P3DVZQ6S.js +228 -0
- package/dist/chunk-P3DVZQ6S.js.map +1 -0
- package/dist/chunk-P4WATND4.js +52 -0
- package/dist/chunk-P4WATND4.js.map +1 -0
- package/dist/chunk-RVPGQBWR.js +169 -0
- package/dist/chunk-RVPGQBWR.js.map +1 -0
- package/dist/chunk-U722UUFN.js +9 -0
- package/dist/chunk-U722UUFN.js.map +1 -0
- package/dist/{chunk-GCPBEMMH.js → chunk-VTCSF7VS.js} +11 -21
- package/dist/chunk-VTCSF7VS.js.map +1 -0
- package/dist/{chunk-2D7Y4IR3.js → chunk-WJKYY5QR.js} +123 -66
- package/dist/chunk-WJKYY5QR.js.map +1 -0
- package/dist/{chunk-5MYAICRE.js → chunk-XUML3MKV.js} +44 -56
- package/dist/chunk-XUML3MKV.js.map +1 -0
- package/dist/document/index.d.ts +358 -6
- package/dist/document/index.js +24 -3
- package/dist/document/index.js.map +1 -1
- package/dist/filesystem/electron.d.ts +73 -4
- package/dist/filesystem/electron.js +16 -3
- package/dist/filesystem/electron.js.map +1 -1
- package/dist/filesystem/index.d.ts +228 -22
- package/dist/filesystem/index.js +133 -19
- package/dist/filesystem/index.js.map +1 -1
- package/dist/filesystem/indexeddb.d.ts +284 -5
- package/dist/filesystem/indexeddb.js +22 -4
- package/dist/filesystem/indexeddb.js.map +1 -1
- package/dist/filesystem/memory.d.ts +152 -4
- package/dist/filesystem/memory.js +12 -3
- package/dist/filesystem/memory.js.map +1 -1
- package/dist/filesystem/native.d.ts +136 -4
- package/dist/filesystem/native.js +27 -3
- package/dist/filesystem/native.js.map +1 -1
- package/dist/host/index.d.ts +32 -9
- package/dist/host/index.js +26 -29
- package/dist/host/index.js.map +1 -1
- package/dist/index.d.ts +14 -10
- package/dist/index.js +204 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +33 -4
- package/dist/mcp/index.js +31 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/zod.d.ts +637 -635
- package/dist/mcp/zod.js +700 -884
- package/dist/mcp/zod.js.map +1 -1
- package/dist/share/index.d.ts +43 -0
- package/dist/share/index.js +13 -0
- package/dist/share/index.js.map +1 -0
- package/dist/{types-CsX6EN2t.d.ts → types-BEg5SQ1_.d.ts} +1 -1
- package/dist/{types-eOlt9J7n.d.ts → types-CueK0tvU.d.ts} +34 -11
- package/dist/vscode/index.d.ts +220 -3
- package/dist/vscode/index.js +382 -2
- package/dist/vscode/index.js.map +1 -1
- package/dist/workspace/index.d.ts +141 -4
- package/dist/workspace/index.js +28 -2
- package/dist/workspace/index.js.map +1 -1
- package/package.json +9 -5
- package/dist/chunk-2D7Y4IR3.js.map +0 -1
- package/dist/chunk-5MYAICRE.js.map +0 -1
- package/dist/chunk-AYZ4HN5M.js +0 -20
- package/dist/chunk-AYZ4HN5M.js.map +0 -1
- package/dist/chunk-GCPBEMMH.js.map +0 -1
- package/dist/chunk-J32GU3F3.js.map +0 -1
- package/dist/chunk-PGR7LK6D.js.map +0 -1
- package/dist/chunk-RQJRQF46.js.map +0 -1
- package/dist/chunk-SI2LU2RP.js.map +0 -1
- package/dist/chunk-W5UUUII5.js +0 -142
- package/dist/chunk-W5UUUII5.js.map +0 -1
- package/dist/document/document-session.d.ts +0 -127
- package/dist/document/document-session.d.ts.map +0 -1
- package/dist/document/document-session.js +0 -749
- package/dist/document/document-session.js.map +0 -1
- package/dist/document/filesystem-target.d.ts +0 -17
- package/dist/document/filesystem-target.d.ts.map +0 -1
- package/dist/document/filesystem-target.js +0 -70
- package/dist/document/filesystem-target.js.map +0 -1
- package/dist/document/index.d.ts.map +0 -1
- package/dist/document/recovery-journal.d.ts +0 -125
- package/dist/document/recovery-journal.d.ts.map +0 -1
- package/dist/document/recovery-journal.js +0 -385
- package/dist/document/recovery-journal.js.map +0 -1
- package/dist/document/types.d.ts +0 -89
- package/dist/document/types.d.ts.map +0 -1
- package/dist/document/types.js +0 -9
- package/dist/document/types.js.map +0 -1
- package/dist/filesystem/commit-lock.d.ts +0 -3
- package/dist/filesystem/commit-lock.d.ts.map +0 -1
- package/dist/filesystem/commit-lock.js +0 -20
- package/dist/filesystem/commit-lock.js.map +0 -1
- package/dist/filesystem/dbk-workspace.d.ts +0 -34
- package/dist/filesystem/dbk-workspace.d.ts.map +0 -1
- package/dist/filesystem/dbk-workspace.js +0 -85
- package/dist/filesystem/dbk-workspace.js.map +0 -1
- package/dist/filesystem/electron-provider-v2.d.ts +0 -36
- package/dist/filesystem/electron-provider-v2.d.ts.map +0 -1
- package/dist/filesystem/electron-provider-v2.js +0 -503
- package/dist/filesystem/electron-provider-v2.js.map +0 -1
- package/dist/filesystem/electron-provider.d.ts +0 -35
- package/dist/filesystem/electron-provider.d.ts.map +0 -1
- package/dist/filesystem/electron-provider.js +0 -69
- package/dist/filesystem/electron-provider.js.map +0 -1
- package/dist/filesystem/electron.d.ts.map +0 -1
- package/dist/filesystem/file-media-provider.d.ts +0 -23
- package/dist/filesystem/file-media-provider.d.ts.map +0 -1
- package/dist/filesystem/file-media-provider.js +0 -85
- package/dist/filesystem/file-media-provider.js.map +0 -1
- package/dist/filesystem/filesystem-content-container.d.ts +0 -24
- package/dist/filesystem/filesystem-content-container.d.ts.map +0 -1
- package/dist/filesystem/filesystem-content-container.js +0 -153
- package/dist/filesystem/filesystem-content-container.js.map +0 -1
- package/dist/filesystem/fs-error.d.ts +0 -46
- package/dist/filesystem/fs-error.d.ts.map +0 -1
- package/dist/filesystem/fs-error.js +0 -170
- package/dist/filesystem/fs-error.js.map +0 -1
- package/dist/filesystem/index.d.ts.map +0 -1
- package/dist/filesystem/indexeddb-content-container.d.ts +0 -22
- package/dist/filesystem/indexeddb-content-container.d.ts.map +0 -1
- package/dist/filesystem/indexeddb-content-container.js +0 -100
- package/dist/filesystem/indexeddb-content-container.js.map +0 -1
- package/dist/filesystem/indexeddb-provider-v2.d.ts +0 -96
- package/dist/filesystem/indexeddb-provider-v2.d.ts.map +0 -1
- package/dist/filesystem/indexeddb-provider-v2.js +0 -1241
- package/dist/filesystem/indexeddb-provider-v2.js.map +0 -1
- package/dist/filesystem/indexeddb-provider.d.ts +0 -39
- package/dist/filesystem/indexeddb-provider.d.ts.map +0 -1
- package/dist/filesystem/indexeddb-provider.js +0 -145
- package/dist/filesystem/indexeddb-provider.js.map +0 -1
- package/dist/filesystem/indexeddb-store.d.ts +0 -34
- package/dist/filesystem/indexeddb-store.d.ts.map +0 -1
- package/dist/filesystem/indexeddb-store.js +0 -165
- package/dist/filesystem/indexeddb-store.js.map +0 -1
- package/dist/filesystem/indexeddb.d.ts.map +0 -1
- package/dist/filesystem/legacy-v2-adapter.d.ts +0 -62
- package/dist/filesystem/legacy-v2-adapter.d.ts.map +0 -1
- package/dist/filesystem/legacy-v2-adapter.js +0 -499
- package/dist/filesystem/legacy-v2-adapter.js.map +0 -1
- package/dist/filesystem/memory-provider-v2.d.ts +0 -92
- package/dist/filesystem/memory-provider-v2.d.ts.map +0 -1
- package/dist/filesystem/memory-provider-v2.js +0 -615
- package/dist/filesystem/memory-provider-v2.js.map +0 -1
- package/dist/filesystem/memory-provider.d.ts +0 -54
- package/dist/filesystem/memory-provider.d.ts.map +0 -1
- package/dist/filesystem/memory-provider.js +0 -132
- package/dist/filesystem/memory-provider.js.map +0 -1
- package/dist/filesystem/memory.d.ts.map +0 -1
- package/dist/filesystem/move-entry.d.ts +0 -9
- package/dist/filesystem/move-entry.d.ts.map +0 -1
- package/dist/filesystem/move-entry.js +0 -103
- package/dist/filesystem/move-entry.js.map +0 -1
- package/dist/filesystem/move-error.d.ts +0 -53
- package/dist/filesystem/move-error.d.ts.map +0 -1
- package/dist/filesystem/move-error.js +0 -99
- package/dist/filesystem/move-error.js.map +0 -1
- package/dist/filesystem/native-provider-v2.d.ts +0 -75
- package/dist/filesystem/native-provider-v2.d.ts.map +0 -1
- package/dist/filesystem/native-provider-v2.js +0 -849
- package/dist/filesystem/native-provider-v2.js.map +0 -1
- package/dist/filesystem/native-provider.d.ts +0 -53
- package/dist/filesystem/native-provider.d.ts.map +0 -1
- package/dist/filesystem/native-provider.js +0 -471
- package/dist/filesystem/native-provider.js.map +0 -1
- package/dist/filesystem/native.d.ts.map +0 -1
- package/dist/filesystem/types.d.ts +0 -83
- package/dist/filesystem/types.d.ts.map +0 -1
- package/dist/filesystem/types.js +0 -16
- package/dist/filesystem/types.js.map +0 -1
- package/dist/filesystem/v2.d.ts +0 -172
- package/dist/filesystem/v2.d.ts.map +0 -1
- package/dist/filesystem/v2.js +0 -16
- package/dist/filesystem/v2.js.map +0 -1
- package/dist/filesystem/workspace-path.d.ts +0 -21
- package/dist/filesystem/workspace-path.d.ts.map +0 -1
- package/dist/filesystem/workspace-path.js +0 -80
- package/dist/filesystem/workspace-path.js.map +0 -1
- package/dist/host/filesystem-v2.d.ts +0 -45
- package/dist/host/filesystem-v2.d.ts.map +0 -1
- package/dist/host/filesystem-v2.js +0 -13
- package/dist/host/filesystem-v2.js.map +0 -1
- package/dist/host/git.d.ts +0 -200
- package/dist/host/git.d.ts.map +0 -1
- package/dist/host/git.js +0 -15
- package/dist/host/git.js.map +0 -1
- package/dist/host/index.d.ts.map +0 -1
- package/dist/host/types.d.ts +0 -232
- package/dist/host/types.d.ts.map +0 -1
- package/dist/host/types.js +0 -10
- package/dist/host/types.js.map +0 -1
- package/dist/host/wire-policy.d.ts +0 -22
- package/dist/host/wire-policy.d.ts.map +0 -1
- package/dist/host/wire-policy.js +0 -92
- package/dist/host/wire-policy.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/types.d.ts +0 -466
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/mcp/types.js +0 -25
- package/dist/mcp/types.js.map +0 -1
- package/dist/mcp/wire.d.ts +0 -29
- package/dist/mcp/wire.d.ts.map +0 -1
- package/dist/mcp/wire.js +0 -1046
- package/dist/mcp/wire.js.map +0 -1
- package/dist/mcp/zod.d.ts.map +0 -1
- package/dist/vscode/index.d.ts.map +0 -1
- package/dist/vscode/messages.d.ts +0 -204
- package/dist/vscode/messages.d.ts.map +0 -1
- package/dist/vscode/messages.js +0 -494
- package/dist/vscode/messages.js.map +0 -1
- package/dist/workspace/index.d.ts.map +0 -1
- package/dist/workspace/reconcile-electron.d.ts +0 -19
- package/dist/workspace/reconcile-electron.d.ts.map +0 -1
- package/dist/workspace/reconcile-electron.js +0 -42
- package/dist/workspace/reconcile-electron.js.map +0 -1
- package/dist/workspace/types.d.ts +0 -69
- package/dist/workspace/types.d.ts.map +0 -1
- package/dist/workspace/types.js +0 -2
- package/dist/workspace/types.js.map +0 -1
- package/dist/workspace/workspace-manager.d.ts +0 -47
- package/dist/workspace/workspace-manager.d.ts.map +0 -1
- package/dist/workspace/workspace-manager.js +0 -126
- package/dist/workspace/workspace-manager.js.map +0 -1
- package/src/document/document-session.ts +0 -834
- package/src/document/filesystem-target.ts +0 -109
- package/src/document/index.ts +0 -41
- package/src/document/recovery-journal.ts +0 -536
- package/src/document/types.ts +0 -116
- package/src/filesystem/commit-lock.ts +0 -29
- package/src/filesystem/dbk-workspace.ts +0 -135
- package/src/filesystem/electron-provider-v2.ts +0 -630
- package/src/filesystem/electron-provider.ts +0 -99
- package/src/filesystem/electron.ts +0 -3
- package/src/filesystem/file-media-provider.ts +0 -103
- package/src/filesystem/filesystem-content-container.ts +0 -164
- package/src/filesystem/fs-error.ts +0 -234
- package/src/filesystem/index.ts +0 -143
- package/src/filesystem/indexeddb-content-container.ts +0 -112
- package/src/filesystem/indexeddb-provider-v2.ts +0 -1740
- package/src/filesystem/indexeddb-provider.ts +0 -171
- package/src/filesystem/indexeddb-store.ts +0 -207
- package/src/filesystem/indexeddb.ts +0 -12
- package/src/filesystem/legacy-v2-adapter.ts +0 -779
- package/src/filesystem/memory-provider-v2.ts +0 -896
- package/src/filesystem/memory-provider.ts +0 -180
- package/src/filesystem/memory.ts +0 -15
- package/src/filesystem/move-entry.ts +0 -142
- package/src/filesystem/move-error.ts +0 -159
- package/src/filesystem/native-provider-v2.ts +0 -1165
- package/src/filesystem/native-provider.ts +0 -537
- package/src/filesystem/native.ts +0 -16
- package/src/filesystem/types.ts +0 -114
- package/src/filesystem/v2.ts +0 -231
- package/src/filesystem/workspace-path.ts +0 -92
- package/src/host/filesystem-v2.ts +0 -104
- package/src/host/git.ts +0 -246
- package/src/host/index.ts +0 -93
- package/src/host/types.ts +0 -274
- package/src/host/wire-policy.ts +0 -103
- package/src/index.ts +0 -10
- package/src/mcp/index.ts +0 -3
- package/src/mcp/types.ts +0 -594
- package/src/mcp/wire.ts +0 -1280
- package/src/mcp/zod.ts +0 -1000
- package/src/vscode/index.ts +0 -2
- package/src/vscode/messages.ts +0 -704
- package/src/workspace/index.ts +0 -17
- package/src/workspace/reconcile-electron.ts +0 -65
- package/src/workspace/types.ts +0 -67
- package/src/workspace/workspace-manager.ts +0 -151
- /package/dist/{chunk-75UUF6C4.js.map → chunk-B56KWCSE.js.map} +0 -0
- /package/dist/{chunk-EGNDADXB.js.map → chunk-MNSZ7EZN.js.map} +0 -0
|
@@ -1,896 +0,0 @@
|
|
|
1
|
-
import { FsError, type FsOperation } from './fs-error.js';
|
|
2
|
-
import type { FileCommitResult } from './types.js';
|
|
3
|
-
import {
|
|
4
|
-
parseFileSystemVersion,
|
|
5
|
-
type FileSystemCreateDirectoryOptions,
|
|
6
|
-
type FileSystemDirectorySnapshot,
|
|
7
|
-
type FileSystemEntrySnapshot,
|
|
8
|
-
type FileSystemFileRead,
|
|
9
|
-
type FileSystemFileSnapshot,
|
|
10
|
-
type FileSystemMoveOptions,
|
|
11
|
-
type FileSystemProviderCapabilities,
|
|
12
|
-
type FileSystemProviderV2,
|
|
13
|
-
type FileSystemRemoveOptions,
|
|
14
|
-
type FileSystemRemoveResult,
|
|
15
|
-
type FileSystemSnapshot,
|
|
16
|
-
type FileSystemSnapshotEntry,
|
|
17
|
-
type FileSystemVersion,
|
|
18
|
-
type FileSystemWatchEvent,
|
|
19
|
-
type FileSystemWatchListener,
|
|
20
|
-
type FileSystemWatchOptions,
|
|
21
|
-
type FileSystemWatchSubscription,
|
|
22
|
-
type FileSystemWriteOptions,
|
|
23
|
-
} from './v2.js';
|
|
24
|
-
import {
|
|
25
|
-
WORKSPACE_ROOT,
|
|
26
|
-
parseWorkspacePath,
|
|
27
|
-
workspacePathBasename,
|
|
28
|
-
workspacePathContains,
|
|
29
|
-
workspacePathDirname,
|
|
30
|
-
type WorkspacePath,
|
|
31
|
-
} from './workspace-path.js';
|
|
32
|
-
|
|
33
|
-
interface MemoryEntryBase {
|
|
34
|
-
readonly version: FileSystemVersion;
|
|
35
|
-
readonly lastModified: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface MemoryFileEntry extends MemoryEntryBase {
|
|
39
|
-
readonly kind: 'file';
|
|
40
|
-
readonly data: ArrayBuffer;
|
|
41
|
-
/** Compatibility metadata only; v2 always treats the payload as bytes. */
|
|
42
|
-
readonly payloadKind: MemoryFilePayloadKind;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface MemoryDirectoryEntry extends MemoryEntryBase {
|
|
46
|
-
readonly kind: 'directory';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type MemoryEntry = MemoryFileEntry | MemoryDirectoryEntry;
|
|
50
|
-
|
|
51
|
-
export type MemoryFilePayloadKind = 'text' | 'binary';
|
|
52
|
-
|
|
53
|
-
export interface MemoryFileSystemV2StateFile {
|
|
54
|
-
readonly path: WorkspacePath;
|
|
55
|
-
readonly data: ArrayBuffer;
|
|
56
|
-
readonly payloadKind: MemoryFilePayloadKind;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface MemoryFileSystemV2CompatibilityFile {
|
|
60
|
-
readonly data: ArrayBuffer;
|
|
61
|
-
readonly payloadKind: MemoryFilePayloadKind;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface MemoryFileSystemV2State {
|
|
65
|
-
readonly revision: number;
|
|
66
|
-
readonly files: readonly MemoryFileSystemV2StateFile[];
|
|
67
|
-
readonly directories: readonly WorkspacePath[];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface MemoryFileSystemV2ReplacementFile {
|
|
71
|
-
readonly path: WorkspacePath;
|
|
72
|
-
readonly data: ArrayBuffer | Uint8Array;
|
|
73
|
-
readonly payloadKind: MemoryFilePayloadKind;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface MemoryFileSystemV2Replacement {
|
|
77
|
-
readonly files: readonly MemoryFileSystemV2ReplacementFile[];
|
|
78
|
-
readonly directories?: readonly WorkspacePath[];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
interface MemoryWatcher {
|
|
82
|
-
readonly listener: FileSystemWatchListener;
|
|
83
|
-
readonly onError: FileSystemWatchOptions['onError'];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
type MemoryWatchEventInput = FileSystemWatchEvent extends infer Event
|
|
87
|
-
? Event extends FileSystemWatchEvent
|
|
88
|
-
? Omit<Event, 'sequence' | 'origin'>
|
|
89
|
-
: never
|
|
90
|
-
: never;
|
|
91
|
-
|
|
92
|
-
const MEMORY_CAPABILITIES: FileSystemProviderCapabilities = Object.freeze({
|
|
93
|
-
writeAtomicity: 'process',
|
|
94
|
-
moveAtomicity: 'process',
|
|
95
|
-
snapshotAtomicity: 'process',
|
|
96
|
-
conditionalWrite: 'process',
|
|
97
|
-
recursiveRemove: true,
|
|
98
|
-
watch: true,
|
|
99
|
-
caseSensitivity: 'sensitive',
|
|
100
|
-
symlinkPolicy: 'unsupported',
|
|
101
|
-
durability: 'volatile',
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Correctness reference for FileSystemProviderV2.
|
|
106
|
-
*
|
|
107
|
-
* Every mutation validates first, stages a fresh map, swaps it once, and only
|
|
108
|
-
* then emits notifications. File buffers are owned by the provider and every
|
|
109
|
-
* read/snapshot returns a defensive copy.
|
|
110
|
-
*/
|
|
111
|
-
export class MemoryFileSystemProviderV2 implements FileSystemProviderV2 {
|
|
112
|
-
public readonly id: string;
|
|
113
|
-
public readonly label: string;
|
|
114
|
-
public readonly capabilities = MEMORY_CAPABILITIES;
|
|
115
|
-
|
|
116
|
-
private entries: Map<WorkspacePath, MemoryEntry>;
|
|
117
|
-
private readonly watchers = new Set<MemoryWatcher>();
|
|
118
|
-
private readonly subscriptions = new Set<FileSystemWatchSubscription>();
|
|
119
|
-
private readonly pendingEvents: FileSystemWatchEvent[] = [];
|
|
120
|
-
private emittingEvents = false;
|
|
121
|
-
private mutationCounter = 0;
|
|
122
|
-
private eventSequence = 0;
|
|
123
|
-
private treeVersion: FileSystemVersion;
|
|
124
|
-
private disposed = false;
|
|
125
|
-
private disposePromise: Promise<void> | null = null;
|
|
126
|
-
|
|
127
|
-
public constructor(id: string, label: string) {
|
|
128
|
-
this.id = id;
|
|
129
|
-
this.label = label;
|
|
130
|
-
this.treeVersion = parseFileSystemVersion(`${id}:0`);
|
|
131
|
-
const now = new Date().toISOString();
|
|
132
|
-
this.entries = new Map([
|
|
133
|
-
[WORKSPACE_ROOT, { kind: 'directory', version: this.treeVersion, lastModified: now }],
|
|
134
|
-
]);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Monotonic in-process revision used by synchronous transient-workspace staging. */
|
|
138
|
-
public get mutationRevision(): number {
|
|
139
|
-
return this.mutationCounter;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** @internal Atomically couple owned bytes to their v1 compatibility kind. */
|
|
143
|
-
public readCompatibilityFile(path: WorkspacePath): MemoryFileSystemV2CompatibilityFile | null {
|
|
144
|
-
this.assertOpen('read');
|
|
145
|
-
const canonical = this.canonical(path);
|
|
146
|
-
const entry = this.entries.get(canonical);
|
|
147
|
-
if (!entry) return null;
|
|
148
|
-
if (entry.kind !== 'file') {
|
|
149
|
-
throw this.error(
|
|
150
|
-
'type-mismatch',
|
|
151
|
-
'read',
|
|
152
|
-
canonical,
|
|
153
|
-
'Expected a file but found a directory.',
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
return Object.freeze({ data: entry.data.slice(0), payloadKind: entry.payloadKind });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** @internal Capture an owned synchronous copy for DBK whole-tree staging. */
|
|
160
|
-
public captureState(): MemoryFileSystemV2State {
|
|
161
|
-
this.assertOpen('snapshot');
|
|
162
|
-
const files: MemoryFileSystemV2StateFile[] = [];
|
|
163
|
-
const directories: WorkspacePath[] = [];
|
|
164
|
-
for (const [path, entry] of [...this.entries].sort(([left], [right]) =>
|
|
165
|
-
compareText(left, right),
|
|
166
|
-
)) {
|
|
167
|
-
if (!path) continue;
|
|
168
|
-
if (entry.kind === 'directory') directories.push(path);
|
|
169
|
-
else {
|
|
170
|
-
files.push(
|
|
171
|
-
Object.freeze({ path, data: entry.data.slice(0), payloadKind: entry.payloadKind }),
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return Object.freeze({
|
|
176
|
-
revision: this.mutationCounter,
|
|
177
|
-
files: Object.freeze(files),
|
|
178
|
-
directories: Object.freeze(directories),
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** @internal Atomically replace a transient workspace after complete validation. */
|
|
183
|
-
public replaceState(replacement: MemoryFileSystemV2Replacement, expectedRevision?: number): void {
|
|
184
|
-
this.assertOpen('write');
|
|
185
|
-
const stagedFiles = new Map<
|
|
186
|
-
WorkspacePath,
|
|
187
|
-
{ readonly data: ArrayBuffer; readonly payloadKind: MemoryFilePayloadKind }
|
|
188
|
-
>();
|
|
189
|
-
const stagedDirectories = new Set<WorkspacePath>();
|
|
190
|
-
const explicitDirectories = new Set<WorkspacePath>();
|
|
191
|
-
|
|
192
|
-
for (const rawDirectory of replacement.directories ?? []) {
|
|
193
|
-
const directory = this.canonicalNonRoot(rawDirectory, 'create-directory');
|
|
194
|
-
if (explicitDirectories.has(directory)) {
|
|
195
|
-
throw this.error(
|
|
196
|
-
'already-exists',
|
|
197
|
-
'create-directory',
|
|
198
|
-
directory,
|
|
199
|
-
'Duplicate replacement directory.',
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
explicitDirectories.add(directory);
|
|
203
|
-
stagedDirectories.add(directory);
|
|
204
|
-
addParentPaths(stagedDirectories, directory);
|
|
205
|
-
}
|
|
206
|
-
for (const rawFile of replacement.files) {
|
|
207
|
-
const path = this.canonicalNonRoot(rawFile.path, 'write');
|
|
208
|
-
if (stagedFiles.has(path)) {
|
|
209
|
-
throw this.error('already-exists', 'write', path, 'Duplicate replacement file.');
|
|
210
|
-
}
|
|
211
|
-
if (!isMemoryFilePayloadKind(rawFile.payloadKind)) {
|
|
212
|
-
throw this.error('type-mismatch', 'write', path, 'Invalid replacement payload kind.');
|
|
213
|
-
}
|
|
214
|
-
stagedFiles.set(path, {
|
|
215
|
-
data: copyBytes(rawFile.data),
|
|
216
|
-
payloadKind: rawFile.payloadKind,
|
|
217
|
-
});
|
|
218
|
-
addParentPaths(stagedDirectories, path);
|
|
219
|
-
}
|
|
220
|
-
for (const path of stagedFiles.keys()) {
|
|
221
|
-
if (stagedDirectories.has(path)) {
|
|
222
|
-
throw this.error(
|
|
223
|
-
'type-mismatch',
|
|
224
|
-
'write',
|
|
225
|
-
path,
|
|
226
|
-
'Replacement path has conflicting file and directory kinds.',
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
for (const parent of parentChain(path)) {
|
|
230
|
-
if (parent && stagedFiles.has(parent)) {
|
|
231
|
-
throw this.error('type-mismatch', 'write', parent, 'A replacement parent is a file.');
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
if (expectedRevision !== undefined && expectedRevision !== this.mutationCounter) {
|
|
236
|
-
throw this.error(
|
|
237
|
-
'conflict',
|
|
238
|
-
'write',
|
|
239
|
-
WORKSPACE_ROOT,
|
|
240
|
-
'Memory filesystem changed while replacement contents were staged.',
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const version = this.nextVersion();
|
|
245
|
-
const now = new Date().toISOString();
|
|
246
|
-
const next = new Map<WorkspacePath, MemoryEntry>([
|
|
247
|
-
[WORKSPACE_ROOT, { kind: 'directory', version, lastModified: now }],
|
|
248
|
-
]);
|
|
249
|
-
this.addDirectories(next, [...stagedDirectories], version, now);
|
|
250
|
-
for (const [path, file] of stagedFiles) {
|
|
251
|
-
next.set(path, {
|
|
252
|
-
kind: 'file',
|
|
253
|
-
data: file.data,
|
|
254
|
-
payloadKind: file.payloadKind,
|
|
255
|
-
version,
|
|
256
|
-
lastModified: now,
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
this.entries = next;
|
|
260
|
-
this.emit({
|
|
261
|
-
type: 'overflow',
|
|
262
|
-
kind: null,
|
|
263
|
-
path: WORKSPACE_ROOT,
|
|
264
|
-
destinationPath: null,
|
|
265
|
-
version: null,
|
|
266
|
-
previousVersion: null,
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/** @internal Synchronous setup hook used before a transient provider is published. */
|
|
271
|
-
public seedText(path: WorkspacePath, content: string): void {
|
|
272
|
-
this.writeFileNow(path, new TextEncoder().encode(content), { createParents: true }, 'text');
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/** @internal Legacy text write that preserves text/binary compatibility metadata. */
|
|
276
|
-
public async writeText(
|
|
277
|
-
path: WorkspacePath,
|
|
278
|
-
content: string,
|
|
279
|
-
options: FileSystemWriteOptions = {},
|
|
280
|
-
): Promise<FileSystemFileSnapshot> {
|
|
281
|
-
return this.writeFileNow(path, new TextEncoder().encode(content), options, 'text');
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/** @internal Legacy binary write that explicitly changes compatibility payload kind. */
|
|
285
|
-
public async writeBinary(
|
|
286
|
-
path: WorkspacePath,
|
|
287
|
-
data: ArrayBuffer | Uint8Array,
|
|
288
|
-
options: FileSystemWriteOptions = {},
|
|
289
|
-
): Promise<FileSystemFileSnapshot> {
|
|
290
|
-
return this.writeFileNow(path, data, options, 'binary');
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/** @internal Atomic legacy compare-and-write over the authoritative v2 entry. */
|
|
294
|
-
public async commitText(
|
|
295
|
-
path: WorkspacePath,
|
|
296
|
-
content: string,
|
|
297
|
-
expectedContent: string | null,
|
|
298
|
-
): Promise<FileCommitResult> {
|
|
299
|
-
this.assertOpen('write');
|
|
300
|
-
const canonical = this.canonicalNonRoot(path, 'write');
|
|
301
|
-
const existing = this.entries.get(canonical);
|
|
302
|
-
if (existing?.kind === 'directory') {
|
|
303
|
-
throw this.error('type-mismatch', 'write', canonical, 'Cannot replace a directory.');
|
|
304
|
-
}
|
|
305
|
-
const currentContent =
|
|
306
|
-
existing?.kind === 'file' && existing.payloadKind === 'text'
|
|
307
|
-
? new TextDecoder().decode(existing.data)
|
|
308
|
-
: null;
|
|
309
|
-
if (
|
|
310
|
-
(existing?.kind === 'file' && existing.payloadKind === 'binary') ||
|
|
311
|
-
(currentContent !== expectedContent && currentContent !== content)
|
|
312
|
-
) {
|
|
313
|
-
return {
|
|
314
|
-
status: 'conflict',
|
|
315
|
-
content: currentContent,
|
|
316
|
-
version: existing?.version ?? null,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
const written = this.writeFileNow(
|
|
320
|
-
canonical,
|
|
321
|
-
new TextEncoder().encode(content),
|
|
322
|
-
{ createParents: true, expectedVersion: existing?.version ?? null },
|
|
323
|
-
'text',
|
|
324
|
-
);
|
|
325
|
-
return { status: 'committed', version: written.version };
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
public async stat(path: WorkspacePath): Promise<FileSystemEntrySnapshot | null> {
|
|
329
|
-
this.assertOpen('stat');
|
|
330
|
-
const canonical = this.canonical(path);
|
|
331
|
-
const entry = this.entries.get(canonical);
|
|
332
|
-
return entry ? this.toEntrySnapshot(canonical, entry) : null;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
public async readFile(path: WorkspacePath): Promise<FileSystemFileRead | null> {
|
|
336
|
-
this.assertOpen('read');
|
|
337
|
-
const canonical = this.canonical(path);
|
|
338
|
-
const entry = this.entries.get(canonical);
|
|
339
|
-
if (!entry) return null;
|
|
340
|
-
if (entry.kind !== 'file') {
|
|
341
|
-
throw this.error(
|
|
342
|
-
'type-mismatch',
|
|
343
|
-
'read',
|
|
344
|
-
canonical,
|
|
345
|
-
'Expected a file but found a directory.',
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
return Object.freeze({
|
|
349
|
-
entry: this.toFileSnapshot(canonical, entry),
|
|
350
|
-
data: entry.data.slice(0),
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
public async readDirectory(path: WorkspacePath): Promise<readonly FileSystemEntrySnapshot[]> {
|
|
355
|
-
this.assertOpen('list');
|
|
356
|
-
const canonical = this.canonical(path);
|
|
357
|
-
const directory = this.entries.get(canonical);
|
|
358
|
-
if (!directory) {
|
|
359
|
-
throw this.error('not-found', 'list', canonical, 'Directory does not exist.');
|
|
360
|
-
}
|
|
361
|
-
if (directory.kind !== 'directory') {
|
|
362
|
-
throw this.error(
|
|
363
|
-
'type-mismatch',
|
|
364
|
-
'list',
|
|
365
|
-
canonical,
|
|
366
|
-
'Expected a directory but found a file.',
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const children: FileSystemEntrySnapshot[] = [];
|
|
371
|
-
for (const [entryPath, entry] of this.entries) {
|
|
372
|
-
if (entryPath && workspacePathDirname(entryPath) === canonical) {
|
|
373
|
-
children.push(this.toEntrySnapshot(entryPath, entry));
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
children.sort(compareSnapshots);
|
|
377
|
-
return Object.freeze(children);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
public async writeFile(
|
|
381
|
-
path: WorkspacePath,
|
|
382
|
-
data: ArrayBuffer | Uint8Array,
|
|
383
|
-
options: FileSystemWriteOptions = {},
|
|
384
|
-
): Promise<FileSystemFileSnapshot> {
|
|
385
|
-
return this.writeFileNow(path, data, options);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
private writeFileNow(
|
|
389
|
-
path: WorkspacePath,
|
|
390
|
-
data: ArrayBuffer | Uint8Array,
|
|
391
|
-
options: FileSystemWriteOptions,
|
|
392
|
-
payloadKind?: MemoryFilePayloadKind,
|
|
393
|
-
): FileSystemFileSnapshot {
|
|
394
|
-
this.assertOpen('write');
|
|
395
|
-
const canonical = this.canonicalNonRoot(path, 'write');
|
|
396
|
-
const existing = this.entries.get(canonical);
|
|
397
|
-
if (existing?.kind === 'directory') {
|
|
398
|
-
throw this.error(
|
|
399
|
-
'type-mismatch',
|
|
400
|
-
'write',
|
|
401
|
-
canonical,
|
|
402
|
-
'Cannot replace a directory with a file.',
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
const mode = options.mode ?? 'upsert';
|
|
407
|
-
if (mode === 'create' && existing) {
|
|
408
|
-
throw this.error('already-exists', 'write', canonical, 'File already exists.');
|
|
409
|
-
}
|
|
410
|
-
if (mode === 'replace' && !existing) {
|
|
411
|
-
throw this.error('not-found', 'write', canonical, 'File does not exist.');
|
|
412
|
-
}
|
|
413
|
-
this.assertExpectedVersion(existing, options.expectedVersion, 'write', canonical);
|
|
414
|
-
|
|
415
|
-
const missingParents = this.planParents(canonical, options.createParents ?? false, 'write');
|
|
416
|
-
const ownedData = copyBytes(data);
|
|
417
|
-
const version = this.nextVersion();
|
|
418
|
-
const now = new Date().toISOString();
|
|
419
|
-
const next = new Map(this.entries);
|
|
420
|
-
this.addDirectories(next, missingParents, version, now);
|
|
421
|
-
next.set(canonical, {
|
|
422
|
-
kind: 'file',
|
|
423
|
-
data: ownedData,
|
|
424
|
-
payloadKind: payloadKind ?? (existing?.kind === 'file' ? existing.payloadKind : 'binary'),
|
|
425
|
-
version,
|
|
426
|
-
lastModified: now,
|
|
427
|
-
});
|
|
428
|
-
this.touchParents(next, canonical, version, now);
|
|
429
|
-
this.entries = next;
|
|
430
|
-
|
|
431
|
-
for (const directoryPath of missingParents) {
|
|
432
|
-
this.emit({
|
|
433
|
-
type: 'created',
|
|
434
|
-
kind: 'directory',
|
|
435
|
-
path: directoryPath,
|
|
436
|
-
destinationPath: null,
|
|
437
|
-
version,
|
|
438
|
-
previousVersion: null,
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
this.emit({
|
|
442
|
-
type: existing ? 'modified' : 'created',
|
|
443
|
-
kind: 'file',
|
|
444
|
-
path: canonical,
|
|
445
|
-
destinationPath: null,
|
|
446
|
-
version,
|
|
447
|
-
previousVersion: existing?.version ?? null,
|
|
448
|
-
});
|
|
449
|
-
return this.toFileSnapshot(canonical, next.get(canonical) as MemoryFileEntry);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
public async createDirectory(
|
|
453
|
-
path: WorkspacePath,
|
|
454
|
-
options: FileSystemCreateDirectoryOptions = {},
|
|
455
|
-
): Promise<FileSystemDirectorySnapshot> {
|
|
456
|
-
this.assertOpen('create-directory');
|
|
457
|
-
const canonical = this.canonical(path);
|
|
458
|
-
const existing = this.entries.get(canonical);
|
|
459
|
-
if (existing) {
|
|
460
|
-
if (existing.kind !== 'directory') {
|
|
461
|
-
throw this.error(
|
|
462
|
-
'type-mismatch',
|
|
463
|
-
'create-directory',
|
|
464
|
-
canonical,
|
|
465
|
-
'Cannot replace a file with a directory.',
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
if ((options.mode ?? 'ensure') === 'create') {
|
|
469
|
-
throw this.error(
|
|
470
|
-
'already-exists',
|
|
471
|
-
'create-directory',
|
|
472
|
-
canonical,
|
|
473
|
-
'Directory already exists.',
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
return this.toDirectorySnapshot(canonical, existing);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const missingParents = this.planParents(
|
|
480
|
-
canonical,
|
|
481
|
-
options.createParents ?? false,
|
|
482
|
-
'create-directory',
|
|
483
|
-
);
|
|
484
|
-
const version = this.nextVersion();
|
|
485
|
-
const now = new Date().toISOString();
|
|
486
|
-
const next = new Map(this.entries);
|
|
487
|
-
const created = [...missingParents, canonical];
|
|
488
|
-
this.addDirectories(next, created, version, now);
|
|
489
|
-
this.touchParents(next, canonical, version, now);
|
|
490
|
-
this.entries = next;
|
|
491
|
-
|
|
492
|
-
for (const directoryPath of created) {
|
|
493
|
-
this.emit({
|
|
494
|
-
type: 'created',
|
|
495
|
-
kind: 'directory',
|
|
496
|
-
path: directoryPath,
|
|
497
|
-
destinationPath: null,
|
|
498
|
-
version,
|
|
499
|
-
previousVersion: null,
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
return this.toDirectorySnapshot(canonical, next.get(canonical) as MemoryDirectoryEntry);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
public async remove(
|
|
506
|
-
path: WorkspacePath,
|
|
507
|
-
options: FileSystemRemoveOptions = {},
|
|
508
|
-
): Promise<FileSystemRemoveResult> {
|
|
509
|
-
this.assertOpen('remove');
|
|
510
|
-
const canonical = this.canonicalNonRoot(path, 'remove');
|
|
511
|
-
const existing = this.entries.get(canonical);
|
|
512
|
-
if (!existing) {
|
|
513
|
-
if ((options.missing ?? 'error') === 'ignore') {
|
|
514
|
-
return Object.freeze({ removed: false, version: this.treeVersion });
|
|
515
|
-
}
|
|
516
|
-
throw this.error('not-found', 'remove', canonical, 'Entry does not exist.');
|
|
517
|
-
}
|
|
518
|
-
this.assertExpectedVersion(existing, options.expectedVersion, 'remove', canonical);
|
|
519
|
-
|
|
520
|
-
const descendants = [...this.entries.keys()].filter((candidate) =>
|
|
521
|
-
workspacePathContains(canonical, candidate, false),
|
|
522
|
-
);
|
|
523
|
-
if (existing.kind === 'directory' && descendants.length > 0 && !options.recursive) {
|
|
524
|
-
throw this.error('not-empty', 'remove', canonical, 'Directory is not empty.');
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const version = this.nextVersion();
|
|
528
|
-
const now = new Date().toISOString();
|
|
529
|
-
const next = new Map(this.entries);
|
|
530
|
-
next.delete(canonical);
|
|
531
|
-
if (existing.kind === 'directory') {
|
|
532
|
-
for (const descendant of descendants) next.delete(descendant);
|
|
533
|
-
}
|
|
534
|
-
this.touchParents(next, canonical, version, now);
|
|
535
|
-
this.entries = next;
|
|
536
|
-
|
|
537
|
-
this.emit({
|
|
538
|
-
type: 'removed',
|
|
539
|
-
kind: existing.kind,
|
|
540
|
-
path: canonical,
|
|
541
|
-
destinationPath: null,
|
|
542
|
-
version: null,
|
|
543
|
-
previousVersion: existing.version,
|
|
544
|
-
});
|
|
545
|
-
return Object.freeze({ removed: true, version });
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
public async move(
|
|
549
|
-
oldPath: WorkspacePath,
|
|
550
|
-
newPath: WorkspacePath,
|
|
551
|
-
options: FileSystemMoveOptions = {},
|
|
552
|
-
): Promise<FileSystemEntrySnapshot> {
|
|
553
|
-
this.assertOpen('move');
|
|
554
|
-
const sourcePath = this.canonicalNonRoot(oldPath, 'move');
|
|
555
|
-
const destinationPath = this.canonicalNonRoot(newPath, 'move');
|
|
556
|
-
const source = this.entries.get(sourcePath);
|
|
557
|
-
if (!source) throw this.error('not-found', 'move', sourcePath, 'Source does not exist.');
|
|
558
|
-
this.assertExpectedVersion(source, options.expectedVersion, 'move', sourcePath);
|
|
559
|
-
if (sourcePath === destinationPath) return this.toEntrySnapshot(sourcePath, source);
|
|
560
|
-
if (this.entries.has(destinationPath)) {
|
|
561
|
-
throw new FsError('already-exists', 'Destination already exists.', {
|
|
562
|
-
operation: 'move',
|
|
563
|
-
path: sourcePath,
|
|
564
|
-
destinationPath,
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
if (source.kind === 'directory' && workspacePathContains(sourcePath, destinationPath, false)) {
|
|
568
|
-
throw new FsError('invalid-path', 'Cannot move a directory into itself.', {
|
|
569
|
-
operation: 'move',
|
|
570
|
-
path: sourcePath,
|
|
571
|
-
destinationPath,
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
const missingParents = this.planParents(
|
|
576
|
-
destinationPath,
|
|
577
|
-
options.createParents ?? false,
|
|
578
|
-
'move',
|
|
579
|
-
);
|
|
580
|
-
const sourcePaths = [...this.entries.keys()].filter((candidate) =>
|
|
581
|
-
workspacePathContains(sourcePath, candidate),
|
|
582
|
-
);
|
|
583
|
-
const sourceSet = new Set(sourcePaths);
|
|
584
|
-
const remapped = sourcePaths.map((currentPath) => {
|
|
585
|
-
const suffix = currentPath.slice(sourcePath.length);
|
|
586
|
-
const target = parseWorkspacePath(`${destinationPath}${suffix}`);
|
|
587
|
-
if (this.entries.has(target) && !sourceSet.has(target)) {
|
|
588
|
-
throw new FsError('already-exists', 'A destination descendant already exists.', {
|
|
589
|
-
operation: 'move',
|
|
590
|
-
path: currentPath,
|
|
591
|
-
destinationPath: target,
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
return [currentPath, target] as const;
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
const version = this.nextVersion();
|
|
598
|
-
const now = new Date().toISOString();
|
|
599
|
-
const next = new Map(this.entries);
|
|
600
|
-
for (const currentPath of sourcePaths) next.delete(currentPath);
|
|
601
|
-
this.addDirectories(next, missingParents, version, now);
|
|
602
|
-
for (const [currentPath, targetPath] of remapped) {
|
|
603
|
-
const current = this.entries.get(currentPath)!;
|
|
604
|
-
next.set(targetPath, this.withVersion(current, version, now));
|
|
605
|
-
}
|
|
606
|
-
this.touchParents(next, sourcePath, version, now);
|
|
607
|
-
this.touchParents(next, destinationPath, version, now);
|
|
608
|
-
this.entries = next;
|
|
609
|
-
|
|
610
|
-
for (const directoryPath of missingParents) {
|
|
611
|
-
this.emit({
|
|
612
|
-
type: 'created',
|
|
613
|
-
kind: 'directory',
|
|
614
|
-
path: directoryPath,
|
|
615
|
-
destinationPath: null,
|
|
616
|
-
version,
|
|
617
|
-
previousVersion: null,
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
this.emit({
|
|
621
|
-
type: 'moved',
|
|
622
|
-
kind: source.kind,
|
|
623
|
-
path: sourcePath,
|
|
624
|
-
destinationPath,
|
|
625
|
-
version,
|
|
626
|
-
previousVersion: source.version,
|
|
627
|
-
});
|
|
628
|
-
return this.toEntrySnapshot(destinationPath, next.get(destinationPath)!);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
public async snapshot(): Promise<FileSystemSnapshot> {
|
|
632
|
-
this.assertOpen('snapshot');
|
|
633
|
-
const entries: FileSystemSnapshotEntry[] = [...this.entries.entries()]
|
|
634
|
-
.sort(([left], [right]) => compareText(left, right))
|
|
635
|
-
.map(([path, entry]) => {
|
|
636
|
-
if (entry.kind === 'file') {
|
|
637
|
-
return Object.freeze({ ...this.toFileSnapshot(path, entry), data: entry.data.slice(0) });
|
|
638
|
-
}
|
|
639
|
-
return this.toDirectorySnapshot(path, entry);
|
|
640
|
-
});
|
|
641
|
-
return Object.freeze({ version: this.treeVersion, entries: Object.freeze(entries) });
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
public watch(
|
|
645
|
-
listener: FileSystemWatchListener,
|
|
646
|
-
options: FileSystemWatchOptions = {},
|
|
647
|
-
): FileSystemWatchSubscription {
|
|
648
|
-
this.assertOpen('watch');
|
|
649
|
-
const watcher: MemoryWatcher = { listener, onError: options.onError };
|
|
650
|
-
this.watchers.add(watcher);
|
|
651
|
-
let closed = false;
|
|
652
|
-
let disposePromise: Promise<void> | null = null;
|
|
653
|
-
const subscription: FileSystemWatchSubscription = {
|
|
654
|
-
get closed() {
|
|
655
|
-
return closed;
|
|
656
|
-
},
|
|
657
|
-
ready: Promise.resolve(),
|
|
658
|
-
dispose: () => {
|
|
659
|
-
if (disposePromise) return disposePromise;
|
|
660
|
-
closed = true;
|
|
661
|
-
this.watchers.delete(watcher);
|
|
662
|
-
this.subscriptions.delete(subscription);
|
|
663
|
-
disposePromise = Promise.resolve();
|
|
664
|
-
return disposePromise;
|
|
665
|
-
},
|
|
666
|
-
};
|
|
667
|
-
this.subscriptions.add(subscription);
|
|
668
|
-
return subscription;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
public dispose(): Promise<void> {
|
|
672
|
-
if (this.disposePromise) return this.disposePromise;
|
|
673
|
-
this.disposed = true;
|
|
674
|
-
this.disposePromise = Promise.all(
|
|
675
|
-
[...this.subscriptions].map((subscription) => subscription.dispose()),
|
|
676
|
-
).then(() => undefined);
|
|
677
|
-
return this.disposePromise;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
private canonical(path: WorkspacePath): WorkspacePath {
|
|
681
|
-
return parseWorkspacePath(path);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
private canonicalNonRoot(path: WorkspacePath, operation: FsOperation): WorkspacePath {
|
|
685
|
-
const canonical = this.canonical(path);
|
|
686
|
-
if (!canonical) {
|
|
687
|
-
throw this.error(
|
|
688
|
-
'invalid-path',
|
|
689
|
-
operation,
|
|
690
|
-
canonical,
|
|
691
|
-
'The workspace root is not a file entry.',
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
return canonical;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
private assertOpen(operation: FsOperation): void {
|
|
698
|
-
if (!this.disposed) return;
|
|
699
|
-
throw new FsError('disposed', 'Filesystem provider is disposed.', { operation });
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
private assertExpectedVersion(
|
|
703
|
-
entry: MemoryEntry | undefined,
|
|
704
|
-
expected: FileSystemVersion | null | undefined,
|
|
705
|
-
operation: FsOperation,
|
|
706
|
-
path: WorkspacePath,
|
|
707
|
-
): void {
|
|
708
|
-
if (expected === undefined) return;
|
|
709
|
-
if (expected === null ? !entry : entry?.version === expected) return;
|
|
710
|
-
throw this.error(
|
|
711
|
-
'conflict',
|
|
712
|
-
operation,
|
|
713
|
-
path,
|
|
714
|
-
'Entry version does not match the expected version.',
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
private planParents(
|
|
719
|
-
path: WorkspacePath,
|
|
720
|
-
createParents: boolean,
|
|
721
|
-
operation: FsOperation,
|
|
722
|
-
): WorkspacePath[] {
|
|
723
|
-
const missing: WorkspacePath[] = [];
|
|
724
|
-
for (const parent of parentChain(path)) {
|
|
725
|
-
if (!parent) continue;
|
|
726
|
-
const entry = this.entries.get(parent);
|
|
727
|
-
if (entry?.kind === 'file') {
|
|
728
|
-
throw this.error(
|
|
729
|
-
'type-mismatch',
|
|
730
|
-
operation,
|
|
731
|
-
parent,
|
|
732
|
-
'A parent path is a file, not a directory.',
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
if (!entry) {
|
|
736
|
-
if (!createParents) {
|
|
737
|
-
throw this.error('not-found', operation, parent, 'Parent directory does not exist.');
|
|
738
|
-
}
|
|
739
|
-
missing.push(parent);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
return missing;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
private addDirectories(
|
|
746
|
-
target: Map<WorkspacePath, MemoryEntry>,
|
|
747
|
-
paths: readonly WorkspacePath[],
|
|
748
|
-
version: FileSystemVersion,
|
|
749
|
-
lastModified: string,
|
|
750
|
-
): void {
|
|
751
|
-
for (const path of paths) {
|
|
752
|
-
target.set(path, { kind: 'directory', version, lastModified });
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
private touchParents(
|
|
757
|
-
target: Map<WorkspacePath, MemoryEntry>,
|
|
758
|
-
path: WorkspacePath,
|
|
759
|
-
version: FileSystemVersion,
|
|
760
|
-
lastModified: string,
|
|
761
|
-
): void {
|
|
762
|
-
for (const parent of parentChain(path)) {
|
|
763
|
-
const entry = target.get(parent);
|
|
764
|
-
if (entry?.kind === 'directory') {
|
|
765
|
-
target.set(parent, { kind: 'directory', version, lastModified });
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
private withVersion(
|
|
771
|
-
entry: MemoryEntry,
|
|
772
|
-
version: FileSystemVersion,
|
|
773
|
-
lastModified: string,
|
|
774
|
-
): MemoryEntry {
|
|
775
|
-
return entry.kind === 'file'
|
|
776
|
-
? {
|
|
777
|
-
kind: 'file',
|
|
778
|
-
data: entry.data,
|
|
779
|
-
payloadKind: entry.payloadKind,
|
|
780
|
-
version,
|
|
781
|
-
lastModified,
|
|
782
|
-
}
|
|
783
|
-
: { kind: 'directory', version, lastModified };
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
private nextVersion(): FileSystemVersion {
|
|
787
|
-
this.mutationCounter += 1;
|
|
788
|
-
this.treeVersion = parseFileSystemVersion(`${this.id}:${this.mutationCounter}`);
|
|
789
|
-
return this.treeVersion;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
private toEntrySnapshot(path: WorkspacePath, entry: MemoryEntry): FileSystemEntrySnapshot {
|
|
793
|
-
return entry.kind === 'file'
|
|
794
|
-
? this.toFileSnapshot(path, entry)
|
|
795
|
-
: this.toDirectorySnapshot(path, entry);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
private toFileSnapshot(path: WorkspacePath, entry: MemoryFileEntry): FileSystemFileSnapshot {
|
|
799
|
-
return Object.freeze({
|
|
800
|
-
kind: 'file',
|
|
801
|
-
path,
|
|
802
|
-
name: workspacePathBasename(path),
|
|
803
|
-
size: entry.data.byteLength,
|
|
804
|
-
version: entry.version,
|
|
805
|
-
lastModified: entry.lastModified,
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
private toDirectorySnapshot(
|
|
810
|
-
path: WorkspacePath,
|
|
811
|
-
entry: MemoryDirectoryEntry,
|
|
812
|
-
): FileSystemDirectorySnapshot {
|
|
813
|
-
return Object.freeze({
|
|
814
|
-
kind: 'directory',
|
|
815
|
-
path,
|
|
816
|
-
name: workspacePathBasename(path),
|
|
817
|
-
size: null,
|
|
818
|
-
version: entry.version,
|
|
819
|
-
lastModified: entry.lastModified,
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
private emit(event: MemoryWatchEventInput): void {
|
|
824
|
-
const complete: FileSystemWatchEvent = Object.freeze({
|
|
825
|
-
...event,
|
|
826
|
-
sequence: ++this.eventSequence,
|
|
827
|
-
origin: 'local',
|
|
828
|
-
});
|
|
829
|
-
this.pendingEvents.push(complete);
|
|
830
|
-
if (this.emittingEvents) return;
|
|
831
|
-
|
|
832
|
-
this.emittingEvents = true;
|
|
833
|
-
try {
|
|
834
|
-
while (this.pendingEvents.length > 0) {
|
|
835
|
-
const pending = this.pendingEvents.shift();
|
|
836
|
-
if (!pending) continue;
|
|
837
|
-
for (const watcher of [...this.watchers]) {
|
|
838
|
-
try {
|
|
839
|
-
watcher.listener(pending);
|
|
840
|
-
} catch {
|
|
841
|
-
// A watcher is observational; it cannot roll back a completed mutation.
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
} finally {
|
|
846
|
-
this.emittingEvents = false;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
private error(
|
|
851
|
-
code: ConstructorParameters<typeof FsError>[0],
|
|
852
|
-
operation: FsOperation,
|
|
853
|
-
path: WorkspacePath,
|
|
854
|
-
message: string,
|
|
855
|
-
): FsError {
|
|
856
|
-
return new FsError(code, message, { operation, path });
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
function parentChain(path: WorkspacePath): WorkspacePath[] {
|
|
861
|
-
const parents: WorkspacePath[] = [];
|
|
862
|
-
let current = workspacePathDirname(path);
|
|
863
|
-
while (true) {
|
|
864
|
-
parents.unshift(current);
|
|
865
|
-
if (!current) return parents;
|
|
866
|
-
current = workspacePathDirname(current);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
function addParentPaths(target: Set<WorkspacePath>, path: WorkspacePath): void {
|
|
871
|
-
for (const parent of parentChain(path)) {
|
|
872
|
-
if (parent) target.add(parent);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
function isMemoryFilePayloadKind(value: unknown): value is MemoryFilePayloadKind {
|
|
877
|
-
return value === 'text' || value === 'binary';
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
function copyBytes(data: ArrayBuffer | Uint8Array): ArrayBuffer {
|
|
881
|
-
const source = ArrayBuffer.isView(data)
|
|
882
|
-
? new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
883
|
-
: new Uint8Array(data);
|
|
884
|
-
const copy = new Uint8Array(source.byteLength);
|
|
885
|
-
copy.set(source);
|
|
886
|
-
return copy.buffer as ArrayBuffer;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
function compareSnapshots(left: FileSystemEntrySnapshot, right: FileSystemEntrySnapshot): number {
|
|
890
|
-
if (left.kind !== right.kind) return left.kind === 'directory' ? -1 : 1;
|
|
891
|
-
return compareText(left.name, right.name);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
function compareText(left: string, right: string): number {
|
|
895
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
896
|
-
}
|