diff --git a/package-lock.json b/package-lock.json index 3323de6..4cd84a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@parcel/watcher": "^2.4.1", "fs-extra": "^11.2.0", "ignore": "^5.3.1", + "node-watch": "^0.7.4", "pino": "^9.3.2", "rotating-file-stream": "^3.2.3", "uuid": "^10.0.0", @@ -7268,6 +7269,15 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/node-watch": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.4.tgz", + "integrity": "sha512-RinNxoz4W1cep1b928fuFhvAQ5ag/+1UlMDV7rbyGthBIgsiEouS4kvRayvvboxii4m8eolKOIBo3OjDqbc+uQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", diff --git a/package.json b/package.json index f08eeb8..a2a367e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@filen/sync", - "version": "0.1.55", + "version": "0.1.56", "description": "Filen Sync", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -56,6 +56,7 @@ "@parcel/watcher": "^2.4.1", "fs-extra": "^11.2.0", "ignore": "^5.3.1", + "node-watch": "^0.7.4", "pino": "^9.3.2", "rotating-file-stream": "^3.2.3", "uuid": "^10.0.0", diff --git a/src/lib/filesystems/local.ts b/src/lib/filesystems/local.ts index fdd439f..d8c6138 100644 --- a/src/lib/filesystems/local.ts +++ b/src/lib/filesystems/local.ts @@ -19,6 +19,7 @@ import { type CloudItem, PauseSignal } from "@filen/sdk" import { postMessageToMain } from "../ipc" import { Semaphore } from "../../semaphore" import { v4 as uuidv4 } from "uuid" +import { type Watcher } from "node-watch" const pipelineAsync = promisify(pipeline) @@ -33,16 +34,19 @@ export type LocalItem = { export type LocalDirectoryTree = Record export type LocalDirectoryINodes = Record + export type LocalTree = { tree: LocalDirectoryTree inodes: LocalDirectoryINodes } + export type LocalTreeError = { localPath: string relativePath: string error: Error uuid: string } + export type LocalTreeIgnoredReason = | "dotFile" | "filenIgnore" @@ -52,6 +56,7 @@ export type LocalTreeIgnoredReason = | "invalidType" | "duplicate" | "permissions" + export type LocalTreeIgnored = { localPath: string relativePath: string @@ -83,7 +88,8 @@ export class LocalFileSystem { errors: [] } public watcherRunning = false - private watcherInstance: watcher.AsyncSubscription | null = null + private watcherInstanceParcel: watcher.AsyncSubscription | null = null + private watcherInstanceNode: Watcher | null = null public readonly itemsMutex = new Semaphore(1) public readonly mutex = new Semaphore(1) public readonly mkdirMutex = new Semaphore(1) @@ -301,11 +307,11 @@ export class LocalFileSystem { await this.watcherMutex.acquire() try { - if (this.watcherInstance) { + if (this.watcherInstanceParcel || this.watcherInstanceNode) { return } - this.watcherInstance = await watcher.subscribe( + this.watcherInstanceParcel = await watcher.subscribe( this.sync.syncPair.localPath, (err, events) => { if (!err && events && events.length > 0) { @@ -313,7 +319,6 @@ export class LocalFileSystem { } }, { - ignore: [".filen.trash.local"], backend: process.platform === "win32" ? "windows" @@ -325,6 +330,21 @@ export class LocalFileSystem { } ) + /* + this.watcherInstanceNode = nodeWatch( + this.sync.syncPair.localPath, + { + persistent: true, + recursive: true, + encoding: "utf8", + delay: 1000 + }, + () => { + this.lastDirectoryChangeTimestamp = Date.now() + } + ) + */ + clearInterval(this.watcherInstanceFallbackInterval) } catch (e) { this.sync.worker.logger.log("error", e, "startDirectoryWatcher") @@ -367,13 +387,17 @@ export class LocalFileSystem { clearInterval(this.watcherInstanceFallbackInterval) try { - if (!this.watcherInstance) { - return + if (this.watcherInstanceParcel) { + await this.watcherInstanceParcel.unsubscribe() + + this.watcherInstanceParcel = null } - await this.watcherInstance.unsubscribe() + if (this.watcherInstanceNode && !this.watcherInstanceNode.isClosed()) { + this.watcherInstanceNode.close() - this.watcherInstance = null + this.watcherInstanceNode = null + } } finally { this.watcherMutex.release() } diff --git a/src/lib/filesystems/remote.ts b/src/lib/filesystems/remote.ts index 3d1f332..84a01e2 100644 --- a/src/lib/filesystems/remote.ts +++ b/src/lib/filesystems/remote.ts @@ -25,10 +25,12 @@ import writeFileAtomic from "write-file-atomic" export type RemoteItem = Prettify & { path: string }> export type RemoteDirectoryTree = Record export type RemoteDirectoryUUIDs = Record + export type RemoteTree = { tree: RemoteDirectoryTree uuids: RemoteDirectoryUUIDs } + export type RemoteTreeIgnoredReason = | "dotFile" | "invalidPath" @@ -38,6 +40,7 @@ export type RemoteTreeIgnoredReason = | "defaultIgnore" | "empty" | "duplicate" + export type RemoteTreeIgnored = { localPath: string relativePath: string diff --git a/src/utils.ts b/src/utils.ts index e4c8ca4..c043f76 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -98,22 +98,25 @@ export function isNameOverMaxLength(name: string): boolean { return name.length + 1 > 255 } -export function isValidPath(inputPath: string): boolean { - // eslint-disable-next-line no-control-regex - const illegalCharsWindows = /[<>:"/\\|?*\x00-\x1F]/ - const reservedNamesWindows = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i - // eslint-disable-next-line no-control-regex, no-useless-escape - const illegalCharsMacOS = /[\/:\x00]/ - // eslint-disable-next-line no-control-regex - const illegalCharsLinux = /[\x00]/ +// eslint-disable-next-line no-control-regex +export const illegalCharsWindows = /[<>:"/\\|?*\x00-\x1F]/ +export const reservedNamesWindows = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i +// eslint-disable-next-line no-control-regex, no-useless-escape +export const illegalCharsMacOS = /[\/:\x00]/ +// eslint-disable-next-line no-control-regex +export const illegalCharsLinux = /[\x00]/ +export function isValidPath(inputPath: string): boolean { if (process.platform === "win32") { inputPath = inputPath.replace(/\\/g, "/") } - if (inputPath.includes("..")) { + /*if ( + (process.platform === "win32" && inputPath.includes("\\..")) || + ((process.platform === "linux" || process.platform === "darwin") && inputPath.includes("/..")) + ) { return false - } + }*/ const parts = inputPath.split("/")