filefive 1.3.0 → 1.4.1
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/README.md +13 -3
- package/dist/App.js +7 -4
- package/dist/LocalWatcher.js +3 -4
- package/dist/RemoteFiles.js +1 -1
- package/dist/RemoteWatcher.js +3 -4
- package/dist/commands/copy.js +8 -6
- package/dist/index.js +2 -2
- package/dist/public/index.js +1 -1
- package/dist/queues/Copy.js +2 -2
- package/dist/queues/Download.js +2 -2
- package/dist/queues/Duplicate.js +1 -1
- package/dist/queues/Queue.js +4 -3
- package/dist/queues/Upload.js +5 -3
- package/dist/transformers/Local.js +17 -0
- package/dist/transformers/git.js +88 -0
- package/dist/types.js +2 -2
- package/dist/utils/filter.js +1 -1
- package/package.json +10 -2
- package/dist/transform.js +0 -24
package/README.md
CHANGED
|
@@ -8,10 +8,18 @@ FileFive is a free open-source SFTP/FTP client and file manager with intuitive a
|
|
|
8
8
|
|
|
9
9
|
It is installed as a Node.js package and uses the web browser as GUI.
|
|
10
10
|
|
|
11
|
-
FileFive has a unique set of features and may be an alternative to FileZilla, Cyberduck, Transmit,
|
|
11
|
+
FileFive has a unique set of features and may be an alternative to FileZilla, Cyberduck, Transmit, and ForkLift.
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
14
|
-
<img src="https://github.com/miroshnikov/filefive/blob/main/screenshot.png" alt="FileFive" />
|
|
14
|
+
<img src="https://github.com/miroshnikov/filefive/blob/main/docs/screenshots/screenshot-1.png" alt="FileFive" />
|
|
15
|
+
</p>
|
|
16
|
+
<p align="center">
|
|
17
|
+
<details>
|
|
18
|
+
<summary>More screenshots</summary>
|
|
19
|
+
<img src="https://github.com/miroshnikov/filefive/blob/main/docs/screenshots/screenshot-2.png" alt="FileFive"/>
|
|
20
|
+
<img src="https://github.com/miroshnikov/filefive/blob/main/docs/screenshots/screenshot-3.png" alt="FileFive"/>
|
|
21
|
+
<img src="https://github.com/miroshnikov/filefive/blob/main/docs/screenshots/screenshot-4.png" alt="FileFive"/>
|
|
22
|
+
</details>
|
|
15
23
|
</p>
|
|
16
24
|
|
|
17
25
|
## Installation
|
|
@@ -40,8 +48,10 @@ Options:
|
|
|
40
48
|
- Minimalistic and intuitive UI, mimicing the look and feel of VSCode Explorer view
|
|
41
49
|
- Search/filter files using wildcards and JavaScript Regular Expressions
|
|
42
50
|
- Synchronized browsing
|
|
51
|
+
- Copy files keeping relative paths, allows synchronization files in nested folders in one click
|
|
43
52
|
- Remote file editing
|
|
44
|
-
-
|
|
53
|
+
- Files' Git statuses (uses your machine's Git installation)
|
|
54
|
+
- Connections/servers are plain files stored on your filesystem, no need to export/import
|
|
45
55
|
- Easy to backup connections and settings in `~/.f5` folder, e.g. by putting them into a Git repo
|
|
46
56
|
- Drag & drop, copy & paste files support
|
|
47
57
|
- Use browser tabs to browse more than one server or transfer files simultaneously
|
package/dist/App.js
CHANGED
|
@@ -14,9 +14,9 @@ const RemoteWatcher_1 = __importDefault(require("./RemoteWatcher"));
|
|
|
14
14
|
const Queue_1 = require("./queues/Queue");
|
|
15
15
|
const Password_1 = __importDefault(require("./Password"));
|
|
16
16
|
const commands_1 = require("./commands");
|
|
17
|
-
const transform_1 = __importDefault(require("./transform"));
|
|
18
17
|
const Local_1 = require("./Local");
|
|
19
18
|
const URI_1 = require("./utils/URI");
|
|
19
|
+
const Local_2 = __importDefault(require("./transformers/Local"));
|
|
20
20
|
class App {
|
|
21
21
|
static async bootstrap(handle, emitter, opener) {
|
|
22
22
|
const dataPath = (0, node_path_1.join)((0, node_os_1.homedir)(), '.f5');
|
|
@@ -39,7 +39,7 @@ class App {
|
|
|
39
39
|
watch: ({ dir }) => commands_1.commands.watch(dir, this.localWatcher, this.remoteWatcher, this.fileWatcher),
|
|
40
40
|
unwatch: ({ dir }) => commands_1.commands.unwatch(dir, this.localWatcher, this.remoteWatcher, this.fileWatcher),
|
|
41
41
|
refresh: ({ dir }) => this.remoteWatcher.refresh(dir),
|
|
42
|
-
copy: ({ src, dest, move, filter, sid }) => commands_1.commands.copy(src, dest, move, filter, sid),
|
|
42
|
+
copy: ({ src, dest, move, filter, root, sid }) => commands_1.commands.copy(src, dest, move, filter, root, sid),
|
|
43
43
|
duplicate: ({ src, filter }) => commands_1.commands.duplicate(src, filter),
|
|
44
44
|
remove: ({ files }) => commands_1.commands.remove(files, connPath),
|
|
45
45
|
clear: ({ file }) => commands_1.commands.clear(file),
|
|
@@ -57,8 +57,11 @@ class App {
|
|
|
57
57
|
this.onError = (error) => emitError(error);
|
|
58
58
|
const emitDir = emitter('dir');
|
|
59
59
|
const sendDirContent = (uri, files) => emitDir({ uri, files });
|
|
60
|
-
this.localWatcher = new LocalWatcher_1.default(
|
|
61
|
-
|
|
60
|
+
this.localWatcher = new LocalWatcher_1.default(async (path, files) => {
|
|
61
|
+
const transformer = new Local_2.default();
|
|
62
|
+
sendDirContent((0, URI_1.createURI)(types_1.LocalFileSystemID, path), await transformer.transform(path, files.map(f => ({ ...f, URI: (0, URI_1.createURI)(types_1.LocalFileSystemID, f.path) }))));
|
|
63
|
+
}, path => this.onError({ type: types_1.FailureType.MissingDir, uri: (0, URI_1.createURI)(types_1.LocalFileSystemID, path) }));
|
|
64
|
+
this.remoteWatcher = new RemoteWatcher_1.default(sendDirContent, uri => this.onError({ type: types_1.FailureType.MissingDir, uri }));
|
|
62
65
|
const emitFile = emitter('file');
|
|
63
66
|
const sendFileStat = (path, stat) => emitFile({ path, stat });
|
|
64
67
|
this.fileWatcher = new FileWatcher_1.default(sendFileStat);
|
package/dist/LocalWatcher.js
CHANGED
|
@@ -7,10 +7,9 @@ const node_path_1 = require("node:path");
|
|
|
7
7
|
const Local_1 = require("./Local");
|
|
8
8
|
const ReferenceCountMap_1 = __importDefault(require("./utils/ReferenceCountMap"));
|
|
9
9
|
class default_1 {
|
|
10
|
-
constructor(listener, onMissing
|
|
10
|
+
constructor(listener, onMissing) {
|
|
11
11
|
this.listener = listener;
|
|
12
12
|
this.onMissing = onMissing;
|
|
13
|
-
this.transform = transform;
|
|
14
13
|
this.watched = new ReferenceCountMap_1.default;
|
|
15
14
|
}
|
|
16
15
|
watch(dir) {
|
|
@@ -24,14 +23,14 @@ class default_1 {
|
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
try {
|
|
27
|
-
this.listener(dir,
|
|
26
|
+
this.listener(dir, (0, Local_1.list)(dir), event, target);
|
|
28
27
|
}
|
|
29
28
|
catch (e) {
|
|
30
29
|
this.watched.del(dir);
|
|
31
30
|
this.onMissing(dir);
|
|
32
31
|
}
|
|
33
32
|
}));
|
|
34
|
-
const files =
|
|
33
|
+
const files = (0, Local_1.list)(dir);
|
|
35
34
|
this.listener(dir, files);
|
|
36
35
|
return files;
|
|
37
36
|
}
|
package/dist/RemoteFiles.js
CHANGED
|
@@ -20,7 +20,7 @@ async function open(file, onLoad) {
|
|
|
20
20
|
try {
|
|
21
21
|
const tmpDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'f5-'));
|
|
22
22
|
const tmpName = (0, node_path_1.join)(tmpDir, (0, node_path_1.basename)(path));
|
|
23
|
-
return commands_1.commands.copy([file], (0, URI_1.createURI)(types_1.LocalFileSystemID, tmpDir), false, null, null, () => {
|
|
23
|
+
return commands_1.commands.copy([file], (0, URI_1.createURI)(types_1.LocalFileSystemID, tmpDir), false, null, null, null, () => {
|
|
24
24
|
files.set(tmpName, { file, deletion: resetDeletion(tmpName, null), sending: false, changed: false });
|
|
25
25
|
watcher.watch(tmpName);
|
|
26
26
|
onLoad(tmpName);
|
package/dist/RemoteWatcher.js
CHANGED
|
@@ -8,10 +8,9 @@ const ReferenceCountMap_1 = __importDefault(require("./utils/ReferenceCountMap")
|
|
|
8
8
|
const Connection_1 = __importDefault(require("./Connection"));
|
|
9
9
|
const URI_1 = require("./utils/URI");
|
|
10
10
|
class RemoteWatcher {
|
|
11
|
-
constructor(listener, onMissing
|
|
11
|
+
constructor(listener, onMissing) {
|
|
12
12
|
this.listener = listener;
|
|
13
13
|
this.onMissing = onMissing;
|
|
14
|
-
this.transform = transform;
|
|
15
14
|
this.watched = new ReferenceCountMap_1.default;
|
|
16
15
|
}
|
|
17
16
|
watch(uri) {
|
|
@@ -34,8 +33,8 @@ class RemoteWatcher {
|
|
|
34
33
|
list(uri) {
|
|
35
34
|
const { id, path } = (0, URI_1.parseURI)(uri);
|
|
36
35
|
Connection_1.default.list(id, path)
|
|
37
|
-
.then(files => this.listener(uri,
|
|
38
|
-
.catch(
|
|
36
|
+
.then(files => this.listener(uri, files))
|
|
37
|
+
.catch(() => {
|
|
39
38
|
this.watched.del(uri);
|
|
40
39
|
this.onMissing(uri);
|
|
41
40
|
});
|
package/dist/commands/copy.js
CHANGED
|
@@ -14,7 +14,7 @@ const Upload_1 = __importDefault(require("../queues/Upload"));
|
|
|
14
14
|
const uniqid_1 = __importDefault(require("../utils/uniqid"));
|
|
15
15
|
const App_1 = __importDefault(require("../App"));
|
|
16
16
|
const ramda_1 = require("ramda");
|
|
17
|
-
function default_1(src, dest, move, filter, sid, onComplete = () => { }) {
|
|
17
|
+
function default_1(src, dest, move, filter, root, sid, onComplete = () => { }) {
|
|
18
18
|
if (!src.length) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
@@ -43,7 +43,7 @@ function default_1(src, dest, move, filter, sid, onComplete = () => { }) {
|
|
|
43
43
|
if (fromId == toId) {
|
|
44
44
|
queueType = move ? types_1.QueueType.Move : types_1.QueueType.Copy;
|
|
45
45
|
connection = fromId;
|
|
46
|
-
queue = new Copy_1.default(connection, from, toPath, filter, state => App_1.default.onQueueUpdate(id, { type: types_1.QueueEventType.Update, state }), onConflict.bind(queue), error => {
|
|
46
|
+
queue = new Copy_1.default(connection, from, toPath, filter, root, state => App_1.default.onQueueUpdate(id, { type: types_1.QueueEventType.Update, state }), onConflict.bind(queue), error => {
|
|
47
47
|
App_1.default.onError({
|
|
48
48
|
type: types_1.FailureType.RemoteError,
|
|
49
49
|
id: fromId,
|
|
@@ -55,11 +55,13 @@ function default_1(src, dest, move, filter, sid, onComplete = () => { }) {
|
|
|
55
55
|
queueType = (0, URI_1.isLocal)(dest) ? types_1.QueueType.Download : types_1.QueueType.Upload;
|
|
56
56
|
connection = queueType == types_1.QueueType.Download ? fromId : toId;
|
|
57
57
|
queue = queueType == types_1.QueueType.Download ?
|
|
58
|
-
new Download_1.default(connection, from, toPath, filter, state => App_1.default.onQueueUpdate(id, { type: types_1.QueueEventType.Update, state }), onConflict.bind(queue), error => App_1.default.onError(error), onFinish) :
|
|
59
|
-
new Upload_1.default(connection, from, toPath, filter, state => App_1.default.onQueueUpdate(id, { type: types_1.QueueEventType.Update, state }), onConflict.bind(queue), error => App_1.default.onError(error), onFinish, App_1.default.remoteWatcher);
|
|
58
|
+
new Download_1.default(connection, from, toPath, filter, root, state => App_1.default.onQueueUpdate(id, { type: types_1.QueueEventType.Update, state }), onConflict.bind(queue), error => App_1.default.onError(error), onFinish) :
|
|
59
|
+
new Upload_1.default(connection, from, toPath, filter, root, state => App_1.default.onQueueUpdate(id, { type: types_1.QueueEventType.Update, state }), onConflict.bind(queue), error => App_1.default.onError(error), onFinish, App_1.default.remoteWatcher);
|
|
60
60
|
}
|
|
61
61
|
Queue_1.queues.set(id, queue);
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
App_1.default.onQueueUpdate(id, { type: types_1.QueueEventType.Create, queueType, connection });
|
|
64
|
+
queue.create();
|
|
65
|
+
}, 100);
|
|
64
66
|
return id;
|
|
65
67
|
}
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ app.use(express_1.default.json());
|
|
|
33
33
|
app.use(express_1.default.static((0, path_1.resolve)(__dirname, 'public')));
|
|
34
34
|
app.use(express_1.default.static((0, path_1.resolve)(__dirname, '../dist/public')));
|
|
35
35
|
const server = app.listen(port, async () => {
|
|
36
|
-
console.log(`
|
|
36
|
+
console.log(`FileFive is up on http://localhost:${port}`);
|
|
37
37
|
if (process.env.NODE_ENV !== 'development') {
|
|
38
38
|
(await open).default(`http://localhost:${port}`);
|
|
39
39
|
}
|
|
@@ -60,7 +60,7 @@ app.post('/api/upload', upload.array('files'), async function (req, res) {
|
|
|
60
60
|
await (0, promises_1.rename)(path, fnm);
|
|
61
61
|
src.push(fnm);
|
|
62
62
|
}
|
|
63
|
-
commands_1.commands.copy(src.map(path => (0, URI_1.createURI)(types_1.LocalFileSystemID, path)), req.body['to'], true, null, null, () => src.forEach(path => (0, promises_1.rm)(path, { force: true })));
|
|
63
|
+
commands_1.commands.copy(src.map(path => (0, URI_1.createURI)(types_1.LocalFileSystemID, path)), req.body['to'], true, null, null, null, () => src.forEach(path => (0, promises_1.rm)(path, { force: true })));
|
|
64
64
|
}
|
|
65
65
|
res.json(true);
|
|
66
66
|
});
|