@zero-transfer/sdk 0.3.1 → 0.4.0
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.cjs +403 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +707 -334
- package/dist/index.d.ts +707 -334
- package/dist/index.mjs +390 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ __export(index_exports, {
|
|
|
40
40
|
ConnectionError: () => ConnectionError,
|
|
41
41
|
DEFAULT_FAILED_SUBDIR: () => DEFAULT_FAILED_SUBDIR,
|
|
42
42
|
DEFAULT_PROCESSED_SUBDIR: () => DEFAULT_PROCESSED_SUBDIR,
|
|
43
|
+
DEFAULT_SSH_ALGORITHM_PREFERENCES: () => DEFAULT_SSH_ALGORITHM_PREFERENCES,
|
|
43
44
|
FtpResponseParser: () => FtpResponseParser,
|
|
44
45
|
InMemoryAuditLog: () => InMemoryAuditLog,
|
|
45
46
|
MftScheduler: () => MftScheduler,
|
|
@@ -53,6 +54,14 @@ __export(index_exports, {
|
|
|
53
54
|
REMOTE_MANIFEST_FORMAT_VERSION: () => REMOTE_MANIFEST_FORMAT_VERSION,
|
|
54
55
|
RouteRegistry: () => RouteRegistry,
|
|
55
56
|
ScheduleRegistry: () => ScheduleRegistry,
|
|
57
|
+
SshAuthSession: () => SshAuthSession,
|
|
58
|
+
SshConnectionManager: () => SshConnectionManager,
|
|
59
|
+
SshDataReader: () => SshDataReader,
|
|
60
|
+
SshDataWriter: () => SshDataWriter,
|
|
61
|
+
SshDisconnectReason: () => SshDisconnectReason,
|
|
62
|
+
SshSessionChannel: () => SshSessionChannel,
|
|
63
|
+
SshTransportConnection: () => SshTransportConnection,
|
|
64
|
+
SshTransportHandshake: () => SshTransportHandshake,
|
|
56
65
|
TimeoutError: () => TimeoutError,
|
|
57
66
|
TransferClient: () => TransferClient,
|
|
58
67
|
TransferEngine: () => TransferEngine,
|
|
@@ -64,6 +73,7 @@ __export(index_exports, {
|
|
|
64
73
|
ZeroTransferError: () => ZeroTransferError,
|
|
65
74
|
assertSafeFtpArgument: () => assertSafeFtpArgument,
|
|
66
75
|
basenameRemotePath: () => basenameRemotePath,
|
|
76
|
+
buildPublickeyCredential: () => buildPublickeyCredential,
|
|
67
77
|
buildRemoteBreadcrumbs: () => buildRemoteBreadcrumbs,
|
|
68
78
|
compareRemoteManifests: () => compareRemoteManifests,
|
|
69
79
|
composeAuditLogs: () => composeAuditLogs,
|
|
@@ -73,6 +83,7 @@ __export(index_exports, {
|
|
|
73
83
|
createAzureBlobProviderFactory: () => createAzureBlobProviderFactory,
|
|
74
84
|
createBandwidthThrottle: () => createBandwidthThrottle,
|
|
75
85
|
createDropboxProviderFactory: () => createDropboxProviderFactory,
|
|
86
|
+
createFileSystemS3MultipartResumeStore: () => createFileSystemS3MultipartResumeStore,
|
|
76
87
|
createFtpProviderFactory: () => createFtpProviderFactory,
|
|
77
88
|
createFtpsProviderFactory: () => createFtpsProviderFactory,
|
|
78
89
|
createGcsProviderFactory: () => createGcsProviderFactory,
|
|
@@ -87,6 +98,7 @@ __export(index_exports, {
|
|
|
87
98
|
createOAuthTokenSecretSource: () => createOAuthTokenSecretSource,
|
|
88
99
|
createOneDriveProviderFactory: () => createOneDriveProviderFactory,
|
|
89
100
|
createOutboxRoute: () => createOutboxRoute,
|
|
101
|
+
createPooledTransferClient: () => createPooledTransferClient,
|
|
90
102
|
createProgressEvent: () => createProgressEvent,
|
|
91
103
|
createProviderTransferExecutor: () => createProviderTransferExecutor,
|
|
92
104
|
createRemoteBrowser: () => createRemoteBrowser,
|
|
@@ -120,6 +132,7 @@ __export(index_exports, {
|
|
|
120
132
|
joinRemotePath: () => joinRemotePath,
|
|
121
133
|
matchKnownHosts: () => matchKnownHosts,
|
|
122
134
|
matchKnownHostsEntry: () => matchKnownHostsEntry,
|
|
135
|
+
negotiateSshAlgorithms: () => negotiateSshAlgorithms,
|
|
123
136
|
nextCronFireAt: () => nextCronFireAt,
|
|
124
137
|
nextScheduleFireAt: () => nextScheduleFireAt,
|
|
125
138
|
noopLogger: () => noopLogger,
|
|
@@ -1930,6 +1943,186 @@ function summarizeDiagnosticError(error) {
|
|
|
1930
1943
|
return { message: String(error) };
|
|
1931
1944
|
}
|
|
1932
1945
|
|
|
1946
|
+
// src/core/ConnectionPool.ts
|
|
1947
|
+
var DEFAULT_MAX_IDLE_PER_KEY = 4;
|
|
1948
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 6e4;
|
|
1949
|
+
function createPooledTransferClient(inner, options = {}) {
|
|
1950
|
+
const maxIdlePerKey = Math.max(1, options.maxIdlePerKey ?? DEFAULT_MAX_IDLE_PER_KEY);
|
|
1951
|
+
const idleTimeoutMs = Math.max(0, options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS);
|
|
1952
|
+
const keyOf = options.keyOf ?? defaultKeyOf;
|
|
1953
|
+
const state = {
|
|
1954
|
+
drained: false,
|
|
1955
|
+
idle: /* @__PURE__ */ new Map()
|
|
1956
|
+
};
|
|
1957
|
+
const release = (key, session, tainted) => {
|
|
1958
|
+
if (tainted || state.drained) {
|
|
1959
|
+
return safelyDisconnect(session);
|
|
1960
|
+
}
|
|
1961
|
+
let bucket = state.idle.get(key);
|
|
1962
|
+
if (bucket === void 0) {
|
|
1963
|
+
bucket = [];
|
|
1964
|
+
state.idle.set(key, bucket);
|
|
1965
|
+
}
|
|
1966
|
+
const entry = { session };
|
|
1967
|
+
if (idleTimeoutMs > 0) {
|
|
1968
|
+
entry.idleTimer = setTimeout(() => {
|
|
1969
|
+
evictEntry(state, key, entry);
|
|
1970
|
+
}, idleTimeoutMs);
|
|
1971
|
+
const timer = entry.idleTimer;
|
|
1972
|
+
if (timer !== void 0 && typeof timer.unref === "function") {
|
|
1973
|
+
timer.unref();
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
bucket.push(entry);
|
|
1977
|
+
while (bucket.length > maxIdlePerKey) {
|
|
1978
|
+
const dropped = bucket.shift();
|
|
1979
|
+
if (dropped !== void 0) {
|
|
1980
|
+
clearEntryTimer(dropped);
|
|
1981
|
+
void safelyDisconnect(dropped.session);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
return Promise.resolve();
|
|
1985
|
+
};
|
|
1986
|
+
const acquire = async (profile) => {
|
|
1987
|
+
const key = keyOf(profile);
|
|
1988
|
+
const bucket = state.idle.get(key);
|
|
1989
|
+
if (bucket !== void 0 && bucket.length > 0) {
|
|
1990
|
+
const entry = bucket.pop();
|
|
1991
|
+
if (entry !== void 0) {
|
|
1992
|
+
clearEntryTimer(entry);
|
|
1993
|
+
if (bucket.length === 0) state.idle.delete(key);
|
|
1994
|
+
return { key, session: entry.session };
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
const session = await inner.connect(profile);
|
|
1998
|
+
return { key, session };
|
|
1999
|
+
};
|
|
2000
|
+
return {
|
|
2001
|
+
connect: async (profile) => {
|
|
2002
|
+
const { key, session } = await acquire(profile);
|
|
2003
|
+
return wrapPooledSession(session, key, release);
|
|
2004
|
+
},
|
|
2005
|
+
drainPool: async () => {
|
|
2006
|
+
state.drained = true;
|
|
2007
|
+
const entries = [];
|
|
2008
|
+
for (const bucket of state.idle.values()) {
|
|
2009
|
+
for (const entry of bucket) {
|
|
2010
|
+
clearEntryTimer(entry);
|
|
2011
|
+
entries.push(entry);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
state.idle.clear();
|
|
2015
|
+
await Promise.all(entries.map((entry) => safelyDisconnect(entry.session)));
|
|
2016
|
+
},
|
|
2017
|
+
getCapabilities: ((providerId) => {
|
|
2018
|
+
if (providerId === void 0) return inner.getCapabilities();
|
|
2019
|
+
return inner.getCapabilities(providerId);
|
|
2020
|
+
}),
|
|
2021
|
+
hasProvider: (providerId) => inner.hasProvider(providerId),
|
|
2022
|
+
poolSize: () => {
|
|
2023
|
+
let total = 0;
|
|
2024
|
+
for (const bucket of state.idle.values()) total += bucket.length;
|
|
2025
|
+
return total;
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
function defaultKeyOf(profile) {
|
|
2030
|
+
const provider = profile.provider ?? profile.protocol ?? "unknown";
|
|
2031
|
+
const host = profile.host ?? "";
|
|
2032
|
+
const port = profile.port ?? "";
|
|
2033
|
+
const username = typeof profile.username === "string" ? profile.username : "";
|
|
2034
|
+
return `${provider}|${host}|${String(port)}|${username}`;
|
|
2035
|
+
}
|
|
2036
|
+
function evictEntry(state, key, entry) {
|
|
2037
|
+
const bucket = state.idle.get(key);
|
|
2038
|
+
if (bucket === void 0) return;
|
|
2039
|
+
const index = bucket.indexOf(entry);
|
|
2040
|
+
if (index < 0) return;
|
|
2041
|
+
bucket.splice(index, 1);
|
|
2042
|
+
if (bucket.length === 0) state.idle.delete(key);
|
|
2043
|
+
clearEntryTimer(entry);
|
|
2044
|
+
void safelyDisconnect(entry.session);
|
|
2045
|
+
}
|
|
2046
|
+
function clearEntryTimer(entry) {
|
|
2047
|
+
if (entry.idleTimer !== void 0) {
|
|
2048
|
+
clearTimeout(entry.idleTimer);
|
|
2049
|
+
delete entry.idleTimer;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
async function safelyDisconnect(session) {
|
|
2053
|
+
try {
|
|
2054
|
+
await session.disconnect();
|
|
2055
|
+
} catch {
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
function isTaintingError(error) {
|
|
2059
|
+
return error instanceof ConnectionError || error instanceof TimeoutError || error instanceof ProtocolError;
|
|
2060
|
+
}
|
|
2061
|
+
function wrapPooledSession(session, key, release) {
|
|
2062
|
+
let tainted = false;
|
|
2063
|
+
let released = false;
|
|
2064
|
+
const guard = (fn) => {
|
|
2065
|
+
let promise;
|
|
2066
|
+
try {
|
|
2067
|
+
promise = fn();
|
|
2068
|
+
} catch (error) {
|
|
2069
|
+
if (isTaintingError(error)) tainted = true;
|
|
2070
|
+
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
2071
|
+
}
|
|
2072
|
+
return promise.catch((error) => {
|
|
2073
|
+
if (isTaintingError(error)) tainted = true;
|
|
2074
|
+
throw error;
|
|
2075
|
+
});
|
|
2076
|
+
};
|
|
2077
|
+
const fs = wrapFs(session.fs, guard);
|
|
2078
|
+
const transfers = session.transfers === void 0 ? void 0 : wrapTransfers(session.transfers, guard);
|
|
2079
|
+
const wrapped = {
|
|
2080
|
+
capabilities: session.capabilities,
|
|
2081
|
+
disconnect: async () => {
|
|
2082
|
+
if (released) return;
|
|
2083
|
+
released = true;
|
|
2084
|
+
await release(key, session, tainted);
|
|
2085
|
+
},
|
|
2086
|
+
fs,
|
|
2087
|
+
provider: session.provider,
|
|
2088
|
+
...transfers !== void 0 ? { transfers } : {}
|
|
2089
|
+
};
|
|
2090
|
+
if (typeof session.raw === "function") {
|
|
2091
|
+
const rawFn = session.raw.bind(session);
|
|
2092
|
+
wrapped.raw = () => rawFn();
|
|
2093
|
+
}
|
|
2094
|
+
return wrapped;
|
|
2095
|
+
}
|
|
2096
|
+
function wrapFs(fs, guard) {
|
|
2097
|
+
const wrapped = {
|
|
2098
|
+
list: (path2, options) => guard(() => options !== void 0 ? fs.list(path2, options) : fs.list(path2)),
|
|
2099
|
+
stat: (path2, options) => guard(() => options !== void 0 ? fs.stat(path2, options) : fs.stat(path2))
|
|
2100
|
+
};
|
|
2101
|
+
if (typeof fs.remove === "function") {
|
|
2102
|
+
const remove = fs.remove.bind(fs);
|
|
2103
|
+
wrapped.remove = (path2, options) => guard(() => options !== void 0 ? remove(path2, options) : remove(path2));
|
|
2104
|
+
}
|
|
2105
|
+
if (typeof fs.rename === "function") {
|
|
2106
|
+
const rename2 = fs.rename.bind(fs);
|
|
2107
|
+
wrapped.rename = (from, to, options) => guard(() => options !== void 0 ? rename2(from, to, options) : rename2(from, to));
|
|
2108
|
+
}
|
|
2109
|
+
if (typeof fs.mkdir === "function") {
|
|
2110
|
+
const mkdir2 = fs.mkdir.bind(fs);
|
|
2111
|
+
wrapped.mkdir = (path2, options) => guard(() => options !== void 0 ? mkdir2(path2, options) : mkdir2(path2));
|
|
2112
|
+
}
|
|
2113
|
+
if (typeof fs.rmdir === "function") {
|
|
2114
|
+
const rmdir = fs.rmdir.bind(fs);
|
|
2115
|
+
wrapped.rmdir = (path2, options) => guard(() => options !== void 0 ? rmdir(path2, options) : rmdir(path2));
|
|
2116
|
+
}
|
|
2117
|
+
return wrapped;
|
|
2118
|
+
}
|
|
2119
|
+
function wrapTransfers(transfers, guard) {
|
|
2120
|
+
return {
|
|
2121
|
+
read: (request) => guard(() => Promise.resolve(transfers.read(request))),
|
|
2122
|
+
write: (request) => guard(() => Promise.resolve(transfers.write(request)))
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
|
|
1933
2126
|
// src/providers/classic/ftp/FtpProvider.ts
|
|
1934
2127
|
var import_node_buffer3 = require("buffer");
|
|
1935
2128
|
var import_node_net = require("net");
|
|
@@ -3814,7 +4007,7 @@ var SshDataWriter = class {
|
|
|
3814
4007
|
length = 0;
|
|
3815
4008
|
writeByte(value) {
|
|
3816
4009
|
this.assertByte(value, "byte");
|
|
3817
|
-
const chunk = import_node_buffer5.Buffer.
|
|
4010
|
+
const chunk = import_node_buffer5.Buffer.alloc(1);
|
|
3818
4011
|
chunk.writeUInt8(value, 0);
|
|
3819
4012
|
return this.push(chunk);
|
|
3820
4013
|
}
|
|
@@ -3832,7 +4025,7 @@ var SshDataWriter = class {
|
|
|
3832
4025
|
retryable: false
|
|
3833
4026
|
});
|
|
3834
4027
|
}
|
|
3835
|
-
const chunk = import_node_buffer5.Buffer.
|
|
4028
|
+
const chunk = import_node_buffer5.Buffer.alloc(4);
|
|
3836
4029
|
chunk.writeUInt32BE(value, 0);
|
|
3837
4030
|
return this.push(chunk);
|
|
3838
4031
|
}
|
|
@@ -3844,7 +4037,7 @@ var SshDataWriter = class {
|
|
|
3844
4037
|
retryable: false
|
|
3845
4038
|
});
|
|
3846
4039
|
}
|
|
3847
|
-
const chunk = import_node_buffer5.Buffer.
|
|
4040
|
+
const chunk = import_node_buffer5.Buffer.alloc(8);
|
|
3848
4041
|
chunk.writeBigUInt64BE(value, 0);
|
|
3849
4042
|
return this.push(chunk);
|
|
3850
4043
|
}
|
|
@@ -5400,7 +5593,7 @@ function encodeSshTransportPacket(payload, options = {}) {
|
|
|
5400
5593
|
}
|
|
5401
5594
|
const padding = options.randomPadding === false ? import_node_buffer12.Buffer.alloc(paddingLength) : (0, import_node_crypto6.randomBytes)(paddingLength);
|
|
5402
5595
|
const packetLength = 1 + body.length + paddingLength;
|
|
5403
|
-
const frame = import_node_buffer12.Buffer.
|
|
5596
|
+
const frame = import_node_buffer12.Buffer.alloc(4 + packetLength);
|
|
5404
5597
|
frame.writeUInt32BE(packetLength, 0);
|
|
5405
5598
|
frame.writeUInt8(paddingLength, 4);
|
|
5406
5599
|
body.copy(frame, 5);
|
|
@@ -6276,7 +6469,7 @@ function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
|
|
|
6276
6469
|
return import_node_buffer15.Buffer.alloc(0);
|
|
6277
6470
|
}
|
|
6278
6471
|
const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
|
|
6279
|
-
const sequenceBuffer = import_node_buffer15.Buffer.
|
|
6472
|
+
const sequenceBuffer = import_node_buffer15.Buffer.alloc(4);
|
|
6280
6473
|
sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
|
|
6281
6474
|
return (0, import_node_crypto8.createHmac)(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
|
|
6282
6475
|
}
|
|
@@ -7228,7 +7421,7 @@ var SftpSession = class {
|
|
|
7228
7421
|
* serializes concurrent calls so byte ordering is preserved.
|
|
7229
7422
|
*/
|
|
7230
7423
|
sendRaw(encodedMessage, requestId) {
|
|
7231
|
-
const frame = import_node_buffer18.Buffer.
|
|
7424
|
+
const frame = import_node_buffer18.Buffer.alloc(4 + encodedMessage.length);
|
|
7232
7425
|
frame.writeUInt32BE(encodedMessage.length, 0);
|
|
7233
7426
|
encodedMessage.copy(frame, 4);
|
|
7234
7427
|
this.channel.sendData(frame).catch((err) => {
|
|
@@ -7346,44 +7539,54 @@ var NATIVE_SFTP_ALGORITHM_PREFERENCES = {
|
|
|
7346
7539
|
"rsa-sha2-256"
|
|
7347
7540
|
]
|
|
7348
7541
|
};
|
|
7349
|
-
var
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7542
|
+
var NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY = 8;
|
|
7543
|
+
function buildNativeSftpCapabilities(maxConcurrency) {
|
|
7544
|
+
return {
|
|
7545
|
+
provider: NATIVE_SFTP_PROVIDER_ID,
|
|
7546
|
+
authentication: ["password", "keyboard-interactive", "publickey"],
|
|
7547
|
+
list: true,
|
|
7548
|
+
stat: true,
|
|
7549
|
+
readStream: true,
|
|
7550
|
+
writeStream: true,
|
|
7551
|
+
serverSideCopy: false,
|
|
7552
|
+
serverSideMove: false,
|
|
7553
|
+
resumeDownload: true,
|
|
7554
|
+
resumeUpload: true,
|
|
7555
|
+
checksum: [],
|
|
7556
|
+
atomicRename: false,
|
|
7557
|
+
chmod: false,
|
|
7558
|
+
chown: false,
|
|
7559
|
+
symlink: true,
|
|
7560
|
+
metadata: ["accessedAt", "group", "modifiedAt", "owner", "permissions"],
|
|
7561
|
+
maxConcurrency,
|
|
7562
|
+
notes: [
|
|
7563
|
+
"Native SSH/SFTP provider using the project's own protocol stack (Waves 1\u20133).",
|
|
7564
|
+
"Supports password, keyboard-interactive, and public-key (Ed25519/RSA) authentication."
|
|
7565
|
+
]
|
|
7566
|
+
};
|
|
7567
|
+
}
|
|
7568
|
+
var NATIVE_SFTP_PROVIDER_CAPABILITIES = buildNativeSftpCapabilities(
|
|
7569
|
+
NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
7570
|
+
);
|
|
7372
7571
|
function createNativeSftpProviderFactory(options = {}) {
|
|
7373
7572
|
validateNativeSftpOptions(options);
|
|
7573
|
+
const capabilities = buildNativeSftpCapabilities(
|
|
7574
|
+
options.maxConcurrency ?? NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
7575
|
+
);
|
|
7374
7576
|
return {
|
|
7375
|
-
capabilities
|
|
7376
|
-
create: () => new NativeSftpProvider(options),
|
|
7577
|
+
capabilities,
|
|
7578
|
+
create: () => new NativeSftpProvider(options, capabilities),
|
|
7377
7579
|
id: NATIVE_SFTP_PROVIDER_ID
|
|
7378
7580
|
};
|
|
7379
7581
|
}
|
|
7380
7582
|
var NativeSftpProvider = class {
|
|
7381
|
-
constructor(options) {
|
|
7583
|
+
constructor(options, capabilities = NATIVE_SFTP_PROVIDER_CAPABILITIES) {
|
|
7382
7584
|
this.options = options;
|
|
7585
|
+
this.capabilities = capabilities;
|
|
7383
7586
|
}
|
|
7384
7587
|
options;
|
|
7385
7588
|
id = NATIVE_SFTP_PROVIDER_ID;
|
|
7386
|
-
capabilities
|
|
7589
|
+
capabilities;
|
|
7387
7590
|
async connect(profile) {
|
|
7388
7591
|
const resolved = await resolveConnectionProfileSecrets(profile);
|
|
7389
7592
|
const username = requireNativeSftpUsername(resolved);
|
|
@@ -8012,6 +8215,14 @@ function validateNativeSftpOptions(options) {
|
|
|
8012
8215
|
retryable: false
|
|
8013
8216
|
});
|
|
8014
8217
|
}
|
|
8218
|
+
if (options.maxConcurrency !== void 0 && (!Number.isInteger(options.maxConcurrency) || options.maxConcurrency <= 0)) {
|
|
8219
|
+
throw new ConfigurationError({
|
|
8220
|
+
details: { maxConcurrency: options.maxConcurrency },
|
|
8221
|
+
message: "Native SFTP provider maxConcurrency must be a positive integer",
|
|
8222
|
+
protocol: "sftp",
|
|
8223
|
+
retryable: false
|
|
8224
|
+
});
|
|
8225
|
+
}
|
|
8015
8226
|
}
|
|
8016
8227
|
|
|
8017
8228
|
// src/providers/web/httpInternals.ts
|
|
@@ -8549,6 +8760,7 @@ async function collectChunks(source) {
|
|
|
8549
8760
|
|
|
8550
8761
|
// src/providers/cloud/GoogleDriveProvider.ts
|
|
8551
8762
|
var import_node_buffer21 = require("buffer");
|
|
8763
|
+
var import_node_crypto10 = require("crypto");
|
|
8552
8764
|
var GDRIVE_API_BASE = "https://www.googleapis.com/drive/v3";
|
|
8553
8765
|
var GDRIVE_UPLOAD_BASE = "https://www.googleapis.com/upload/drive/v3";
|
|
8554
8766
|
var GDRIVE_FOLDER_MIME = "application/vnd.google-apps.folder";
|
|
@@ -8833,7 +9045,7 @@ var GoogleDriveTransferOperations = class {
|
|
|
8833
9045
|
const existing = await this.findExisting(parentId, name);
|
|
8834
9046
|
const metadata = { name };
|
|
8835
9047
|
if (existing === void 0) metadata["parents"] = [parentId];
|
|
8836
|
-
const boundary = `----zt-boundary-${
|
|
9048
|
+
const boundary = `----zt-boundary-${(0, import_node_crypto10.randomBytes)(16).toString("hex")}`;
|
|
8837
9049
|
const bodyParts = [];
|
|
8838
9050
|
const enc = new TextEncoder();
|
|
8839
9051
|
bodyParts.push(
|
|
@@ -11356,12 +11568,14 @@ function createWebDavProviderFactory(options = {}) {
|
|
|
11356
11568
|
const secure = options.secure ?? false;
|
|
11357
11569
|
const basePath = options.basePath ?? "";
|
|
11358
11570
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
11571
|
+
const uploadStreaming = options.uploadStreaming ?? "when-known-size";
|
|
11359
11572
|
if (typeof fetchImpl !== "function") {
|
|
11360
11573
|
throw new ConfigurationError({
|
|
11361
11574
|
message: "Global fetch is unavailable; supply WebDavProviderOptions.fetch explicitly",
|
|
11362
11575
|
retryable: false
|
|
11363
11576
|
});
|
|
11364
11577
|
}
|
|
11578
|
+
const streamingNote = uploadStreaming === "always" ? "PUT bodies are always streamed (chunked when size is unknown)." : uploadStreaming === "never" ? "PUT bodies are buffered in memory (uploadStreaming: 'never')." : "PUT bodies stream when totalBytes is known; otherwise buffered in memory.";
|
|
11365
11579
|
const capabilities = {
|
|
11366
11580
|
atomicRename: false,
|
|
11367
11581
|
authentication: ["anonymous", "password", "token"],
|
|
@@ -11371,7 +11585,7 @@ function createWebDavProviderFactory(options = {}) {
|
|
|
11371
11585
|
list: true,
|
|
11372
11586
|
maxConcurrency: 8,
|
|
11373
11587
|
metadata: ["modifiedAt", "mimeType", "uniqueId"],
|
|
11374
|
-
notes: [
|
|
11588
|
+
notes: [streamingNote],
|
|
11375
11589
|
provider: id,
|
|
11376
11590
|
readStream: true,
|
|
11377
11591
|
resumeDownload: true,
|
|
@@ -11390,7 +11604,8 @@ function createWebDavProviderFactory(options = {}) {
|
|
|
11390
11604
|
defaultHeaders: { ...options.defaultHeaders ?? {} },
|
|
11391
11605
|
fetch: fetchImpl,
|
|
11392
11606
|
id,
|
|
11393
|
-
secure
|
|
11607
|
+
secure,
|
|
11608
|
+
uploadStreaming
|
|
11394
11609
|
}),
|
|
11395
11610
|
id
|
|
11396
11611
|
};
|
|
@@ -11424,7 +11639,8 @@ var WebDavProvider = class {
|
|
|
11424
11639
|
capabilities: this.internals.capabilities,
|
|
11425
11640
|
fetch: this.internals.fetch,
|
|
11426
11641
|
headers,
|
|
11427
|
-
id: this.internals.id
|
|
11642
|
+
id: this.internals.id,
|
|
11643
|
+
uploadStreaming: this.internals.uploadStreaming
|
|
11428
11644
|
};
|
|
11429
11645
|
if (profile.timeoutMs !== void 0) sessionOptions.timeoutMs = profile.timeoutMs;
|
|
11430
11646
|
return new WebDavSession(sessionOptions);
|
|
@@ -11549,13 +11765,53 @@ var WebDavTransferOperations = class {
|
|
|
11549
11765
|
}
|
|
11550
11766
|
const normalized = normalizeRemotePath(request.endpoint.path);
|
|
11551
11767
|
const url = resolveUrl(this.options.baseUrl, normalized);
|
|
11552
|
-
const
|
|
11768
|
+
const totalBytes = request.totalBytes;
|
|
11769
|
+
const policy = this.options.uploadStreaming;
|
|
11770
|
+
const shouldStream = policy === "always" || policy === "when-known-size" && typeof totalBytes === "number" && totalBytes >= 0;
|
|
11771
|
+
if (!shouldStream) {
|
|
11772
|
+
const buffered = await collectChunks6(request.content);
|
|
11773
|
+
const headers2 = {
|
|
11774
|
+
"Content-Length": String(buffered.byteLength),
|
|
11775
|
+
"Content-Type": "application/octet-stream"
|
|
11776
|
+
};
|
|
11777
|
+
const init2 = {
|
|
11778
|
+
body: buffered,
|
|
11779
|
+
headers: headers2,
|
|
11780
|
+
method: "PUT"
|
|
11781
|
+
};
|
|
11782
|
+
if (request.signal !== void 0) init2.signal = request.signal;
|
|
11783
|
+
const response2 = await dispatchRequest(this.options, url, init2);
|
|
11784
|
+
if (!response2.ok) {
|
|
11785
|
+
throw mapResponseError(response2, normalized);
|
|
11786
|
+
}
|
|
11787
|
+
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
11788
|
+
const result2 = {
|
|
11789
|
+
bytesTransferred: buffered.byteLength,
|
|
11790
|
+
totalBytes: buffered.byteLength
|
|
11791
|
+
};
|
|
11792
|
+
const etag2 = response2.headers.get("etag");
|
|
11793
|
+
if (etag2 !== null) result2.checksum = etag2;
|
|
11794
|
+
return result2;
|
|
11795
|
+
}
|
|
11796
|
+
let bytesTransferred = 0;
|
|
11797
|
+
const knownTotal = typeof totalBytes === "number" ? totalBytes : void 0;
|
|
11798
|
+
const stream = asyncIterableToReadableStream(request.content, (chunk) => {
|
|
11799
|
+
bytesTransferred += chunk.byteLength;
|
|
11800
|
+
if (knownTotal !== void 0) {
|
|
11801
|
+
request.reportProgress(bytesTransferred, knownTotal);
|
|
11802
|
+
} else {
|
|
11803
|
+
request.reportProgress(bytesTransferred);
|
|
11804
|
+
}
|
|
11805
|
+
});
|
|
11553
11806
|
const headers = {
|
|
11554
|
-
"Content-Length": String(buffered.byteLength),
|
|
11555
11807
|
"Content-Type": "application/octet-stream"
|
|
11556
11808
|
};
|
|
11809
|
+
if (knownTotal !== void 0) {
|
|
11810
|
+
headers["Content-Length"] = String(knownTotal);
|
|
11811
|
+
}
|
|
11557
11812
|
const init = {
|
|
11558
|
-
body:
|
|
11813
|
+
body: stream,
|
|
11814
|
+
duplex: "half",
|
|
11559
11815
|
headers,
|
|
11560
11816
|
method: "PUT"
|
|
11561
11817
|
};
|
|
@@ -11564,10 +11820,9 @@ var WebDavTransferOperations = class {
|
|
|
11564
11820
|
if (!response.ok) {
|
|
11565
11821
|
throw mapResponseError(response, normalized);
|
|
11566
11822
|
}
|
|
11567
|
-
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
11568
11823
|
const result = {
|
|
11569
|
-
bytesTransferred
|
|
11570
|
-
totalBytes:
|
|
11824
|
+
bytesTransferred,
|
|
11825
|
+
...knownTotal !== void 0 ? { totalBytes: knownTotal } : { totalBytes: bytesTransferred }
|
|
11571
11826
|
};
|
|
11572
11827
|
const etag = response.headers.get("etag");
|
|
11573
11828
|
if (etag !== null) result.checksum = etag;
|
|
@@ -11589,6 +11844,36 @@ async function collectChunks6(source) {
|
|
|
11589
11844
|
}
|
|
11590
11845
|
return out;
|
|
11591
11846
|
}
|
|
11847
|
+
function asyncIterableToReadableStream(source, onChunk) {
|
|
11848
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
11849
|
+
return new ReadableStream({
|
|
11850
|
+
async pull(controller) {
|
|
11851
|
+
try {
|
|
11852
|
+
const next = await iterator.next();
|
|
11853
|
+
if (next.done === true) {
|
|
11854
|
+
controller.close();
|
|
11855
|
+
return;
|
|
11856
|
+
}
|
|
11857
|
+
const chunk = next.value;
|
|
11858
|
+
if (chunk.byteLength === 0) {
|
|
11859
|
+
return;
|
|
11860
|
+
}
|
|
11861
|
+
controller.enqueue(chunk);
|
|
11862
|
+
onChunk(chunk);
|
|
11863
|
+
} catch (error) {
|
|
11864
|
+
controller.error(error);
|
|
11865
|
+
}
|
|
11866
|
+
},
|
|
11867
|
+
async cancel(reason) {
|
|
11868
|
+
if (typeof iterator.return === "function") {
|
|
11869
|
+
try {
|
|
11870
|
+
await iterator.return(reason);
|
|
11871
|
+
} catch {
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11874
|
+
}
|
|
11875
|
+
});
|
|
11876
|
+
}
|
|
11592
11877
|
function parsePropfindResponses(xml, baseUrl) {
|
|
11593
11878
|
const entries = [];
|
|
11594
11879
|
const responseRegex = /<(?:[a-zA-Z0-9-]+:)?response\b[^>]*>([\s\S]*?)<\/(?:[a-zA-Z0-9-]+:)?response>/gi;
|
|
@@ -11668,8 +11953,13 @@ function parentOf(path2) {
|
|
|
11668
11953
|
return path2.slice(0, idx);
|
|
11669
11954
|
}
|
|
11670
11955
|
|
|
11956
|
+
// src/providers/web/S3Provider.ts
|
|
11957
|
+
var import_node_crypto12 = require("crypto");
|
|
11958
|
+
var import_promises3 = require("fs/promises");
|
|
11959
|
+
var import_node_path3 = require("path");
|
|
11960
|
+
|
|
11671
11961
|
// src/providers/web/awsSigv4.ts
|
|
11672
|
-
var
|
|
11962
|
+
var import_node_crypto11 = require("crypto");
|
|
11673
11963
|
function signSigV4(input) {
|
|
11674
11964
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
11675
11965
|
const amzDate = formatAmzDate(now);
|
|
@@ -11739,13 +12029,13 @@ function encodeRfc3986(value) {
|
|
|
11739
12029
|
);
|
|
11740
12030
|
}
|
|
11741
12031
|
function sha256Hex(data) {
|
|
11742
|
-
return (0,
|
|
12032
|
+
return (0, import_node_crypto11.createHash)("sha256").update(data).digest("hex");
|
|
11743
12033
|
}
|
|
11744
12034
|
function hmac(key, data) {
|
|
11745
|
-
return (0,
|
|
12035
|
+
return (0, import_node_crypto11.createHmac)("sha256", key).update(data, "utf8").digest();
|
|
11746
12036
|
}
|
|
11747
12037
|
function hmacHex(key, data) {
|
|
11748
|
-
return (0,
|
|
12038
|
+
return (0, import_node_crypto11.createHmac)("sha256", key).update(data, "utf8").digest("hex");
|
|
11749
12039
|
}
|
|
11750
12040
|
|
|
11751
12041
|
// src/providers/web/S3Provider.ts
|
|
@@ -11762,6 +12052,48 @@ function createMemoryS3MultipartResumeStore() {
|
|
|
11762
12052
|
}
|
|
11763
12053
|
};
|
|
11764
12054
|
}
|
|
12055
|
+
function createFileSystemS3MultipartResumeStore(options) {
|
|
12056
|
+
const directory = options.directory;
|
|
12057
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
12058
|
+
throw new ConfigurationError({
|
|
12059
|
+
message: "createFileSystemS3MultipartResumeStore requires a non-empty directory option",
|
|
12060
|
+
retryable: false
|
|
12061
|
+
});
|
|
12062
|
+
}
|
|
12063
|
+
const fileFor = (key) => {
|
|
12064
|
+
const hash = (0, import_node_crypto12.createHash)("sha256").update(`${key.bucket}\0${key.jobId}\0${key.path}`).digest("hex");
|
|
12065
|
+
return (0, import_node_path3.join)(directory, `${hash}.json`);
|
|
12066
|
+
};
|
|
12067
|
+
return {
|
|
12068
|
+
async clear(key) {
|
|
12069
|
+
try {
|
|
12070
|
+
await (0, import_promises3.unlink)(fileFor(key));
|
|
12071
|
+
} catch (error) {
|
|
12072
|
+
if (error.code !== "ENOENT") throw error;
|
|
12073
|
+
}
|
|
12074
|
+
},
|
|
12075
|
+
async load(key) {
|
|
12076
|
+
try {
|
|
12077
|
+
const text = await (0, import_promises3.readFile)(fileFor(key), "utf8");
|
|
12078
|
+
const parsed = JSON.parse(text);
|
|
12079
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.uploadId !== "string" || !Array.isArray(parsed.parts)) {
|
|
12080
|
+
return void 0;
|
|
12081
|
+
}
|
|
12082
|
+
return parsed;
|
|
12083
|
+
} catch (error) {
|
|
12084
|
+
if (error.code === "ENOENT") return void 0;
|
|
12085
|
+
throw error;
|
|
12086
|
+
}
|
|
12087
|
+
},
|
|
12088
|
+
async save(key, checkpoint) {
|
|
12089
|
+
await (0, import_promises3.mkdir)(directory, { recursive: true });
|
|
12090
|
+
const target = fileFor(key);
|
|
12091
|
+
const tmp = `${target}.${String(process.pid)}.${String(Date.now())}.tmp`;
|
|
12092
|
+
await (0, import_promises3.writeFile)(tmp, JSON.stringify(checkpoint), { encoding: "utf8", mode: 384 });
|
|
12093
|
+
await (0, import_promises3.rename)(tmp, target);
|
|
12094
|
+
}
|
|
12095
|
+
};
|
|
12096
|
+
}
|
|
11765
12097
|
var DEFAULT_MULTIPART_PART_SIZE = 8 * 1024 * 1024;
|
|
11766
12098
|
var DEFAULT_MULTIPART_THRESHOLD = 8 * 1024 * 1024;
|
|
11767
12099
|
var S3_CHECKSUM_CAPABILITIES = ["etag"];
|
|
@@ -11789,7 +12121,7 @@ function createS3ProviderFactory(options = {}) {
|
|
|
11789
12121
|
retryable: false
|
|
11790
12122
|
});
|
|
11791
12123
|
}
|
|
11792
|
-
const multipartEnabled = options.multipart?.enabled ??
|
|
12124
|
+
const multipartEnabled = options.multipart?.enabled ?? true;
|
|
11793
12125
|
const multipart = {
|
|
11794
12126
|
enabled: multipartEnabled,
|
|
11795
12127
|
partSizeBytes: options.multipart?.partSizeBytes ?? DEFAULT_MULTIPART_PART_SIZE,
|
|
@@ -11806,9 +12138,11 @@ function createS3ProviderFactory(options = {}) {
|
|
|
11806
12138
|
maxConcurrency: 16,
|
|
11807
12139
|
metadata: ["modifiedAt", "mimeType", "uniqueId"],
|
|
11808
12140
|
notes: multipartEnabled ? [
|
|
11809
|
-
`S3 multipart upload enabled (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B)
|
|
12141
|
+
`S3 multipart upload enabled by default (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B).`,
|
|
12142
|
+
"Payloads at or below the threshold automatically fall back to single-shot PUT.",
|
|
12143
|
+
"Pass `multipart: { enabled: false }` to force the legacy single-shot behaviour."
|
|
11810
12144
|
] : [
|
|
11811
|
-
"S3 provider performs single-shot PUT uploads;
|
|
12145
|
+
"S3 provider performs single-shot PUT uploads; entire object is buffered in memory before transmission."
|
|
11812
12146
|
],
|
|
11813
12147
|
provider: id,
|
|
11814
12148
|
readStream: true,
|
|
@@ -12426,15 +12760,15 @@ function getBuiltinCapabilityMatrix() {
|
|
|
12426
12760
|
{
|
|
12427
12761
|
capabilities: createS3ProviderFactory({ fetch: noopFetch }).capabilities,
|
|
12428
12762
|
id: "s3",
|
|
12429
|
-
label: "S3-compatible (
|
|
12763
|
+
label: "S3-compatible (multipart uploads, default)"
|
|
12430
12764
|
},
|
|
12431
12765
|
{
|
|
12432
12766
|
capabilities: createS3ProviderFactory({
|
|
12433
12767
|
fetch: noopFetch,
|
|
12434
|
-
multipart: { enabled:
|
|
12768
|
+
multipart: { enabled: false }
|
|
12435
12769
|
}).capabilities,
|
|
12436
|
-
id: "s3:
|
|
12437
|
-
label: "S3-compatible (
|
|
12770
|
+
id: "s3:single-shot",
|
|
12771
|
+
label: "S3-compatible (single-shot uploads)"
|
|
12438
12772
|
},
|
|
12439
12773
|
{
|
|
12440
12774
|
capabilities: createDropboxProviderFactory({ fetch: noopFetch }).capabilities,
|
|
@@ -14522,9 +14856,9 @@ function deepFreeze(value) {
|
|
|
14522
14856
|
}
|
|
14523
14857
|
|
|
14524
14858
|
// src/mft/webhooks.ts
|
|
14525
|
-
var
|
|
14859
|
+
var import_node_crypto13 = require("crypto");
|
|
14526
14860
|
function signWebhookPayload(payload, secret, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
14527
|
-
const mac = (0,
|
|
14861
|
+
const mac = (0, import_node_crypto13.createHmac)("sha256", secret);
|
|
14528
14862
|
mac.update(`${timestamp}.${payload}`);
|
|
14529
14863
|
return { digest: mac.digest("hex"), timestamp };
|
|
14530
14864
|
}
|
|
@@ -15115,6 +15449,7 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15115
15449
|
ConnectionError,
|
|
15116
15450
|
DEFAULT_FAILED_SUBDIR,
|
|
15117
15451
|
DEFAULT_PROCESSED_SUBDIR,
|
|
15452
|
+
DEFAULT_SSH_ALGORITHM_PREFERENCES,
|
|
15118
15453
|
FtpResponseParser,
|
|
15119
15454
|
InMemoryAuditLog,
|
|
15120
15455
|
MftScheduler,
|
|
@@ -15128,6 +15463,14 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15128
15463
|
REMOTE_MANIFEST_FORMAT_VERSION,
|
|
15129
15464
|
RouteRegistry,
|
|
15130
15465
|
ScheduleRegistry,
|
|
15466
|
+
SshAuthSession,
|
|
15467
|
+
SshConnectionManager,
|
|
15468
|
+
SshDataReader,
|
|
15469
|
+
SshDataWriter,
|
|
15470
|
+
SshDisconnectReason,
|
|
15471
|
+
SshSessionChannel,
|
|
15472
|
+
SshTransportConnection,
|
|
15473
|
+
SshTransportHandshake,
|
|
15131
15474
|
TimeoutError,
|
|
15132
15475
|
TransferClient,
|
|
15133
15476
|
TransferEngine,
|
|
@@ -15139,6 +15482,7 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15139
15482
|
ZeroTransferError,
|
|
15140
15483
|
assertSafeFtpArgument,
|
|
15141
15484
|
basenameRemotePath,
|
|
15485
|
+
buildPublickeyCredential,
|
|
15142
15486
|
buildRemoteBreadcrumbs,
|
|
15143
15487
|
compareRemoteManifests,
|
|
15144
15488
|
composeAuditLogs,
|
|
@@ -15148,6 +15492,7 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15148
15492
|
createAzureBlobProviderFactory,
|
|
15149
15493
|
createBandwidthThrottle,
|
|
15150
15494
|
createDropboxProviderFactory,
|
|
15495
|
+
createFileSystemS3MultipartResumeStore,
|
|
15151
15496
|
createFtpProviderFactory,
|
|
15152
15497
|
createFtpsProviderFactory,
|
|
15153
15498
|
createGcsProviderFactory,
|
|
@@ -15162,6 +15507,7 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15162
15507
|
createOAuthTokenSecretSource,
|
|
15163
15508
|
createOneDriveProviderFactory,
|
|
15164
15509
|
createOutboxRoute,
|
|
15510
|
+
createPooledTransferClient,
|
|
15165
15511
|
createProgressEvent,
|
|
15166
15512
|
createProviderTransferExecutor,
|
|
15167
15513
|
createRemoteBrowser,
|
|
@@ -15195,6 +15541,7 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15195
15541
|
joinRemotePath,
|
|
15196
15542
|
matchKnownHosts,
|
|
15197
15543
|
matchKnownHostsEntry,
|
|
15544
|
+
negotiateSshAlgorithms,
|
|
15198
15545
|
nextCronFireAt,
|
|
15199
15546
|
nextScheduleFireAt,
|
|
15200
15547
|
noopLogger,
|