@zero-transfer/classic 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.cjs +233 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +315 -5
- package/dist/index.d.ts +315 -5
- package/dist/index.mjs +232 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -66,6 +66,7 @@ __export(classic_exports, {
|
|
|
66
66
|
createLocalProviderFactory: () => createLocalProviderFactory,
|
|
67
67
|
createMemoryProviderFactory: () => createMemoryProviderFactory,
|
|
68
68
|
createOAuthTokenSecretSource: () => createOAuthTokenSecretSource,
|
|
69
|
+
createPooledTransferClient: () => createPooledTransferClient,
|
|
69
70
|
createProgressEvent: () => createProgressEvent,
|
|
70
71
|
createProviderTransferExecutor: () => createProviderTransferExecutor,
|
|
71
72
|
createRemoteBrowser: () => createRemoteBrowser,
|
|
@@ -1892,6 +1893,186 @@ function summarizeDiagnosticError(error) {
|
|
|
1892
1893
|
return { message: String(error) };
|
|
1893
1894
|
}
|
|
1894
1895
|
|
|
1896
|
+
// src/core/ConnectionPool.ts
|
|
1897
|
+
var DEFAULT_MAX_IDLE_PER_KEY = 4;
|
|
1898
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 6e4;
|
|
1899
|
+
function createPooledTransferClient(inner, options = {}) {
|
|
1900
|
+
const maxIdlePerKey = Math.max(1, options.maxIdlePerKey ?? DEFAULT_MAX_IDLE_PER_KEY);
|
|
1901
|
+
const idleTimeoutMs = Math.max(0, options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS);
|
|
1902
|
+
const keyOf = options.keyOf ?? defaultKeyOf;
|
|
1903
|
+
const state = {
|
|
1904
|
+
drained: false,
|
|
1905
|
+
idle: /* @__PURE__ */ new Map()
|
|
1906
|
+
};
|
|
1907
|
+
const release = (key, session, tainted) => {
|
|
1908
|
+
if (tainted || state.drained) {
|
|
1909
|
+
return safelyDisconnect(session);
|
|
1910
|
+
}
|
|
1911
|
+
let bucket = state.idle.get(key);
|
|
1912
|
+
if (bucket === void 0) {
|
|
1913
|
+
bucket = [];
|
|
1914
|
+
state.idle.set(key, bucket);
|
|
1915
|
+
}
|
|
1916
|
+
const entry = { session };
|
|
1917
|
+
if (idleTimeoutMs > 0) {
|
|
1918
|
+
entry.idleTimer = setTimeout(() => {
|
|
1919
|
+
evictEntry(state, key, entry);
|
|
1920
|
+
}, idleTimeoutMs);
|
|
1921
|
+
const timer = entry.idleTimer;
|
|
1922
|
+
if (timer !== void 0 && typeof timer.unref === "function") {
|
|
1923
|
+
timer.unref();
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
bucket.push(entry);
|
|
1927
|
+
while (bucket.length > maxIdlePerKey) {
|
|
1928
|
+
const dropped = bucket.shift();
|
|
1929
|
+
if (dropped !== void 0) {
|
|
1930
|
+
clearEntryTimer(dropped);
|
|
1931
|
+
void safelyDisconnect(dropped.session);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
return Promise.resolve();
|
|
1935
|
+
};
|
|
1936
|
+
const acquire = async (profile) => {
|
|
1937
|
+
const key = keyOf(profile);
|
|
1938
|
+
const bucket = state.idle.get(key);
|
|
1939
|
+
if (bucket !== void 0 && bucket.length > 0) {
|
|
1940
|
+
const entry = bucket.pop();
|
|
1941
|
+
if (entry !== void 0) {
|
|
1942
|
+
clearEntryTimer(entry);
|
|
1943
|
+
if (bucket.length === 0) state.idle.delete(key);
|
|
1944
|
+
return { key, session: entry.session };
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
const session = await inner.connect(profile);
|
|
1948
|
+
return { key, session };
|
|
1949
|
+
};
|
|
1950
|
+
return {
|
|
1951
|
+
connect: async (profile) => {
|
|
1952
|
+
const { key, session } = await acquire(profile);
|
|
1953
|
+
return wrapPooledSession(session, key, release);
|
|
1954
|
+
},
|
|
1955
|
+
drainPool: async () => {
|
|
1956
|
+
state.drained = true;
|
|
1957
|
+
const entries = [];
|
|
1958
|
+
for (const bucket of state.idle.values()) {
|
|
1959
|
+
for (const entry of bucket) {
|
|
1960
|
+
clearEntryTimer(entry);
|
|
1961
|
+
entries.push(entry);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
state.idle.clear();
|
|
1965
|
+
await Promise.all(entries.map((entry) => safelyDisconnect(entry.session)));
|
|
1966
|
+
},
|
|
1967
|
+
getCapabilities: ((providerId) => {
|
|
1968
|
+
if (providerId === void 0) return inner.getCapabilities();
|
|
1969
|
+
return inner.getCapabilities(providerId);
|
|
1970
|
+
}),
|
|
1971
|
+
hasProvider: (providerId) => inner.hasProvider(providerId),
|
|
1972
|
+
poolSize: () => {
|
|
1973
|
+
let total = 0;
|
|
1974
|
+
for (const bucket of state.idle.values()) total += bucket.length;
|
|
1975
|
+
return total;
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
function defaultKeyOf(profile) {
|
|
1980
|
+
const provider = profile.provider ?? profile.protocol ?? "unknown";
|
|
1981
|
+
const host = profile.host ?? "";
|
|
1982
|
+
const port = profile.port ?? "";
|
|
1983
|
+
const username = typeof profile.username === "string" ? profile.username : "";
|
|
1984
|
+
return `${provider}|${host}|${String(port)}|${username}`;
|
|
1985
|
+
}
|
|
1986
|
+
function evictEntry(state, key, entry) {
|
|
1987
|
+
const bucket = state.idle.get(key);
|
|
1988
|
+
if (bucket === void 0) return;
|
|
1989
|
+
const index = bucket.indexOf(entry);
|
|
1990
|
+
if (index < 0) return;
|
|
1991
|
+
bucket.splice(index, 1);
|
|
1992
|
+
if (bucket.length === 0) state.idle.delete(key);
|
|
1993
|
+
clearEntryTimer(entry);
|
|
1994
|
+
void safelyDisconnect(entry.session);
|
|
1995
|
+
}
|
|
1996
|
+
function clearEntryTimer(entry) {
|
|
1997
|
+
if (entry.idleTimer !== void 0) {
|
|
1998
|
+
clearTimeout(entry.idleTimer);
|
|
1999
|
+
delete entry.idleTimer;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
async function safelyDisconnect(session) {
|
|
2003
|
+
try {
|
|
2004
|
+
await session.disconnect();
|
|
2005
|
+
} catch {
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
function isTaintingError(error) {
|
|
2009
|
+
return error instanceof ConnectionError || error instanceof TimeoutError || error instanceof ProtocolError;
|
|
2010
|
+
}
|
|
2011
|
+
function wrapPooledSession(session, key, release) {
|
|
2012
|
+
let tainted = false;
|
|
2013
|
+
let released = false;
|
|
2014
|
+
const guard = (fn) => {
|
|
2015
|
+
let promise;
|
|
2016
|
+
try {
|
|
2017
|
+
promise = fn();
|
|
2018
|
+
} catch (error) {
|
|
2019
|
+
if (isTaintingError(error)) tainted = true;
|
|
2020
|
+
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
2021
|
+
}
|
|
2022
|
+
return promise.catch((error) => {
|
|
2023
|
+
if (isTaintingError(error)) tainted = true;
|
|
2024
|
+
throw error;
|
|
2025
|
+
});
|
|
2026
|
+
};
|
|
2027
|
+
const fs = wrapFs(session.fs, guard);
|
|
2028
|
+
const transfers = session.transfers === void 0 ? void 0 : wrapTransfers(session.transfers, guard);
|
|
2029
|
+
const wrapped = {
|
|
2030
|
+
capabilities: session.capabilities,
|
|
2031
|
+
disconnect: async () => {
|
|
2032
|
+
if (released) return;
|
|
2033
|
+
released = true;
|
|
2034
|
+
await release(key, session, tainted);
|
|
2035
|
+
},
|
|
2036
|
+
fs,
|
|
2037
|
+
provider: session.provider,
|
|
2038
|
+
...transfers !== void 0 ? { transfers } : {}
|
|
2039
|
+
};
|
|
2040
|
+
if (typeof session.raw === "function") {
|
|
2041
|
+
const rawFn = session.raw.bind(session);
|
|
2042
|
+
wrapped.raw = () => rawFn();
|
|
2043
|
+
}
|
|
2044
|
+
return wrapped;
|
|
2045
|
+
}
|
|
2046
|
+
function wrapFs(fs, guard) {
|
|
2047
|
+
const wrapped = {
|
|
2048
|
+
list: (path2, options) => guard(() => options !== void 0 ? fs.list(path2, options) : fs.list(path2)),
|
|
2049
|
+
stat: (path2, options) => guard(() => options !== void 0 ? fs.stat(path2, options) : fs.stat(path2))
|
|
2050
|
+
};
|
|
2051
|
+
if (typeof fs.remove === "function") {
|
|
2052
|
+
const remove = fs.remove.bind(fs);
|
|
2053
|
+
wrapped.remove = (path2, options) => guard(() => options !== void 0 ? remove(path2, options) : remove(path2));
|
|
2054
|
+
}
|
|
2055
|
+
if (typeof fs.rename === "function") {
|
|
2056
|
+
const rename2 = fs.rename.bind(fs);
|
|
2057
|
+
wrapped.rename = (from, to, options) => guard(() => options !== void 0 ? rename2(from, to, options) : rename2(from, to));
|
|
2058
|
+
}
|
|
2059
|
+
if (typeof fs.mkdir === "function") {
|
|
2060
|
+
const mkdir2 = fs.mkdir.bind(fs);
|
|
2061
|
+
wrapped.mkdir = (path2, options) => guard(() => options !== void 0 ? mkdir2(path2, options) : mkdir2(path2));
|
|
2062
|
+
}
|
|
2063
|
+
if (typeof fs.rmdir === "function") {
|
|
2064
|
+
const rmdir = fs.rmdir.bind(fs);
|
|
2065
|
+
wrapped.rmdir = (path2, options) => guard(() => options !== void 0 ? rmdir(path2, options) : rmdir(path2));
|
|
2066
|
+
}
|
|
2067
|
+
return wrapped;
|
|
2068
|
+
}
|
|
2069
|
+
function wrapTransfers(transfers, guard) {
|
|
2070
|
+
return {
|
|
2071
|
+
read: (request) => guard(() => Promise.resolve(transfers.read(request))),
|
|
2072
|
+
write: (request) => guard(() => Promise.resolve(transfers.write(request)))
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
|
|
1895
2076
|
// src/providers/local/LocalProvider.ts
|
|
1896
2077
|
var import_node_fs = require("fs");
|
|
1897
2078
|
var import_promises2 = require("fs/promises");
|
|
@@ -6400,7 +6581,7 @@ var SshDataWriter = class {
|
|
|
6400
6581
|
length = 0;
|
|
6401
6582
|
writeByte(value) {
|
|
6402
6583
|
this.assertByte(value, "byte");
|
|
6403
|
-
const chunk = import_node_buffer7.Buffer.
|
|
6584
|
+
const chunk = import_node_buffer7.Buffer.alloc(1);
|
|
6404
6585
|
chunk.writeUInt8(value, 0);
|
|
6405
6586
|
return this.push(chunk);
|
|
6406
6587
|
}
|
|
@@ -6418,7 +6599,7 @@ var SshDataWriter = class {
|
|
|
6418
6599
|
retryable: false
|
|
6419
6600
|
});
|
|
6420
6601
|
}
|
|
6421
|
-
const chunk = import_node_buffer7.Buffer.
|
|
6602
|
+
const chunk = import_node_buffer7.Buffer.alloc(4);
|
|
6422
6603
|
chunk.writeUInt32BE(value, 0);
|
|
6423
6604
|
return this.push(chunk);
|
|
6424
6605
|
}
|
|
@@ -6430,7 +6611,7 @@ var SshDataWriter = class {
|
|
|
6430
6611
|
retryable: false
|
|
6431
6612
|
});
|
|
6432
6613
|
}
|
|
6433
|
-
const chunk = import_node_buffer7.Buffer.
|
|
6614
|
+
const chunk = import_node_buffer7.Buffer.alloc(8);
|
|
6434
6615
|
chunk.writeBigUInt64BE(value, 0);
|
|
6435
6616
|
return this.push(chunk);
|
|
6436
6617
|
}
|
|
@@ -7986,7 +8167,7 @@ function encodeSshTransportPacket(payload, options = {}) {
|
|
|
7986
8167
|
}
|
|
7987
8168
|
const padding = options.randomPadding === false ? import_node_buffer14.Buffer.alloc(paddingLength) : (0, import_node_crypto6.randomBytes)(paddingLength);
|
|
7988
8169
|
const packetLength = 1 + body.length + paddingLength;
|
|
7989
|
-
const frame = import_node_buffer14.Buffer.
|
|
8170
|
+
const frame = import_node_buffer14.Buffer.alloc(4 + packetLength);
|
|
7990
8171
|
frame.writeUInt32BE(packetLength, 0);
|
|
7991
8172
|
frame.writeUInt8(paddingLength, 4);
|
|
7992
8173
|
body.copy(frame, 5);
|
|
@@ -8862,7 +9043,7 @@ function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
|
|
|
8862
9043
|
return import_node_buffer17.Buffer.alloc(0);
|
|
8863
9044
|
}
|
|
8864
9045
|
const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
|
|
8865
|
-
const sequenceBuffer = import_node_buffer17.Buffer.
|
|
9046
|
+
const sequenceBuffer = import_node_buffer17.Buffer.alloc(4);
|
|
8866
9047
|
sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
|
|
8867
9048
|
return (0, import_node_crypto8.createHmac)(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
|
|
8868
9049
|
}
|
|
@@ -9814,7 +9995,7 @@ var SftpSession = class {
|
|
|
9814
9995
|
* serializes concurrent calls so byte ordering is preserved.
|
|
9815
9996
|
*/
|
|
9816
9997
|
sendRaw(encodedMessage, requestId) {
|
|
9817
|
-
const frame = import_node_buffer20.Buffer.
|
|
9998
|
+
const frame = import_node_buffer20.Buffer.alloc(4 + encodedMessage.length);
|
|
9818
9999
|
frame.writeUInt32BE(encodedMessage.length, 0);
|
|
9819
10000
|
encodedMessage.copy(frame, 4);
|
|
9820
10001
|
this.channel.sendData(frame).catch((err) => {
|
|
@@ -9932,44 +10113,54 @@ var NATIVE_SFTP_ALGORITHM_PREFERENCES = {
|
|
|
9932
10113
|
"rsa-sha2-256"
|
|
9933
10114
|
]
|
|
9934
10115
|
};
|
|
9935
|
-
var
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
10116
|
+
var NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY = 8;
|
|
10117
|
+
function buildNativeSftpCapabilities(maxConcurrency) {
|
|
10118
|
+
return {
|
|
10119
|
+
provider: NATIVE_SFTP_PROVIDER_ID,
|
|
10120
|
+
authentication: ["password", "keyboard-interactive", "publickey"],
|
|
10121
|
+
list: true,
|
|
10122
|
+
stat: true,
|
|
10123
|
+
readStream: true,
|
|
10124
|
+
writeStream: true,
|
|
10125
|
+
serverSideCopy: false,
|
|
10126
|
+
serverSideMove: false,
|
|
10127
|
+
resumeDownload: true,
|
|
10128
|
+
resumeUpload: true,
|
|
10129
|
+
checksum: [],
|
|
10130
|
+
atomicRename: false,
|
|
10131
|
+
chmod: false,
|
|
10132
|
+
chown: false,
|
|
10133
|
+
symlink: true,
|
|
10134
|
+
metadata: ["accessedAt", "group", "modifiedAt", "owner", "permissions"],
|
|
10135
|
+
maxConcurrency,
|
|
10136
|
+
notes: [
|
|
10137
|
+
"Native SSH/SFTP provider using the project's own protocol stack (Waves 1\u20133).",
|
|
10138
|
+
"Supports password, keyboard-interactive, and public-key (Ed25519/RSA) authentication."
|
|
10139
|
+
]
|
|
10140
|
+
};
|
|
10141
|
+
}
|
|
10142
|
+
var NATIVE_SFTP_PROVIDER_CAPABILITIES = buildNativeSftpCapabilities(
|
|
10143
|
+
NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
10144
|
+
);
|
|
9958
10145
|
function createNativeSftpProviderFactory(options = {}) {
|
|
9959
10146
|
validateNativeSftpOptions(options);
|
|
10147
|
+
const capabilities = buildNativeSftpCapabilities(
|
|
10148
|
+
options.maxConcurrency ?? NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
10149
|
+
);
|
|
9960
10150
|
return {
|
|
9961
|
-
capabilities
|
|
9962
|
-
create: () => new NativeSftpProvider(options),
|
|
10151
|
+
capabilities,
|
|
10152
|
+
create: () => new NativeSftpProvider(options, capabilities),
|
|
9963
10153
|
id: NATIVE_SFTP_PROVIDER_ID
|
|
9964
10154
|
};
|
|
9965
10155
|
}
|
|
9966
10156
|
var NativeSftpProvider = class {
|
|
9967
|
-
constructor(options) {
|
|
10157
|
+
constructor(options, capabilities = NATIVE_SFTP_PROVIDER_CAPABILITIES) {
|
|
9968
10158
|
this.options = options;
|
|
10159
|
+
this.capabilities = capabilities;
|
|
9969
10160
|
}
|
|
9970
10161
|
options;
|
|
9971
10162
|
id = NATIVE_SFTP_PROVIDER_ID;
|
|
9972
|
-
capabilities
|
|
10163
|
+
capabilities;
|
|
9973
10164
|
async connect(profile) {
|
|
9974
10165
|
const resolved = await resolveConnectionProfileSecrets(profile);
|
|
9975
10166
|
const username = requireNativeSftpUsername(resolved);
|
|
@@ -10598,6 +10789,14 @@ function validateNativeSftpOptions(options) {
|
|
|
10598
10789
|
retryable: false
|
|
10599
10790
|
});
|
|
10600
10791
|
}
|
|
10792
|
+
if (options.maxConcurrency !== void 0 && (!Number.isInteger(options.maxConcurrency) || options.maxConcurrency <= 0)) {
|
|
10793
|
+
throw new ConfigurationError({
|
|
10794
|
+
details: { maxConcurrency: options.maxConcurrency },
|
|
10795
|
+
message: "Native SFTP provider maxConcurrency must be a positive integer",
|
|
10796
|
+
protocol: "sftp",
|
|
10797
|
+
retryable: false
|
|
10798
|
+
});
|
|
10799
|
+
}
|
|
10601
10800
|
}
|
|
10602
10801
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10603
10802
|
0 && (module.exports = {
|
|
@@ -10637,6 +10836,7 @@ function validateNativeSftpOptions(options) {
|
|
|
10637
10836
|
createLocalProviderFactory,
|
|
10638
10837
|
createMemoryProviderFactory,
|
|
10639
10838
|
createOAuthTokenSecretSource,
|
|
10839
|
+
createPooledTransferClient,
|
|
10640
10840
|
createProgressEvent,
|
|
10641
10841
|
createProviderTransferExecutor,
|
|
10642
10842
|
createRemoteBrowser,
|