@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.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/classic/ftp/FtpProvider.ts
|
|
1771
1951
|
import { Buffer as Buffer4 } from "buffer";
|
|
1772
1952
|
import { createConnection, isIP } from "net";
|
|
@@ -3653,7 +3833,7 @@ var SshDataWriter = class {
|
|
|
3653
3833
|
length = 0;
|
|
3654
3834
|
writeByte(value) {
|
|
3655
3835
|
this.assertByte(value, "byte");
|
|
3656
|
-
const chunk = Buffer6.
|
|
3836
|
+
const chunk = Buffer6.alloc(1);
|
|
3657
3837
|
chunk.writeUInt8(value, 0);
|
|
3658
3838
|
return this.push(chunk);
|
|
3659
3839
|
}
|
|
@@ -3671,7 +3851,7 @@ var SshDataWriter = class {
|
|
|
3671
3851
|
retryable: false
|
|
3672
3852
|
});
|
|
3673
3853
|
}
|
|
3674
|
-
const chunk = Buffer6.
|
|
3854
|
+
const chunk = Buffer6.alloc(4);
|
|
3675
3855
|
chunk.writeUInt32BE(value, 0);
|
|
3676
3856
|
return this.push(chunk);
|
|
3677
3857
|
}
|
|
@@ -3683,7 +3863,7 @@ var SshDataWriter = class {
|
|
|
3683
3863
|
retryable: false
|
|
3684
3864
|
});
|
|
3685
3865
|
}
|
|
3686
|
-
const chunk = Buffer6.
|
|
3866
|
+
const chunk = Buffer6.alloc(8);
|
|
3687
3867
|
chunk.writeBigUInt64BE(value, 0);
|
|
3688
3868
|
return this.push(chunk);
|
|
3689
3869
|
}
|
|
@@ -5239,7 +5419,7 @@ function encodeSshTransportPacket(payload, options = {}) {
|
|
|
5239
5419
|
}
|
|
5240
5420
|
const padding = options.randomPadding === false ? Buffer13.alloc(paddingLength) : randomBytes2(paddingLength);
|
|
5241
5421
|
const packetLength = 1 + body.length + paddingLength;
|
|
5242
|
-
const frame = Buffer13.
|
|
5422
|
+
const frame = Buffer13.alloc(4 + packetLength);
|
|
5243
5423
|
frame.writeUInt32BE(packetLength, 0);
|
|
5244
5424
|
frame.writeUInt8(paddingLength, 4);
|
|
5245
5425
|
body.copy(frame, 5);
|
|
@@ -6120,7 +6300,7 @@ function computeMac(macAlgorithm, macKey, sequence, packet, macLength) {
|
|
|
6120
6300
|
return Buffer16.alloc(0);
|
|
6121
6301
|
}
|
|
6122
6302
|
const hashName = macAlgorithm === "hmac-sha2-512" ? "sha512" : "sha256";
|
|
6123
|
-
const sequenceBuffer = Buffer16.
|
|
6303
|
+
const sequenceBuffer = Buffer16.alloc(4);
|
|
6124
6304
|
sequenceBuffer.writeUInt32BE(sequence >>> 0, 0);
|
|
6125
6305
|
return createHmac2(hashName, macKey).update(sequenceBuffer).update(packet).digest().subarray(0, macLength);
|
|
6126
6306
|
}
|
|
@@ -7072,7 +7252,7 @@ var SftpSession = class {
|
|
|
7072
7252
|
* serializes concurrent calls so byte ordering is preserved.
|
|
7073
7253
|
*/
|
|
7074
7254
|
sendRaw(encodedMessage, requestId) {
|
|
7075
|
-
const frame = Buffer19.
|
|
7255
|
+
const frame = Buffer19.alloc(4 + encodedMessage.length);
|
|
7076
7256
|
frame.writeUInt32BE(encodedMessage.length, 0);
|
|
7077
7257
|
encodedMessage.copy(frame, 4);
|
|
7078
7258
|
this.channel.sendData(frame).catch((err) => {
|
|
@@ -7190,44 +7370,54 @@ var NATIVE_SFTP_ALGORITHM_PREFERENCES = {
|
|
|
7190
7370
|
"rsa-sha2-256"
|
|
7191
7371
|
]
|
|
7192
7372
|
};
|
|
7193
|
-
var
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7373
|
+
var NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY = 8;
|
|
7374
|
+
function buildNativeSftpCapabilities(maxConcurrency) {
|
|
7375
|
+
return {
|
|
7376
|
+
provider: NATIVE_SFTP_PROVIDER_ID,
|
|
7377
|
+
authentication: ["password", "keyboard-interactive", "publickey"],
|
|
7378
|
+
list: true,
|
|
7379
|
+
stat: true,
|
|
7380
|
+
readStream: true,
|
|
7381
|
+
writeStream: true,
|
|
7382
|
+
serverSideCopy: false,
|
|
7383
|
+
serverSideMove: false,
|
|
7384
|
+
resumeDownload: true,
|
|
7385
|
+
resumeUpload: true,
|
|
7386
|
+
checksum: [],
|
|
7387
|
+
atomicRename: false,
|
|
7388
|
+
chmod: false,
|
|
7389
|
+
chown: false,
|
|
7390
|
+
symlink: true,
|
|
7391
|
+
metadata: ["accessedAt", "group", "modifiedAt", "owner", "permissions"],
|
|
7392
|
+
maxConcurrency,
|
|
7393
|
+
notes: [
|
|
7394
|
+
"Native SSH/SFTP provider using the project's own protocol stack (Waves 1\u20133).",
|
|
7395
|
+
"Supports password, keyboard-interactive, and public-key (Ed25519/RSA) authentication."
|
|
7396
|
+
]
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7399
|
+
var NATIVE_SFTP_PROVIDER_CAPABILITIES = buildNativeSftpCapabilities(
|
|
7400
|
+
NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
7401
|
+
);
|
|
7216
7402
|
function createNativeSftpProviderFactory(options = {}) {
|
|
7217
7403
|
validateNativeSftpOptions(options);
|
|
7404
|
+
const capabilities = buildNativeSftpCapabilities(
|
|
7405
|
+
options.maxConcurrency ?? NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
7406
|
+
);
|
|
7218
7407
|
return {
|
|
7219
|
-
capabilities
|
|
7220
|
-
create: () => new NativeSftpProvider(options),
|
|
7408
|
+
capabilities,
|
|
7409
|
+
create: () => new NativeSftpProvider(options, capabilities),
|
|
7221
7410
|
id: NATIVE_SFTP_PROVIDER_ID
|
|
7222
7411
|
};
|
|
7223
7412
|
}
|
|
7224
7413
|
var NativeSftpProvider = class {
|
|
7225
|
-
constructor(options) {
|
|
7414
|
+
constructor(options, capabilities = NATIVE_SFTP_PROVIDER_CAPABILITIES) {
|
|
7226
7415
|
this.options = options;
|
|
7416
|
+
this.capabilities = capabilities;
|
|
7227
7417
|
}
|
|
7228
7418
|
options;
|
|
7229
7419
|
id = NATIVE_SFTP_PROVIDER_ID;
|
|
7230
|
-
capabilities
|
|
7420
|
+
capabilities;
|
|
7231
7421
|
async connect(profile) {
|
|
7232
7422
|
const resolved = await resolveConnectionProfileSecrets(profile);
|
|
7233
7423
|
const username = requireNativeSftpUsername(resolved);
|
|
@@ -7856,6 +8046,14 @@ function validateNativeSftpOptions(options) {
|
|
|
7856
8046
|
retryable: false
|
|
7857
8047
|
});
|
|
7858
8048
|
}
|
|
8049
|
+
if (options.maxConcurrency !== void 0 && (!Number.isInteger(options.maxConcurrency) || options.maxConcurrency <= 0)) {
|
|
8050
|
+
throw new ConfigurationError({
|
|
8051
|
+
details: { maxConcurrency: options.maxConcurrency },
|
|
8052
|
+
message: "Native SFTP provider maxConcurrency must be a positive integer",
|
|
8053
|
+
protocol: "sftp",
|
|
8054
|
+
retryable: false
|
|
8055
|
+
});
|
|
8056
|
+
}
|
|
7859
8057
|
}
|
|
7860
8058
|
|
|
7861
8059
|
// src/providers/web/httpInternals.ts
|
|
@@ -8393,6 +8591,7 @@ async function collectChunks(source) {
|
|
|
8393
8591
|
|
|
8394
8592
|
// src/providers/cloud/GoogleDriveProvider.ts
|
|
8395
8593
|
import { Buffer as Buffer22 } from "buffer";
|
|
8594
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
8396
8595
|
var GDRIVE_API_BASE = "https://www.googleapis.com/drive/v3";
|
|
8397
8596
|
var GDRIVE_UPLOAD_BASE = "https://www.googleapis.com/upload/drive/v3";
|
|
8398
8597
|
var GDRIVE_FOLDER_MIME = "application/vnd.google-apps.folder";
|
|
@@ -8677,7 +8876,7 @@ var GoogleDriveTransferOperations = class {
|
|
|
8677
8876
|
const existing = await this.findExisting(parentId, name);
|
|
8678
8877
|
const metadata = { name };
|
|
8679
8878
|
if (existing === void 0) metadata["parents"] = [parentId];
|
|
8680
|
-
const boundary = `----zt-boundary-${
|
|
8879
|
+
const boundary = `----zt-boundary-${randomBytes3(16).toString("hex")}`;
|
|
8681
8880
|
const bodyParts = [];
|
|
8682
8881
|
const enc = new TextEncoder();
|
|
8683
8882
|
bodyParts.push(
|
|
@@ -11210,12 +11409,14 @@ function createWebDavProviderFactory(options = {}) {
|
|
|
11210
11409
|
const secure = options.secure ?? false;
|
|
11211
11410
|
const basePath = options.basePath ?? "";
|
|
11212
11411
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
11412
|
+
const uploadStreaming = options.uploadStreaming ?? "when-known-size";
|
|
11213
11413
|
if (typeof fetchImpl !== "function") {
|
|
11214
11414
|
throw new ConfigurationError({
|
|
11215
11415
|
message: "Global fetch is unavailable; supply WebDavProviderOptions.fetch explicitly",
|
|
11216
11416
|
retryable: false
|
|
11217
11417
|
});
|
|
11218
11418
|
}
|
|
11419
|
+
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.";
|
|
11219
11420
|
const capabilities = {
|
|
11220
11421
|
atomicRename: false,
|
|
11221
11422
|
authentication: ["anonymous", "password", "token"],
|
|
@@ -11225,7 +11426,7 @@ function createWebDavProviderFactory(options = {}) {
|
|
|
11225
11426
|
list: true,
|
|
11226
11427
|
maxConcurrency: 8,
|
|
11227
11428
|
metadata: ["modifiedAt", "mimeType", "uniqueId"],
|
|
11228
|
-
notes: [
|
|
11429
|
+
notes: [streamingNote],
|
|
11229
11430
|
provider: id,
|
|
11230
11431
|
readStream: true,
|
|
11231
11432
|
resumeDownload: true,
|
|
@@ -11244,7 +11445,8 @@ function createWebDavProviderFactory(options = {}) {
|
|
|
11244
11445
|
defaultHeaders: { ...options.defaultHeaders ?? {} },
|
|
11245
11446
|
fetch: fetchImpl,
|
|
11246
11447
|
id,
|
|
11247
|
-
secure
|
|
11448
|
+
secure,
|
|
11449
|
+
uploadStreaming
|
|
11248
11450
|
}),
|
|
11249
11451
|
id
|
|
11250
11452
|
};
|
|
@@ -11278,7 +11480,8 @@ var WebDavProvider = class {
|
|
|
11278
11480
|
capabilities: this.internals.capabilities,
|
|
11279
11481
|
fetch: this.internals.fetch,
|
|
11280
11482
|
headers,
|
|
11281
|
-
id: this.internals.id
|
|
11483
|
+
id: this.internals.id,
|
|
11484
|
+
uploadStreaming: this.internals.uploadStreaming
|
|
11282
11485
|
};
|
|
11283
11486
|
if (profile.timeoutMs !== void 0) sessionOptions.timeoutMs = profile.timeoutMs;
|
|
11284
11487
|
return new WebDavSession(sessionOptions);
|
|
@@ -11403,13 +11606,53 @@ var WebDavTransferOperations = class {
|
|
|
11403
11606
|
}
|
|
11404
11607
|
const normalized = normalizeRemotePath(request.endpoint.path);
|
|
11405
11608
|
const url = resolveUrl(this.options.baseUrl, normalized);
|
|
11406
|
-
const
|
|
11609
|
+
const totalBytes = request.totalBytes;
|
|
11610
|
+
const policy = this.options.uploadStreaming;
|
|
11611
|
+
const shouldStream = policy === "always" || policy === "when-known-size" && typeof totalBytes === "number" && totalBytes >= 0;
|
|
11612
|
+
if (!shouldStream) {
|
|
11613
|
+
const buffered = await collectChunks6(request.content);
|
|
11614
|
+
const headers2 = {
|
|
11615
|
+
"Content-Length": String(buffered.byteLength),
|
|
11616
|
+
"Content-Type": "application/octet-stream"
|
|
11617
|
+
};
|
|
11618
|
+
const init2 = {
|
|
11619
|
+
body: buffered,
|
|
11620
|
+
headers: headers2,
|
|
11621
|
+
method: "PUT"
|
|
11622
|
+
};
|
|
11623
|
+
if (request.signal !== void 0) init2.signal = request.signal;
|
|
11624
|
+
const response2 = await dispatchRequest(this.options, url, init2);
|
|
11625
|
+
if (!response2.ok) {
|
|
11626
|
+
throw mapResponseError(response2, normalized);
|
|
11627
|
+
}
|
|
11628
|
+
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
11629
|
+
const result2 = {
|
|
11630
|
+
bytesTransferred: buffered.byteLength,
|
|
11631
|
+
totalBytes: buffered.byteLength
|
|
11632
|
+
};
|
|
11633
|
+
const etag2 = response2.headers.get("etag");
|
|
11634
|
+
if (etag2 !== null) result2.checksum = etag2;
|
|
11635
|
+
return result2;
|
|
11636
|
+
}
|
|
11637
|
+
let bytesTransferred = 0;
|
|
11638
|
+
const knownTotal = typeof totalBytes === "number" ? totalBytes : void 0;
|
|
11639
|
+
const stream = asyncIterableToReadableStream(request.content, (chunk) => {
|
|
11640
|
+
bytesTransferred += chunk.byteLength;
|
|
11641
|
+
if (knownTotal !== void 0) {
|
|
11642
|
+
request.reportProgress(bytesTransferred, knownTotal);
|
|
11643
|
+
} else {
|
|
11644
|
+
request.reportProgress(bytesTransferred);
|
|
11645
|
+
}
|
|
11646
|
+
});
|
|
11407
11647
|
const headers = {
|
|
11408
|
-
"Content-Length": String(buffered.byteLength),
|
|
11409
11648
|
"Content-Type": "application/octet-stream"
|
|
11410
11649
|
};
|
|
11650
|
+
if (knownTotal !== void 0) {
|
|
11651
|
+
headers["Content-Length"] = String(knownTotal);
|
|
11652
|
+
}
|
|
11411
11653
|
const init = {
|
|
11412
|
-
body:
|
|
11654
|
+
body: stream,
|
|
11655
|
+
duplex: "half",
|
|
11413
11656
|
headers,
|
|
11414
11657
|
method: "PUT"
|
|
11415
11658
|
};
|
|
@@ -11418,10 +11661,9 @@ var WebDavTransferOperations = class {
|
|
|
11418
11661
|
if (!response.ok) {
|
|
11419
11662
|
throw mapResponseError(response, normalized);
|
|
11420
11663
|
}
|
|
11421
|
-
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
11422
11664
|
const result = {
|
|
11423
|
-
bytesTransferred
|
|
11424
|
-
totalBytes:
|
|
11665
|
+
bytesTransferred,
|
|
11666
|
+
...knownTotal !== void 0 ? { totalBytes: knownTotal } : { totalBytes: bytesTransferred }
|
|
11425
11667
|
};
|
|
11426
11668
|
const etag = response.headers.get("etag");
|
|
11427
11669
|
if (etag !== null) result.checksum = etag;
|
|
@@ -11443,6 +11685,36 @@ async function collectChunks6(source) {
|
|
|
11443
11685
|
}
|
|
11444
11686
|
return out;
|
|
11445
11687
|
}
|
|
11688
|
+
function asyncIterableToReadableStream(source, onChunk) {
|
|
11689
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
11690
|
+
return new ReadableStream({
|
|
11691
|
+
async pull(controller) {
|
|
11692
|
+
try {
|
|
11693
|
+
const next = await iterator.next();
|
|
11694
|
+
if (next.done === true) {
|
|
11695
|
+
controller.close();
|
|
11696
|
+
return;
|
|
11697
|
+
}
|
|
11698
|
+
const chunk = next.value;
|
|
11699
|
+
if (chunk.byteLength === 0) {
|
|
11700
|
+
return;
|
|
11701
|
+
}
|
|
11702
|
+
controller.enqueue(chunk);
|
|
11703
|
+
onChunk(chunk);
|
|
11704
|
+
} catch (error) {
|
|
11705
|
+
controller.error(error);
|
|
11706
|
+
}
|
|
11707
|
+
},
|
|
11708
|
+
async cancel(reason) {
|
|
11709
|
+
if (typeof iterator.return === "function") {
|
|
11710
|
+
try {
|
|
11711
|
+
await iterator.return(reason);
|
|
11712
|
+
} catch {
|
|
11713
|
+
}
|
|
11714
|
+
}
|
|
11715
|
+
}
|
|
11716
|
+
});
|
|
11717
|
+
}
|
|
11446
11718
|
function parsePropfindResponses(xml, baseUrl) {
|
|
11447
11719
|
const entries = [];
|
|
11448
11720
|
const responseRegex = /<(?:[a-zA-Z0-9-]+:)?response\b[^>]*>([\s\S]*?)<\/(?:[a-zA-Z0-9-]+:)?response>/gi;
|
|
@@ -11522,6 +11794,17 @@ function parentOf(path2) {
|
|
|
11522
11794
|
return path2.slice(0, idx);
|
|
11523
11795
|
}
|
|
11524
11796
|
|
|
11797
|
+
// src/providers/web/S3Provider.ts
|
|
11798
|
+
import { createHash as createHash5 } from "crypto";
|
|
11799
|
+
import {
|
|
11800
|
+
mkdir as fsMkdir,
|
|
11801
|
+
readFile as fsReadFile,
|
|
11802
|
+
rename as fsRename,
|
|
11803
|
+
unlink as fsUnlink,
|
|
11804
|
+
writeFile as fsWriteFile
|
|
11805
|
+
} from "fs/promises";
|
|
11806
|
+
import { join as joinPath4 } from "path";
|
|
11807
|
+
|
|
11525
11808
|
// src/providers/web/awsSigv4.ts
|
|
11526
11809
|
import { createHash as createHash4, createHmac as createHmac3 } from "crypto";
|
|
11527
11810
|
function signSigV4(input) {
|
|
@@ -11616,6 +11899,48 @@ function createMemoryS3MultipartResumeStore() {
|
|
|
11616
11899
|
}
|
|
11617
11900
|
};
|
|
11618
11901
|
}
|
|
11902
|
+
function createFileSystemS3MultipartResumeStore(options) {
|
|
11903
|
+
const directory = options.directory;
|
|
11904
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
11905
|
+
throw new ConfigurationError({
|
|
11906
|
+
message: "createFileSystemS3MultipartResumeStore requires a non-empty directory option",
|
|
11907
|
+
retryable: false
|
|
11908
|
+
});
|
|
11909
|
+
}
|
|
11910
|
+
const fileFor = (key) => {
|
|
11911
|
+
const hash = createHash5("sha256").update(`${key.bucket}\0${key.jobId}\0${key.path}`).digest("hex");
|
|
11912
|
+
return joinPath4(directory, `${hash}.json`);
|
|
11913
|
+
};
|
|
11914
|
+
return {
|
|
11915
|
+
async clear(key) {
|
|
11916
|
+
try {
|
|
11917
|
+
await fsUnlink(fileFor(key));
|
|
11918
|
+
} catch (error) {
|
|
11919
|
+
if (error.code !== "ENOENT") throw error;
|
|
11920
|
+
}
|
|
11921
|
+
},
|
|
11922
|
+
async load(key) {
|
|
11923
|
+
try {
|
|
11924
|
+
const text = await fsReadFile(fileFor(key), "utf8");
|
|
11925
|
+
const parsed = JSON.parse(text);
|
|
11926
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.uploadId !== "string" || !Array.isArray(parsed.parts)) {
|
|
11927
|
+
return void 0;
|
|
11928
|
+
}
|
|
11929
|
+
return parsed;
|
|
11930
|
+
} catch (error) {
|
|
11931
|
+
if (error.code === "ENOENT") return void 0;
|
|
11932
|
+
throw error;
|
|
11933
|
+
}
|
|
11934
|
+
},
|
|
11935
|
+
async save(key, checkpoint) {
|
|
11936
|
+
await fsMkdir(directory, { recursive: true });
|
|
11937
|
+
const target = fileFor(key);
|
|
11938
|
+
const tmp = `${target}.${String(process.pid)}.${String(Date.now())}.tmp`;
|
|
11939
|
+
await fsWriteFile(tmp, JSON.stringify(checkpoint), { encoding: "utf8", mode: 384 });
|
|
11940
|
+
await fsRename(tmp, target);
|
|
11941
|
+
}
|
|
11942
|
+
};
|
|
11943
|
+
}
|
|
11619
11944
|
var DEFAULT_MULTIPART_PART_SIZE = 8 * 1024 * 1024;
|
|
11620
11945
|
var DEFAULT_MULTIPART_THRESHOLD = 8 * 1024 * 1024;
|
|
11621
11946
|
var S3_CHECKSUM_CAPABILITIES = ["etag"];
|
|
@@ -11643,7 +11968,7 @@ function createS3ProviderFactory(options = {}) {
|
|
|
11643
11968
|
retryable: false
|
|
11644
11969
|
});
|
|
11645
11970
|
}
|
|
11646
|
-
const multipartEnabled = options.multipart?.enabled ??
|
|
11971
|
+
const multipartEnabled = options.multipart?.enabled ?? true;
|
|
11647
11972
|
const multipart = {
|
|
11648
11973
|
enabled: multipartEnabled,
|
|
11649
11974
|
partSizeBytes: options.multipart?.partSizeBytes ?? DEFAULT_MULTIPART_PART_SIZE,
|
|
@@ -11660,9 +11985,11 @@ function createS3ProviderFactory(options = {}) {
|
|
|
11660
11985
|
maxConcurrency: 16,
|
|
11661
11986
|
metadata: ["modifiedAt", "mimeType", "uniqueId"],
|
|
11662
11987
|
notes: multipartEnabled ? [
|
|
11663
|
-
`S3 multipart upload enabled (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B)
|
|
11988
|
+
`S3 multipart upload enabled by default (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B).`,
|
|
11989
|
+
"Payloads at or below the threshold automatically fall back to single-shot PUT.",
|
|
11990
|
+
"Pass `multipart: { enabled: false }` to force the legacy single-shot behaviour."
|
|
11664
11991
|
] : [
|
|
11665
|
-
"S3 provider performs single-shot PUT uploads;
|
|
11992
|
+
"S3 provider performs single-shot PUT uploads; entire object is buffered in memory before transmission."
|
|
11666
11993
|
],
|
|
11667
11994
|
provider: id,
|
|
11668
11995
|
readStream: true,
|
|
@@ -12280,15 +12607,15 @@ function getBuiltinCapabilityMatrix() {
|
|
|
12280
12607
|
{
|
|
12281
12608
|
capabilities: createS3ProviderFactory({ fetch: noopFetch }).capabilities,
|
|
12282
12609
|
id: "s3",
|
|
12283
|
-
label: "S3-compatible (
|
|
12610
|
+
label: "S3-compatible (multipart uploads, default)"
|
|
12284
12611
|
},
|
|
12285
12612
|
{
|
|
12286
12613
|
capabilities: createS3ProviderFactory({
|
|
12287
12614
|
fetch: noopFetch,
|
|
12288
|
-
multipart: { enabled:
|
|
12615
|
+
multipart: { enabled: false }
|
|
12289
12616
|
}).capabilities,
|
|
12290
|
-
id: "s3:
|
|
12291
|
-
label: "S3-compatible (
|
|
12617
|
+
id: "s3:single-shot",
|
|
12618
|
+
label: "S3-compatible (single-shot uploads)"
|
|
12292
12619
|
},
|
|
12293
12620
|
{
|
|
12294
12621
|
capabilities: createDropboxProviderFactory({ fetch: noopFetch }).capabilities,
|
|
@@ -14968,6 +15295,7 @@ export {
|
|
|
14968
15295
|
ConnectionError,
|
|
14969
15296
|
DEFAULT_FAILED_SUBDIR,
|
|
14970
15297
|
DEFAULT_PROCESSED_SUBDIR,
|
|
15298
|
+
DEFAULT_SSH_ALGORITHM_PREFERENCES,
|
|
14971
15299
|
FtpResponseParser,
|
|
14972
15300
|
InMemoryAuditLog,
|
|
14973
15301
|
MftScheduler,
|
|
@@ -14981,6 +15309,14 @@ export {
|
|
|
14981
15309
|
REMOTE_MANIFEST_FORMAT_VERSION,
|
|
14982
15310
|
RouteRegistry,
|
|
14983
15311
|
ScheduleRegistry,
|
|
15312
|
+
SshAuthSession,
|
|
15313
|
+
SshConnectionManager,
|
|
15314
|
+
SshDataReader,
|
|
15315
|
+
SshDataWriter,
|
|
15316
|
+
SshDisconnectReason,
|
|
15317
|
+
SshSessionChannel,
|
|
15318
|
+
SshTransportConnection,
|
|
15319
|
+
SshTransportHandshake,
|
|
14984
15320
|
TimeoutError,
|
|
14985
15321
|
TransferClient,
|
|
14986
15322
|
TransferEngine,
|
|
@@ -14992,6 +15328,7 @@ export {
|
|
|
14992
15328
|
ZeroTransferError,
|
|
14993
15329
|
assertSafeFtpArgument,
|
|
14994
15330
|
basenameRemotePath,
|
|
15331
|
+
buildPublickeyCredential,
|
|
14995
15332
|
buildRemoteBreadcrumbs,
|
|
14996
15333
|
compareRemoteManifests,
|
|
14997
15334
|
composeAuditLogs,
|
|
@@ -15001,6 +15338,7 @@ export {
|
|
|
15001
15338
|
createAzureBlobProviderFactory,
|
|
15002
15339
|
createBandwidthThrottle,
|
|
15003
15340
|
createDropboxProviderFactory,
|
|
15341
|
+
createFileSystemS3MultipartResumeStore,
|
|
15004
15342
|
createFtpProviderFactory,
|
|
15005
15343
|
createFtpsProviderFactory,
|
|
15006
15344
|
createGcsProviderFactory,
|
|
@@ -15015,6 +15353,7 @@ export {
|
|
|
15015
15353
|
createOAuthTokenSecretSource,
|
|
15016
15354
|
createOneDriveProviderFactory,
|
|
15017
15355
|
createOutboxRoute,
|
|
15356
|
+
createPooledTransferClient,
|
|
15018
15357
|
createProgressEvent,
|
|
15019
15358
|
createProviderTransferExecutor,
|
|
15020
15359
|
createRemoteBrowser,
|
|
@@ -15048,6 +15387,7 @@ export {
|
|
|
15048
15387
|
joinRemotePath,
|
|
15049
15388
|
matchKnownHosts,
|
|
15050
15389
|
matchKnownHostsEntry,
|
|
15390
|
+
negotiateSshAlgorithms,
|
|
15051
15391
|
nextCronFireAt,
|
|
15052
15392
|
nextScheduleFireAt,
|
|
15053
15393
|
noopLogger,
|