@filen/sync 0.1.1 → 0.1.3

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 (43) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +1 -1
  3. package/SECURITY.md +17 -17
  4. package/dist/constants.d.ts +7 -0
  5. package/dist/constants.js +51 -1
  6. package/dist/constants.js.map +1 -1
  7. package/dist/ignorer.d.ts +14 -0
  8. package/dist/ignorer.js +69 -0
  9. package/dist/ignorer.js.map +1 -0
  10. package/dist/index.d.ts +27 -6
  11. package/dist/index.js +113 -13
  12. package/dist/index.js.map +1 -1
  13. package/dist/lib/deltas.d.ts +9 -31
  14. package/dist/lib/deltas.js +59 -65
  15. package/dist/lib/deltas.js.map +1 -1
  16. package/dist/lib/filesystems/local.d.ts +25 -20
  17. package/dist/lib/filesystems/local.js +203 -55
  18. package/dist/lib/filesystems/local.js.map +1 -1
  19. package/dist/lib/filesystems/remote.d.ts +18 -7
  20. package/dist/lib/filesystems/remote.js +435 -61
  21. package/dist/lib/filesystems/remote.js.map +1 -1
  22. package/dist/lib/ipc.d.ts +9 -0
  23. package/dist/lib/ipc.js +56 -0
  24. package/dist/lib/ipc.js.map +1 -0
  25. package/dist/lib/logger.d.ts +14 -0
  26. package/dist/lib/logger.js +93 -0
  27. package/dist/lib/logger.js.map +1 -0
  28. package/dist/lib/state.d.ts +4 -15
  29. package/dist/lib/state.js +16 -29
  30. package/dist/lib/state.js.map +1 -1
  31. package/dist/lib/sync.d.ts +23 -9
  32. package/dist/lib/sync.js +242 -28
  33. package/dist/lib/sync.js.map +1 -1
  34. package/dist/lib/tasks.d.ts +18 -33
  35. package/dist/lib/tasks.js +52 -29
  36. package/dist/lib/tasks.js.map +1 -1
  37. package/dist/types.d.ts +175 -0
  38. package/dist/utils.d.ts +24 -0
  39. package/dist/utils.js +132 -1
  40. package/dist/utils.js.map +1 -1
  41. package/package.json +60 -58
  42. package/tsconfig.json +24 -24
  43. package/index.d.ts +0 -298
@@ -4,43 +4,278 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.RemoteFileSystem = void 0;
7
+ const sdk_1 = require("@filen/sdk");
7
8
  const path_1 = __importDefault(require("path"));
8
9
  const semaphore_1 = require("../../semaphore");
9
10
  const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const ipc_1 = require("../ipc");
12
+ const utils_1 = require("../../utils");
13
+ const uuid_1 = require("uuid");
14
+ const constants_1 = require("../../constants");
10
15
  class RemoteFileSystem {
11
- constructor({ sync }) {
16
+ constructor(sync) {
12
17
  this.getDirectoryTreeCache = {
13
18
  timestamp: 0,
14
19
  tree: {},
15
20
  uuids: {}
16
21
  };
22
+ this.previousTreeRawResponse = "";
17
23
  this.mutex = new semaphore_1.Semaphore(1);
18
24
  this.mkdirMutex = new semaphore_1.Semaphore(1);
25
+ this.itemsMutex = new semaphore_1.Semaphore(1);
26
+ this.deviceIdCache = "";
19
27
  this.sync = sync;
20
28
  }
21
- async getDirectoryTree() {
22
- // TODO: Actual implementation using cache + different endpoint with deviceId
23
- // Account for duplicate path names for directories/files. Only keep the first one discovered in the tree.
29
+ async getDeviceId() {
30
+ if (this.deviceIdCache.length > 0) {
31
+ return this.deviceIdCache;
32
+ }
33
+ const deviceIdFile = path_1.default.join(this.sync.dbPath, this.sync.syncPair.uuid, "deviceId");
34
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(deviceIdFile));
35
+ let deviceId = "";
36
+ if (!(await fs_extra_1.default.exists(deviceIdFile))) {
37
+ deviceId = (0, uuid_1.v4)();
38
+ await fs_extra_1.default.writeFile(deviceIdFile, deviceId, {
39
+ encoding: "utf-8"
40
+ });
41
+ }
42
+ else {
43
+ deviceId = await fs_extra_1.default.readFile(deviceIdFile, {
44
+ encoding: "utf-8"
45
+ });
46
+ }
47
+ this.deviceIdCache = deviceId;
48
+ return deviceId;
49
+ }
50
+ async getDirectoryTree(skipCache = false) {
51
+ var _a;
52
+ const deviceId = await this.getDeviceId();
53
+ const dir = await this.sync.sdk.api(3).dir().tree({
54
+ uuid: this.sync.syncPair.remoteParentUUID,
55
+ deviceId,
56
+ skipCache,
57
+ includeRaw: true
58
+ });
59
+ // Data did not change, use cache
60
+ if (dir.files.length === 0 && dir.folders.length === 0) {
61
+ if (this.getDirectoryTreeCache.timestamp === 0) {
62
+ // Re-run but with API caching disabled since our internal client cache is empty
63
+ return await this.getDirectoryTree(true);
64
+ }
65
+ return {
66
+ result: this.getDirectoryTreeCache,
67
+ ignored: [],
68
+ changed: false
69
+ };
70
+ }
71
+ // eslint-disable-next-line quotes
72
+ const rawEx = dir.raw.split('"randomBytes"');
73
+ // Compare API response with the previous dataset, this way we can save time computing the tree if it's the same
74
+ if (rawEx.length === 2 && this.previousTreeRawResponse === rawEx[0] && this.getDirectoryTreeCache.timestamp !== 0) {
75
+ return {
76
+ result: this.getDirectoryTreeCache,
77
+ ignored: [],
78
+ changed: false
79
+ };
80
+ }
81
+ const baseFolder = dir.folders[0];
82
+ if (!baseFolder) {
83
+ throw new Error("Could not get base folder.");
84
+ }
85
+ const baseFolderParent = baseFolder[2];
86
+ if (baseFolderParent !== "base") {
87
+ throw new Error("Invalid base folder parent.");
88
+ }
89
+ const folderNames = { base: "/" };
24
90
  const tree = {};
25
- const dir = await this.sync.sdk.cloud().getDirectoryTree({ uuid: this.sync.syncPair.remoteParentUUID });
26
91
  const uuids = {};
27
- for (const path in dir) {
28
- if (!dir[path] || dir[path].parent === "base" || path.startsWith(".filen.trash.local")) {
29
- continue;
92
+ const pathsAdded = {};
93
+ const ignored = [];
94
+ for (const folder of dir.folders) {
95
+ try {
96
+ const decrypted = await this.sync.sdk.crypto().decrypt().folderMetadata({ metadata: folder[1] });
97
+ if (folder[2] !== "base" && decrypted.name.length === 0) {
98
+ continue;
99
+ }
100
+ const parentPath = folder[2] === "base" ? "" : `${folderNames[folder[2]]}/`;
101
+ const folderPath = folder[2] === "base" ? "" : `${parentPath}${decrypted.name}`;
102
+ const localPath = path_1.default.join(this.sync.syncPair.localPath, folderPath);
103
+ folderNames[folder[0]] = folderPath;
104
+ if (folderPath.startsWith(constants_1.LOCAL_TRASH_NAME) || decrypted.name.startsWith(constants_1.LOCAL_TRASH_NAME)) {
105
+ continue;
106
+ }
107
+ if (this.sync.remoteIgnorer.ignores(folderPath)) {
108
+ ignored.push({
109
+ localPath,
110
+ relativePath: folderPath,
111
+ reason: "remoteIgnore"
112
+ });
113
+ continue;
114
+ }
115
+ if (this.sync.excludeDotFiles && decrypted.name.startsWith(".")) {
116
+ ignored.push({
117
+ localPath,
118
+ relativePath: folderPath,
119
+ reason: "dotFile"
120
+ });
121
+ continue;
122
+ }
123
+ if ((0, utils_1.isPathOverMaxLength)(localPath)) {
124
+ ignored.push({
125
+ localPath,
126
+ relativePath: folderPath,
127
+ reason: "pathLength"
128
+ });
129
+ continue;
130
+ }
131
+ if ((0, utils_1.isNameOverMaxLength)(decrypted.name)) {
132
+ ignored.push({
133
+ localPath,
134
+ relativePath: folderPath,
135
+ reason: "nameLength"
136
+ });
137
+ continue;
138
+ }
139
+ if (!(0, utils_1.isValidPath)(localPath)) {
140
+ ignored.push({
141
+ localPath,
142
+ relativePath: folderPath,
143
+ reason: "invalidPath"
144
+ });
145
+ continue;
146
+ }
147
+ if ((0, utils_1.isDirectoryPathIgnoredByDefault)(folderPath) || (0, utils_1.isRelativePathIgnoredByDefault)(folderPath)) {
148
+ ignored.push({
149
+ localPath,
150
+ relativePath: folderPath,
151
+ reason: "defaultIgnore"
152
+ });
153
+ continue;
154
+ }
155
+ const lowercasePath = folderPath.toLowerCase();
156
+ if (pathsAdded[lowercasePath]) {
157
+ continue;
158
+ }
159
+ pathsAdded[lowercasePath] = true;
160
+ const item = {
161
+ type: "directory",
162
+ uuid: folder[0],
163
+ name: decrypted.name,
164
+ size: 0,
165
+ path: folderPath
166
+ };
167
+ tree[folderPath] = item;
168
+ uuids[folder[0]] = item;
30
169
  }
31
- const item = Object.assign(Object.assign({}, dir[path]), { path });
32
- tree[path] = item;
33
- const treeItem = tree[path];
34
- if (treeItem) {
35
- uuids[treeItem.uuid] = item;
170
+ catch (e) {
171
+ this.sync.worker.logger.log("error", e, "filesystems.remote.getDirectoryTree");
36
172
  }
37
173
  }
174
+ if (Object.keys(folderNames).length === 0) {
175
+ throw new Error("Could not build directory tree.");
176
+ }
177
+ await (0, utils_1.promiseAllSettledChunked)(dir.files.map(async (file) => {
178
+ try {
179
+ const decrypted = await this.sync.sdk.crypto().decrypt().fileMetadata({ metadata: file[5] });
180
+ if (decrypted.name.length === 0) {
181
+ return;
182
+ }
183
+ const parentPath = folderNames[file[4]];
184
+ const filePath = `${parentPath}/${decrypted.name}`;
185
+ const localPath = path_1.default.join(this.sync.syncPair.localPath, filePath);
186
+ if (filePath.startsWith(constants_1.LOCAL_TRASH_NAME) || decrypted.name.startsWith(constants_1.LOCAL_TRASH_NAME)) {
187
+ return;
188
+ }
189
+ if (this.sync.remoteIgnorer.ignores(filePath)) {
190
+ ignored.push({
191
+ localPath,
192
+ relativePath: filePath,
193
+ reason: "remoteIgnore"
194
+ });
195
+ return;
196
+ }
197
+ if (this.sync.excludeDotFiles && decrypted.name.startsWith(".")) {
198
+ ignored.push({
199
+ localPath,
200
+ relativePath: filePath,
201
+ reason: "dotFile"
202
+ });
203
+ return;
204
+ }
205
+ if ((0, utils_1.isPathOverMaxLength)(localPath)) {
206
+ ignored.push({
207
+ localPath,
208
+ relativePath: filePath,
209
+ reason: "pathLength"
210
+ });
211
+ return;
212
+ }
213
+ if ((0, utils_1.isNameOverMaxLength)(decrypted.name)) {
214
+ ignored.push({
215
+ localPath,
216
+ relativePath: filePath,
217
+ reason: "nameLength"
218
+ });
219
+ return;
220
+ }
221
+ if (!(0, utils_1.isValidPath)(localPath)) {
222
+ ignored.push({
223
+ localPath,
224
+ relativePath: filePath,
225
+ reason: "invalidPath"
226
+ });
227
+ return;
228
+ }
229
+ if ((0, utils_1.isDirectoryPathIgnoredByDefault)(filePath) || (0, utils_1.isRelativePathIgnoredByDefault)(filePath)) {
230
+ ignored.push({
231
+ localPath,
232
+ relativePath: filePath,
233
+ reason: "defaultIgnore"
234
+ });
235
+ return;
236
+ }
237
+ const lowercasePath = filePath.toLowerCase();
238
+ if (pathsAdded[lowercasePath]) {
239
+ return;
240
+ }
241
+ pathsAdded[lowercasePath] = true;
242
+ const item = {
243
+ type: "file",
244
+ uuid: file[0],
245
+ name: decrypted.name,
246
+ size: decrypted.size,
247
+ mime: decrypted.mime,
248
+ lastModified: (0, utils_1.convertTimestampToMs)(decrypted.lastModified),
249
+ version: file[6],
250
+ chunks: file[3],
251
+ key: decrypted.key,
252
+ bucket: file[1],
253
+ region: file[2],
254
+ creation: decrypted.creation,
255
+ hash: decrypted.hash,
256
+ path: filePath
257
+ };
258
+ tree[filePath] = item;
259
+ uuids[item.uuid] = item;
260
+ }
261
+ catch (e) {
262
+ this.sync.worker.logger.log("error", e, "filesystems.remote.getDirectoryTree");
263
+ }
264
+ }));
265
+ this.previousTreeRawResponse = rawEx.length === 2 ? (_a = rawEx[0]) !== null && _a !== void 0 ? _a : "" : "";
38
266
  this.getDirectoryTreeCache = {
39
267
  timestamp: Date.now(),
40
268
  tree,
41
269
  uuids
42
270
  };
43
- return { tree, uuids };
271
+ return {
272
+ result: {
273
+ tree,
274
+ uuids
275
+ },
276
+ ignored,
277
+ changed: true
278
+ };
44
279
  }
45
280
  /**
46
281
  * Find the corresponding UUID of the relative path.
@@ -90,6 +325,7 @@ class RemoteFileSystem {
90
325
  const basename = path_1.default.posix.basename(relativePath);
91
326
  if (parentPath === "/" || parentPath === "." || parentPath.length <= 0) {
92
327
  const uuid = await this.sync.sdk.cloud().createDirectory({ name: basename, parent: this.sync.syncPair.remoteParentUUID });
328
+ await this.itemsMutex.acquire();
93
329
  this.getDirectoryTreeCache.tree[relativePath] = {
94
330
  type: "directory",
95
331
  uuid,
@@ -97,6 +333,14 @@ class RemoteFileSystem {
97
333
  size: 0,
98
334
  path: relativePath
99
335
  };
336
+ this.getDirectoryTreeCache.uuids[uuid] = {
337
+ type: "directory",
338
+ uuid,
339
+ name: basename,
340
+ size: 0,
341
+ path: relativePath
342
+ };
343
+ this.itemsMutex.release();
100
344
  return uuid;
101
345
  }
102
346
  const pathEx = relativePath.split("/");
@@ -116,6 +360,7 @@ class RemoteFileSystem {
116
360
  const parentIsBase = partParentPath === "/" || partParentPath === "." || partParentPath === "";
117
361
  const parentUUID = parentIsBase ? this.sync.syncPair.remoteParentUUID : parentItem.uuid;
118
362
  const uuid = await this.sync.sdk.cloud().createDirectory({ name: partBasename, parent: parentUUID });
363
+ await this.itemsMutex.acquire();
119
364
  this.getDirectoryTreeCache.tree[relativePath] = {
120
365
  type: "directory",
121
366
  uuid,
@@ -123,6 +368,14 @@ class RemoteFileSystem {
123
368
  size: 0,
124
369
  path: relativePath
125
370
  };
371
+ this.getDirectoryTreeCache.uuids[uuid] = {
372
+ type: "directory",
373
+ uuid,
374
+ name: partBasename,
375
+ size: 0,
376
+ path: relativePath
377
+ };
378
+ this.itemsMutex.release();
126
379
  }
127
380
  }
128
381
  if (!this.getDirectoryTreeCache.tree[relativePath]) {
@@ -150,14 +403,15 @@ class RemoteFileSystem {
150
403
  await this.mutex.acquire();
151
404
  try {
152
405
  const uuid = await this.pathToItemUUID({ relativePath });
153
- if (!uuid || !this.getDirectoryTreeCache.tree[relativePath]) {
406
+ const item = this.getDirectoryTreeCache.tree[relativePath];
407
+ if (!uuid || !item) {
154
408
  return;
155
409
  }
156
410
  const acceptedTypes = !type ? ["directory", "file"] : type === "directory" ? ["directory"] : ["file"];
157
- if (!acceptedTypes.includes(this.getDirectoryTreeCache.tree[relativePath].type)) {
411
+ if (!acceptedTypes.includes(item.type)) {
158
412
  return;
159
413
  }
160
- if (this.getDirectoryTreeCache.tree[relativePath].type === "directory") {
414
+ if (item.type === "directory") {
161
415
  if (permanent) {
162
416
  await this.sync.sdk.cloud().deleteDirectory({ uuid });
163
417
  }
@@ -173,19 +427,26 @@ class RemoteFileSystem {
173
427
  await this.sync.sdk.cloud().trashFile({ uuid });
174
428
  }
175
429
  }
430
+ await this.itemsMutex.acquire();
176
431
  delete this.getDirectoryTreeCache.tree[relativePath];
432
+ delete this.getDirectoryTreeCache.uuids[uuid];
177
433
  for (const entry in this.getDirectoryTreeCache.tree) {
178
- if (entry.startsWith(relativePath + "/")) {
434
+ if (entry.startsWith(relativePath + "/") || entry === relativePath) {
435
+ const entryItem = this.getDirectoryTreeCache.tree[entry];
436
+ if (entryItem) {
437
+ delete this.getDirectoryTreeCache.uuids[entryItem.uuid];
438
+ }
179
439
  delete this.getDirectoryTreeCache.tree[entry];
180
440
  }
181
441
  }
442
+ this.itemsMutex.release();
182
443
  }
183
444
  finally {
184
445
  this.mutex.release();
185
446
  }
186
447
  }
187
448
  /**
188
- * Rename a file/directory inside the remote sync path. Recursively creates intermediate directories if needed.
449
+ * Rename/Move a file/directory inside the remote sync path. Recursively creates intermediate directories if needed.
189
450
  * @date 3/2/2024 - 9:35:12 PM
190
451
  *
191
452
  * @public
@@ -228,7 +489,10 @@ class RemoteFileSystem {
228
489
  return;
229
490
  }
230
491
  if (item.type === "directory") {
231
- await this.sync.sdk.cloud().renameDirectory({ uuid, name: newBasename });
492
+ await this.sync.sdk.cloud().renameDirectory({
493
+ uuid,
494
+ name: newBasename
495
+ });
232
496
  }
233
497
  else {
234
498
  await this.sync.sdk.cloud().renameFile({
@@ -237,16 +501,17 @@ class RemoteFileSystem {
237
501
  name: newBasename
238
502
  });
239
503
  }
240
- const oldItem = this.getDirectoryTreeCache.tree[fromRelativePath];
241
- if (oldItem) {
242
- this.getDirectoryTreeCache.tree[toRelativePath] = Object.assign(Object.assign({}, oldItem), { name: newBasename });
243
- }
244
- delete this.getDirectoryTreeCache.tree[fromRelativePath];
245
504
  }
246
505
  else {
506
+ if (toRelativePath.startsWith(fromRelativePath)) {
507
+ return;
508
+ }
247
509
  if (oldBasename !== newBasename) {
248
510
  if (item.type === "directory") {
249
- await this.sync.sdk.cloud().renameDirectory({ uuid, name: newBasename });
511
+ await this.sync.sdk.cloud().renameDirectory({
512
+ uuid,
513
+ name: newBasename
514
+ });
250
515
  }
251
516
  else {
252
517
  await this.sync.sdk.cloud().renameFile({
@@ -258,14 +523,18 @@ class RemoteFileSystem {
258
523
  }
259
524
  if (newParentPath === "/" || newParentPath === "." || newParentPath === "") {
260
525
  if (item.type === "directory") {
261
- await this.sync.sdk
262
- .cloud()
263
- .moveDirectory({ uuid, to: this.sync.syncPair.remoteParentUUID, metadata: itemMetadata });
526
+ await this.sync.sdk.cloud().moveDirectory({
527
+ uuid,
528
+ to: this.sync.syncPair.remoteParentUUID,
529
+ metadata: itemMetadata
530
+ });
264
531
  }
265
532
  else {
266
- await this.sync.sdk
267
- .cloud()
268
- .moveFile({ uuid, to: this.sync.syncPair.remoteParentUUID, metadata: itemMetadata });
533
+ await this.sync.sdk.cloud().moveFile({
534
+ uuid,
535
+ to: this.sync.syncPair.remoteParentUUID,
536
+ metadata: itemMetadata
537
+ });
269
538
  }
270
539
  }
271
540
  else {
@@ -275,29 +544,39 @@ class RemoteFileSystem {
275
544
  throw new Error(`Could not find path ${newParentPath}.`);
276
545
  }
277
546
  if (item.type === "directory") {
278
- await this.sync.sdk
279
- .cloud()
280
- .moveDirectory({ uuid, to: newParentItem.uuid, metadata: itemMetadata });
547
+ await this.sync.sdk.cloud().moveDirectory({
548
+ uuid,
549
+ to: newParentItem.uuid,
550
+ metadata: itemMetadata
551
+ });
281
552
  }
282
553
  else {
283
- await this.sync.sdk.cloud().moveFile({ uuid, to: newParentItem.uuid, metadata: itemMetadata });
554
+ await this.sync.sdk.cloud().moveFile({
555
+ uuid,
556
+ to: newParentItem.uuid,
557
+ metadata: itemMetadata
558
+ });
284
559
  }
285
560
  }
286
- const oldItem = this.getDirectoryTreeCache.tree[fromRelativePath];
287
- if (oldItem) {
288
- this.getDirectoryTreeCache.tree[toRelativePath] = Object.assign(Object.assign({}, oldItem), { name: newBasename });
289
- }
561
+ await this.itemsMutex.acquire();
562
+ this.getDirectoryTreeCache.tree[toRelativePath] = Object.assign(Object.assign({}, item), { name: path_1.default.basename(toRelativePath), path: toRelativePath });
563
+ this.getDirectoryTreeCache.uuids[item.uuid] = Object.assign(Object.assign({}, item), { name: path_1.default.basename(toRelativePath), path: toRelativePath });
290
564
  delete this.getDirectoryTreeCache.tree[fromRelativePath];
291
565
  for (const oldPath in this.getDirectoryTreeCache.tree) {
292
- if (oldPath.startsWith(fromRelativePath + "/")) {
566
+ if (oldPath.startsWith(fromRelativePath + "/") && oldPath !== fromRelativePath) {
293
567
  const newPath = oldPath.split(fromRelativePath).join(toRelativePath);
294
568
  const oldItem = this.getDirectoryTreeCache.tree[oldPath];
295
569
  if (oldItem) {
296
- this.getDirectoryTreeCache.tree[newPath] = Object.assign(Object.assign({}, oldItem), { name: newBasename });
570
+ this.getDirectoryTreeCache.tree[newPath] = Object.assign(Object.assign({}, oldItem), { name: path_1.default.basename(newPath), path: newPath });
571
+ delete this.getDirectoryTreeCache.tree[oldPath];
572
+ const oldItemUUID = this.getDirectoryTreeCache.uuids[oldItem.uuid];
573
+ if (oldItemUUID) {
574
+ this.getDirectoryTreeCache.uuids[oldItem.uuid] = Object.assign(Object.assign({}, oldItemUUID), { name: path_1.default.basename(newPath), path: newPath });
575
+ }
297
576
  }
298
- delete this.getDirectoryTreeCache.tree[oldPath];
299
577
  }
300
578
  }
579
+ this.itemsMutex.release();
301
580
  }
302
581
  }
303
582
  finally {
@@ -315,28 +594,123 @@ class RemoteFileSystem {
315
594
  * @returns {Promise<fs.Stats>}
316
595
  */
317
596
  async download({ relativePath }) {
597
+ var _a;
318
598
  const localPath = path_1.default.posix.join(this.sync.syncPair.localPath, relativePath);
319
- const uuid = await this.pathToItemUUID({ relativePath });
320
- const item = this.getDirectoryTreeCache.tree[relativePath];
321
- if (!uuid || !item) {
322
- throw new Error(`Could not download ${relativePath}: File not found.`);
599
+ const tmpLocalPath = path_1.default.join(this.sync.syncPair.localPath, constants_1.LOCAL_TRASH_NAME, (0, uuid_1.v4)());
600
+ const signalKey = `upload:${relativePath}`;
601
+ if (!this.sync.pauseSignals[signalKey]) {
602
+ this.sync.pauseSignals[signalKey] = new sdk_1.PauseSignal();
323
603
  }
324
- if (item.type === "directory") {
325
- throw new Error(`Could not download ${relativePath}: Not a file.`);
604
+ if (!this.sync.abortControllers[signalKey]) {
605
+ this.sync.abortControllers[signalKey] = new AbortController();
326
606
  }
327
- const tmpPath = await this.sync.sdk.cloud().downloadFileToLocal({
328
- uuid,
329
- bucket: item.bucket,
330
- region: item.region,
331
- chunks: item.chunks,
332
- version: item.version,
333
- key: item.key
334
- });
335
- await fs_extra_1.default.move(tmpPath, localPath, {
336
- overwrite: true
607
+ (0, ipc_1.postMessageToMain)({
608
+ type: "transfer",
609
+ syncPair: this.sync.syncPair,
610
+ data: {
611
+ of: "download",
612
+ type: "queued",
613
+ relativePath,
614
+ localPath
615
+ }
337
616
  });
338
- await fs_extra_1.default.utimes(localPath, Date.now(), item.lastModified);
339
- return await fs_extra_1.default.stat(localPath);
617
+ try {
618
+ const uuid = await this.pathToItemUUID({ relativePath });
619
+ const item = this.getDirectoryTreeCache.tree[relativePath];
620
+ if (!uuid || !item) {
621
+ throw new Error(`Could not download ${relativePath}: File not found.`);
622
+ }
623
+ if (item.type === "directory") {
624
+ throw new Error(`Could not download ${relativePath}: Not a file.`);
625
+ }
626
+ await this.sync.sdk.cloud().downloadFileToLocal({
627
+ uuid,
628
+ bucket: item.bucket,
629
+ region: item.region,
630
+ chunks: item.chunks,
631
+ version: item.version,
632
+ to: tmpLocalPath,
633
+ key: item.key,
634
+ size: item.size,
635
+ pauseSignal: this.sync.pauseSignals[signalKey],
636
+ abortSignal: (_a = this.sync.abortControllers[signalKey]) === null || _a === void 0 ? void 0 : _a.signal,
637
+ onError: err => {
638
+ this.sync.worker.logger.log("error", err, "filesystems.remote.download");
639
+ (0, ipc_1.postMessageToMain)({
640
+ type: "transfer",
641
+ syncPair: this.sync.syncPair,
642
+ data: {
643
+ of: "download",
644
+ type: "error",
645
+ relativePath,
646
+ localPath,
647
+ error: (0, utils_1.serializeError)(err)
648
+ }
649
+ });
650
+ },
651
+ onProgress: bytes => {
652
+ (0, ipc_1.postMessageToMain)({
653
+ type: "transfer",
654
+ syncPair: this.sync.syncPair,
655
+ data: {
656
+ of: "download",
657
+ type: "progress",
658
+ relativePath,
659
+ localPath,
660
+ bytes
661
+ }
662
+ });
663
+ },
664
+ onStarted: () => {
665
+ (0, ipc_1.postMessageToMain)({
666
+ type: "transfer",
667
+ syncPair: this.sync.syncPair,
668
+ data: {
669
+ of: "download",
670
+ type: "started",
671
+ relativePath,
672
+ localPath
673
+ }
674
+ });
675
+ }
676
+ });
677
+ await fs_extra_1.default.utimes(tmpLocalPath, new Date(), new Date((0, utils_1.convertTimestampToMs)(item.lastModified)));
678
+ await fs_extra_1.default.move(tmpLocalPath, localPath, {
679
+ overwrite: true
680
+ });
681
+ (0, ipc_1.postMessageToMain)({
682
+ type: "transfer",
683
+ syncPair: this.sync.syncPair,
684
+ data: {
685
+ of: "download",
686
+ type: "finished",
687
+ relativePath,
688
+ localPath
689
+ }
690
+ });
691
+ return await fs_extra_1.default.stat(localPath);
692
+ }
693
+ catch (e) {
694
+ this.sync.worker.logger.log("error", e, "filesystems.remote.download");
695
+ if (e instanceof Error) {
696
+ (0, ipc_1.postMessageToMain)({
697
+ type: "transfer",
698
+ syncPair: this.sync.syncPair,
699
+ data: {
700
+ of: "download",
701
+ type: "error",
702
+ relativePath,
703
+ localPath,
704
+ error: (0, utils_1.serializeError)(e)
705
+ }
706
+ });
707
+ }
708
+ throw e;
709
+ }
710
+ finally {
711
+ delete this.sync.pauseSignals[signalKey];
712
+ delete this.sync.abortControllers[signalKey];
713
+ }
340
714
  }
341
715
  }
342
716
  exports.RemoteFileSystem = RemoteFileSystem;