@zero-transfer/dropbox 0.3.1 → 0.4.2

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/dist/index.d.ts CHANGED
@@ -833,7 +833,41 @@ interface TransferEngineOptions {
833
833
  /** Clock used for receipts and progress events. Defaults to `new Date()`. */
834
834
  now?: () => Date;
835
835
  }
836
- /** Executes transfer jobs and produces audit-friendly receipts. */
836
+ /**
837
+ * Executes transfer jobs and produces audit-friendly receipts.
838
+ *
839
+ * The engine is the lowest-level entry point in the transfer stack: it owns
840
+ * retry policy, attempt history, abort propagation, progress event
841
+ * normalization, and receipt construction. Most callers reach the engine
842
+ * indirectly through {@link runRoute}, {@link uploadFile}, {@link downloadFile},
843
+ * {@link copyBetween}, or {@link TransferQueue}; instantiate it directly when
844
+ * you need full control over execution semantics.
845
+ *
846
+ * @example Execute a single job with a custom executor
847
+ * ```ts
848
+ * import { TransferEngine, type TransferExecutor, type TransferJob } from "@zero-transfer/sdk";
849
+ *
850
+ * const engine = new TransferEngine();
851
+ *
852
+ * const executor: TransferExecutor = async ({ job, signal, onProgress }) => {
853
+ * onProgress?.({ jobId: job.id, bytesTransferred: 0 });
854
+ * // … perform the bytes here, honoring `signal` …
855
+ * return { jobId: job.id, bytesTransferred: 1234, completedAt: new Date() };
856
+ * };
857
+ *
858
+ * const job: TransferJob = {
859
+ * id: "manual-1",
860
+ * operation: "upload",
861
+ * source: { profile: localProfile, path: "./data.bin" },
862
+ * destination: { profile: s3Profile, path: "/data/data.bin" },
863
+ * };
864
+ *
865
+ * const receipt = await engine.execute(job, executor, {
866
+ * retry: { maxAttempts: 3, baseDelayMs: 250 },
867
+ * });
868
+ * console.log(receipt.attempts.length); // 1 on success
869
+ * ```
870
+ */
837
871
  declare class TransferEngine {
838
872
  private readonly now;
839
873
  /**
@@ -1584,8 +1618,20 @@ interface ClientDiagnostics {
1584
1618
  /**
1585
1619
  * Returns a redaction-safe snapshot of the providers registered with a client.
1586
1620
  *
1621
+ * Use this when rendering a setup screen, generating a support bundle, or
1622
+ * asserting in tests that the expected provider factories were registered.
1623
+ *
1587
1624
  * @param client - Transfer client to inspect.
1588
1625
  * @returns Provider id and capability snapshot tuples.
1626
+ *
1627
+ * @example List registered providers
1628
+ * ```ts
1629
+ * import { summarizeClientDiagnostics } from "@zero-transfer/sdk";
1630
+ *
1631
+ * for (const { id, capabilities } of summarizeClientDiagnostics(client).providers) {
1632
+ * console.log(`${id}: streaming=${capabilities.readStream} resume=${capabilities.resumeDownload}`);
1633
+ * }
1634
+ * ```
1589
1635
  */
1590
1636
  declare function summarizeClientDiagnostics(client: TransferClient): ClientDiagnostics;
1591
1637
  /** Per-step duration measurements collected by {@link runConnectionDiagnostics}. */
@@ -1638,11 +1684,98 @@ interface RunConnectionDiagnosticsOptions {
1638
1684
  /**
1639
1685
  * Connects to a profile, captures capability and listing samples, and returns a redaction-safe report.
1640
1686
  *
1687
+ * Useful for connectivity "ping" pages, smoke tests, and bug reports. Secrets
1688
+ * in the profile are redacted via {@link redactConnectionProfile} before being
1689
+ * returned. The session is always disconnected before the function returns,
1690
+ * including when probes throw.
1691
+ *
1641
1692
  * @param options - Diagnostic probe options.
1642
1693
  * @returns Diagnostic report including timings and any captured error.
1694
+ *
1695
+ * @example Probe an SFTP connection
1696
+ * ```ts
1697
+ * import { runConnectionDiagnostics } from "@zero-transfer/sdk";
1698
+ *
1699
+ * const report = await runConnectionDiagnostics({
1700
+ * client,
1701
+ * profile: {
1702
+ * host: "sftp.example.com",
1703
+ * provider: "sftp",
1704
+ * username: "deploy",
1705
+ * ssh: { privateKey: { path: "./keys/id_ed25519" } },
1706
+ * },
1707
+ * listPath: "/uploads",
1708
+ * });
1709
+ *
1710
+ * if (!report.ok) {
1711
+ * console.error("connection failed:", report.error);
1712
+ * } else {
1713
+ * console.log(`connect=${report.timings.connectMs}ms list=${report.timings.listMs}ms`);
1714
+ * console.log(report.sample); // up to 5 entries from /uploads
1715
+ * }
1716
+ * ```
1643
1717
  */
1644
1718
  declare function runConnectionDiagnostics(options: RunConnectionDiagnosticsOptions): Promise<ConnectionDiagnosticsResult>;
1645
1719
 
1720
+ /** Options for {@link createPooledTransferClient}. */
1721
+ interface ConnectionPoolOptions {
1722
+ /**
1723
+ * Maximum number of *idle* sessions retained per pool key.
1724
+ *
1725
+ * Active leases are not counted against this limit — the cap only applies
1726
+ * to sessions waiting in the pool. When more than `maxIdlePerKey` sessions
1727
+ * become idle simultaneously, the oldest ones are disconnected. Defaults
1728
+ * to `4`.
1729
+ */
1730
+ maxIdlePerKey?: number;
1731
+ /**
1732
+ * How long an idle session may sit unused before it is automatically
1733
+ * disconnected. Defaults to `60_000` ms. Set to `0` to disable the timer
1734
+ * (idle sessions persist until `drainPool()` is called).
1735
+ */
1736
+ idleTimeoutMs?: number;
1737
+ /**
1738
+ * Custom pool key derivation. Receives the resolved
1739
+ * {@link ConnectionProfile} (after TransferClient validation) and must
1740
+ * return a string. Sessions with matching keys are pooled together; never
1741
+ * include secrets in the key.
1742
+ *
1743
+ * The default derives the key from `provider`, `host`, `port`, and
1744
+ * `username`.
1745
+ */
1746
+ keyOf?: (profile: ConnectionProfile) => string;
1747
+ }
1748
+ /**
1749
+ * Pool-aware {@link TransferClient} returned by
1750
+ * {@link createPooledTransferClient}.
1751
+ */
1752
+ interface PooledTransferClient {
1753
+ /** Opens (or leases) a pooled provider session. */
1754
+ connect(profile: ConnectionProfile): Promise<TransferSession>;
1755
+ /** Inspects the registered providers (delegated to the underlying client). */
1756
+ hasProvider(providerId: ProviderId): boolean;
1757
+ /** Returns the registered capability snapshots (delegated). */
1758
+ getCapabilities(): CapabilitySet[];
1759
+ /** Returns a specific capability snapshot (delegated). */
1760
+ getCapabilities(providerId: ProviderId): CapabilitySet;
1761
+ /**
1762
+ * Disconnects every idle session and prevents further pooling. After
1763
+ * `drainPool()` resolves, subsequent `connect()` calls still work but
1764
+ * always create fresh sessions (and never return them to the pool).
1765
+ */
1766
+ drainPool(): Promise<void>;
1767
+ /** Returns the number of idle sessions currently held in the pool. */
1768
+ poolSize(): number;
1769
+ }
1770
+ /**
1771
+ * Wraps a {@link TransferClient} with connection pooling.
1772
+ *
1773
+ * @param inner - Underlying client used to create real provider sessions.
1774
+ * @param options - Pool sizing, eviction, and key-derivation overrides.
1775
+ * @returns A {@link PooledTransferClient} that reuses idle sessions.
1776
+ */
1777
+ declare function createPooledTransferClient(inner: TransferClient, options?: ConnectionPoolOptions): PooledTransferClient;
1778
+
1646
1779
  /** Options used to create a local file-system provider factory. */
1647
1780
  interface LocalProviderOptions {
1648
1781
  /** Root directory exposed as `/`. When omitted, `profile.host` is treated as the root path. */
@@ -1687,8 +1820,29 @@ interface MemoryProviderOptions {
1687
1820
  /**
1688
1821
  * Creates a provider factory backed by deterministic in-memory fixture entries.
1689
1822
  *
1823
+ * Useful for tests and examples where you want a real `TransferSession` without
1824
+ * touching disk or the network. Entries are pre-seeded; mutations made through
1825
+ * the session are visible to subsequent operations on the same provider.
1826
+ *
1690
1827
  * @param options - Optional fixture entries to expose through the memory provider.
1691
1828
  * @returns Provider factory suitable for `createTransferClient({ providers: [...] })`.
1829
+ *
1830
+ * @example Seed entries and read them back
1831
+ * ```ts
1832
+ * import { createMemoryProviderFactory, createTransferClient } from "@zero-transfer/sdk";
1833
+ *
1834
+ * const client = createTransferClient({
1835
+ * providers: [createMemoryProviderFactory({
1836
+ * entries: [
1837
+ * { path: "/fixtures/hello.txt", content: "hello world" },
1838
+ * { path: "/fixtures/data.bin", content: new Uint8Array([1, 2, 3]) },
1839
+ * ],
1840
+ * })],
1841
+ * });
1842
+ *
1843
+ * const session = await client.connect({ host: "fixtures", provider: "memory" });
1844
+ * console.log(await session.fs.list("/fixtures"));
1845
+ * ```
1692
1846
  */
1693
1847
  declare function createMemoryProviderFactory(options?: MemoryProviderOptions): ProviderFactory;
1694
1848
 
@@ -2426,9 +2580,47 @@ interface TransferPlanSummary {
2426
2580
  /** Counts grouped by action. */
2427
2581
  actions: Record<string, number>;
2428
2582
  }
2429
- /** Creates a transfer plan from dry-run planning input. */
2583
+ /**
2584
+ * Creates a transfer plan from dry-run planning input.
2585
+ *
2586
+ * Plans are immutable, structured descriptions of intended work. Pair with
2587
+ * {@link createSyncPlan} or {@link createAtomicDeployPlan} for end-to-end
2588
+ * planning, or build steps by hand when you need full control. Pass the plan
2589
+ * to {@link createTransferJobsFromPlan} to materialize executable jobs.
2590
+ *
2591
+ * @example Build a plan with two upload steps and inspect it
2592
+ * ```ts
2593
+ * import { createTransferPlan, summarizeTransferPlan } from "@zero-transfer/sdk";
2594
+ *
2595
+ * const plan = createTransferPlan({
2596
+ * id: "manual-batch",
2597
+ * steps: [
2598
+ * { action: "upload", source: "./a.bin", destination: "/lake/a.bin", expectedBytes: 1024 },
2599
+ * { action: "upload", source: "./b.bin", destination: "/lake/b.bin", expectedBytes: 2048 },
2600
+ * ],
2601
+ * });
2602
+ *
2603
+ * console.table(summarizeTransferPlan(plan));
2604
+ * ```
2605
+ */
2430
2606
  declare function createTransferPlan(input: TransferPlanInput): TransferPlan;
2431
- /** Summarizes a transfer plan for diagnostics, previews, and tests. */
2607
+ /**
2608
+ * Summarizes a transfer plan for diagnostics, previews, and tests.
2609
+ *
2610
+ * Returns aggregate counts (total / executable / skipped / destructive),
2611
+ * total expected bytes, and a per-action histogram. Useful for printing a
2612
+ * one-line plan summary before executing or for asserting plan shape in
2613
+ * tests.
2614
+ *
2615
+ * @example Print a plan preview
2616
+ * ```ts
2617
+ * import { summarizeTransferPlan } from "@zero-transfer/sdk";
2618
+ *
2619
+ * const summary = summarizeTransferPlan(plan);
2620
+ * console.log(`${summary.executableSteps} steps, ${summary.totalExpectedBytes} bytes total`);
2621
+ * console.log("Actions:", summary.actions);
2622
+ * ```
2623
+ */
2432
2624
  declare function summarizeTransferPlan(plan: TransferPlan): TransferPlanSummary;
2433
2625
  /** Converts executable plan steps into transfer jobs while preserving order. */
2434
2626
  declare function createTransferJobsFromPlan(plan: TransferPlan): TransferJob[];
@@ -2505,7 +2697,41 @@ interface TransferQueueSummary {
2505
2697
  /** Failed queue items in queue order. */
2506
2698
  failures: TransferQueueItem[];
2507
2699
  }
2508
- /** Minimal transfer queue with concurrency, pause/resume, cancellation, and drain summaries. */
2700
+ /**
2701
+ * Minimal transfer queue with concurrency, pause/resume, cancellation, and drain summaries.
2702
+ *
2703
+ * Wrap a {@link TransferEngine} with a queue when you need to run many transfers
2704
+ * concurrently with bounded parallelism, observe per-job progress, or drive
2705
+ * a UI from a single source of truth. Items are FIFO; failures and successes
2706
+ * are surfaced via observers and in the final {@link TransferQueueSummary}.
2707
+ *
2708
+ * @example Run a batch of uploads with concurrency=4
2709
+ * ```ts
2710
+ * import {
2711
+ * TransferQueue,
2712
+ * createProviderTransferExecutor,
2713
+ * } from "@zero-transfer/sdk";
2714
+ *
2715
+ * const queue = new TransferQueue({
2716
+ * concurrency: 4,
2717
+ * executor: createProviderTransferExecutor({ client }),
2718
+ * onProgress: (e) => console.log(`${e.jobId}: ${e.bytesTransferred}`),
2719
+ * onError: (item, err) => console.error(`${item.job.id} failed`, err),
2720
+ * });
2721
+ *
2722
+ * for (const file of files) {
2723
+ * queue.enqueue({
2724
+ * id: file.name,
2725
+ * operation: "upload",
2726
+ * source: { profile: localProfile, path: file.path },
2727
+ * destination: { profile: s3Profile, path: `/lake/${file.name}` },
2728
+ * });
2729
+ * }
2730
+ *
2731
+ * const summary = await queue.drain();
2732
+ * console.log(`Completed ${summary.completed} / ${summary.total}`);
2733
+ * ```
2734
+ */
2509
2735
  declare class TransferQueue {
2510
2736
  private readonly engine;
2511
2737
  private readonly items;
@@ -2785,6 +3011,26 @@ interface DiffRemoteTreesOptions {
2785
3011
  * @param destinationPath - Destination-side root path being compared.
2786
3012
  * @param options - Optional comparison controls.
2787
3013
  * @returns Diff result containing entries and a summary.
3014
+ *
3015
+ * @example Diff two SFTP subtrees and feed the result into createSyncPlan
3016
+ * ```ts
3017
+ * import { createSyncPlan, diffRemoteTrees } from "@zero-transfer/sdk";
3018
+ *
3019
+ * const diff = await diffRemoteTrees(
3020
+ * srcSession.fs, "/exports",
3021
+ * dstSession.fs, "/exports",
3022
+ * { compareUniqueId: true },
3023
+ * );
3024
+ *
3025
+ * console.log(diff.summary); // { added, removed, changed, unchanged }
3026
+ *
3027
+ * const plan = createSyncPlan({
3028
+ * id: "exports-sync",
3029
+ * diff,
3030
+ * source: { provider: "sftp", rootPath: "/exports" },
3031
+ * destination: { provider: "sftp", rootPath: "/exports" },
3032
+ * });
3033
+ * ```
2788
3034
  */
2789
3035
  declare function diffRemoteTrees(source: RemoteFileSystem, sourcePath: string, destination: RemoteFileSystem, destinationPath: string, options?: DiffRemoteTreesOptions): Promise<RemoteTreeDiff>;
2790
3036
 
@@ -2856,6 +3102,31 @@ interface CreateSyncPlanOptions {
2856
3102
  * @param options - Inputs and policies that shape the plan.
2857
3103
  * @returns Transfer plan ready for `createTransferJobsFromPlan` or queue execution.
2858
3104
  * @throws {@link ConfigurationError} When `conflictPolicy: "error"` encounters a conflict.
3105
+ *
3106
+ * @example Mirror SFTP → S3 with deletes
3107
+ * ```ts
3108
+ * import {
3109
+ * createSyncPlan,
3110
+ * diffRemoteTrees,
3111
+ * summarizeTransferPlan,
3112
+ * } from "@zero-transfer/sdk";
3113
+ *
3114
+ * const diff = await diffRemoteTrees(
3115
+ * srcSession.fs, "/dist",
3116
+ * dstSession.fs, "/releases/current",
3117
+ * );
3118
+ *
3119
+ * const plan = createSyncPlan({
3120
+ * id: "release-mirror",
3121
+ * diff,
3122
+ * source: { provider: "sftp", rootPath: "/dist" },
3123
+ * destination: { provider: "s3", rootPath: "/releases/current" },
3124
+ * deletePolicy: "mirror",
3125
+ * conflictPolicy: "overwrite",
3126
+ * });
3127
+ *
3128
+ * console.table(summarizeTransferPlan(plan));
3129
+ * ```
2859
3130
  */
2860
3131
  declare function createSyncPlan(options: CreateSyncPlanOptions): TransferPlan;
2861
3132
 
@@ -2971,9 +3242,39 @@ interface CreateAtomicDeployPlanOptions {
2971
3242
  /**
2972
3243
  * Builds an {@link AtomicDeployPlan} that stages a release, swaps it live, and prunes old releases.
2973
3244
  *
3245
+ * The plan describes a blue/green-style deploy:
3246
+ * 1. Upload to a timestamped staging directory under `<destination>/.releases/`.
3247
+ * 2. Atomically swap the `current` symlink/rename to point at the new release.
3248
+ * 3. Optionally prune old releases beyond `retain`.
3249
+ *
3250
+ * No I/O is performed — the host executes the plan steps. Pair with
3251
+ * {@link createTransferPlan} or {@link createTransferJobsFromPlan} to execute.
3252
+ *
2974
3253
  * @param options - Inputs and policies that shape the deploy.
2975
3254
  * @returns Structured deploy plan ready for execution by the calling host.
2976
3255
  * @throws {@link ConfigurationError} When `retain` is less than `1` or the destination root is empty.
3256
+ *
3257
+ * @example Plan a release with rollback path
3258
+ * ```ts
3259
+ * import { createAtomicDeployPlan } from "@zero-transfer/sdk";
3260
+ *
3261
+ * const plan = createAtomicDeployPlan({
3262
+ * id: "web-2026-04-28",
3263
+ * source: { rootPath: "./dist" },
3264
+ * destination: {
3265
+ * profile: { host: "web1.example.com", provider: "sftp", username: "deploy" },
3266
+ * rootPath: "/srv/www",
3267
+ * },
3268
+ * retain: 5,
3269
+ * existingReleases: [
3270
+ * "/srv/www/.releases/2026-04-21T00-00-00Z",
3271
+ * "/srv/www/.releases/2026-04-14T00-00-00Z",
3272
+ * ],
3273
+ * });
3274
+ *
3275
+ * console.log(plan.swap); // staging → current rename
3276
+ * console.log(plan.prune); // releases scheduled for removal
3277
+ * ```
2977
3278
  */
2978
3279
  declare function createAtomicDeployPlan(options: CreateAtomicDeployPlanOptions): AtomicDeployPlan;
2979
3280
 
@@ -3187,8 +3488,32 @@ interface DropboxProviderOptions {
3187
3488
  *
3188
3489
  * The bearer token is resolved per-connection from `profile.password`. The
3189
3490
  * `profile.host` field is unused; Dropbox connections are identified solely by
3190
- * their token.
3491
+ * their token. Uploads go to `/2/files/upload` (single-shot); resumable upload
3492
+ * sessions are not yet supported.
3493
+ *
3494
+ * @param options - Optional API base URL overrides and fetch implementation.
3495
+ * @returns Provider factory suitable for `createTransferClient({ providers: [...] })`.
3496
+ *
3497
+ * @example Upload a backup to Dropbox
3498
+ * ```ts
3499
+ * import { createDropboxProviderFactory, createTransferClient, uploadFile } from "@zero-transfer/sdk";
3500
+ *
3501
+ * const client = createTransferClient({ providers: [createDropboxProviderFactory()] });
3502
+ *
3503
+ * await uploadFile({
3504
+ * client,
3505
+ * localPath: "./backups/db.dump",
3506
+ * destination: {
3507
+ * path: "/Backups/2026-04-28/db.dump",
3508
+ * profile: {
3509
+ * host: "",
3510
+ * provider: "dropbox",
3511
+ * password: { env: "DROPBOX_ACCESS_TOKEN" },
3512
+ * },
3513
+ * },
3514
+ * });
3515
+ * ```
3191
3516
  */
3192
3517
  declare function createDropboxProviderFactory(options?: DropboxProviderOptions): ProviderFactory;
3193
3518
 
3194
- export { AbortError, type AtomicDeployActivateOperation, type AtomicDeployActivateStep, type AtomicDeployPlan, type AtomicDeployPruneStep, type AtomicDeployStrategy, type AuthenticationCapability, AuthenticationError, AuthorizationError, type BandwidthSleep, type BandwidthThrottle, type BandwidthThrottleOptions, type Base64EnvSecretSource, type BuiltInProviderId, CLASSIC_PROVIDER_IDS, type CapabilitySet, type ChecksumCapability, type ClassicProviderId, type ClientDiagnostics, type CompareRemoteManifestsOptions, ConfigurationError, type ConnectionDiagnosticTimings, type ConnectionDiagnosticsResult, ConnectionError, type ConnectionProfile, type CopyBetweenOptions, type CreateAtomicDeployPlanOptions, type CreateRemoteBrowserOptions, type CreateRemoteManifestOptions, type CreateSyncPlanOptions, type DiffRemoteTreesOptions, type DownloadFileOptions, type DropboxProviderOptions, type EnvSecretSource, type FileSecretSource, type FileZillaSite, type FriendlyTransferOptions, type FtpReplyErrorInput, type ImportFileZillaSitesResult, type ImportOpenSshConfigOptions, type ImportOpenSshConfigResult, type ImportWinScpSessionsResult, type KnownHostsEntry, type KnownHostsMarker, type ListOptions, type LocalProviderOptions, type LogLevel, type LogRecord, type LogRecordInput, type LoggerMethod, type MemoryProviderEntry, type MemoryProviderOptions, type MetadataCapability, type MkdirOptions, type OAuthAccessToken, type OAuthRefreshCallback, type OAuthTokenSecretSourceOptions, type OpenSshConfigEntry, ParseError, PathAlreadyExistsError, PathNotFoundError, PermissionDeniedError, type ProgressEventInput, ProtocolError, type AuthenticationCapability as ProviderAuthenticationCapability, type CapabilitySet as ProviderCapabilities, type ChecksumCapability as ProviderChecksumCapability, type ProviderFactory, type ProviderId, type MetadataCapability as ProviderMetadataCapability, ProviderRegistry, type ProviderSelection, type ProviderTransferEndpointRole, type ProviderTransferExecutorOptions, type ProviderTransferOperations, type ProviderTransferReadRequest, type ProviderTransferReadResult, type ProviderTransferRequest, type ProviderTransferSessionResolver, type ProviderTransferSessionResolverInput, type ProviderTransferWriteRequest, type ProviderTransferWriteResult, REDACTED, REMOTE_MANIFEST_FORMAT_VERSION, type RemoteBreadcrumb, type RemoteBrowser, type RemoteBrowserFilter, type RemoteBrowserSnapshot, type RemoteEntry, type RemoteEntrySortKey, type RemoteEntrySortOrder, type RemoteEntryType, type RemoteFileAdapter, type RemoteFileEndpoint, type RemoteFileSystem, type RemoteManifest, type RemoteManifestEntry, type RemotePermissions, type RemoteProtocol, type RemoteStat, type RemoteTreeDiff, type RemoteTreeDiffEntry, type RemoteTreeDiffReason, type RemoteTreeDiffStatus, type RemoteTreeDiffSummary, type RemoteTreeEntry, type RemoteTreeFilter, type RemoveOptions, type RenameOptions, type ResolveSecretOptions, type ResolvedConnectionProfile, type ResolvedOpenSshHost, type ResolvedSshProfile, type ResolvedTlsProfile, type RmdirOptions, type RunConnectionDiagnosticsOptions, type SecretProvider, type SecretSource, type SecretValue, type SpecializedErrorDetails, type SshAgentSource, type SshAlgorithms, type SshKeyboardInteractiveChallenge, type SshKeyboardInteractiveHandler, type SshKeyboardInteractivePrompt, type SshKnownHostsSource, type SshProfile, type SshSocketFactory, type SshSocketFactoryContext, type StatOptions, type SyncConflictPolicy, type SyncDeletePolicy, type SyncDirection, type SyncEndpointInput, TimeoutError, type TlsProfile, type TlsSecretSource, type TransferAttempt, type TransferAttemptError, type TransferBandwidthLimit, type TransferByteRange, TransferClient, type TransferClientOptions, type TransferDataChunk, type TransferDataSource, type TransferEndpoint, TransferEngine, type TransferEngineExecuteOptions, type TransferEngineOptions, TransferError, type TransferExecutionContext, type TransferExecutionResult, type TransferExecutor, type TransferJob, type TransferOperation, type TransferPlan, type TransferPlanAction, type TransferPlanInput, type TransferPlanStep, type TransferPlanSummary, type TransferProgressEvent, type TransferProvider, TransferQueue, type TransferQueueExecutorResolver, type TransferQueueItem, type TransferQueueItemStatus, type TransferQueueOptions, type TransferQueueRunOptions, type TransferQueueSummary, type TransferReceipt, type TransferResult, type TransferResultInput, type TransferRetryDecisionInput, type TransferRetryPolicy, type TransferSession, type TransferTimeoutPolicy, type TransferVerificationResult, UnsupportedFeatureError, type UploadFileOptions, type ValueSecretSource, VerificationError, type WalkRemoteTreeOptions, type WinScpSession, ZeroTransfer, type ZeroTransferCapabilities, ZeroTransferError, type ZeroTransferErrorDetails, type ZeroTransferLogger, type ZeroTransferOptions, assertSafeFtpArgument, basenameRemotePath, buildRemoteBreadcrumbs, compareRemoteManifests, copyBetween, createAtomicDeployPlan, createBandwidthThrottle, createDropboxProviderFactory, createLocalProviderFactory, createMemoryProviderFactory, createOAuthTokenSecretSource, createProgressEvent, createProviderTransferExecutor, createRemoteBrowser, createRemoteManifest, createSyncPlan, createTransferClient, createTransferJobsFromPlan, createTransferPlan, createTransferResult, diffRemoteTrees, downloadFile, emitLog, errorFromFtpReply, filterRemoteEntries, importFileZillaSites, importOpenSshConfig, importWinScpSessions, isClassicProviderId, isSensitiveKey, joinRemotePath, matchKnownHosts, matchKnownHostsEntry, noopLogger, normalizeRemotePath, parentRemotePath, parseKnownHosts, parseOpenSshConfig, parseRemoteManifest, redactCommand, redactConnectionProfile, redactObject, redactSecretSource, redactValue, resolveConnectionProfileSecrets, resolveOpenSshHost, resolveProviderId, resolveSecret, runConnectionDiagnostics, serializeRemoteManifest, sortRemoteEntries, summarizeClientDiagnostics, summarizeTransferPlan, throttleByteIterable, uploadFile, validateConnectionProfile, walkRemoteTree };
3519
+ export { AbortError, type AtomicDeployActivateOperation, type AtomicDeployActivateStep, type AtomicDeployPlan, type AtomicDeployPruneStep, type AtomicDeployStrategy, type AuthenticationCapability, AuthenticationError, AuthorizationError, type BandwidthSleep, type BandwidthThrottle, type BandwidthThrottleOptions, type Base64EnvSecretSource, type BuiltInProviderId, CLASSIC_PROVIDER_IDS, type CapabilitySet, type ChecksumCapability, type ClassicProviderId, type ClientDiagnostics, type CompareRemoteManifestsOptions, ConfigurationError, type ConnectionDiagnosticTimings, type ConnectionDiagnosticsResult, ConnectionError, type ConnectionPoolOptions, type ConnectionProfile, type CopyBetweenOptions, type CreateAtomicDeployPlanOptions, type CreateRemoteBrowserOptions, type CreateRemoteManifestOptions, type CreateSyncPlanOptions, type DiffRemoteTreesOptions, type DownloadFileOptions, type DropboxProviderOptions, type EnvSecretSource, type FileSecretSource, type FileZillaSite, type FriendlyTransferOptions, type FtpReplyErrorInput, type ImportFileZillaSitesResult, type ImportOpenSshConfigOptions, type ImportOpenSshConfigResult, type ImportWinScpSessionsResult, type KnownHostsEntry, type KnownHostsMarker, type ListOptions, type LocalProviderOptions, type LogLevel, type LogRecord, type LogRecordInput, type LoggerMethod, type MemoryProviderEntry, type MemoryProviderOptions, type MetadataCapability, type MkdirOptions, type OAuthAccessToken, type OAuthRefreshCallback, type OAuthTokenSecretSourceOptions, type OpenSshConfigEntry, ParseError, PathAlreadyExistsError, PathNotFoundError, PermissionDeniedError, type PooledTransferClient, type ProgressEventInput, ProtocolError, type AuthenticationCapability as ProviderAuthenticationCapability, type CapabilitySet as ProviderCapabilities, type ChecksumCapability as ProviderChecksumCapability, type ProviderFactory, type ProviderId, type MetadataCapability as ProviderMetadataCapability, ProviderRegistry, type ProviderSelection, type ProviderTransferEndpointRole, type ProviderTransferExecutorOptions, type ProviderTransferOperations, type ProviderTransferReadRequest, type ProviderTransferReadResult, type ProviderTransferRequest, type ProviderTransferSessionResolver, type ProviderTransferSessionResolverInput, type ProviderTransferWriteRequest, type ProviderTransferWriteResult, REDACTED, REMOTE_MANIFEST_FORMAT_VERSION, type RemoteBreadcrumb, type RemoteBrowser, type RemoteBrowserFilter, type RemoteBrowserSnapshot, type RemoteEntry, type RemoteEntrySortKey, type RemoteEntrySortOrder, type RemoteEntryType, type RemoteFileAdapter, type RemoteFileEndpoint, type RemoteFileSystem, type RemoteManifest, type RemoteManifestEntry, type RemotePermissions, type RemoteProtocol, type RemoteStat, type RemoteTreeDiff, type RemoteTreeDiffEntry, type RemoteTreeDiffReason, type RemoteTreeDiffStatus, type RemoteTreeDiffSummary, type RemoteTreeEntry, type RemoteTreeFilter, type RemoveOptions, type RenameOptions, type ResolveSecretOptions, type ResolvedConnectionProfile, type ResolvedOpenSshHost, type ResolvedSshProfile, type ResolvedTlsProfile, type RmdirOptions, type RunConnectionDiagnosticsOptions, type SecretProvider, type SecretSource, type SecretValue, type SpecializedErrorDetails, type SshAgentSource, type SshAlgorithms, type SshKeyboardInteractiveChallenge, type SshKeyboardInteractiveHandler, type SshKeyboardInteractivePrompt, type SshKnownHostsSource, type SshProfile, type SshSocketFactory, type SshSocketFactoryContext, type StatOptions, type SyncConflictPolicy, type SyncDeletePolicy, type SyncDirection, type SyncEndpointInput, TimeoutError, type TlsProfile, type TlsSecretSource, type TransferAttempt, type TransferAttemptError, type TransferBandwidthLimit, type TransferByteRange, TransferClient, type TransferClientOptions, type TransferDataChunk, type TransferDataSource, type TransferEndpoint, TransferEngine, type TransferEngineExecuteOptions, type TransferEngineOptions, TransferError, type TransferExecutionContext, type TransferExecutionResult, type TransferExecutor, type TransferJob, type TransferOperation, type TransferPlan, type TransferPlanAction, type TransferPlanInput, type TransferPlanStep, type TransferPlanSummary, type TransferProgressEvent, type TransferProvider, TransferQueue, type TransferQueueExecutorResolver, type TransferQueueItem, type TransferQueueItemStatus, type TransferQueueOptions, type TransferQueueRunOptions, type TransferQueueSummary, type TransferReceipt, type TransferResult, type TransferResultInput, type TransferRetryDecisionInput, type TransferRetryPolicy, type TransferSession, type TransferTimeoutPolicy, type TransferVerificationResult, UnsupportedFeatureError, type UploadFileOptions, type ValueSecretSource, VerificationError, type WalkRemoteTreeOptions, type WinScpSession, ZeroTransfer, type ZeroTransferCapabilities, ZeroTransferError, type ZeroTransferErrorDetails, type ZeroTransferLogger, type ZeroTransferOptions, assertSafeFtpArgument, basenameRemotePath, buildRemoteBreadcrumbs, compareRemoteManifests, copyBetween, createAtomicDeployPlan, createBandwidthThrottle, createDropboxProviderFactory, createLocalProviderFactory, createMemoryProviderFactory, createOAuthTokenSecretSource, createPooledTransferClient, createProgressEvent, createProviderTransferExecutor, createRemoteBrowser, createRemoteManifest, createSyncPlan, createTransferClient, createTransferJobsFromPlan, createTransferPlan, createTransferResult, diffRemoteTrees, downloadFile, emitLog, errorFromFtpReply, filterRemoteEntries, importFileZillaSites, importOpenSshConfig, importWinScpSessions, isClassicProviderId, isSensitiveKey, joinRemotePath, matchKnownHosts, matchKnownHostsEntry, noopLogger, normalizeRemotePath, parentRemotePath, parseKnownHosts, parseOpenSshConfig, parseRemoteManifest, redactCommand, redactConnectionProfile, redactObject, redactSecretSource, redactValue, resolveConnectionProfileSecrets, resolveOpenSshHost, resolveProviderId, resolveSecret, runConnectionDiagnostics, serializeRemoteManifest, sortRemoteEntries, summarizeClientDiagnostics, summarizeTransferPlan, throttleByteIterable, uploadFile, validateConnectionProfile, walkRemoteTree };
package/dist/index.mjs CHANGED
@@ -1767,6 +1767,186 @@ function summarizeDiagnosticError(error) {
1767
1767
  return { message: String(error) };
1768
1768
  }
1769
1769
 
1770
+ // src/core/ConnectionPool.ts
1771
+ var DEFAULT_MAX_IDLE_PER_KEY = 4;
1772
+ var DEFAULT_IDLE_TIMEOUT_MS = 6e4;
1773
+ function createPooledTransferClient(inner, options = {}) {
1774
+ const maxIdlePerKey = Math.max(1, options.maxIdlePerKey ?? DEFAULT_MAX_IDLE_PER_KEY);
1775
+ const idleTimeoutMs = Math.max(0, options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS);
1776
+ const keyOf = options.keyOf ?? defaultKeyOf;
1777
+ const state = {
1778
+ drained: false,
1779
+ idle: /* @__PURE__ */ new Map()
1780
+ };
1781
+ const release = (key, session, tainted) => {
1782
+ if (tainted || state.drained) {
1783
+ return safelyDisconnect(session);
1784
+ }
1785
+ let bucket = state.idle.get(key);
1786
+ if (bucket === void 0) {
1787
+ bucket = [];
1788
+ state.idle.set(key, bucket);
1789
+ }
1790
+ const entry = { session };
1791
+ if (idleTimeoutMs > 0) {
1792
+ entry.idleTimer = setTimeout(() => {
1793
+ evictEntry(state, key, entry);
1794
+ }, idleTimeoutMs);
1795
+ const timer = entry.idleTimer;
1796
+ if (timer !== void 0 && typeof timer.unref === "function") {
1797
+ timer.unref();
1798
+ }
1799
+ }
1800
+ bucket.push(entry);
1801
+ while (bucket.length > maxIdlePerKey) {
1802
+ const dropped = bucket.shift();
1803
+ if (dropped !== void 0) {
1804
+ clearEntryTimer(dropped);
1805
+ void safelyDisconnect(dropped.session);
1806
+ }
1807
+ }
1808
+ return Promise.resolve();
1809
+ };
1810
+ const acquire = async (profile) => {
1811
+ const key = keyOf(profile);
1812
+ const bucket = state.idle.get(key);
1813
+ if (bucket !== void 0 && bucket.length > 0) {
1814
+ const entry = bucket.pop();
1815
+ if (entry !== void 0) {
1816
+ clearEntryTimer(entry);
1817
+ if (bucket.length === 0) state.idle.delete(key);
1818
+ return { key, session: entry.session };
1819
+ }
1820
+ }
1821
+ const session = await inner.connect(profile);
1822
+ return { key, session };
1823
+ };
1824
+ return {
1825
+ connect: async (profile) => {
1826
+ const { key, session } = await acquire(profile);
1827
+ return wrapPooledSession(session, key, release);
1828
+ },
1829
+ drainPool: async () => {
1830
+ state.drained = true;
1831
+ const entries = [];
1832
+ for (const bucket of state.idle.values()) {
1833
+ for (const entry of bucket) {
1834
+ clearEntryTimer(entry);
1835
+ entries.push(entry);
1836
+ }
1837
+ }
1838
+ state.idle.clear();
1839
+ await Promise.all(entries.map((entry) => safelyDisconnect(entry.session)));
1840
+ },
1841
+ getCapabilities: ((providerId) => {
1842
+ if (providerId === void 0) return inner.getCapabilities();
1843
+ return inner.getCapabilities(providerId);
1844
+ }),
1845
+ hasProvider: (providerId) => inner.hasProvider(providerId),
1846
+ poolSize: () => {
1847
+ let total = 0;
1848
+ for (const bucket of state.idle.values()) total += bucket.length;
1849
+ return total;
1850
+ }
1851
+ };
1852
+ }
1853
+ function defaultKeyOf(profile) {
1854
+ const provider = profile.provider ?? profile.protocol ?? "unknown";
1855
+ const host = profile.host ?? "";
1856
+ const port = profile.port ?? "";
1857
+ const username = typeof profile.username === "string" ? profile.username : "";
1858
+ return `${provider}|${host}|${String(port)}|${username}`;
1859
+ }
1860
+ function evictEntry(state, key, entry) {
1861
+ const bucket = state.idle.get(key);
1862
+ if (bucket === void 0) return;
1863
+ const index = bucket.indexOf(entry);
1864
+ if (index < 0) return;
1865
+ bucket.splice(index, 1);
1866
+ if (bucket.length === 0) state.idle.delete(key);
1867
+ clearEntryTimer(entry);
1868
+ void safelyDisconnect(entry.session);
1869
+ }
1870
+ function clearEntryTimer(entry) {
1871
+ if (entry.idleTimer !== void 0) {
1872
+ clearTimeout(entry.idleTimer);
1873
+ delete entry.idleTimer;
1874
+ }
1875
+ }
1876
+ async function safelyDisconnect(session) {
1877
+ try {
1878
+ await session.disconnect();
1879
+ } catch {
1880
+ }
1881
+ }
1882
+ function isTaintingError(error) {
1883
+ return error instanceof ConnectionError || error instanceof TimeoutError || error instanceof ProtocolError;
1884
+ }
1885
+ function wrapPooledSession(session, key, release) {
1886
+ let tainted = false;
1887
+ let released = false;
1888
+ const guard = (fn) => {
1889
+ let promise;
1890
+ try {
1891
+ promise = fn();
1892
+ } catch (error) {
1893
+ if (isTaintingError(error)) tainted = true;
1894
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
1895
+ }
1896
+ return promise.catch((error) => {
1897
+ if (isTaintingError(error)) tainted = true;
1898
+ throw error;
1899
+ });
1900
+ };
1901
+ const fs = wrapFs(session.fs, guard);
1902
+ const transfers = session.transfers === void 0 ? void 0 : wrapTransfers(session.transfers, guard);
1903
+ const wrapped = {
1904
+ capabilities: session.capabilities,
1905
+ disconnect: async () => {
1906
+ if (released) return;
1907
+ released = true;
1908
+ await release(key, session, tainted);
1909
+ },
1910
+ fs,
1911
+ provider: session.provider,
1912
+ ...transfers !== void 0 ? { transfers } : {}
1913
+ };
1914
+ if (typeof session.raw === "function") {
1915
+ const rawFn = session.raw.bind(session);
1916
+ wrapped.raw = () => rawFn();
1917
+ }
1918
+ return wrapped;
1919
+ }
1920
+ function wrapFs(fs, guard) {
1921
+ const wrapped = {
1922
+ list: (path2, options) => guard(() => options !== void 0 ? fs.list(path2, options) : fs.list(path2)),
1923
+ stat: (path2, options) => guard(() => options !== void 0 ? fs.stat(path2, options) : fs.stat(path2))
1924
+ };
1925
+ if (typeof fs.remove === "function") {
1926
+ const remove = fs.remove.bind(fs);
1927
+ wrapped.remove = (path2, options) => guard(() => options !== void 0 ? remove(path2, options) : remove(path2));
1928
+ }
1929
+ if (typeof fs.rename === "function") {
1930
+ const rename2 = fs.rename.bind(fs);
1931
+ wrapped.rename = (from, to, options) => guard(() => options !== void 0 ? rename2(from, to, options) : rename2(from, to));
1932
+ }
1933
+ if (typeof fs.mkdir === "function") {
1934
+ const mkdir2 = fs.mkdir.bind(fs);
1935
+ wrapped.mkdir = (path2, options) => guard(() => options !== void 0 ? mkdir2(path2, options) : mkdir2(path2));
1936
+ }
1937
+ if (typeof fs.rmdir === "function") {
1938
+ const rmdir = fs.rmdir.bind(fs);
1939
+ wrapped.rmdir = (path2, options) => guard(() => options !== void 0 ? rmdir(path2, options) : rmdir(path2));
1940
+ }
1941
+ return wrapped;
1942
+ }
1943
+ function wrapTransfers(transfers, guard) {
1944
+ return {
1945
+ read: (request) => guard(() => Promise.resolve(transfers.read(request))),
1946
+ write: (request) => guard(() => Promise.resolve(transfers.write(request)))
1947
+ };
1948
+ }
1949
+
1770
1950
  // src/providers/local/LocalProvider.ts
1771
1951
  import { createReadStream } from "fs";
1772
1952
  import {
@@ -5078,6 +5258,7 @@ export {
5078
5258
  createLocalProviderFactory,
5079
5259
  createMemoryProviderFactory,
5080
5260
  createOAuthTokenSecretSource,
5261
+ createPooledTransferClient,
5081
5262
  createProgressEvent,
5082
5263
  createProviderTransferExecutor,
5083
5264
  createRemoteBrowser,