@zero-transfer/s3 0.4.6 → 0.4.8
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/README.md +0 -2
- package/dist/index.cjs +397 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +234 -27
- package/dist/index.d.ts +234 -27
- package/dist/index.mjs +394 -99
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,7 @@ __export(s3_exports, {
|
|
|
60
60
|
copyBetween: () => copyBetween,
|
|
61
61
|
createAtomicDeployPlan: () => createAtomicDeployPlan,
|
|
62
62
|
createBandwidthThrottle: () => createBandwidthThrottle,
|
|
63
|
+
createDefaultRetryPolicy: () => createDefaultRetryPolicy,
|
|
63
64
|
createFileSystemS3MultipartResumeStore: () => createFileSystemS3MultipartResumeStore,
|
|
64
65
|
createLocalProviderFactory: () => createLocalProviderFactory,
|
|
65
66
|
createMemoryProviderFactory: () => createMemoryProviderFactory,
|
|
@@ -98,8 +99,10 @@ __export(s3_exports, {
|
|
|
98
99
|
parseRemoteManifest: () => parseRemoteManifest,
|
|
99
100
|
redactCommand: () => redactCommand,
|
|
100
101
|
redactConnectionProfile: () => redactConnectionProfile,
|
|
102
|
+
redactErrorForLogging: () => redactErrorForLogging,
|
|
101
103
|
redactObject: () => redactObject,
|
|
102
104
|
redactSecretSource: () => redactSecretSource,
|
|
105
|
+
redactUrlForLogging: () => redactUrlForLogging,
|
|
103
106
|
redactValue: () => redactValue,
|
|
104
107
|
resolveConnectionProfileSecrets: () => resolveConnectionProfileSecrets,
|
|
105
108
|
resolveOpenSshHost: () => resolveOpenSshHost,
|
|
@@ -120,6 +123,68 @@ module.exports = __toCommonJS(s3_exports);
|
|
|
120
123
|
// src/client/ZeroTransfer.ts
|
|
121
124
|
var import_node_events = require("events");
|
|
122
125
|
|
|
126
|
+
// src/logging/redaction.ts
|
|
127
|
+
var REDACTED = "[REDACTED]";
|
|
128
|
+
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
129
|
+
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
130
|
+
var URL_KEY_PATTERN = /(?:url|uri|href)$/i;
|
|
131
|
+
function isSensitiveKey(key) {
|
|
132
|
+
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
133
|
+
}
|
|
134
|
+
function redactCommand(command) {
|
|
135
|
+
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
136
|
+
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function redactValue(value) {
|
|
140
|
+
if (typeof value === "string") {
|
|
141
|
+
return redactCommand(value);
|
|
142
|
+
}
|
|
143
|
+
if (Array.isArray(value)) {
|
|
144
|
+
return value.map((item) => redactValue(item));
|
|
145
|
+
}
|
|
146
|
+
if (value !== null && typeof value === "object") {
|
|
147
|
+
return redactObject(value);
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
function redactObject(input) {
|
|
152
|
+
return Object.fromEntries(
|
|
153
|
+
Object.entries(input).map(([key, value]) => {
|
|
154
|
+
if (isSensitiveKey(key)) {
|
|
155
|
+
return [key, REDACTED];
|
|
156
|
+
}
|
|
157
|
+
if (URL_KEY_PATTERN.test(key) && typeof value === "string") {
|
|
158
|
+
return [key, redactUrlForLogging(value)];
|
|
159
|
+
}
|
|
160
|
+
return [key, redactValue(value)];
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function redactUrlForLogging(url) {
|
|
165
|
+
let parsed;
|
|
166
|
+
try {
|
|
167
|
+
parsed = typeof url === "string" ? new URL(url) : url;
|
|
168
|
+
} catch {
|
|
169
|
+
return REDACTED;
|
|
170
|
+
}
|
|
171
|
+
const origin = parsed.host.length > 0 ? `${parsed.protocol}//${parsed.host}` : parsed.protocol;
|
|
172
|
+
const query = parsed.search.length > 0 ? `?${REDACTED}` : "";
|
|
173
|
+
return `${origin}${parsed.pathname}${query}`;
|
|
174
|
+
}
|
|
175
|
+
function redactErrorForLogging(error) {
|
|
176
|
+
if (error !== null && typeof error === "object") {
|
|
177
|
+
const candidate = error;
|
|
178
|
+
if (typeof candidate.toJSON === "function") {
|
|
179
|
+
return redactObject(candidate.toJSON());
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (error instanceof Error) {
|
|
183
|
+
return redactObject({ message: error.message, name: error.name });
|
|
184
|
+
}
|
|
185
|
+
return { message: redactValue(typeof error === "string" ? error : String(error)) };
|
|
186
|
+
}
|
|
187
|
+
|
|
123
188
|
// src/errors/ZeroTransferError.ts
|
|
124
189
|
var ZeroTransferError = class extends Error {
|
|
125
190
|
/** Stable machine-readable error code. */
|
|
@@ -161,6 +226,11 @@ var ZeroTransferError = class extends Error {
|
|
|
161
226
|
/**
|
|
162
227
|
* Serializes the error into a plain object suitable for logs or API responses.
|
|
163
228
|
*
|
|
229
|
+
* `details` and `command` are passed through secret redaction so serialized
|
|
230
|
+
* errors never leak credentials, signed URLs, or raw protocol commands. The
|
|
231
|
+
* live {@link ZeroTransferError.details | details} property stays unredacted
|
|
232
|
+
* for programmatic consumers.
|
|
233
|
+
*
|
|
164
234
|
* @returns A JSON-safe object containing public structured error fields.
|
|
165
235
|
*/
|
|
166
236
|
toJSON() {
|
|
@@ -170,12 +240,12 @@ var ZeroTransferError = class extends Error {
|
|
|
170
240
|
message: this.message,
|
|
171
241
|
protocol: this.protocol,
|
|
172
242
|
host: this.host,
|
|
173
|
-
command: this.command,
|
|
243
|
+
command: this.command === void 0 ? void 0 : redactCommand(this.command),
|
|
174
244
|
ftpCode: this.ftpCode,
|
|
175
245
|
sftpCode: this.sftpCode,
|
|
176
246
|
path: this.path,
|
|
177
247
|
retryable: this.retryable,
|
|
178
|
-
details: this.details
|
|
248
|
+
details: this.details === void 0 ? void 0 : redactObject(this.details)
|
|
179
249
|
};
|
|
180
250
|
}
|
|
181
251
|
};
|
|
@@ -698,15 +768,20 @@ var ProviderRegistry = class {
|
|
|
698
768
|
var TransferClient = class {
|
|
699
769
|
/** Provider registry used by this client. */
|
|
700
770
|
registry;
|
|
771
|
+
/** Execution defaults applied when call sites omit their own values. */
|
|
772
|
+
defaults;
|
|
701
773
|
logger;
|
|
702
774
|
/**
|
|
703
775
|
* Creates a transfer client without opening any provider connections.
|
|
704
776
|
*
|
|
705
|
-
* @param options - Optional registry, provider factories, and
|
|
777
|
+
* @param options - Optional registry, provider factories, logger, and execution defaults.
|
|
706
778
|
*/
|
|
707
779
|
constructor(options = {}) {
|
|
708
780
|
this.registry = options.registry ?? new ProviderRegistry();
|
|
709
781
|
this.logger = options.logger ?? noopLogger;
|
|
782
|
+
if (options.defaults !== void 0) {
|
|
783
|
+
this.defaults = { ...options.defaults };
|
|
784
|
+
}
|
|
710
785
|
for (const provider of options.providers ?? []) {
|
|
711
786
|
this.registry.register(provider);
|
|
712
787
|
}
|
|
@@ -1279,18 +1354,25 @@ var TransferEngine = class {
|
|
|
1279
1354
|
for (let attemptNumber = 1; attemptNumber <= maxAttempts; attemptNumber += 1) {
|
|
1280
1355
|
this.throwIfAborted(abortScope.signal, job);
|
|
1281
1356
|
const attemptStartedAt = this.now();
|
|
1357
|
+
const attemptScope = createAttemptScope(
|
|
1358
|
+
abortScope.signal,
|
|
1359
|
+
options.timeout,
|
|
1360
|
+
job,
|
|
1361
|
+
attemptNumber
|
|
1362
|
+
);
|
|
1282
1363
|
const context = this.createExecutionContext(
|
|
1283
1364
|
job,
|
|
1284
1365
|
attemptNumber,
|
|
1285
1366
|
attemptStartedAt,
|
|
1286
1367
|
options,
|
|
1287
|
-
|
|
1368
|
+
attemptScope.signal,
|
|
1288
1369
|
(bytesTransferred) => {
|
|
1289
1370
|
latestBytesTransferred = bytesTransferred;
|
|
1290
|
-
}
|
|
1371
|
+
},
|
|
1372
|
+
attemptScope.notifyProgress
|
|
1291
1373
|
);
|
|
1292
1374
|
try {
|
|
1293
|
-
const result = await runExecutor(executor, context,
|
|
1375
|
+
const result = await runExecutor(executor, context, attemptScope.signal, job);
|
|
1294
1376
|
context.throwIfAborted();
|
|
1295
1377
|
latestBytesTransferred = result.bytesTransferred;
|
|
1296
1378
|
const completedAt = this.now();
|
|
@@ -1308,16 +1390,27 @@ var TransferEngine = class {
|
|
|
1308
1390
|
summarizeError(error)
|
|
1309
1391
|
);
|
|
1310
1392
|
attempts.push(attempt);
|
|
1311
|
-
if (error instanceof AbortError ||
|
|
1393
|
+
if (error instanceof AbortError || abortScope.signal?.aborted === true) {
|
|
1312
1394
|
throw error;
|
|
1313
1395
|
}
|
|
1314
|
-
const retryInput = {
|
|
1396
|
+
const retryInput = {
|
|
1397
|
+
attempt: attemptNumber,
|
|
1398
|
+
elapsedMs: Math.max(0, completedAt.getTime() - startedAt.getTime()),
|
|
1399
|
+
error,
|
|
1400
|
+
job
|
|
1401
|
+
};
|
|
1315
1402
|
const shouldRetry = attemptNumber < maxAttempts && (options.retry?.shouldRetry?.(retryInput) ?? isRetryable(error));
|
|
1316
1403
|
if (shouldRetry) {
|
|
1317
1404
|
options.retry?.onRetry?.(retryInput);
|
|
1405
|
+
const delayMs = normalizeDelayMs(options.retry?.getDelayMs?.(retryInput));
|
|
1406
|
+
if (delayMs > 0) {
|
|
1407
|
+
await sleepWithAbort(delayMs, abortScope.signal, job);
|
|
1408
|
+
}
|
|
1318
1409
|
continue;
|
|
1319
1410
|
}
|
|
1320
1411
|
throw createTransferFailure(job, error, attempts);
|
|
1412
|
+
} finally {
|
|
1413
|
+
attemptScope.dispose();
|
|
1321
1414
|
}
|
|
1322
1415
|
}
|
|
1323
1416
|
throw createTransferFailure(job, void 0, attempts);
|
|
@@ -1325,12 +1418,13 @@ var TransferEngine = class {
|
|
|
1325
1418
|
abortScope.dispose();
|
|
1326
1419
|
}
|
|
1327
1420
|
}
|
|
1328
|
-
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred) {
|
|
1421
|
+
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred, notifyProgress) {
|
|
1329
1422
|
const context = {
|
|
1330
1423
|
attempt,
|
|
1331
1424
|
job,
|
|
1332
1425
|
reportProgress: (bytesTransferred, totalBytes) => {
|
|
1333
1426
|
this.throwIfAborted(signal, job);
|
|
1427
|
+
notifyProgress();
|
|
1334
1428
|
updateBytesTransferred(bytesTransferred);
|
|
1335
1429
|
const progressInput = {
|
|
1336
1430
|
bytesTransferred,
|
|
@@ -1399,6 +1493,96 @@ function createAbortScope(parentSignal, timeout, job) {
|
|
|
1399
1493
|
signal: controller.signal
|
|
1400
1494
|
};
|
|
1401
1495
|
}
|
|
1496
|
+
function createAttemptScope(parentSignal, timeout, job, attempt) {
|
|
1497
|
+
const attemptTimeoutMs = normalizeTimeoutMs(timeout?.attemptTimeoutMs);
|
|
1498
|
+
const stallTimeoutMs = normalizeTimeoutMs(timeout?.stallTimeoutMs);
|
|
1499
|
+
if (attemptTimeoutMs === void 0 && stallTimeoutMs === void 0) {
|
|
1500
|
+
const scope = {
|
|
1501
|
+
dispose: () => void 0,
|
|
1502
|
+
notifyProgress: () => void 0
|
|
1503
|
+
};
|
|
1504
|
+
if (parentSignal !== void 0) scope.signal = parentSignal;
|
|
1505
|
+
return scope;
|
|
1506
|
+
}
|
|
1507
|
+
const controller = new AbortController();
|
|
1508
|
+
const retryable = timeout?.retryable ?? true;
|
|
1509
|
+
const abortFromParent = () => controller.abort(parentSignal?.reason);
|
|
1510
|
+
if (parentSignal?.aborted === true) {
|
|
1511
|
+
abortFromParent();
|
|
1512
|
+
} else {
|
|
1513
|
+
parentSignal?.addEventListener("abort", abortFromParent, { once: true });
|
|
1514
|
+
}
|
|
1515
|
+
const attemptTimer = attemptTimeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
1516
|
+
controller.abort(
|
|
1517
|
+
new TimeoutError({
|
|
1518
|
+
details: { attempt, attemptTimeoutMs, jobId: job.id, operation: job.operation },
|
|
1519
|
+
message: `Transfer attempt ${String(attempt)} timed out after ${String(attemptTimeoutMs)}ms: ${job.id}`,
|
|
1520
|
+
retryable
|
|
1521
|
+
})
|
|
1522
|
+
);
|
|
1523
|
+
}, attemptTimeoutMs);
|
|
1524
|
+
let stallTimer;
|
|
1525
|
+
const armStallWatchdog = () => {
|
|
1526
|
+
if (stallTimeoutMs === void 0 || controller.signal.aborted) return;
|
|
1527
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1528
|
+
stallTimer = setTimeout(() => {
|
|
1529
|
+
controller.abort(
|
|
1530
|
+
new TimeoutError({
|
|
1531
|
+
details: { attempt, jobId: job.id, operation: job.operation, stallTimeoutMs },
|
|
1532
|
+
message: `Transfer attempt ${String(attempt)} stalled (no progress for ${String(stallTimeoutMs)}ms): ${job.id}`,
|
|
1533
|
+
retryable
|
|
1534
|
+
})
|
|
1535
|
+
);
|
|
1536
|
+
}, stallTimeoutMs);
|
|
1537
|
+
};
|
|
1538
|
+
armStallWatchdog();
|
|
1539
|
+
return {
|
|
1540
|
+
dispose: () => {
|
|
1541
|
+
if (attemptTimer !== void 0) clearTimeout(attemptTimer);
|
|
1542
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1543
|
+
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
1544
|
+
},
|
|
1545
|
+
notifyProgress: armStallWatchdog,
|
|
1546
|
+
signal: controller.signal
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
function sleepWithAbort(delayMs, signal, job) {
|
|
1550
|
+
return new Promise((resolve, reject) => {
|
|
1551
|
+
if (signal === void 0) {
|
|
1552
|
+
setTimeout(resolve, delayMs);
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
if (signal.aborted) {
|
|
1556
|
+
reject(toAbortFailure(signal, job));
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const rejectAbort = () => {
|
|
1560
|
+
clearTimeout(timer);
|
|
1561
|
+
reject(toAbortFailure(signal, job));
|
|
1562
|
+
};
|
|
1563
|
+
const timer = setTimeout(() => {
|
|
1564
|
+
signal.removeEventListener("abort", rejectAbort);
|
|
1565
|
+
resolve();
|
|
1566
|
+
}, delayMs);
|
|
1567
|
+
signal.addEventListener("abort", rejectAbort, { once: true });
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
function toAbortFailure(signal, job) {
|
|
1571
|
+
if (signal.reason instanceof ZeroTransferError) {
|
|
1572
|
+
return signal.reason;
|
|
1573
|
+
}
|
|
1574
|
+
return new AbortError({
|
|
1575
|
+
details: { jobId: job.id, operation: job.operation },
|
|
1576
|
+
message: `Transfer job aborted: ${job.id}`,
|
|
1577
|
+
retryable: false
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
function normalizeDelayMs(value) {
|
|
1581
|
+
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1582
|
+
return 0;
|
|
1583
|
+
}
|
|
1584
|
+
return Math.floor(value);
|
|
1585
|
+
}
|
|
1402
1586
|
function normalizeTimeoutMs(value) {
|
|
1403
1587
|
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1404
1588
|
return void 0;
|
|
@@ -1567,7 +1751,7 @@ async function runRoute(options) {
|
|
|
1567
1751
|
const executor = createProviderTransferExecutor({
|
|
1568
1752
|
resolveSession: ({ role }) => sessions.get(role)
|
|
1569
1753
|
});
|
|
1570
|
-
return await engine.execute(job, executor, buildExecuteOptions(options));
|
|
1754
|
+
return await engine.execute(job, executor, buildExecuteOptions(options, client));
|
|
1571
1755
|
} finally {
|
|
1572
1756
|
if (destinationSession !== void 0) {
|
|
1573
1757
|
await destinationSession.disconnect();
|
|
@@ -1604,12 +1788,14 @@ function defaultJobId(route, now) {
|
|
|
1604
1788
|
const timestamp = (now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
1605
1789
|
return `route:${route.id}:${timestamp.toString(36)}`;
|
|
1606
1790
|
}
|
|
1607
|
-
function buildExecuteOptions(options) {
|
|
1791
|
+
function buildExecuteOptions(options, client) {
|
|
1608
1792
|
const execute = {};
|
|
1793
|
+
const retry = options.retry ?? client.defaults?.retry;
|
|
1794
|
+
const timeout = options.timeout ?? client.defaults?.timeout;
|
|
1609
1795
|
if (options.signal !== void 0) execute.signal = options.signal;
|
|
1610
|
-
if (
|
|
1796
|
+
if (retry !== void 0) execute.retry = retry;
|
|
1611
1797
|
if (options.onProgress !== void 0) execute.onProgress = options.onProgress;
|
|
1612
|
-
if (
|
|
1798
|
+
if (timeout !== void 0) execute.timeout = timeout;
|
|
1613
1799
|
if (options.bandwidthLimit !== void 0) execute.bandwidthLimit = options.bandwidthLimit;
|
|
1614
1800
|
return execute;
|
|
1615
1801
|
}
|
|
@@ -1666,41 +1852,6 @@ function defaultRouteSuffix(source, destination) {
|
|
|
1666
1852
|
return `${source}->${destination}`;
|
|
1667
1853
|
}
|
|
1668
1854
|
|
|
1669
|
-
// src/logging/redaction.ts
|
|
1670
|
-
var REDACTED = "[REDACTED]";
|
|
1671
|
-
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
1672
|
-
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
1673
|
-
function isSensitiveKey(key) {
|
|
1674
|
-
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
1675
|
-
}
|
|
1676
|
-
function redactCommand(command) {
|
|
1677
|
-
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
1678
|
-
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
1679
|
-
});
|
|
1680
|
-
}
|
|
1681
|
-
function redactValue(value) {
|
|
1682
|
-
if (typeof value === "string") {
|
|
1683
|
-
return redactCommand(value);
|
|
1684
|
-
}
|
|
1685
|
-
if (Array.isArray(value)) {
|
|
1686
|
-
return value.map((item) => redactValue(item));
|
|
1687
|
-
}
|
|
1688
|
-
if (value !== null && typeof value === "object") {
|
|
1689
|
-
return redactObject(value);
|
|
1690
|
-
}
|
|
1691
|
-
return value;
|
|
1692
|
-
}
|
|
1693
|
-
function redactObject(input) {
|
|
1694
|
-
return Object.fromEntries(
|
|
1695
|
-
Object.entries(input).map(([key, value]) => {
|
|
1696
|
-
if (isSensitiveKey(key)) {
|
|
1697
|
-
return [key, REDACTED];
|
|
1698
|
-
}
|
|
1699
|
-
return [key, redactValue(value)];
|
|
1700
|
-
})
|
|
1701
|
-
);
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
1855
|
// src/profiles/SecretSource.ts
|
|
1705
1856
|
var import_node_buffer2 = require("buffer");
|
|
1706
1857
|
var import_promises = require("fs/promises");
|
|
@@ -2072,11 +2223,11 @@ var import_promises2 = require("fs/promises");
|
|
|
2072
2223
|
var import_node_path2 = __toESM(require("path"));
|
|
2073
2224
|
|
|
2074
2225
|
// src/utils/path.ts
|
|
2075
|
-
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n]/;
|
|
2226
|
+
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n\0]/;
|
|
2076
2227
|
function assertSafeFtpArgument(value, label = "path") {
|
|
2077
2228
|
if (UNSAFE_FTP_ARGUMENT_PATTERN.test(value)) {
|
|
2078
2229
|
throw new ConfigurationError({
|
|
2079
|
-
message: `Unsafe FTP ${label}: CR and
|
|
2230
|
+
message: `Unsafe FTP ${label}: CR, LF, and NUL characters are not allowed`,
|
|
2080
2231
|
retryable: false,
|
|
2081
2232
|
details: {
|
|
2082
2233
|
label
|
|
@@ -3378,7 +3529,6 @@ function expandAlgorithms(values) {
|
|
|
3378
3529
|
}
|
|
3379
3530
|
|
|
3380
3531
|
// src/profiles/importers/FileZillaImporter.ts
|
|
3381
|
-
var import_node_buffer5 = require("buffer");
|
|
3382
3532
|
function importFileZillaSites(xml) {
|
|
3383
3533
|
const events = tokenizeXml(xml);
|
|
3384
3534
|
if (events.length === 0) {
|
|
@@ -3394,7 +3544,6 @@ function importFileZillaSites(xml) {
|
|
|
3394
3544
|
const folderNamePending = [];
|
|
3395
3545
|
let inServer = false;
|
|
3396
3546
|
let serverFields = {};
|
|
3397
|
-
let serverPasswordEncoding;
|
|
3398
3547
|
let activeTag;
|
|
3399
3548
|
let captureFolderName = false;
|
|
3400
3549
|
for (const event of events) {
|
|
@@ -3407,13 +3556,9 @@ function importFileZillaSites(xml) {
|
|
|
3407
3556
|
if (event.name === "Server") {
|
|
3408
3557
|
inServer = true;
|
|
3409
3558
|
serverFields = {};
|
|
3410
|
-
serverPasswordEncoding = void 0;
|
|
3411
3559
|
continue;
|
|
3412
3560
|
}
|
|
3413
3561
|
activeTag = event.name;
|
|
3414
|
-
if (event.name === "Pass" && inServer) {
|
|
3415
|
-
serverPasswordEncoding = event.attributes["encoding"];
|
|
3416
|
-
}
|
|
3417
3562
|
if (event.name === "Name" && !inServer && folderNamePending.length > 0) {
|
|
3418
3563
|
captureFolderName = true;
|
|
3419
3564
|
}
|
|
@@ -3439,7 +3584,7 @@ function importFileZillaSites(xml) {
|
|
|
3439
3584
|
}
|
|
3440
3585
|
if (event.name === "Server") {
|
|
3441
3586
|
const folder = folderStack.filter((segment) => segment !== "");
|
|
3442
|
-
const result = buildSiteFromFields(serverFields
|
|
3587
|
+
const result = buildSiteFromFields(serverFields);
|
|
3443
3588
|
if (result.kind === "site") {
|
|
3444
3589
|
sites.push({ ...result.site, folder });
|
|
3445
3590
|
} else {
|
|
@@ -3451,7 +3596,6 @@ function importFileZillaSites(xml) {
|
|
|
3451
3596
|
}
|
|
3452
3597
|
inServer = false;
|
|
3453
3598
|
serverFields = {};
|
|
3454
|
-
serverPasswordEncoding = void 0;
|
|
3455
3599
|
activeTag = void 0;
|
|
3456
3600
|
continue;
|
|
3457
3601
|
}
|
|
@@ -3460,7 +3604,7 @@ function importFileZillaSites(xml) {
|
|
|
3460
3604
|
}
|
|
3461
3605
|
return { sites, skipped };
|
|
3462
3606
|
}
|
|
3463
|
-
function buildSiteFromFields(fields
|
|
3607
|
+
function buildSiteFromFields(fields) {
|
|
3464
3608
|
const name = (fields["Name"] ?? fields["Host"] ?? "Untitled").trim();
|
|
3465
3609
|
const host = (fields["Host"] ?? "").trim();
|
|
3466
3610
|
if (host === "") return { kind: "skipped", name };
|
|
@@ -3479,18 +3623,9 @@ function buildSiteFromFields(fields, passwordEncoding) {
|
|
|
3479
3623
|
}
|
|
3480
3624
|
const user = fields["User"]?.trim();
|
|
3481
3625
|
if (user !== void 0 && user !== "") profile.username = { value: user };
|
|
3482
|
-
let password;
|
|
3483
3626
|
const rawPass = fields["Pass"];
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
password = import_node_buffer5.Buffer.from(rawPass, "base64").toString("utf8");
|
|
3487
|
-
} else {
|
|
3488
|
-
password = rawPass;
|
|
3489
|
-
}
|
|
3490
|
-
if (password !== void 0 && password !== "") profile.password = { value: password };
|
|
3491
|
-
}
|
|
3492
|
-
const site = { name, profile };
|
|
3493
|
-
if (password !== void 0) site.password = password;
|
|
3627
|
+
const hasStoredPassword = rawPass !== void 0 && rawPass !== "";
|
|
3628
|
+
const site = { hasStoredPassword, name, profile };
|
|
3494
3629
|
const logonText = fields["Logontype"];
|
|
3495
3630
|
if (logonText !== void 0) {
|
|
3496
3631
|
const logonType = Number.parseInt(logonText.trim(), 10);
|
|
@@ -3733,6 +3868,62 @@ function mapFtp550(details) {
|
|
|
3733
3868
|
return new PermissionDeniedError(details);
|
|
3734
3869
|
}
|
|
3735
3870
|
|
|
3871
|
+
// src/transfers/createDefaultRetryPolicy.ts
|
|
3872
|
+
var DEFAULT_MAX_ATTEMPTS = 4;
|
|
3873
|
+
var DEFAULT_BASE_DELAY_MS = 250;
|
|
3874
|
+
var DEFAULT_MAX_DELAY_MS = 3e4;
|
|
3875
|
+
var DEFAULT_MAX_ELAPSED_MS = 3e5;
|
|
3876
|
+
function createDefaultRetryPolicy(options = {}) {
|
|
3877
|
+
const maxAttempts = normalizePositiveInteger(options.maxAttempts, DEFAULT_MAX_ATTEMPTS);
|
|
3878
|
+
const baseDelayMs = normalizeNonNegative(options.baseDelayMs, DEFAULT_BASE_DELAY_MS);
|
|
3879
|
+
const maxDelayMs = normalizeNonNegative(options.maxDelayMs, DEFAULT_MAX_DELAY_MS);
|
|
3880
|
+
const maxElapsedMs = normalizeNonNegative(options.maxElapsedMs, DEFAULT_MAX_ELAPSED_MS);
|
|
3881
|
+
const random = options.random ?? Math.random;
|
|
3882
|
+
return {
|
|
3883
|
+
getDelayMs(input) {
|
|
3884
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3885
|
+
if (retryAfterMs !== void 0) {
|
|
3886
|
+
return retryAfterMs;
|
|
3887
|
+
}
|
|
3888
|
+
const exponentialMs = baseDelayMs * 2 ** (input.attempt - 1);
|
|
3889
|
+
const cappedMs = Math.min(maxDelayMs, exponentialMs);
|
|
3890
|
+
return Math.floor(random() * cappedMs);
|
|
3891
|
+
},
|
|
3892
|
+
maxAttempts,
|
|
3893
|
+
shouldRetry(input) {
|
|
3894
|
+
if (!(input.error instanceof ZeroTransferError) || !input.error.retryable) {
|
|
3895
|
+
return false;
|
|
3896
|
+
}
|
|
3897
|
+
if (input.elapsedMs >= maxElapsedMs) {
|
|
3898
|
+
return false;
|
|
3899
|
+
}
|
|
3900
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3901
|
+
if (retryAfterMs !== void 0 && input.elapsedMs + retryAfterMs > maxElapsedMs) {
|
|
3902
|
+
return false;
|
|
3903
|
+
}
|
|
3904
|
+
return true;
|
|
3905
|
+
}
|
|
3906
|
+
};
|
|
3907
|
+
}
|
|
3908
|
+
function readRetryAfterMs(error) {
|
|
3909
|
+
if (!(error instanceof ZeroTransferError)) return void 0;
|
|
3910
|
+
const value = error.details?.["retryAfterMs"];
|
|
3911
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
|
|
3912
|
+
return Math.floor(value);
|
|
3913
|
+
}
|
|
3914
|
+
function normalizePositiveInteger(value, fallback) {
|
|
3915
|
+
if (value === void 0 || !Number.isFinite(value) || value < 1) {
|
|
3916
|
+
return fallback;
|
|
3917
|
+
}
|
|
3918
|
+
return Math.floor(value);
|
|
3919
|
+
}
|
|
3920
|
+
function normalizeNonNegative(value, fallback) {
|
|
3921
|
+
if (value === void 0 || !Number.isFinite(value) || value < 0) {
|
|
3922
|
+
return fallback;
|
|
3923
|
+
}
|
|
3924
|
+
return Math.floor(value);
|
|
3925
|
+
}
|
|
3926
|
+
|
|
3736
3927
|
// src/transfers/TransferPlan.ts
|
|
3737
3928
|
function createTransferPlan(input) {
|
|
3738
3929
|
const plan = {
|
|
@@ -3830,8 +4021,8 @@ var TransferQueue = class {
|
|
|
3830
4021
|
this.concurrency = normalizeConcurrency(options.concurrency);
|
|
3831
4022
|
this.defaultExecutor = options.executor;
|
|
3832
4023
|
this.resolveExecutor = options.resolveExecutor;
|
|
3833
|
-
this.retry = options.retry;
|
|
3834
|
-
this.timeout = options.timeout;
|
|
4024
|
+
this.retry = options.retry ?? options.client?.defaults?.retry;
|
|
4025
|
+
this.timeout = options.timeout ?? options.client?.defaults?.timeout;
|
|
3835
4026
|
this.bandwidthLimit = options.bandwidthLimit;
|
|
3836
4027
|
this.onProgress = options.onProgress;
|
|
3837
4028
|
this.onReceipt = options.onReceipt;
|
|
@@ -4914,11 +5105,12 @@ var import_node_path3 = require("path");
|
|
|
4914
5105
|
|
|
4915
5106
|
// src/providers/web/awsSigv4.ts
|
|
4916
5107
|
var import_node_crypto2 = require("crypto");
|
|
5108
|
+
var UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
|
|
4917
5109
|
function signSigV4(input) {
|
|
4918
5110
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
4919
5111
|
const amzDate = formatAmzDate(now);
|
|
4920
5112
|
const dateStamp = amzDate.slice(0, 8);
|
|
4921
|
-
const payloadHash = input.body !== void 0 ? sha256Hex(input.body) : sha256Hex(new Uint8Array());
|
|
5113
|
+
const payloadHash = input.unsignedPayload === true ? UNSIGNED_PAYLOAD : input.body !== void 0 ? sha256Hex(input.body) : sha256Hex(new Uint8Array());
|
|
4922
5114
|
input.headers["host"] = input.url.host;
|
|
4923
5115
|
input.headers["x-amz-date"] = amzDate;
|
|
4924
5116
|
input.headers["x-amz-content-sha256"] = payloadHash;
|
|
@@ -4993,7 +5185,7 @@ function hmacHex(key, data) {
|
|
|
4993
5185
|
}
|
|
4994
5186
|
|
|
4995
5187
|
// src/providers/web/httpInternals.ts
|
|
4996
|
-
var
|
|
5188
|
+
var import_node_buffer5 = require("buffer");
|
|
4997
5189
|
function parseContentRangeTotal(value) {
|
|
4998
5190
|
const match = /\/(\d+)\s*$/.exec(value);
|
|
4999
5191
|
if (match === null) return void 0;
|
|
@@ -5019,8 +5211,48 @@ function formatRangeHeader(offset, length) {
|
|
|
5019
5211
|
const end = offset + length - 1;
|
|
5020
5212
|
return `bytes=${String(offset)}-${String(end)}`;
|
|
5021
5213
|
}
|
|
5022
|
-
|
|
5023
|
-
|
|
5214
|
+
var ERROR_BODY_EXCERPT_LIMIT = 2048;
|
|
5215
|
+
async function readErrorBodyExcerpt(response) {
|
|
5216
|
+
try {
|
|
5217
|
+
const text = await response.text();
|
|
5218
|
+
if (text.length === 0) return void 0;
|
|
5219
|
+
return text.length > ERROR_BODY_EXCERPT_LIMIT ? `${text.slice(0, ERROR_BODY_EXCERPT_LIMIT)}... [truncated]` : text;
|
|
5220
|
+
} catch {
|
|
5221
|
+
return void 0;
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
function parseRetryAfterMs(value, now = Date.now) {
|
|
5225
|
+
if (value === null) return void 0;
|
|
5226
|
+
const trimmed = value.trim();
|
|
5227
|
+
if (trimmed.length === 0) return void 0;
|
|
5228
|
+
if (/^\d+$/.test(trimmed)) {
|
|
5229
|
+
const seconds = Number.parseInt(trimmed, 10);
|
|
5230
|
+
return Number.isFinite(seconds) ? seconds * 1e3 : void 0;
|
|
5231
|
+
}
|
|
5232
|
+
if (!/[A-Za-z]/.test(trimmed)) return void 0;
|
|
5233
|
+
const retryAt = Date.parse(trimmed);
|
|
5234
|
+
if (Number.isNaN(retryAt)) return void 0;
|
|
5235
|
+
return Math.max(0, retryAt - now());
|
|
5236
|
+
}
|
|
5237
|
+
async function mapResponseErrorWithBody(response, path2) {
|
|
5238
|
+
return mapResponseError(response, path2, await readErrorBodyExcerpt(response));
|
|
5239
|
+
}
|
|
5240
|
+
function mapResponseError(response, path2, bodyExcerpt) {
|
|
5241
|
+
const details = {
|
|
5242
|
+
path: path2,
|
|
5243
|
+
status: response.status,
|
|
5244
|
+
statusText: response.statusText
|
|
5245
|
+
};
|
|
5246
|
+
if (bodyExcerpt !== void 0) details["body"] = bodyExcerpt;
|
|
5247
|
+
if (response.status === 429 || response.status === 503) {
|
|
5248
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
5249
|
+
if (retryAfterMs !== void 0) details["retryAfterMs"] = retryAfterMs;
|
|
5250
|
+
return new ConnectionError({
|
|
5251
|
+
details,
|
|
5252
|
+
message: response.status === 429 ? `HTTP request for ${path2} was rate limited (429)` : `HTTP service unavailable for ${path2} (503)`,
|
|
5253
|
+
retryable: true
|
|
5254
|
+
});
|
|
5255
|
+
}
|
|
5024
5256
|
if (response.status === 401) {
|
|
5025
5257
|
return new AuthenticationError({
|
|
5026
5258
|
details,
|
|
@@ -5052,18 +5284,58 @@ async function* webStreamToAsyncIterable(body) {
|
|
|
5052
5284
|
const reader = body.getReader();
|
|
5053
5285
|
try {
|
|
5054
5286
|
while (true) {
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5287
|
+
let result;
|
|
5288
|
+
try {
|
|
5289
|
+
result = await reader.read();
|
|
5290
|
+
} catch (error) {
|
|
5291
|
+
if (error instanceof ZeroTransferError) throw error;
|
|
5292
|
+
throw new ConnectionError({
|
|
5293
|
+
cause: error,
|
|
5294
|
+
message: "HTTP response stream was interrupted before completion",
|
|
5295
|
+
retryable: true
|
|
5296
|
+
});
|
|
5297
|
+
}
|
|
5298
|
+
if (result.done) break;
|
|
5299
|
+
if (result.value !== void 0) yield result.value;
|
|
5058
5300
|
}
|
|
5059
5301
|
} finally {
|
|
5060
5302
|
reader.releaseLock();
|
|
5061
5303
|
}
|
|
5062
5304
|
}
|
|
5305
|
+
function asyncIterableToReadableStream(source, onChunk) {
|
|
5306
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
5307
|
+
return new ReadableStream({
|
|
5308
|
+
async pull(controller) {
|
|
5309
|
+
try {
|
|
5310
|
+
const next = await iterator.next();
|
|
5311
|
+
if (next.done === true) {
|
|
5312
|
+
controller.close();
|
|
5313
|
+
return;
|
|
5314
|
+
}
|
|
5315
|
+
const chunk = next.value;
|
|
5316
|
+
if (chunk.byteLength === 0) {
|
|
5317
|
+
return;
|
|
5318
|
+
}
|
|
5319
|
+
controller.enqueue(chunk);
|
|
5320
|
+
onChunk(chunk);
|
|
5321
|
+
} catch (error) {
|
|
5322
|
+
controller.error(error);
|
|
5323
|
+
}
|
|
5324
|
+
},
|
|
5325
|
+
async cancel(reason) {
|
|
5326
|
+
if (typeof iterator.return === "function") {
|
|
5327
|
+
try {
|
|
5328
|
+
await iterator.return(reason);
|
|
5329
|
+
} catch {
|
|
5330
|
+
}
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
});
|
|
5334
|
+
}
|
|
5063
5335
|
function secretToString(value) {
|
|
5064
5336
|
if (typeof value === "string") return value;
|
|
5065
|
-
if (value instanceof Uint8Array ||
|
|
5066
|
-
return
|
|
5337
|
+
if (value instanceof Uint8Array || import_node_buffer5.Buffer.isBuffer(value)) {
|
|
5338
|
+
return import_node_buffer5.Buffer.from(value).toString("utf8");
|
|
5067
5339
|
}
|
|
5068
5340
|
return String(value);
|
|
5069
5341
|
}
|
|
@@ -5275,7 +5547,7 @@ var S3FileSystem = class {
|
|
|
5275
5547
|
url.searchParams.set("delimiter", "/");
|
|
5276
5548
|
if (prefix.length > 0) url.searchParams.set("prefix", prefix);
|
|
5277
5549
|
const response = await s3Fetch(this.options, "GET", url);
|
|
5278
|
-
if (!response.ok) throw
|
|
5550
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5279
5551
|
const body = await response.text();
|
|
5280
5552
|
return parseListObjectsV2(body, prefix);
|
|
5281
5553
|
}
|
|
@@ -5283,7 +5555,7 @@ var S3FileSystem = class {
|
|
|
5283
5555
|
const normalized = normalizeRemotePath(path2);
|
|
5284
5556
|
const url = buildObjectUrl(this.options, normalized);
|
|
5285
5557
|
const response = await s3Fetch(this.options, "HEAD", url);
|
|
5286
|
-
if (!response.ok) throw
|
|
5558
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5287
5559
|
const stat = {
|
|
5288
5560
|
exists: true,
|
|
5289
5561
|
name: basenameRemotePath(normalized),
|
|
@@ -5323,12 +5595,12 @@ var S3TransferOperations = class {
|
|
|
5323
5595
|
extraHeaders: headers
|
|
5324
5596
|
});
|
|
5325
5597
|
if (!response.ok && response.status !== 206) {
|
|
5326
|
-
throw
|
|
5598
|
+
throw await mapResponseErrorWithBody(response, normalized);
|
|
5327
5599
|
}
|
|
5328
5600
|
const body = response.body;
|
|
5329
5601
|
if (body === null) {
|
|
5330
5602
|
throw new ConnectionError({
|
|
5331
|
-
message: `S3 response had no body for ${url
|
|
5603
|
+
message: `S3 response had no body for ${redactUrlForLogging(url)}`,
|
|
5332
5604
|
retryable: true
|
|
5333
5605
|
});
|
|
5334
5606
|
}
|
|
@@ -5364,19 +5636,33 @@ var S3TransferOperations = class {
|
|
|
5364
5636
|
}
|
|
5365
5637
|
return this.writeSingleShot(request, normalized);
|
|
5366
5638
|
}
|
|
5639
|
+
/**
|
|
5640
|
+
* Single PUT upload used when multipart is disabled. Streams the body with
|
|
5641
|
+
* a declared `Content-Length` (signed as `UNSIGNED-PAYLOAD`) when the
|
|
5642
|
+
* caller knows the total size; S3 requires a length up front, so only
|
|
5643
|
+
* unknown-size payloads fall back to buffering the content in memory.
|
|
5644
|
+
*/
|
|
5367
5645
|
async writeSingleShot(request, normalized) {
|
|
5368
5646
|
const url = buildObjectUrl(this.options, normalized);
|
|
5369
|
-
const
|
|
5647
|
+
const totalBytes = request.totalBytes;
|
|
5648
|
+
if (typeof totalBytes !== "number" || totalBytes < 0) {
|
|
5649
|
+
const buffered = await collectChunks(request.content);
|
|
5650
|
+
return this.singleShotFromBuffer(request, normalized, buffered);
|
|
5651
|
+
}
|
|
5652
|
+
let bytesTransferred = 0;
|
|
5653
|
+
const stream = asyncIterableToReadableStream(request.content, (chunk) => {
|
|
5654
|
+
bytesTransferred += chunk.byteLength;
|
|
5655
|
+
request.reportProgress(bytesTransferred, totalBytes);
|
|
5656
|
+
});
|
|
5370
5657
|
const response = await s3Fetch(this.options, "PUT", url, {
|
|
5371
5658
|
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
5372
|
-
|
|
5373
|
-
|
|
5659
|
+
extraHeaders: { "content-type": "application/octet-stream" },
|
|
5660
|
+
streamBody: { content: stream, contentLength: totalBytes }
|
|
5374
5661
|
});
|
|
5375
|
-
if (!response.ok) throw
|
|
5376
|
-
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
5662
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5377
5663
|
const result = {
|
|
5378
|
-
bytesTransferred
|
|
5379
|
-
totalBytes
|
|
5664
|
+
bytesTransferred,
|
|
5665
|
+
totalBytes
|
|
5380
5666
|
};
|
|
5381
5667
|
const etag = response.headers.get("etag");
|
|
5382
5668
|
if (etag !== null) result.checksum = etag;
|
|
@@ -5440,7 +5726,7 @@ var S3TransferOperations = class {
|
|
|
5440
5726
|
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
5441
5727
|
extraHeaders: { "content-type": "application/octet-stream" }
|
|
5442
5728
|
});
|
|
5443
|
-
if (!initiateResponse.ok) throw
|
|
5729
|
+
if (!initiateResponse.ok) throw await mapResponseErrorWithBody(initiateResponse, normalized);
|
|
5444
5730
|
const initiateBody = await initiateResponse.text();
|
|
5445
5731
|
const initiated = innerText(initiateBody, "UploadId");
|
|
5446
5732
|
if (initiated === void 0 || initiated === "") {
|
|
@@ -5479,7 +5765,7 @@ var S3TransferOperations = class {
|
|
|
5479
5765
|
body: partBytes.bytes
|
|
5480
5766
|
});
|
|
5481
5767
|
if (!partResponse.ok) {
|
|
5482
|
-
throw
|
|
5768
|
+
throw await mapResponseErrorWithBody(partResponse, normalized);
|
|
5483
5769
|
}
|
|
5484
5770
|
const partEtag = partResponse.headers.get("etag");
|
|
5485
5771
|
if (partEtag === null) {
|
|
@@ -5535,7 +5821,7 @@ var S3TransferOperations = class {
|
|
|
5535
5821
|
if (resumeStore === void 0) {
|
|
5536
5822
|
await abortMultipart(this.options, objectUrl, uploadId).catch(() => void 0);
|
|
5537
5823
|
}
|
|
5538
|
-
throw
|
|
5824
|
+
throw await mapResponseErrorWithBody(completeResponse, normalized);
|
|
5539
5825
|
}
|
|
5540
5826
|
if (resumeStore !== void 0) await resumeStore.clear(resumeKey);
|
|
5541
5827
|
const completeBody = await completeResponse.text();
|
|
@@ -5554,7 +5840,7 @@ var S3TransferOperations = class {
|
|
|
5554
5840
|
body: buffered,
|
|
5555
5841
|
extraHeaders: { "content-type": "application/octet-stream" }
|
|
5556
5842
|
});
|
|
5557
|
-
if (!response.ok) throw
|
|
5843
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5558
5844
|
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
5559
5845
|
const result = {
|
|
5560
5846
|
bytesTransferred: buffered.byteLength,
|
|
@@ -5572,6 +5858,8 @@ async function s3Fetch(options, method, url, fetchOptions = {}) {
|
|
|
5572
5858
|
};
|
|
5573
5859
|
if (fetchOptions.body !== void 0) {
|
|
5574
5860
|
headers["content-length"] = String(fetchOptions.body.byteLength);
|
|
5861
|
+
} else if (fetchOptions.streamBody !== void 0) {
|
|
5862
|
+
headers["content-length"] = String(fetchOptions.streamBody.contentLength);
|
|
5575
5863
|
}
|
|
5576
5864
|
signSigV4({
|
|
5577
5865
|
accessKeyId: options.accessKeyId,
|
|
@@ -5582,10 +5870,16 @@ async function s3Fetch(options, method, url, fetchOptions = {}) {
|
|
|
5582
5870
|
service: options.service,
|
|
5583
5871
|
url,
|
|
5584
5872
|
...fetchOptions.body !== void 0 ? { body: fetchOptions.body } : {},
|
|
5873
|
+
...fetchOptions.streamBody !== void 0 ? { unsignedPayload: true } : {},
|
|
5585
5874
|
...options.sessionToken !== void 0 ? { sessionToken: options.sessionToken } : {}
|
|
5586
5875
|
});
|
|
5587
5876
|
const init = { headers, method };
|
|
5588
|
-
if (fetchOptions.body !== void 0)
|
|
5877
|
+
if (fetchOptions.body !== void 0) {
|
|
5878
|
+
init.body = fetchOptions.body;
|
|
5879
|
+
} else if (fetchOptions.streamBody !== void 0) {
|
|
5880
|
+
init.body = fetchOptions.streamBody.content;
|
|
5881
|
+
init.duplex = "half";
|
|
5882
|
+
}
|
|
5589
5883
|
if (fetchOptions.signal !== void 0) init.signal = fetchOptions.signal;
|
|
5590
5884
|
const controller = new AbortController();
|
|
5591
5885
|
const upstreamSignal = init.signal ?? null;
|
|
@@ -5603,10 +5897,11 @@ async function s3Fetch(options, method, url, fetchOptions = {}) {
|
|
|
5603
5897
|
try {
|
|
5604
5898
|
return await options.fetch(url.toString(), { ...init, signal: controller.signal });
|
|
5605
5899
|
} catch (error) {
|
|
5900
|
+
const safeUrl = redactUrlForLogging(url);
|
|
5606
5901
|
throw new ConnectionError({
|
|
5607
5902
|
cause: error,
|
|
5608
|
-
details: { url:
|
|
5609
|
-
message: `S3 request to ${
|
|
5903
|
+
details: { url: safeUrl },
|
|
5904
|
+
message: `S3 request to ${safeUrl} failed`,
|
|
5610
5905
|
retryable: true
|
|
5611
5906
|
});
|
|
5612
5907
|
} finally {
|
|
@@ -5779,6 +6074,7 @@ function innerText(xml, tag) {
|
|
|
5779
6074
|
copyBetween,
|
|
5780
6075
|
createAtomicDeployPlan,
|
|
5781
6076
|
createBandwidthThrottle,
|
|
6077
|
+
createDefaultRetryPolicy,
|
|
5782
6078
|
createFileSystemS3MultipartResumeStore,
|
|
5783
6079
|
createLocalProviderFactory,
|
|
5784
6080
|
createMemoryProviderFactory,
|
|
@@ -5817,8 +6113,10 @@ function innerText(xml, tag) {
|
|
|
5817
6113
|
parseRemoteManifest,
|
|
5818
6114
|
redactCommand,
|
|
5819
6115
|
redactConnectionProfile,
|
|
6116
|
+
redactErrorForLogging,
|
|
5820
6117
|
redactObject,
|
|
5821
6118
|
redactSecretSource,
|
|
6119
|
+
redactUrlForLogging,
|
|
5822
6120
|
redactValue,
|
|
5823
6121
|
resolveConnectionProfileSecrets,
|
|
5824
6122
|
resolveOpenSshHost,
|