@zero-transfer/s3 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 +236 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +103 -2
- package/dist/index.d.ts +103 -2
- package/dist/index.mjs +240 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -60,10 +60,12 @@ __export(s3_exports, {
|
|
|
60
60
|
copyBetween: () => copyBetween,
|
|
61
61
|
createAtomicDeployPlan: () => createAtomicDeployPlan,
|
|
62
62
|
createBandwidthThrottle: () => createBandwidthThrottle,
|
|
63
|
+
createFileSystemS3MultipartResumeStore: () => createFileSystemS3MultipartResumeStore,
|
|
63
64
|
createLocalProviderFactory: () => createLocalProviderFactory,
|
|
64
65
|
createMemoryProviderFactory: () => createMemoryProviderFactory,
|
|
65
66
|
createMemoryS3MultipartResumeStore: () => createMemoryS3MultipartResumeStore,
|
|
66
67
|
createOAuthTokenSecretSource: () => createOAuthTokenSecretSource,
|
|
68
|
+
createPooledTransferClient: () => createPooledTransferClient,
|
|
67
69
|
createProgressEvent: () => createProgressEvent,
|
|
68
70
|
createProviderTransferExecutor: () => createProviderTransferExecutor,
|
|
69
71
|
createRemoteBrowser: () => createRemoteBrowser,
|
|
@@ -1883,6 +1885,186 @@ function summarizeDiagnosticError(error) {
|
|
|
1883
1885
|
return { message: String(error) };
|
|
1884
1886
|
}
|
|
1885
1887
|
|
|
1888
|
+
// src/core/ConnectionPool.ts
|
|
1889
|
+
var DEFAULT_MAX_IDLE_PER_KEY = 4;
|
|
1890
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 6e4;
|
|
1891
|
+
function createPooledTransferClient(inner, options = {}) {
|
|
1892
|
+
const maxIdlePerKey = Math.max(1, options.maxIdlePerKey ?? DEFAULT_MAX_IDLE_PER_KEY);
|
|
1893
|
+
const idleTimeoutMs = Math.max(0, options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS);
|
|
1894
|
+
const keyOf = options.keyOf ?? defaultKeyOf;
|
|
1895
|
+
const state = {
|
|
1896
|
+
drained: false,
|
|
1897
|
+
idle: /* @__PURE__ */ new Map()
|
|
1898
|
+
};
|
|
1899
|
+
const release = (key, session, tainted) => {
|
|
1900
|
+
if (tainted || state.drained) {
|
|
1901
|
+
return safelyDisconnect(session);
|
|
1902
|
+
}
|
|
1903
|
+
let bucket = state.idle.get(key);
|
|
1904
|
+
if (bucket === void 0) {
|
|
1905
|
+
bucket = [];
|
|
1906
|
+
state.idle.set(key, bucket);
|
|
1907
|
+
}
|
|
1908
|
+
const entry = { session };
|
|
1909
|
+
if (idleTimeoutMs > 0) {
|
|
1910
|
+
entry.idleTimer = setTimeout(() => {
|
|
1911
|
+
evictEntry(state, key, entry);
|
|
1912
|
+
}, idleTimeoutMs);
|
|
1913
|
+
const timer = entry.idleTimer;
|
|
1914
|
+
if (timer !== void 0 && typeof timer.unref === "function") {
|
|
1915
|
+
timer.unref();
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
bucket.push(entry);
|
|
1919
|
+
while (bucket.length > maxIdlePerKey) {
|
|
1920
|
+
const dropped = bucket.shift();
|
|
1921
|
+
if (dropped !== void 0) {
|
|
1922
|
+
clearEntryTimer(dropped);
|
|
1923
|
+
void safelyDisconnect(dropped.session);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
return Promise.resolve();
|
|
1927
|
+
};
|
|
1928
|
+
const acquire = async (profile) => {
|
|
1929
|
+
const key = keyOf(profile);
|
|
1930
|
+
const bucket = state.idle.get(key);
|
|
1931
|
+
if (bucket !== void 0 && bucket.length > 0) {
|
|
1932
|
+
const entry = bucket.pop();
|
|
1933
|
+
if (entry !== void 0) {
|
|
1934
|
+
clearEntryTimer(entry);
|
|
1935
|
+
if (bucket.length === 0) state.idle.delete(key);
|
|
1936
|
+
return { key, session: entry.session };
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
const session = await inner.connect(profile);
|
|
1940
|
+
return { key, session };
|
|
1941
|
+
};
|
|
1942
|
+
return {
|
|
1943
|
+
connect: async (profile) => {
|
|
1944
|
+
const { key, session } = await acquire(profile);
|
|
1945
|
+
return wrapPooledSession(session, key, release);
|
|
1946
|
+
},
|
|
1947
|
+
drainPool: async () => {
|
|
1948
|
+
state.drained = true;
|
|
1949
|
+
const entries = [];
|
|
1950
|
+
for (const bucket of state.idle.values()) {
|
|
1951
|
+
for (const entry of bucket) {
|
|
1952
|
+
clearEntryTimer(entry);
|
|
1953
|
+
entries.push(entry);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
state.idle.clear();
|
|
1957
|
+
await Promise.all(entries.map((entry) => safelyDisconnect(entry.session)));
|
|
1958
|
+
},
|
|
1959
|
+
getCapabilities: ((providerId) => {
|
|
1960
|
+
if (providerId === void 0) return inner.getCapabilities();
|
|
1961
|
+
return inner.getCapabilities(providerId);
|
|
1962
|
+
}),
|
|
1963
|
+
hasProvider: (providerId) => inner.hasProvider(providerId),
|
|
1964
|
+
poolSize: () => {
|
|
1965
|
+
let total = 0;
|
|
1966
|
+
for (const bucket of state.idle.values()) total += bucket.length;
|
|
1967
|
+
return total;
|
|
1968
|
+
}
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
function defaultKeyOf(profile) {
|
|
1972
|
+
const provider = profile.provider ?? profile.protocol ?? "unknown";
|
|
1973
|
+
const host = profile.host ?? "";
|
|
1974
|
+
const port = profile.port ?? "";
|
|
1975
|
+
const username = typeof profile.username === "string" ? profile.username : "";
|
|
1976
|
+
return `${provider}|${host}|${String(port)}|${username}`;
|
|
1977
|
+
}
|
|
1978
|
+
function evictEntry(state, key, entry) {
|
|
1979
|
+
const bucket = state.idle.get(key);
|
|
1980
|
+
if (bucket === void 0) return;
|
|
1981
|
+
const index = bucket.indexOf(entry);
|
|
1982
|
+
if (index < 0) return;
|
|
1983
|
+
bucket.splice(index, 1);
|
|
1984
|
+
if (bucket.length === 0) state.idle.delete(key);
|
|
1985
|
+
clearEntryTimer(entry);
|
|
1986
|
+
void safelyDisconnect(entry.session);
|
|
1987
|
+
}
|
|
1988
|
+
function clearEntryTimer(entry) {
|
|
1989
|
+
if (entry.idleTimer !== void 0) {
|
|
1990
|
+
clearTimeout(entry.idleTimer);
|
|
1991
|
+
delete entry.idleTimer;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
async function safelyDisconnect(session) {
|
|
1995
|
+
try {
|
|
1996
|
+
await session.disconnect();
|
|
1997
|
+
} catch {
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
function isTaintingError(error) {
|
|
2001
|
+
return error instanceof ConnectionError || error instanceof TimeoutError || error instanceof ProtocolError;
|
|
2002
|
+
}
|
|
2003
|
+
function wrapPooledSession(session, key, release) {
|
|
2004
|
+
let tainted = false;
|
|
2005
|
+
let released = false;
|
|
2006
|
+
const guard = (fn) => {
|
|
2007
|
+
let promise;
|
|
2008
|
+
try {
|
|
2009
|
+
promise = fn();
|
|
2010
|
+
} catch (error) {
|
|
2011
|
+
if (isTaintingError(error)) tainted = true;
|
|
2012
|
+
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
2013
|
+
}
|
|
2014
|
+
return promise.catch((error) => {
|
|
2015
|
+
if (isTaintingError(error)) tainted = true;
|
|
2016
|
+
throw error;
|
|
2017
|
+
});
|
|
2018
|
+
};
|
|
2019
|
+
const fs = wrapFs(session.fs, guard);
|
|
2020
|
+
const transfers = session.transfers === void 0 ? void 0 : wrapTransfers(session.transfers, guard);
|
|
2021
|
+
const wrapped = {
|
|
2022
|
+
capabilities: session.capabilities,
|
|
2023
|
+
disconnect: async () => {
|
|
2024
|
+
if (released) return;
|
|
2025
|
+
released = true;
|
|
2026
|
+
await release(key, session, tainted);
|
|
2027
|
+
},
|
|
2028
|
+
fs,
|
|
2029
|
+
provider: session.provider,
|
|
2030
|
+
...transfers !== void 0 ? { transfers } : {}
|
|
2031
|
+
};
|
|
2032
|
+
if (typeof session.raw === "function") {
|
|
2033
|
+
const rawFn = session.raw.bind(session);
|
|
2034
|
+
wrapped.raw = () => rawFn();
|
|
2035
|
+
}
|
|
2036
|
+
return wrapped;
|
|
2037
|
+
}
|
|
2038
|
+
function wrapFs(fs, guard) {
|
|
2039
|
+
const wrapped = {
|
|
2040
|
+
list: (path2, options) => guard(() => options !== void 0 ? fs.list(path2, options) : fs.list(path2)),
|
|
2041
|
+
stat: (path2, options) => guard(() => options !== void 0 ? fs.stat(path2, options) : fs.stat(path2))
|
|
2042
|
+
};
|
|
2043
|
+
if (typeof fs.remove === "function") {
|
|
2044
|
+
const remove = fs.remove.bind(fs);
|
|
2045
|
+
wrapped.remove = (path2, options) => guard(() => options !== void 0 ? remove(path2, options) : remove(path2));
|
|
2046
|
+
}
|
|
2047
|
+
if (typeof fs.rename === "function") {
|
|
2048
|
+
const rename2 = fs.rename.bind(fs);
|
|
2049
|
+
wrapped.rename = (from, to, options) => guard(() => options !== void 0 ? rename2(from, to, options) : rename2(from, to));
|
|
2050
|
+
}
|
|
2051
|
+
if (typeof fs.mkdir === "function") {
|
|
2052
|
+
const mkdir2 = fs.mkdir.bind(fs);
|
|
2053
|
+
wrapped.mkdir = (path2, options) => guard(() => options !== void 0 ? mkdir2(path2, options) : mkdir2(path2));
|
|
2054
|
+
}
|
|
2055
|
+
if (typeof fs.rmdir === "function") {
|
|
2056
|
+
const rmdir = fs.rmdir.bind(fs);
|
|
2057
|
+
wrapped.rmdir = (path2, options) => guard(() => options !== void 0 ? rmdir(path2, options) : rmdir(path2));
|
|
2058
|
+
}
|
|
2059
|
+
return wrapped;
|
|
2060
|
+
}
|
|
2061
|
+
function wrapTransfers(transfers, guard) {
|
|
2062
|
+
return {
|
|
2063
|
+
read: (request) => guard(() => Promise.resolve(transfers.read(request))),
|
|
2064
|
+
write: (request) => guard(() => Promise.resolve(transfers.write(request)))
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
|
|
1886
2068
|
// src/providers/local/LocalProvider.ts
|
|
1887
2069
|
var import_node_fs = require("fs");
|
|
1888
2070
|
var import_promises2 = require("fs/promises");
|
|
@@ -4711,6 +4893,11 @@ function isModifiedAtDifferent2(source, destination, toleranceMs) {
|
|
|
4711
4893
|
return Math.abs(sourceTime - destinationTime) > toleranceMs;
|
|
4712
4894
|
}
|
|
4713
4895
|
|
|
4896
|
+
// src/providers/web/S3Provider.ts
|
|
4897
|
+
var import_node_crypto3 = require("crypto");
|
|
4898
|
+
var import_promises3 = require("fs/promises");
|
|
4899
|
+
var import_node_path3 = require("path");
|
|
4900
|
+
|
|
4714
4901
|
// src/providers/web/awsSigv4.ts
|
|
4715
4902
|
var import_node_crypto2 = require("crypto");
|
|
4716
4903
|
function signSigV4(input) {
|
|
@@ -4881,6 +5068,48 @@ function createMemoryS3MultipartResumeStore() {
|
|
|
4881
5068
|
}
|
|
4882
5069
|
};
|
|
4883
5070
|
}
|
|
5071
|
+
function createFileSystemS3MultipartResumeStore(options) {
|
|
5072
|
+
const directory = options.directory;
|
|
5073
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
5074
|
+
throw new ConfigurationError({
|
|
5075
|
+
message: "createFileSystemS3MultipartResumeStore requires a non-empty directory option",
|
|
5076
|
+
retryable: false
|
|
5077
|
+
});
|
|
5078
|
+
}
|
|
5079
|
+
const fileFor = (key) => {
|
|
5080
|
+
const hash = (0, import_node_crypto3.createHash)("sha256").update(`${key.bucket}\0${key.jobId}\0${key.path}`).digest("hex");
|
|
5081
|
+
return (0, import_node_path3.join)(directory, `${hash}.json`);
|
|
5082
|
+
};
|
|
5083
|
+
return {
|
|
5084
|
+
async clear(key) {
|
|
5085
|
+
try {
|
|
5086
|
+
await (0, import_promises3.unlink)(fileFor(key));
|
|
5087
|
+
} catch (error) {
|
|
5088
|
+
if (error.code !== "ENOENT") throw error;
|
|
5089
|
+
}
|
|
5090
|
+
},
|
|
5091
|
+
async load(key) {
|
|
5092
|
+
try {
|
|
5093
|
+
const text = await (0, import_promises3.readFile)(fileFor(key), "utf8");
|
|
5094
|
+
const parsed = JSON.parse(text);
|
|
5095
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.uploadId !== "string" || !Array.isArray(parsed.parts)) {
|
|
5096
|
+
return void 0;
|
|
5097
|
+
}
|
|
5098
|
+
return parsed;
|
|
5099
|
+
} catch (error) {
|
|
5100
|
+
if (error.code === "ENOENT") return void 0;
|
|
5101
|
+
throw error;
|
|
5102
|
+
}
|
|
5103
|
+
},
|
|
5104
|
+
async save(key, checkpoint) {
|
|
5105
|
+
await (0, import_promises3.mkdir)(directory, { recursive: true });
|
|
5106
|
+
const target = fileFor(key);
|
|
5107
|
+
const tmp = `${target}.${String(process.pid)}.${String(Date.now())}.tmp`;
|
|
5108
|
+
await (0, import_promises3.writeFile)(tmp, JSON.stringify(checkpoint), { encoding: "utf8", mode: 384 });
|
|
5109
|
+
await (0, import_promises3.rename)(tmp, target);
|
|
5110
|
+
}
|
|
5111
|
+
};
|
|
5112
|
+
}
|
|
4884
5113
|
var DEFAULT_MULTIPART_PART_SIZE = 8 * 1024 * 1024;
|
|
4885
5114
|
var DEFAULT_MULTIPART_THRESHOLD = 8 * 1024 * 1024;
|
|
4886
5115
|
var S3_CHECKSUM_CAPABILITIES = ["etag"];
|
|
@@ -4908,7 +5137,7 @@ function createS3ProviderFactory(options = {}) {
|
|
|
4908
5137
|
retryable: false
|
|
4909
5138
|
});
|
|
4910
5139
|
}
|
|
4911
|
-
const multipartEnabled = options.multipart?.enabled ??
|
|
5140
|
+
const multipartEnabled = options.multipart?.enabled ?? true;
|
|
4912
5141
|
const multipart = {
|
|
4913
5142
|
enabled: multipartEnabled,
|
|
4914
5143
|
partSizeBytes: options.multipart?.partSizeBytes ?? DEFAULT_MULTIPART_PART_SIZE,
|
|
@@ -4925,9 +5154,11 @@ function createS3ProviderFactory(options = {}) {
|
|
|
4925
5154
|
maxConcurrency: 16,
|
|
4926
5155
|
metadata: ["modifiedAt", "mimeType", "uniqueId"],
|
|
4927
5156
|
notes: multipartEnabled ? [
|
|
4928
|
-
`S3 multipart upload enabled (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B)
|
|
5157
|
+
`S3 multipart upload enabled by default (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B).`,
|
|
5158
|
+
"Payloads at or below the threshold automatically fall back to single-shot PUT.",
|
|
5159
|
+
"Pass `multipart: { enabled: false }` to force the legacy single-shot behaviour."
|
|
4929
5160
|
] : [
|
|
4930
|
-
"S3 provider performs single-shot PUT uploads;
|
|
5161
|
+
"S3 provider performs single-shot PUT uploads; entire object is buffered in memory before transmission."
|
|
4931
5162
|
],
|
|
4932
5163
|
provider: id,
|
|
4933
5164
|
readStream: true,
|
|
@@ -5534,10 +5765,12 @@ function innerText(xml, tag) {
|
|
|
5534
5765
|
copyBetween,
|
|
5535
5766
|
createAtomicDeployPlan,
|
|
5536
5767
|
createBandwidthThrottle,
|
|
5768
|
+
createFileSystemS3MultipartResumeStore,
|
|
5537
5769
|
createLocalProviderFactory,
|
|
5538
5770
|
createMemoryProviderFactory,
|
|
5539
5771
|
createMemoryS3MultipartResumeStore,
|
|
5540
5772
|
createOAuthTokenSecretSource,
|
|
5773
|
+
createPooledTransferClient,
|
|
5541
5774
|
createProgressEvent,
|
|
5542
5775
|
createProviderTransferExecutor,
|
|
5543
5776
|
createRemoteBrowser,
|