import pathModule from "path"; /** * Chunk large Promise.all executions. * * @export * @async * @template T * @param {Promise[]} promises * @param {number} [chunkSize=10000] * @param {boolean} [withResults=true] * @returns {Promise} */ export declare function promiseAllChunked(promises: Promise[], chunkSize?: number, withResults?: boolean): Promise; /** * Chunk large Promise.allSettled executions. * * @export * @async * @template T * @param {Promise[]} promises * @param {number} [chunkSize=10000] * @param {boolean} [withResults=true] * @returns {Promise} */ export declare function promiseAllSettledChunked(promises: Promise[], chunkSize?: number, withResults?: boolean): Promise; /** * Convert a timestamp from seconds to milliseconds, tolerating a missing/invalid value. * * Some file metadata decrypts WITHOUT a `lastModified` (older files, or other clients) even though the SDK * type declares it required. A non-finite input must not flow on: it would become NaN and poison every * downstream comparison (`NaN !== NaN` makes a file look changed every cycle) and `new Date(NaN)` in the * post-download `utimes` throws, so the file never finishes syncing. Treat an unknown time as the epoch — * comparisons stay well-defined and the uuid-based change detection still drives correctness. * * @export * @param {number} timestamp * @returns {number} */ export declare function convertTimestampToMs(timestamp: number): number; export declare function isPathOverMaxLength(path: string): boolean; export declare function isNameOverMaxLength(name: string): boolean; export declare const illegalCharsWindows: RegExp; export declare const reservedNamesWindows: RegExp; export declare const illegalCharsMacOS: RegExp; export declare const illegalCharsLinux: RegExp; export declare function isValidPath(inputPath: string): boolean; /** * Assert that a local filesystem path derived from a sync-relative path stays strictly INSIDE the sync root — * the last-line sink guard against path traversal. * * Remote item names are attacker-controlled: a shared folder can carry a maliciously-crafted name whose * decrypted value contains `..` segments, and the backend's end-to-end encryption means the name can only be * validated client-side (a custom client can create a folder literally named `..`). The engine builds a local * path by joining that name onto the sync root, and `path.join` silently COLLAPSES `..`, so the result can * point OUTSIDE the root — a write there (a download move, mkdir, delete, or rename) could overwrite or delete * arbitrary user files (e.g. `~/.bashrc` → RCE on next shell). The tree-build filter (`isPathIgnored`) already * refuses such items up front; this validates again at every real filesystem sink so a stale pre-fix base * entry or any filter gap can never reach a write. Fails LOUD (throws) rather than writing out of bounds; a * legitimate synced path always resolves strictly within the root (the root itself is never a write target). * * @export * @param {string} syncRoot The pair's local sync root. * @param {string} localPath The already-joined local path about to be written/read. */ export declare function assertPathWithinSyncRoot(syncRoot: string, localPath: string): void; /** * Pure containment core behind {@link assertPathWithinSyncRoot} and the remote tree-build filter: is `localPath` * strictly INSIDE `syncRoot` (the root itself excluded)? * * Uses `path.relative` rather than a lexical `resolved.startsWith(root + sep)`: when the sync root is a * filesystem MOUNT ROOT — a Windows mapped-drive root (`Z:\`) or a UNC share root (`\\NAS\share`), the common * Synology-over-SMB shape — `path.resolve()` already returns a value ending in a separator, so appending one * more yields a DOUBLED separator (`Z:\\`, `\\NAS\share\\`) that no legitimate child can start with. That made * the guard reject every synced item at a mount-root pair (v3.0.50: 7000+ "path traversal" errors). `relative` * has no such edge: a strictly-inside child yields a non-empty, non-`..`, relative result. It also still * rejects the classic prefix-sibling bypass (`/root` vs `/rootX`), a cross-drive target, and the root itself. * * @param pathImpl Injectable `path` implementation (defaults to host). Tests pass `path.win32` / `path.posix` * so the real Windows SMB shapes are asserted regardless of the host OS. */ export declare function isPathWithinSyncRoot(syncRoot: string, localPath: string, pathImpl?: typeof pathModule): boolean; /** * Deterministic winner for a case-insensitive path collision: a directory outranks a same-name file, otherwise the * case-sensitively smaller path wins. Order-independent, so the surviving item is stable regardless of the order the two * colliding entries arrive in (the /v3/dir/tree response is unordered; a filesystem walk's order is FS-defined). Without * this, a case-collision could flip the tree between runs and drive an endless delete+re-download / rename loop. * * @param {{ path: string; isDirectory: boolean }} incumbent The item currently holding the lowercased-path slot. * @param {string} candidatePath The colliding candidate's relative path. * @param {boolean} candidateIsDirectory Whether the candidate is a directory. * @returns {boolean} True if the incumbent keeps the slot; false if the candidate should replace it. */ export declare function caseCollisionIncumbentWins(incumbent: { path: string; isDirectory: boolean; }, candidatePath: string, candidateIsDirectory: boolean): boolean; export declare function isNameIgnoredByDefault(name: string): boolean; export declare function isRelativePathIgnoredByDefault(path: string): boolean; export declare function isAbsolutePathIgnoredByDefault(path: string): boolean; export type SerializedError = { name: string; message: string; stack?: string; stringified: string; }; export declare function serializeError(error: Error): SerializedError; export declare function deserializeError(serializedError: SerializedError): Error; /** * Replace a path with it's new parent path. * * @export * @param {string} path * @param {string} from * @param {string} to * @returns {string} */ export declare function replacePathStartWithFromAndTo(path: string, from: string, to: string): string; /** * Check if a path includes a dot file. * * @export * @param {string} path * @returns {boolean} */ export declare function pathIncludesDotFile(path: string): boolean; /** * Whether `relativePath` is the ROOT `.filenignore` — the synced ignore-config file. It is the ignore list * the engine reads from `/.filenignore` every cycle, so it should reach every machine that shares * the pair: it is EXEMPT from the `excludeDotFiles` dotfile filter (otherwise users who exclude dotfiles — * the ones who most want a curated ignore list — would never get it shared). It is NOT exempt from an * explicit `.filenignore` rule, so a user can still opt the file out of syncing on purpose (maintainer * decision 2026-06-29). Only the ROOT file qualifies (a nested `dir/.filenignore` is a normal file the engine * never reads); the local scan emits it WITHOUT a leading slash, the remote scan + the delta paths WITH one, * so both forms are accepted. Cheap (two string compares) — it runs in the per-entry scan hot path. */ export declare function isSyncedIgnoreFile(relativePath: string): boolean; export declare function normalizeUTime(time: number): number; /** * We need to normalize lastModified times (milliseconds) for delta comparison. * Some filesystems provide different floating point precisions, therefore sometimes borking the comparison. * We normalize it to a second. * Downside is that we _can_ miss modifications if they happen in the 1000ms window that we are rounding too. * * @export * @param {number} time * @returns {number} */ export declare function normalizeLastModifiedMsForComparison(time: number): number; export declare function fastHash(input: string): string; export declare function tryingToSyncDesktop(path: string): boolean; export declare function pathSyncedByICloud(path: string): Promise; export declare function isPathSyncedByICloud(path: string): Promise;