@theia/filesystem 1.65.0-next.6 → 1.65.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 (147) hide show
  1. package/lib/browser/download/file-download-command-contribution.d.ts +1 -1
  2. package/lib/browser/download/file-download-command-contribution.d.ts.map +1 -1
  3. package/lib/browser/download/file-download-command-contribution.js +3 -3
  4. package/lib/browser/download/file-download-command-contribution.js.map +1 -1
  5. package/lib/browser/download/file-download-frontend-module.d.ts.map +1 -1
  6. package/lib/browser/download/file-download-frontend-module.js +2 -1
  7. package/lib/browser/download/file-download-frontend-module.js.map +1 -1
  8. package/lib/browser/download/file-download-service.d.ts +2 -10
  9. package/lib/browser/download/file-download-service.d.ts.map +1 -1
  10. package/lib/browser/download/file-download-service.js +8 -7
  11. package/lib/browser/download/file-download-service.js.map +1 -1
  12. package/lib/browser/file-resource.js +1 -1
  13. package/lib/browser/file-resource.js.map +1 -1
  14. package/lib/browser/file-service.d.ts +1 -1
  15. package/lib/browser/file-service.d.ts.map +1 -1
  16. package/lib/browser/file-service.js +1 -1
  17. package/lib/browser/file-service.js.map +1 -1
  18. package/lib/browser/file-tree/file-tree-widget.d.ts +1 -1
  19. package/lib/browser/file-tree/file-tree-widget.d.ts.map +1 -1
  20. package/lib/browser/file-tree/file-tree-widget.js +3 -3
  21. package/lib/browser/file-tree/file-tree-widget.js.map +1 -1
  22. package/lib/browser/filesystem-frontend-contribution.d.ts +5 -4
  23. package/lib/browser/filesystem-frontend-contribution.d.ts.map +1 -1
  24. package/lib/browser/filesystem-frontend-contribution.js +5 -5
  25. package/lib/browser/filesystem-frontend-contribution.js.map +1 -1
  26. package/lib/browser/filesystem-frontend-module.d.ts.map +1 -1
  27. package/lib/browser/filesystem-frontend-module.js +4 -3
  28. package/lib/browser/filesystem-frontend-module.js.map +1 -1
  29. package/lib/browser/filesystem-saveable-service.d.ts.map +1 -1
  30. package/lib/browser/filesystem-saveable-service.js +5 -1
  31. package/lib/browser/filesystem-saveable-service.js.map +1 -1
  32. package/lib/browser/index.d.ts +0 -1
  33. package/lib/browser/index.d.ts.map +1 -1
  34. package/lib/browser/index.js +0 -1
  35. package/lib/browser/index.js.map +1 -1
  36. package/lib/browser/{file-upload-service.d.ts → upload/file-upload-service-impl.d.ts} +16 -52
  37. package/lib/browser/upload/file-upload-service-impl.d.ts.map +1 -0
  38. package/lib/browser/{file-upload-service.js → upload/file-upload-service-impl.js} +27 -27
  39. package/lib/browser/upload/file-upload-service-impl.js.map +1 -0
  40. package/lib/browser-only/browser-only-filesystem-frontend-module.d.ts.map +1 -1
  41. package/lib/browser-only/browser-only-filesystem-frontend-module.js +8 -0
  42. package/lib/browser-only/browser-only-filesystem-frontend-module.js.map +1 -1
  43. package/lib/browser-only/download/file-download-command-contribution.d.ts +15 -0
  44. package/lib/browser-only/download/file-download-command-contribution.d.ts.map +1 -0
  45. package/lib/browser-only/download/file-download-command-contribution.js +55 -0
  46. package/lib/browser-only/download/file-download-command-contribution.js.map +1 -0
  47. package/lib/browser-only/download/file-download-frontend-module.d.ts +4 -0
  48. package/lib/browser-only/download/file-download-frontend-module.d.ts.map +1 -0
  49. package/lib/browser-only/download/file-download-frontend-module.js +27 -0
  50. package/lib/browser-only/download/file-download-frontend-module.js.map +1 -0
  51. package/lib/browser-only/download/file-download-service.d.ts +86 -0
  52. package/lib/browser-only/download/file-download-service.d.ts.map +1 -0
  53. package/lib/browser-only/download/file-download-service.js +551 -0
  54. package/lib/browser-only/download/file-download-service.js.map +1 -0
  55. package/lib/browser-only/file-search.d.ts +38 -0
  56. package/lib/browser-only/file-search.d.ts.map +1 -0
  57. package/lib/browser-only/file-search.js +153 -0
  58. package/lib/browser-only/file-search.js.map +1 -0
  59. package/lib/browser-only/opfs-filesystem-initialization.d.ts +4 -2
  60. package/lib/browser-only/opfs-filesystem-initialization.d.ts.map +1 -1
  61. package/lib/browser-only/opfs-filesystem-initialization.js +4 -1
  62. package/lib/browser-only/opfs-filesystem-initialization.js.map +1 -1
  63. package/lib/browser-only/opfs-filesystem-provider.d.ts +89 -12
  64. package/lib/browser-only/opfs-filesystem-provider.d.ts.map +1 -1
  65. package/lib/browser-only/opfs-filesystem-provider.js +345 -181
  66. package/lib/browser-only/opfs-filesystem-provider.js.map +1 -1
  67. package/lib/browser-only/upload/file-upload-service-impl.d.ts +67 -0
  68. package/lib/browser-only/upload/file-upload-service-impl.d.ts.map +1 -0
  69. package/lib/browser-only/upload/file-upload-service-impl.js +328 -0
  70. package/lib/browser-only/upload/file-upload-service-impl.js.map +1 -0
  71. package/lib/common/download/file-download.d.ts +17 -0
  72. package/lib/common/download/file-download.d.ts.map +1 -0
  73. package/lib/common/download/{file-download-data.js → file-download.js} +3 -2
  74. package/lib/common/download/file-download.js.map +1 -0
  75. package/lib/common/files.d.ts +8 -1
  76. package/lib/common/files.d.ts.map +1 -1
  77. package/lib/common/files.js +35 -1
  78. package/lib/common/files.js.map +1 -1
  79. package/lib/{browser → common}/filesystem-preferences.d.ts +3 -1
  80. package/lib/common/filesystem-preferences.d.ts.map +1 -0
  81. package/lib/{browser → common}/filesystem-preferences.js +17 -11
  82. package/lib/common/filesystem-preferences.js.map +1 -0
  83. package/lib/common/index.d.ts +1 -0
  84. package/lib/common/index.d.ts.map +1 -1
  85. package/lib/common/index.js +1 -0
  86. package/lib/common/index.js.map +1 -1
  87. package/lib/common/io.js +7 -1
  88. package/lib/common/io.js.map +1 -1
  89. package/lib/common/upload/file-upload.d.ts +45 -0
  90. package/lib/common/upload/file-upload.d.ts.map +1 -0
  91. package/{src/common/download/file-download-data.ts → lib/common/upload/file-upload.js} +6 -13
  92. package/lib/common/upload/file-upload.js.map +1 -0
  93. package/lib/node/disk-file-system-provider.d.ts.map +1 -1
  94. package/lib/node/disk-file-system-provider.js +2 -4
  95. package/lib/node/disk-file-system-provider.js.map +1 -1
  96. package/lib/node/download/file-download-handler.js +2 -2
  97. package/lib/node/download/file-download-handler.js.map +1 -1
  98. package/lib/node/filesystem-backend-module.d.ts.map +1 -1
  99. package/lib/node/filesystem-backend-module.js +3 -1
  100. package/lib/node/filesystem-backend-module.js.map +1 -1
  101. package/lib/node/parcel-watcher/parcel-filesystem-service.d.ts +2 -2
  102. package/lib/node/parcel-watcher/parcel-filesystem-service.d.ts.map +1 -1
  103. package/lib/node/parcel-watcher/parcel-filesystem-service.js.map +1 -1
  104. package/lib/node/upload/node-file-upload-service.d.ts.map +1 -0
  105. package/lib/node/{node-file-upload-service.js → upload/node-file-upload-service.js} +1 -1
  106. package/lib/node/upload/node-file-upload-service.js.map +1 -0
  107. package/package.json +11 -5
  108. package/src/browser/download/file-download-command-contribution.ts +1 -1
  109. package/src/browser/download/file-download-frontend-module.ts +3 -2
  110. package/src/browser/download/file-download-service.ts +7 -12
  111. package/src/browser/file-resource.ts +1 -1
  112. package/src/browser/file-service.ts +1 -1
  113. package/src/browser/file-tree/file-tree-widget.tsx +1 -1
  114. package/src/browser/filesystem-frontend-contribution.ts +4 -5
  115. package/src/browser/filesystem-frontend-module.ts +4 -3
  116. package/src/browser/filesystem-saveable-service.ts +5 -1
  117. package/src/browser/index.ts +0 -1
  118. package/src/browser/{file-upload-service.ts → upload/file-upload-service-impl.ts} +31 -72
  119. package/src/browser-only/browser-only-filesystem-frontend-module.ts +10 -0
  120. package/src/browser-only/download/file-download-command-contribution.ts +56 -0
  121. package/src/browser-only/download/file-download-frontend-module.ts +26 -0
  122. package/src/browser-only/download/file-download-service.ts +726 -0
  123. package/src/browser-only/file-search.ts +170 -0
  124. package/src/browser-only/opfs-filesystem-initialization.ts +7 -4
  125. package/src/browser-only/opfs-filesystem-provider.ts +402 -189
  126. package/src/browser-only/upload/file-upload-service-impl.ts +408 -0
  127. package/src/common/download/file-download.ts +40 -0
  128. package/src/common/files.ts +42 -1
  129. package/src/{browser → common}/filesystem-preferences.ts +14 -14
  130. package/src/common/index.ts +1 -0
  131. package/src/common/io.ts +6 -1
  132. package/src/common/upload/file-upload.ts +65 -0
  133. package/src/node/disk-file-system-provider.ts +3 -4
  134. package/src/node/download/file-download-handler.ts +1 -1
  135. package/src/node/filesystem-backend-module.ts +3 -1
  136. package/src/node/parcel-watcher/parcel-filesystem-service.ts +2 -2
  137. package/src/node/{node-file-upload-service.ts → upload/node-file-upload-service.ts} +1 -1
  138. package/lib/browser/file-upload-service.d.ts.map +0 -1
  139. package/lib/browser/file-upload-service.js.map +0 -1
  140. package/lib/browser/filesystem-preferences.d.ts.map +0 -1
  141. package/lib/browser/filesystem-preferences.js.map +0 -1
  142. package/lib/common/download/file-download-data.d.ts +0 -7
  143. package/lib/common/download/file-download-data.d.ts.map +0 -1
  144. package/lib/common/download/file-download-data.js.map +0 -1
  145. package/lib/node/node-file-upload-service.d.ts.map +0 -1
  146. package/lib/node/node-file-upload-service.js.map +0 -1
  147. /package/lib/node/{node-file-upload-service.d.ts → upload/node-file-upload-service.d.ts} +0 -0
@@ -20,18 +20,42 @@ const tslib_1 = require("tslib");
20
20
  const inversify_1 = require("@theia/core/shared/inversify");
21
21
  const files_1 = require("../common/files");
22
22
  const core_1 = require("@theia/core");
23
+ const encoding_service_1 = require("@theia/core/lib/common/encoding-service");
24
+ const buffer_1 = require("@theia/core/lib/common/buffer");
25
+ const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
26
+ const opfs_worker_1 = require("opfs-worker");
23
27
  const opfs_filesystem_initialization_1 = require("./opfs-filesystem-initialization");
28
+ const stream_1 = require("@theia/core/lib/common/stream");
29
+ const io_1 = require("../common/io");
30
+ const file_uri_1 = require("@theia/core/lib/common/file-uri");
24
31
  let OPFSFileSystemProvider = class OPFSFileSystemProvider {
25
32
  constructor() {
26
- this.capabilities = 2 /* FileSystemProviderCapabilities.FileReadWrite */;
33
+ this.BUFFER_SIZE = 64 * 1024;
34
+ this.capabilities = 2 /* FileSystemProviderCapabilities.FileReadWrite */ |
35
+ 4 /* FileSystemProviderCapabilities.FileOpenReadWriteClose */ |
36
+ 8 /* FileSystemProviderCapabilities.FileFolderCopy */ |
37
+ 33554432 /* FileSystemProviderCapabilities.Update */;
27
38
  this.onDidChangeCapabilities = core_1.Event.None;
28
39
  this.onDidChangeFileEmitter = new core_1.Emitter();
29
40
  this.onDidChangeFile = this.onDidChangeFileEmitter.event;
30
41
  this.onFileWatchError = core_1.Event.None;
42
+ this.toDispose = new core_1.DisposableCollection(this.onDidChangeFileEmitter);
31
43
  }
44
+ /**
45
+ * Initializes the OPFS file system provider
46
+ */
32
47
  init() {
33
48
  const setup = async () => {
34
- this.directoryHandle = await this.initialization.getRootDirectory();
49
+ const root = await this.initialization.getRootDirectory();
50
+ const broadcastChannel = this.initialization.getBroadcastChannel();
51
+ // Set up file change listening via BroadcastChannel
52
+ broadcastChannel.onmessage = this.handleFileSystemChange.bind(this);
53
+ // Initialize the file system
54
+ this.fs = new opfs_worker_1.OPFSFileSystem({
55
+ root,
56
+ broadcastChannel,
57
+ hashAlgorithm: false,
58
+ });
35
59
  await this.initialization.initializeFS(new Proxy(this, {
36
60
  get(target, prop, receiver) {
37
61
  if (prop === 'initialized') {
@@ -44,179 +68,360 @@ let OPFSFileSystemProvider = class OPFSFileSystemProvider {
44
68
  };
45
69
  this.initialized = setup();
46
70
  }
47
- watch(_resource, _opts) {
48
- return core_1.Disposable.NULL;
71
+ /**
72
+ * Watches a resource for file system changes
73
+ */
74
+ watch(resource, opts) {
75
+ if (!resource || !resource.path) {
76
+ return core_1.Disposable.NULL;
77
+ }
78
+ const unwatch = this.fs.watch(formatPath(resource), {
79
+ recursive: opts.recursive,
80
+ exclude: opts.excludes,
81
+ });
82
+ return core_1.Disposable.create(unwatch);
83
+ }
84
+ /**
85
+ * Creates an index from the map of entries
86
+ */
87
+ async createIndex(entries) {
88
+ const arrayEntries = [];
89
+ for (const [uri, content] of entries) {
90
+ arrayEntries.push([formatPath(uri), content]);
91
+ }
92
+ await this.fs.createIndex(arrayEntries);
49
93
  }
50
- async exists(resource) {
94
+ /**
95
+ * Retrieves the current file system index
96
+ */
97
+ async index() {
98
+ const opfsIndex = await this.fs.index();
99
+ const index = new Map();
100
+ for (const [path, stats] of opfsIndex.entries()) {
101
+ const uri = new core_1.URI(path);
102
+ index.set(uri, formatStat(stats));
103
+ }
104
+ return index;
105
+ }
106
+ /**
107
+ * Clears the file system
108
+ */
109
+ async clear() {
51
110
  try {
52
- await this.initialized;
53
- await this.toFileSystemHandle(resource);
54
- return true;
111
+ await this.fs.clear();
55
112
  }
56
113
  catch (error) {
114
+ throw toFileSystemProviderError(error);
115
+ }
116
+ }
117
+ /**
118
+ * Checks if a resource exists
119
+ */
120
+ async exists(resource) {
121
+ if (!resource || !resource.path) {
57
122
  return false;
58
123
  }
124
+ await this.initialized;
125
+ try {
126
+ return await this.fs.exists(formatPath(resource));
127
+ }
128
+ catch (error) {
129
+ throw toFileSystemProviderError(error);
130
+ }
59
131
  }
132
+ /**
133
+ * Gets file system statistics for a resource
134
+ */
60
135
  async stat(resource) {
136
+ if (!resource || !resource.path) {
137
+ throw (0, files_1.createFileSystemProviderError)('Invalid resource URI', files_1.FileSystemProviderErrorCode.FileNotFound);
138
+ }
139
+ await this.initialized;
61
140
  try {
62
- await this.initialized;
63
- const handle = await this.toFileSystemHandle(resource);
64
- if (handle.kind === 'file') {
65
- const fileHandle = handle;
66
- const file = await fileHandle.getFile();
67
- return {
68
- type: files_1.FileType.File,
69
- ctime: file.lastModified,
70
- mtime: file.lastModified,
71
- size: file.size
72
- };
73
- }
74
- else if (handle.kind === 'directory') {
75
- return {
76
- type: files_1.FileType.Directory,
77
- ctime: 0,
78
- mtime: 0,
79
- size: 0
80
- };
81
- }
82
- throw (0, files_1.createFileSystemProviderError)('Unknown file handle error', files_1.FileSystemProviderErrorCode.Unknown);
141
+ const path = formatPath(resource);
142
+ const stats = await this.fs.stat(path);
143
+ return formatStat(stats);
83
144
  }
84
145
  catch (error) {
85
146
  throw toFileSystemProviderError(error);
86
147
  }
87
148
  }
149
+ /**
150
+ * Creates a directory
151
+ */
88
152
  async mkdir(resource) {
153
+ if (!resource || !resource.path) {
154
+ throw (0, files_1.createFileSystemProviderError)('Invalid resource URI', files_1.FileSystemProviderErrorCode.FileNotFound);
155
+ }
89
156
  await this.initialized;
90
157
  try {
91
- await this.toFileSystemHandle(resource, { create: true, isDirectory: true });
158
+ const path = formatPath(resource);
159
+ await this.fs.mkdir(path, { recursive: true });
92
160
  this.onDidChangeFileEmitter.fire([{ resource, type: 1 /* FileChangeType.ADDED */ }]);
93
161
  }
94
162
  catch (error) {
95
- throw toFileSystemProviderError(error, true);
163
+ throw toFileSystemProviderError(error);
96
164
  }
97
165
  }
166
+ /**
167
+ * Reads directory contents
168
+ */
98
169
  async readdir(resource) {
170
+ if (!resource || !resource.path) {
171
+ throw (0, files_1.createFileSystemProviderError)('Invalid resource URI', files_1.FileSystemProviderErrorCode.FileNotFound);
172
+ }
99
173
  await this.initialized;
100
174
  try {
101
- // Get the directory handle from the directoryHandle
102
- const directoryHandle = await this.toFileSystemHandle(resource, { create: false, isDirectory: true });
103
- const result = [];
104
- // Iterate through the entries in the directory (files and subdirectories)
105
- for await (const [name, handle] of directoryHandle.entries()) {
106
- // Determine the type of the entry (file or directory)
107
- if (handle.kind === 'file') {
108
- result.push([name, files_1.FileType.File]);
109
- }
110
- else if (handle.kind === 'directory') {
111
- result.push([name, files_1.FileType.Directory]);
112
- }
113
- }
114
- return result;
175
+ const path = formatPath(resource);
176
+ const entries = await this.fs.readDir(path);
177
+ return entries.map(entry => [
178
+ entry.name,
179
+ entry.isFile ? files_1.FileType.File : files_1.FileType.Directory
180
+ ]);
115
181
  }
116
182
  catch (error) {
117
- throw toFileSystemProviderError(error, true);
183
+ throw toFileSystemProviderError(error);
118
184
  }
119
185
  }
120
- async delete(resource, _opts) {
186
+ /**
187
+ * Deletes a resource
188
+ */
189
+ async delete(resource, opts) {
190
+ if (!resource || !resource.path) {
191
+ throw (0, files_1.createFileSystemProviderError)('Invalid resource URI', files_1.FileSystemProviderErrorCode.FileNotFound);
192
+ }
121
193
  await this.initialized;
122
194
  try {
123
- const parentURI = resource.parent;
124
- const parentHandle = await this.toFileSystemHandle(parentURI, { create: false, isDirectory: true });
125
- if (parentHandle.kind !== 'directory') {
126
- throw (0, files_1.createFileSystemProviderError)(new Error('Parent is not a directory'), files_1.FileSystemProviderErrorCode.FileNotADirectory);
127
- }
128
- const name = resource.path.base;
129
- return parentHandle.removeEntry(name, { recursive: _opts.recursive });
195
+ const path = formatPath(resource);
196
+ await this.fs.remove(path, { recursive: opts.recursive });
130
197
  }
131
198
  catch (error) {
132
199
  throw toFileSystemProviderError(error);
133
200
  }
134
- finally {
135
- this.onDidChangeFileEmitter.fire([{ resource, type: 2 /* FileChangeType.DELETED */ }]);
136
- }
137
201
  }
202
+ /**
203
+ * Renames a resource from one location to another
204
+ */
138
205
  async rename(from, to, opts) {
206
+ if (!from || !from.path || !to || !to.path) {
207
+ throw (0, files_1.createFileSystemProviderError)('Invalid source or destination URI', files_1.FileSystemProviderErrorCode.FileNotFound);
208
+ }
139
209
  await this.initialized;
140
210
  try {
141
- const fromHandle = await this.toFileSystemHandle(from);
142
- // Check whether the source is a file or directory
143
- if (fromHandle.kind === 'directory') {
144
- // Create the new directory and get the handle
145
- await this.mkdir(to);
146
- const toHandle = await this.toFileSystemHandle(to);
147
- await copyDirectoryContents(fromHandle, toHandle);
148
- // Delete the old directory
149
- await this.delete(from, { recursive: true, useTrash: false });
150
- }
151
- else {
152
- const content = await this.readFile(from);
153
- await this.writeFile(to, content, { create: true, overwrite: opts.overwrite });
154
- await this.delete(from, { recursive: true, useTrash: false });
155
- }
156
- this.onDidChangeFileEmitter.fire([{ resource: to, type: 1 /* FileChangeType.ADDED */ }]);
211
+ const fromPath = formatPath(from);
212
+ const toPath = formatPath(to);
213
+ await this.fs.rename(fromPath, toPath, {
214
+ overwrite: opts.overwrite,
215
+ });
157
216
  }
158
217
  catch (error) {
159
218
  throw toFileSystemProviderError(error);
160
219
  }
161
220
  }
221
+ /**
222
+ * Copies a resource from one location to another
223
+ */
224
+ async copy(from, to, opts) {
225
+ if (!from || !from.path || !to || !to.path) {
226
+ throw (0, files_1.createFileSystemProviderError)('Invalid source or destination URI', files_1.FileSystemProviderErrorCode.FileNotFound);
227
+ }
228
+ await this.initialized;
229
+ try {
230
+ const fromPath = formatPath(from);
231
+ const toPath = formatPath(to);
232
+ await this.fs.copy(fromPath, toPath, {
233
+ overwrite: opts.overwrite,
234
+ recursive: true,
235
+ });
236
+ }
237
+ catch (error) {
238
+ throw toFileSystemProviderError(error);
239
+ }
240
+ }
241
+ /**
242
+ * Reads file content as binary data
243
+ */
162
244
  async readFile(resource) {
245
+ if (!resource || !resource.path) {
246
+ throw (0, files_1.createFileSystemProviderError)('Invalid resource URI', files_1.FileSystemProviderErrorCode.FileNotFound);
247
+ }
163
248
  await this.initialized;
164
249
  try {
165
- // Get the file handle from the directoryHandle
166
- const fileHandle = await this.toFileSystemHandle(resource, { create: false, isDirectory: false });
167
- // Get the file itself (which includes the content)
168
- const file = await fileHandle.getFile();
169
- // Read the file as an ArrayBuffer and convert it to Uint8Array
170
- const arrayBuffer = await file.arrayBuffer();
171
- return new Uint8Array(arrayBuffer);
250
+ return await this.fs.readFile(formatPath(resource), 'binary');
172
251
  }
173
252
  catch (error) {
174
- throw toFileSystemProviderError(error, false);
253
+ throw toFileSystemProviderError(error);
175
254
  }
176
255
  }
256
+ /**
257
+ * Reads file content as a stream
258
+ */
259
+ readFileStream(resource, opts, token) {
260
+ const stream = (0, stream_1.newWriteableStream)(chunks => buffer_1.BinaryBuffer.concat(chunks.map(chunk => buffer_1.BinaryBuffer.wrap(chunk))).buffer);
261
+ (0, io_1.readFileIntoStream)(this, resource, stream, data => data.buffer, {
262
+ ...opts,
263
+ bufferSize: this.BUFFER_SIZE
264
+ }, token);
265
+ return stream;
266
+ }
267
+ /**
268
+ * Writes binary content to a file
269
+ */
177
270
  async writeFile(resource, content, opts) {
178
271
  await this.initialized;
179
- let writeableHandle = undefined;
272
+ let handle = undefined;
273
+ if (!resource || !resource.path) {
274
+ throw (0, files_1.createFileSystemProviderError)('Invalid resource URI', files_1.FileSystemProviderErrorCode.FileNotFound);
275
+ }
276
+ if (!content || !(content instanceof Uint8Array)) {
277
+ throw (0, files_1.createFileSystemProviderError)('Invalid content: must be Uint8Array', files_1.FileSystemProviderErrorCode.Unknown);
278
+ }
180
279
  try {
181
- // Validate target unless { create: true, overwrite: true }
280
+ const path = formatPath(resource);
182
281
  if (!opts.create || !opts.overwrite) {
183
- const fileExists = await this.stat(resource).then(() => true, () => false);
282
+ const fileExists = await this.fs.exists(path);
184
283
  if (fileExists) {
185
284
  if (!opts.overwrite) {
186
285
  throw (0, files_1.createFileSystemProviderError)('File already exists', files_1.FileSystemProviderErrorCode.FileExists);
187
286
  }
188
287
  }
189
- else {
190
- if (!opts.create) {
191
- throw (0, files_1.createFileSystemProviderError)('File does not exist', files_1.FileSystemProviderErrorCode.FileNotFound);
192
- }
288
+ else if (!opts.create) {
289
+ throw (0, files_1.createFileSystemProviderError)('File does not exist', files_1.FileSystemProviderErrorCode.FileNotFound);
193
290
  }
194
291
  }
195
- const handle = await this.toFileSystemHandle(resource, { create: true, isDirectory: false });
196
292
  // Open
197
- writeableHandle = await (handle === null || handle === void 0 ? void 0 : handle.createWritable());
293
+ handle = await this.open(resource, { create: true });
198
294
  // Write content at once
199
- await (writeableHandle === null || writeableHandle === void 0 ? void 0 : writeableHandle.write(content));
200
- this.onDidChangeFileEmitter.fire([{ resource: resource, type: 0 /* FileChangeType.UPDATED */ }]);
295
+ await this.write(handle, 0, content, 0, content.byteLength);
201
296
  }
202
297
  catch (error) {
203
- throw toFileSystemProviderError(error, false);
298
+ throw toFileSystemProviderError(error);
204
299
  }
205
300
  finally {
206
- if (typeof writeableHandle !== 'undefined') {
207
- await writeableHandle.close();
301
+ if (typeof handle === 'number') {
302
+ await this.close(handle);
303
+ }
304
+ }
305
+ }
306
+ // #region Open/Read/Write/Close Operations
307
+ /**
308
+ * Opens a file and returns a file descriptor
309
+ */
310
+ async open(resource, opts) {
311
+ await this.initialized;
312
+ if (!resource || !resource.path) {
313
+ throw (0, files_1.createFileSystemProviderError)('Invalid resource URI', files_1.FileSystemProviderErrorCode.FileNotFound);
314
+ }
315
+ try {
316
+ const path = formatPath(resource);
317
+ const fileExists = await this.fs.exists(path);
318
+ if (!opts.create && !fileExists) {
319
+ throw (0, files_1.createFileSystemProviderError)('File does not exist', files_1.FileSystemProviderErrorCode.FileNotFound);
208
320
  }
321
+ const fd = await this.fs.open(path, {
322
+ create: opts.create,
323
+ truncate: opts.create
324
+ });
325
+ return fd;
326
+ }
327
+ catch (error) {
328
+ throw toFileSystemProviderError(error);
209
329
  }
210
330
  }
211
331
  /**
212
- * Returns the FileSystemHandle for the given resource given by a URI.
213
- * @param resource URI/path of the resource
214
- * @param options Options for the creation of the handle while traversing the path
215
- * @returns FileSystemHandle for the given resource
332
+ * Closes a file descriptor
216
333
  */
217
- async toFileSystemHandle(resource, options) {
218
- const pathParts = resource.path.toString().split(core_1.Path.separator).filter(Boolean);
219
- return recursiveFileSystemHandle(this.directoryHandle, pathParts, options);
334
+ async close(fd) {
335
+ try {
336
+ await this.fs.close(fd);
337
+ }
338
+ catch (error) {
339
+ throw toFileSystemProviderError(error);
340
+ }
341
+ }
342
+ /**
343
+ * Reads data from a file descriptor
344
+ */
345
+ async read(fd, pos, data, offset, length) {
346
+ try {
347
+ const result = await this.fs.read(fd, data, offset, length, pos);
348
+ return result.bytesRead;
349
+ }
350
+ catch (error) {
351
+ throw toFileSystemProviderError(error);
352
+ }
353
+ }
354
+ /**
355
+ * Writes data to a file descriptor
356
+ */
357
+ async write(fd, pos, data, offset, length) {
358
+ try {
359
+ return await this.fs.write(fd, data, offset, length, pos, true);
360
+ }
361
+ catch (error) {
362
+ throw toFileSystemProviderError(error);
363
+ }
364
+ }
365
+ // #endregion
366
+ // #region Text File Updates
367
+ /**
368
+ * Updates a text file with content changes
369
+ */
370
+ async updateFile(resource, changes, opts) {
371
+ try {
372
+ const content = await this.readFile(resource);
373
+ const decoded = this.encodingService.decode(buffer_1.BinaryBuffer.wrap(content), opts.readEncoding);
374
+ const newContent = vscode_languageserver_textdocument_1.TextDocument.update(vscode_languageserver_textdocument_1.TextDocument.create('', '', 1, decoded), changes, 2).getText();
375
+ const encoding = await this.encodingService.toResourceEncoding(opts.writeEncoding, {
376
+ overwriteEncoding: opts.overwriteEncoding,
377
+ read: async (length) => {
378
+ const fd = await this.open(resource, { create: false });
379
+ try {
380
+ const data = new Uint8Array(length);
381
+ await this.read(fd, 0, data, 0, length);
382
+ return data;
383
+ }
384
+ finally {
385
+ await this.close(fd);
386
+ }
387
+ }
388
+ });
389
+ const encoded = this.encodingService.encode(newContent, encoding);
390
+ await this.writeFile(resource, encoded.buffer, { create: false, overwrite: true });
391
+ const stat = await this.stat(resource);
392
+ return Object.assign(stat, { encoding: encoding.encoding });
393
+ }
394
+ catch (error) {
395
+ throw toFileSystemProviderError(error);
396
+ }
397
+ }
398
+ // #endregion
399
+ /**
400
+ * Handles file system change events from BroadcastChannel
401
+ */
402
+ async handleFileSystemChange(event) {
403
+ var _a;
404
+ if (!((_a = event.data) === null || _a === void 0 ? void 0 : _a.path)) {
405
+ return;
406
+ }
407
+ const resource = new core_1.URI('file://' + event.data.path);
408
+ let changeType;
409
+ if (event.data.type === opfs_worker_1.WatchEventType.Added) {
410
+ changeType = 1 /* FileChangeType.ADDED */;
411
+ }
412
+ else if (event.data.type === opfs_worker_1.WatchEventType.Removed) {
413
+ changeType = 2 /* FileChangeType.DELETED */;
414
+ }
415
+ else {
416
+ changeType = 0 /* FileChangeType.UPDATED */;
417
+ }
418
+ this.onDidChangeFileEmitter.fire([{ resource, type: changeType }]);
419
+ }
420
+ /**
421
+ * Disposes the file system provider
422
+ */
423
+ dispose() {
424
+ this.toDispose.dispose();
220
425
  }
221
426
  };
222
427
  exports.OPFSFileSystemProvider = OPFSFileSystemProvider;
@@ -224,6 +429,10 @@ tslib_1.__decorate([
224
429
  (0, inversify_1.inject)(opfs_filesystem_initialization_1.OPFSInitialization),
225
430
  tslib_1.__metadata("design:type", Object)
226
431
  ], OPFSFileSystemProvider.prototype, "initialization", void 0);
432
+ tslib_1.__decorate([
433
+ (0, inversify_1.inject)(encoding_service_1.EncodingService),
434
+ tslib_1.__metadata("design:type", encoding_service_1.EncodingService)
435
+ ], OPFSFileSystemProvider.prototype, "encodingService", void 0);
227
436
  tslib_1.__decorate([
228
437
  (0, inversify_1.postConstruct)(),
229
438
  tslib_1.__metadata("design:type", Function),
@@ -233,91 +442,46 @@ tslib_1.__decorate([
233
442
  exports.OPFSFileSystemProvider = OPFSFileSystemProvider = tslib_1.__decorate([
234
443
  (0, inversify_1.injectable)()
235
444
  ], OPFSFileSystemProvider);
236
- // #region Helper functions
237
- async function recursiveFileSystemHandle(handle, pathParts, options) {
238
- // We reached the end of the path, this happens only when not creating
239
- if (pathParts.length === 0) {
240
- return handle;
241
- }
242
- // If there are parts left, the handle must be a directory
243
- if (handle.kind !== 'directory') {
244
- throw (0, files_1.createFileSystemProviderError)('Not a directory', files_1.FileSystemProviderErrorCode.FileNotADirectory);
245
- }
246
- const dirHandle = handle;
247
- // We need to create it and thus we need to stop early to create the file or directory
248
- if (pathParts.length === 1 && (options === null || options === void 0 ? void 0 : options.create)) {
249
- if (options === null || options === void 0 ? void 0 : options.isDirectory) {
250
- return dirHandle.getDirectoryHandle(pathParts[0], { create: options.create });
251
- }
252
- else {
253
- return dirHandle.getFileHandle(pathParts[0], { create: options.create });
254
- }
255
- }
256
- // Continue to resolve the path
257
- const part = pathParts.shift();
258
- for await (const entry of dirHandle.entries()) {
259
- // Check the entry name in the current directory
260
- if (entry[0] === part) {
261
- return recursiveFileSystemHandle(entry[1], pathParts, options);
262
- }
263
- }
264
- // If we haven't found the part, we need to create it along the way
265
- if (options === null || options === void 0 ? void 0 : options.create) {
266
- const newHandle = await dirHandle.getDirectoryHandle(part, { create: true });
267
- return recursiveFileSystemHandle(newHandle, pathParts, options);
268
- }
269
- throw (0, files_1.createFileSystemProviderError)('File not found', files_1.FileSystemProviderErrorCode.FileNotFound);
445
+ /**
446
+ * Formats a URI or string resource to a file system path
447
+ */
448
+ function formatPath(resource) {
449
+ return file_uri_1.FileUri.fsPath(resource);
270
450
  }
271
- // Function to copy directory contents recursively
272
- async function copyDirectoryContents(sourceHandle, destinationHandle) {
273
- for await (const [name, handle] of sourceHandle.entries()) {
274
- if (handle.kind === 'file') {
275
- const file = await handle.getFile();
276
- const newFileHandle = await destinationHandle.getFileHandle(name, { create: true });
277
- const writable = await newFileHandle.createWritable();
278
- try {
279
- await writable.write(await file.arrayBuffer());
280
- }
281
- finally {
282
- await writable.close();
283
- }
284
- }
285
- else if (handle.kind === 'directory') {
286
- const newSubDirHandle = await destinationHandle.getDirectoryHandle(name, { create: true });
287
- await copyDirectoryContents(handle, newSubDirHandle);
288
- }
289
- }
451
+ /**
452
+ * Creates a Stat object from OPFS stats
453
+ */
454
+ function formatStat(stats) {
455
+ return {
456
+ type: stats.isDirectory ? files_1.FileType.Directory : files_1.FileType.File,
457
+ ctime: new Date(stats.ctime).getTime(),
458
+ mtime: new Date(stats.mtime).getTime(),
459
+ size: stats.size
460
+ };
290
461
  }
291
- function toFileSystemProviderError(error, is_dir) {
462
+ /**
463
+ * Converts OPFS errors to file system provider errors
464
+ */
465
+ function toFileSystemProviderError(error) {
292
466
  if (error instanceof files_1.FileSystemProviderError) {
293
- return error; // avoid double conversion
467
+ return error;
294
468
  }
295
469
  let code;
296
- switch (error.name) {
297
- case 'NotFoundError':
298
- code = files_1.FileSystemProviderErrorCode.FileNotFound;
299
- break;
300
- case 'InvalidModificationError':
301
- code = files_1.FileSystemProviderErrorCode.FileExists;
302
- break;
303
- case 'NotAllowedError':
304
- code = files_1.FileSystemProviderErrorCode.NoPermissions;
305
- break;
306
- case 'TypeMismatchError':
307
- if (!is_dir) {
308
- code = files_1.FileSystemProviderErrorCode.FileIsADirectory;
309
- }
310
- else {
311
- code = files_1.FileSystemProviderErrorCode.FileNotADirectory;
312
- }
313
- break;
314
- case 'QuotaExceededError':
315
- code = files_1.FileSystemProviderErrorCode.FileTooLarge;
316
- break;
317
- default:
318
- code = files_1.FileSystemProviderErrorCode.Unknown;
470
+ if (error.name === 'NotFoundError' || error.name === 'ENOENT') {
471
+ code = files_1.FileSystemProviderErrorCode.FileNotFound;
472
+ }
473
+ else if (error.name === 'NotAllowedError' || error.name === 'SecurityError' || error.name === 'EACCES') {
474
+ code = files_1.FileSystemProviderErrorCode.NoPermissions;
475
+ }
476
+ else if (error.name === 'QuotaExceededError' || error.name === 'ENOSPC') {
477
+ code = files_1.FileSystemProviderErrorCode.FileTooLarge;
478
+ }
479
+ else if (error.name === 'PathError' || error.name === 'INVALID_PATH') {
480
+ code = files_1.FileSystemProviderErrorCode.FileNotADirectory;
481
+ }
482
+ else {
483
+ code = files_1.FileSystemProviderErrorCode.Unknown;
319
484
  }
320
485
  return (0, files_1.createFileSystemProviderError)(error, code);
321
486
  }
322
- // #endregion
323
487
  //# sourceMappingURL=opfs-filesystem-provider.js.map