@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.mjs
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
// src/client/ZeroTransfer.ts
|
|
2
2
|
import { EventEmitter } from "events";
|
|
3
3
|
|
|
4
|
+
// src/logging/redaction.ts
|
|
5
|
+
var REDACTED = "[REDACTED]";
|
|
6
|
+
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
7
|
+
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
8
|
+
var URL_KEY_PATTERN = /(?:url|uri|href)$/i;
|
|
9
|
+
function isSensitiveKey(key) {
|
|
10
|
+
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
11
|
+
}
|
|
12
|
+
function redactCommand(command) {
|
|
13
|
+
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
14
|
+
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function redactValue(value) {
|
|
18
|
+
if (typeof value === "string") {
|
|
19
|
+
return redactCommand(value);
|
|
20
|
+
}
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
return value.map((item) => redactValue(item));
|
|
23
|
+
}
|
|
24
|
+
if (value !== null && typeof value === "object") {
|
|
25
|
+
return redactObject(value);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
function redactObject(input) {
|
|
30
|
+
return Object.fromEntries(
|
|
31
|
+
Object.entries(input).map(([key, value]) => {
|
|
32
|
+
if (isSensitiveKey(key)) {
|
|
33
|
+
return [key, REDACTED];
|
|
34
|
+
}
|
|
35
|
+
if (URL_KEY_PATTERN.test(key) && typeof value === "string") {
|
|
36
|
+
return [key, redactUrlForLogging(value)];
|
|
37
|
+
}
|
|
38
|
+
return [key, redactValue(value)];
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
function redactUrlForLogging(url) {
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = typeof url === "string" ? new URL(url) : url;
|
|
46
|
+
} catch {
|
|
47
|
+
return REDACTED;
|
|
48
|
+
}
|
|
49
|
+
const origin = parsed.host.length > 0 ? `${parsed.protocol}//${parsed.host}` : parsed.protocol;
|
|
50
|
+
const query = parsed.search.length > 0 ? `?${REDACTED}` : "";
|
|
51
|
+
return `${origin}${parsed.pathname}${query}`;
|
|
52
|
+
}
|
|
53
|
+
function redactErrorForLogging(error) {
|
|
54
|
+
if (error !== null && typeof error === "object") {
|
|
55
|
+
const candidate = error;
|
|
56
|
+
if (typeof candidate.toJSON === "function") {
|
|
57
|
+
return redactObject(candidate.toJSON());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (error instanceof Error) {
|
|
61
|
+
return redactObject({ message: error.message, name: error.name });
|
|
62
|
+
}
|
|
63
|
+
return { message: redactValue(typeof error === "string" ? error : String(error)) };
|
|
64
|
+
}
|
|
65
|
+
|
|
4
66
|
// src/errors/ZeroTransferError.ts
|
|
5
67
|
var ZeroTransferError = class extends Error {
|
|
6
68
|
/** Stable machine-readable error code. */
|
|
@@ -42,6 +104,11 @@ var ZeroTransferError = class extends Error {
|
|
|
42
104
|
/**
|
|
43
105
|
* Serializes the error into a plain object suitable for logs or API responses.
|
|
44
106
|
*
|
|
107
|
+
* `details` and `command` are passed through secret redaction so serialized
|
|
108
|
+
* errors never leak credentials, signed URLs, or raw protocol commands. The
|
|
109
|
+
* live {@link ZeroTransferError.details | details} property stays unredacted
|
|
110
|
+
* for programmatic consumers.
|
|
111
|
+
*
|
|
45
112
|
* @returns A JSON-safe object containing public structured error fields.
|
|
46
113
|
*/
|
|
47
114
|
toJSON() {
|
|
@@ -51,12 +118,12 @@ var ZeroTransferError = class extends Error {
|
|
|
51
118
|
message: this.message,
|
|
52
119
|
protocol: this.protocol,
|
|
53
120
|
host: this.host,
|
|
54
|
-
command: this.command,
|
|
121
|
+
command: this.command === void 0 ? void 0 : redactCommand(this.command),
|
|
55
122
|
ftpCode: this.ftpCode,
|
|
56
123
|
sftpCode: this.sftpCode,
|
|
57
124
|
path: this.path,
|
|
58
125
|
retryable: this.retryable,
|
|
59
|
-
details: this.details
|
|
126
|
+
details: this.details === void 0 ? void 0 : redactObject(this.details)
|
|
60
127
|
};
|
|
61
128
|
}
|
|
62
129
|
};
|
|
@@ -579,15 +646,20 @@ var ProviderRegistry = class {
|
|
|
579
646
|
var TransferClient = class {
|
|
580
647
|
/** Provider registry used by this client. */
|
|
581
648
|
registry;
|
|
649
|
+
/** Execution defaults applied when call sites omit their own values. */
|
|
650
|
+
defaults;
|
|
582
651
|
logger;
|
|
583
652
|
/**
|
|
584
653
|
* Creates a transfer client without opening any provider connections.
|
|
585
654
|
*
|
|
586
|
-
* @param options - Optional registry, provider factories, and
|
|
655
|
+
* @param options - Optional registry, provider factories, logger, and execution defaults.
|
|
587
656
|
*/
|
|
588
657
|
constructor(options = {}) {
|
|
589
658
|
this.registry = options.registry ?? new ProviderRegistry();
|
|
590
659
|
this.logger = options.logger ?? noopLogger;
|
|
660
|
+
if (options.defaults !== void 0) {
|
|
661
|
+
this.defaults = { ...options.defaults };
|
|
662
|
+
}
|
|
591
663
|
for (const provider of options.providers ?? []) {
|
|
592
664
|
this.registry.register(provider);
|
|
593
665
|
}
|
|
@@ -1160,18 +1232,25 @@ var TransferEngine = class {
|
|
|
1160
1232
|
for (let attemptNumber = 1; attemptNumber <= maxAttempts; attemptNumber += 1) {
|
|
1161
1233
|
this.throwIfAborted(abortScope.signal, job);
|
|
1162
1234
|
const attemptStartedAt = this.now();
|
|
1235
|
+
const attemptScope = createAttemptScope(
|
|
1236
|
+
abortScope.signal,
|
|
1237
|
+
options.timeout,
|
|
1238
|
+
job,
|
|
1239
|
+
attemptNumber
|
|
1240
|
+
);
|
|
1163
1241
|
const context = this.createExecutionContext(
|
|
1164
1242
|
job,
|
|
1165
1243
|
attemptNumber,
|
|
1166
1244
|
attemptStartedAt,
|
|
1167
1245
|
options,
|
|
1168
|
-
|
|
1246
|
+
attemptScope.signal,
|
|
1169
1247
|
(bytesTransferred) => {
|
|
1170
1248
|
latestBytesTransferred = bytesTransferred;
|
|
1171
|
-
}
|
|
1249
|
+
},
|
|
1250
|
+
attemptScope.notifyProgress
|
|
1172
1251
|
);
|
|
1173
1252
|
try {
|
|
1174
|
-
const result = await runExecutor(executor, context,
|
|
1253
|
+
const result = await runExecutor(executor, context, attemptScope.signal, job);
|
|
1175
1254
|
context.throwIfAborted();
|
|
1176
1255
|
latestBytesTransferred = result.bytesTransferred;
|
|
1177
1256
|
const completedAt = this.now();
|
|
@@ -1189,16 +1268,27 @@ var TransferEngine = class {
|
|
|
1189
1268
|
summarizeError(error)
|
|
1190
1269
|
);
|
|
1191
1270
|
attempts.push(attempt);
|
|
1192
|
-
if (error instanceof AbortError ||
|
|
1271
|
+
if (error instanceof AbortError || abortScope.signal?.aborted === true) {
|
|
1193
1272
|
throw error;
|
|
1194
1273
|
}
|
|
1195
|
-
const retryInput = {
|
|
1274
|
+
const retryInput = {
|
|
1275
|
+
attempt: attemptNumber,
|
|
1276
|
+
elapsedMs: Math.max(0, completedAt.getTime() - startedAt.getTime()),
|
|
1277
|
+
error,
|
|
1278
|
+
job
|
|
1279
|
+
};
|
|
1196
1280
|
const shouldRetry = attemptNumber < maxAttempts && (options.retry?.shouldRetry?.(retryInput) ?? isRetryable(error));
|
|
1197
1281
|
if (shouldRetry) {
|
|
1198
1282
|
options.retry?.onRetry?.(retryInput);
|
|
1283
|
+
const delayMs = normalizeDelayMs(options.retry?.getDelayMs?.(retryInput));
|
|
1284
|
+
if (delayMs > 0) {
|
|
1285
|
+
await sleepWithAbort(delayMs, abortScope.signal, job);
|
|
1286
|
+
}
|
|
1199
1287
|
continue;
|
|
1200
1288
|
}
|
|
1201
1289
|
throw createTransferFailure(job, error, attempts);
|
|
1290
|
+
} finally {
|
|
1291
|
+
attemptScope.dispose();
|
|
1202
1292
|
}
|
|
1203
1293
|
}
|
|
1204
1294
|
throw createTransferFailure(job, void 0, attempts);
|
|
@@ -1206,12 +1296,13 @@ var TransferEngine = class {
|
|
|
1206
1296
|
abortScope.dispose();
|
|
1207
1297
|
}
|
|
1208
1298
|
}
|
|
1209
|
-
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred) {
|
|
1299
|
+
createExecutionContext(job, attempt, startedAt, options, signal, updateBytesTransferred, notifyProgress) {
|
|
1210
1300
|
const context = {
|
|
1211
1301
|
attempt,
|
|
1212
1302
|
job,
|
|
1213
1303
|
reportProgress: (bytesTransferred, totalBytes) => {
|
|
1214
1304
|
this.throwIfAborted(signal, job);
|
|
1305
|
+
notifyProgress();
|
|
1215
1306
|
updateBytesTransferred(bytesTransferred);
|
|
1216
1307
|
const progressInput = {
|
|
1217
1308
|
bytesTransferred,
|
|
@@ -1280,6 +1371,96 @@ function createAbortScope(parentSignal, timeout, job) {
|
|
|
1280
1371
|
signal: controller.signal
|
|
1281
1372
|
};
|
|
1282
1373
|
}
|
|
1374
|
+
function createAttemptScope(parentSignal, timeout, job, attempt) {
|
|
1375
|
+
const attemptTimeoutMs = normalizeTimeoutMs(timeout?.attemptTimeoutMs);
|
|
1376
|
+
const stallTimeoutMs = normalizeTimeoutMs(timeout?.stallTimeoutMs);
|
|
1377
|
+
if (attemptTimeoutMs === void 0 && stallTimeoutMs === void 0) {
|
|
1378
|
+
const scope = {
|
|
1379
|
+
dispose: () => void 0,
|
|
1380
|
+
notifyProgress: () => void 0
|
|
1381
|
+
};
|
|
1382
|
+
if (parentSignal !== void 0) scope.signal = parentSignal;
|
|
1383
|
+
return scope;
|
|
1384
|
+
}
|
|
1385
|
+
const controller = new AbortController();
|
|
1386
|
+
const retryable = timeout?.retryable ?? true;
|
|
1387
|
+
const abortFromParent = () => controller.abort(parentSignal?.reason);
|
|
1388
|
+
if (parentSignal?.aborted === true) {
|
|
1389
|
+
abortFromParent();
|
|
1390
|
+
} else {
|
|
1391
|
+
parentSignal?.addEventListener("abort", abortFromParent, { once: true });
|
|
1392
|
+
}
|
|
1393
|
+
const attemptTimer = attemptTimeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
1394
|
+
controller.abort(
|
|
1395
|
+
new TimeoutError({
|
|
1396
|
+
details: { attempt, attemptTimeoutMs, jobId: job.id, operation: job.operation },
|
|
1397
|
+
message: `Transfer attempt ${String(attempt)} timed out after ${String(attemptTimeoutMs)}ms: ${job.id}`,
|
|
1398
|
+
retryable
|
|
1399
|
+
})
|
|
1400
|
+
);
|
|
1401
|
+
}, attemptTimeoutMs);
|
|
1402
|
+
let stallTimer;
|
|
1403
|
+
const armStallWatchdog = () => {
|
|
1404
|
+
if (stallTimeoutMs === void 0 || controller.signal.aborted) return;
|
|
1405
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1406
|
+
stallTimer = setTimeout(() => {
|
|
1407
|
+
controller.abort(
|
|
1408
|
+
new TimeoutError({
|
|
1409
|
+
details: { attempt, jobId: job.id, operation: job.operation, stallTimeoutMs },
|
|
1410
|
+
message: `Transfer attempt ${String(attempt)} stalled (no progress for ${String(stallTimeoutMs)}ms): ${job.id}`,
|
|
1411
|
+
retryable
|
|
1412
|
+
})
|
|
1413
|
+
);
|
|
1414
|
+
}, stallTimeoutMs);
|
|
1415
|
+
};
|
|
1416
|
+
armStallWatchdog();
|
|
1417
|
+
return {
|
|
1418
|
+
dispose: () => {
|
|
1419
|
+
if (attemptTimer !== void 0) clearTimeout(attemptTimer);
|
|
1420
|
+
if (stallTimer !== void 0) clearTimeout(stallTimer);
|
|
1421
|
+
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
1422
|
+
},
|
|
1423
|
+
notifyProgress: armStallWatchdog,
|
|
1424
|
+
signal: controller.signal
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
function sleepWithAbort(delayMs, signal, job) {
|
|
1428
|
+
return new Promise((resolve, reject) => {
|
|
1429
|
+
if (signal === void 0) {
|
|
1430
|
+
setTimeout(resolve, delayMs);
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
if (signal.aborted) {
|
|
1434
|
+
reject(toAbortFailure(signal, job));
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
const rejectAbort = () => {
|
|
1438
|
+
clearTimeout(timer);
|
|
1439
|
+
reject(toAbortFailure(signal, job));
|
|
1440
|
+
};
|
|
1441
|
+
const timer = setTimeout(() => {
|
|
1442
|
+
signal.removeEventListener("abort", rejectAbort);
|
|
1443
|
+
resolve();
|
|
1444
|
+
}, delayMs);
|
|
1445
|
+
signal.addEventListener("abort", rejectAbort, { once: true });
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
function toAbortFailure(signal, job) {
|
|
1449
|
+
if (signal.reason instanceof ZeroTransferError) {
|
|
1450
|
+
return signal.reason;
|
|
1451
|
+
}
|
|
1452
|
+
return new AbortError({
|
|
1453
|
+
details: { jobId: job.id, operation: job.operation },
|
|
1454
|
+
message: `Transfer job aborted: ${job.id}`,
|
|
1455
|
+
retryable: false
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
function normalizeDelayMs(value) {
|
|
1459
|
+
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1460
|
+
return 0;
|
|
1461
|
+
}
|
|
1462
|
+
return Math.floor(value);
|
|
1463
|
+
}
|
|
1283
1464
|
function normalizeTimeoutMs(value) {
|
|
1284
1465
|
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
|
|
1285
1466
|
return void 0;
|
|
@@ -1448,7 +1629,7 @@ async function runRoute(options) {
|
|
|
1448
1629
|
const executor = createProviderTransferExecutor({
|
|
1449
1630
|
resolveSession: ({ role }) => sessions.get(role)
|
|
1450
1631
|
});
|
|
1451
|
-
return await engine.execute(job, executor, buildExecuteOptions(options));
|
|
1632
|
+
return await engine.execute(job, executor, buildExecuteOptions(options, client));
|
|
1452
1633
|
} finally {
|
|
1453
1634
|
if (destinationSession !== void 0) {
|
|
1454
1635
|
await destinationSession.disconnect();
|
|
@@ -1485,12 +1666,14 @@ function defaultJobId(route, now) {
|
|
|
1485
1666
|
const timestamp = (now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
1486
1667
|
return `route:${route.id}:${timestamp.toString(36)}`;
|
|
1487
1668
|
}
|
|
1488
|
-
function buildExecuteOptions(options) {
|
|
1669
|
+
function buildExecuteOptions(options, client) {
|
|
1489
1670
|
const execute = {};
|
|
1671
|
+
const retry = options.retry ?? client.defaults?.retry;
|
|
1672
|
+
const timeout = options.timeout ?? client.defaults?.timeout;
|
|
1490
1673
|
if (options.signal !== void 0) execute.signal = options.signal;
|
|
1491
|
-
if (
|
|
1674
|
+
if (retry !== void 0) execute.retry = retry;
|
|
1492
1675
|
if (options.onProgress !== void 0) execute.onProgress = options.onProgress;
|
|
1493
|
-
if (
|
|
1676
|
+
if (timeout !== void 0) execute.timeout = timeout;
|
|
1494
1677
|
if (options.bandwidthLimit !== void 0) execute.bandwidthLimit = options.bandwidthLimit;
|
|
1495
1678
|
return execute;
|
|
1496
1679
|
}
|
|
@@ -1547,41 +1730,6 @@ function defaultRouteSuffix(source, destination) {
|
|
|
1547
1730
|
return `${source}->${destination}`;
|
|
1548
1731
|
}
|
|
1549
1732
|
|
|
1550
|
-
// src/logging/redaction.ts
|
|
1551
|
-
var REDACTED = "[REDACTED]";
|
|
1552
|
-
var SENSITIVE_KEY_PATTERN = /(?:password|passphrase|privatekey|token|secret|username|user)$/i;
|
|
1553
|
-
var SECRET_COMMAND_PATTERN = /^(PASS|USER|ACCT)\s+(.+)$/i;
|
|
1554
|
-
function isSensitiveKey(key) {
|
|
1555
|
-
return SENSITIVE_KEY_PATTERN.test(key.replace(/[_-]/g, ""));
|
|
1556
|
-
}
|
|
1557
|
-
function redactCommand(command) {
|
|
1558
|
-
return command.replace(SECRET_COMMAND_PATTERN, (_fullMatch, commandName) => {
|
|
1559
|
-
return `${commandName.toUpperCase()} ${REDACTED}`;
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
function redactValue(value) {
|
|
1563
|
-
if (typeof value === "string") {
|
|
1564
|
-
return redactCommand(value);
|
|
1565
|
-
}
|
|
1566
|
-
if (Array.isArray(value)) {
|
|
1567
|
-
return value.map((item) => redactValue(item));
|
|
1568
|
-
}
|
|
1569
|
-
if (value !== null && typeof value === "object") {
|
|
1570
|
-
return redactObject(value);
|
|
1571
|
-
}
|
|
1572
|
-
return value;
|
|
1573
|
-
}
|
|
1574
|
-
function redactObject(input) {
|
|
1575
|
-
return Object.fromEntries(
|
|
1576
|
-
Object.entries(input).map(([key, value]) => {
|
|
1577
|
-
if (isSensitiveKey(key)) {
|
|
1578
|
-
return [key, REDACTED];
|
|
1579
|
-
}
|
|
1580
|
-
return [key, redactValue(value)];
|
|
1581
|
-
})
|
|
1582
|
-
);
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
1733
|
// src/profiles/SecretSource.ts
|
|
1586
1734
|
import { Buffer as Buffer3 } from "buffer";
|
|
1587
1735
|
import { readFile } from "fs/promises";
|
|
@@ -1963,11 +2111,11 @@ import {
|
|
|
1963
2111
|
import path from "path";
|
|
1964
2112
|
|
|
1965
2113
|
// src/utils/path.ts
|
|
1966
|
-
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n]/;
|
|
2114
|
+
var UNSAFE_FTP_ARGUMENT_PATTERN = /[\r\n\0]/;
|
|
1967
2115
|
function assertSafeFtpArgument(value, label = "path") {
|
|
1968
2116
|
if (UNSAFE_FTP_ARGUMENT_PATTERN.test(value)) {
|
|
1969
2117
|
throw new ConfigurationError({
|
|
1970
|
-
message: `Unsafe FTP ${label}: CR and
|
|
2118
|
+
message: `Unsafe FTP ${label}: CR, LF, and NUL characters are not allowed`,
|
|
1971
2119
|
retryable: false,
|
|
1972
2120
|
details: {
|
|
1973
2121
|
label
|
|
@@ -3269,7 +3417,6 @@ function expandAlgorithms(values) {
|
|
|
3269
3417
|
}
|
|
3270
3418
|
|
|
3271
3419
|
// src/profiles/importers/FileZillaImporter.ts
|
|
3272
|
-
import { Buffer as Buffer6 } from "buffer";
|
|
3273
3420
|
function importFileZillaSites(xml) {
|
|
3274
3421
|
const events = tokenizeXml(xml);
|
|
3275
3422
|
if (events.length === 0) {
|
|
@@ -3285,7 +3432,6 @@ function importFileZillaSites(xml) {
|
|
|
3285
3432
|
const folderNamePending = [];
|
|
3286
3433
|
let inServer = false;
|
|
3287
3434
|
let serverFields = {};
|
|
3288
|
-
let serverPasswordEncoding;
|
|
3289
3435
|
let activeTag;
|
|
3290
3436
|
let captureFolderName = false;
|
|
3291
3437
|
for (const event of events) {
|
|
@@ -3298,13 +3444,9 @@ function importFileZillaSites(xml) {
|
|
|
3298
3444
|
if (event.name === "Server") {
|
|
3299
3445
|
inServer = true;
|
|
3300
3446
|
serverFields = {};
|
|
3301
|
-
serverPasswordEncoding = void 0;
|
|
3302
3447
|
continue;
|
|
3303
3448
|
}
|
|
3304
3449
|
activeTag = event.name;
|
|
3305
|
-
if (event.name === "Pass" && inServer) {
|
|
3306
|
-
serverPasswordEncoding = event.attributes["encoding"];
|
|
3307
|
-
}
|
|
3308
3450
|
if (event.name === "Name" && !inServer && folderNamePending.length > 0) {
|
|
3309
3451
|
captureFolderName = true;
|
|
3310
3452
|
}
|
|
@@ -3330,7 +3472,7 @@ function importFileZillaSites(xml) {
|
|
|
3330
3472
|
}
|
|
3331
3473
|
if (event.name === "Server") {
|
|
3332
3474
|
const folder = folderStack.filter((segment) => segment !== "");
|
|
3333
|
-
const result = buildSiteFromFields(serverFields
|
|
3475
|
+
const result = buildSiteFromFields(serverFields);
|
|
3334
3476
|
if (result.kind === "site") {
|
|
3335
3477
|
sites.push({ ...result.site, folder });
|
|
3336
3478
|
} else {
|
|
@@ -3342,7 +3484,6 @@ function importFileZillaSites(xml) {
|
|
|
3342
3484
|
}
|
|
3343
3485
|
inServer = false;
|
|
3344
3486
|
serverFields = {};
|
|
3345
|
-
serverPasswordEncoding = void 0;
|
|
3346
3487
|
activeTag = void 0;
|
|
3347
3488
|
continue;
|
|
3348
3489
|
}
|
|
@@ -3351,7 +3492,7 @@ function importFileZillaSites(xml) {
|
|
|
3351
3492
|
}
|
|
3352
3493
|
return { sites, skipped };
|
|
3353
3494
|
}
|
|
3354
|
-
function buildSiteFromFields(fields
|
|
3495
|
+
function buildSiteFromFields(fields) {
|
|
3355
3496
|
const name = (fields["Name"] ?? fields["Host"] ?? "Untitled").trim();
|
|
3356
3497
|
const host = (fields["Host"] ?? "").trim();
|
|
3357
3498
|
if (host === "") return { kind: "skipped", name };
|
|
@@ -3370,18 +3511,9 @@ function buildSiteFromFields(fields, passwordEncoding) {
|
|
|
3370
3511
|
}
|
|
3371
3512
|
const user = fields["User"]?.trim();
|
|
3372
3513
|
if (user !== void 0 && user !== "") profile.username = { value: user };
|
|
3373
|
-
let password;
|
|
3374
3514
|
const rawPass = fields["Pass"];
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
password = Buffer6.from(rawPass, "base64").toString("utf8");
|
|
3378
|
-
} else {
|
|
3379
|
-
password = rawPass;
|
|
3380
|
-
}
|
|
3381
|
-
if (password !== void 0 && password !== "") profile.password = { value: password };
|
|
3382
|
-
}
|
|
3383
|
-
const site = { name, profile };
|
|
3384
|
-
if (password !== void 0) site.password = password;
|
|
3515
|
+
const hasStoredPassword = rawPass !== void 0 && rawPass !== "";
|
|
3516
|
+
const site = { hasStoredPassword, name, profile };
|
|
3385
3517
|
const logonText = fields["Logontype"];
|
|
3386
3518
|
if (logonText !== void 0) {
|
|
3387
3519
|
const logonType = Number.parseInt(logonText.trim(), 10);
|
|
@@ -3624,6 +3756,62 @@ function mapFtp550(details) {
|
|
|
3624
3756
|
return new PermissionDeniedError(details);
|
|
3625
3757
|
}
|
|
3626
3758
|
|
|
3759
|
+
// src/transfers/createDefaultRetryPolicy.ts
|
|
3760
|
+
var DEFAULT_MAX_ATTEMPTS = 4;
|
|
3761
|
+
var DEFAULT_BASE_DELAY_MS = 250;
|
|
3762
|
+
var DEFAULT_MAX_DELAY_MS = 3e4;
|
|
3763
|
+
var DEFAULT_MAX_ELAPSED_MS = 3e5;
|
|
3764
|
+
function createDefaultRetryPolicy(options = {}) {
|
|
3765
|
+
const maxAttempts = normalizePositiveInteger(options.maxAttempts, DEFAULT_MAX_ATTEMPTS);
|
|
3766
|
+
const baseDelayMs = normalizeNonNegative(options.baseDelayMs, DEFAULT_BASE_DELAY_MS);
|
|
3767
|
+
const maxDelayMs = normalizeNonNegative(options.maxDelayMs, DEFAULT_MAX_DELAY_MS);
|
|
3768
|
+
const maxElapsedMs = normalizeNonNegative(options.maxElapsedMs, DEFAULT_MAX_ELAPSED_MS);
|
|
3769
|
+
const random = options.random ?? Math.random;
|
|
3770
|
+
return {
|
|
3771
|
+
getDelayMs(input) {
|
|
3772
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3773
|
+
if (retryAfterMs !== void 0) {
|
|
3774
|
+
return retryAfterMs;
|
|
3775
|
+
}
|
|
3776
|
+
const exponentialMs = baseDelayMs * 2 ** (input.attempt - 1);
|
|
3777
|
+
const cappedMs = Math.min(maxDelayMs, exponentialMs);
|
|
3778
|
+
return Math.floor(random() * cappedMs);
|
|
3779
|
+
},
|
|
3780
|
+
maxAttempts,
|
|
3781
|
+
shouldRetry(input) {
|
|
3782
|
+
if (!(input.error instanceof ZeroTransferError) || !input.error.retryable) {
|
|
3783
|
+
return false;
|
|
3784
|
+
}
|
|
3785
|
+
if (input.elapsedMs >= maxElapsedMs) {
|
|
3786
|
+
return false;
|
|
3787
|
+
}
|
|
3788
|
+
const retryAfterMs = readRetryAfterMs(input.error);
|
|
3789
|
+
if (retryAfterMs !== void 0 && input.elapsedMs + retryAfterMs > maxElapsedMs) {
|
|
3790
|
+
return false;
|
|
3791
|
+
}
|
|
3792
|
+
return true;
|
|
3793
|
+
}
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3796
|
+
function readRetryAfterMs(error) {
|
|
3797
|
+
if (!(error instanceof ZeroTransferError)) return void 0;
|
|
3798
|
+
const value = error.details?.["retryAfterMs"];
|
|
3799
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
|
|
3800
|
+
return Math.floor(value);
|
|
3801
|
+
}
|
|
3802
|
+
function normalizePositiveInteger(value, fallback) {
|
|
3803
|
+
if (value === void 0 || !Number.isFinite(value) || value < 1) {
|
|
3804
|
+
return fallback;
|
|
3805
|
+
}
|
|
3806
|
+
return Math.floor(value);
|
|
3807
|
+
}
|
|
3808
|
+
function normalizeNonNegative(value, fallback) {
|
|
3809
|
+
if (value === void 0 || !Number.isFinite(value) || value < 0) {
|
|
3810
|
+
return fallback;
|
|
3811
|
+
}
|
|
3812
|
+
return Math.floor(value);
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3627
3815
|
// src/transfers/TransferPlan.ts
|
|
3628
3816
|
function createTransferPlan(input) {
|
|
3629
3817
|
const plan = {
|
|
@@ -3721,8 +3909,8 @@ var TransferQueue = class {
|
|
|
3721
3909
|
this.concurrency = normalizeConcurrency(options.concurrency);
|
|
3722
3910
|
this.defaultExecutor = options.executor;
|
|
3723
3911
|
this.resolveExecutor = options.resolveExecutor;
|
|
3724
|
-
this.retry = options.retry;
|
|
3725
|
-
this.timeout = options.timeout;
|
|
3912
|
+
this.retry = options.retry ?? options.client?.defaults?.retry;
|
|
3913
|
+
this.timeout = options.timeout ?? options.client?.defaults?.timeout;
|
|
3726
3914
|
this.bandwidthLimit = options.bandwidthLimit;
|
|
3727
3915
|
this.onProgress = options.onProgress;
|
|
3728
3916
|
this.onReceipt = options.onReceipt;
|
|
@@ -4811,11 +4999,12 @@ import { join as joinPath } from "path";
|
|
|
4811
4999
|
|
|
4812
5000
|
// src/providers/web/awsSigv4.ts
|
|
4813
5001
|
import { createHash, createHmac as createHmac2 } from "crypto";
|
|
5002
|
+
var UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
|
|
4814
5003
|
function signSigV4(input) {
|
|
4815
5004
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
4816
5005
|
const amzDate = formatAmzDate(now);
|
|
4817
5006
|
const dateStamp = amzDate.slice(0, 8);
|
|
4818
|
-
const payloadHash = input.body !== void 0 ? sha256Hex(input.body) : sha256Hex(new Uint8Array());
|
|
5007
|
+
const payloadHash = input.unsignedPayload === true ? UNSIGNED_PAYLOAD : input.body !== void 0 ? sha256Hex(input.body) : sha256Hex(new Uint8Array());
|
|
4819
5008
|
input.headers["host"] = input.url.host;
|
|
4820
5009
|
input.headers["x-amz-date"] = amzDate;
|
|
4821
5010
|
input.headers["x-amz-content-sha256"] = payloadHash;
|
|
@@ -4890,7 +5079,7 @@ function hmacHex(key, data) {
|
|
|
4890
5079
|
}
|
|
4891
5080
|
|
|
4892
5081
|
// src/providers/web/httpInternals.ts
|
|
4893
|
-
import { Buffer as
|
|
5082
|
+
import { Buffer as Buffer6 } from "buffer";
|
|
4894
5083
|
function parseContentRangeTotal(value) {
|
|
4895
5084
|
const match = /\/(\d+)\s*$/.exec(value);
|
|
4896
5085
|
if (match === null) return void 0;
|
|
@@ -4916,8 +5105,48 @@ function formatRangeHeader(offset, length) {
|
|
|
4916
5105
|
const end = offset + length - 1;
|
|
4917
5106
|
return `bytes=${String(offset)}-${String(end)}`;
|
|
4918
5107
|
}
|
|
4919
|
-
|
|
4920
|
-
|
|
5108
|
+
var ERROR_BODY_EXCERPT_LIMIT = 2048;
|
|
5109
|
+
async function readErrorBodyExcerpt(response) {
|
|
5110
|
+
try {
|
|
5111
|
+
const text = await response.text();
|
|
5112
|
+
if (text.length === 0) return void 0;
|
|
5113
|
+
return text.length > ERROR_BODY_EXCERPT_LIMIT ? `${text.slice(0, ERROR_BODY_EXCERPT_LIMIT)}... [truncated]` : text;
|
|
5114
|
+
} catch {
|
|
5115
|
+
return void 0;
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
function parseRetryAfterMs(value, now = Date.now) {
|
|
5119
|
+
if (value === null) return void 0;
|
|
5120
|
+
const trimmed = value.trim();
|
|
5121
|
+
if (trimmed.length === 0) return void 0;
|
|
5122
|
+
if (/^\d+$/.test(trimmed)) {
|
|
5123
|
+
const seconds = Number.parseInt(trimmed, 10);
|
|
5124
|
+
return Number.isFinite(seconds) ? seconds * 1e3 : void 0;
|
|
5125
|
+
}
|
|
5126
|
+
if (!/[A-Za-z]/.test(trimmed)) return void 0;
|
|
5127
|
+
const retryAt = Date.parse(trimmed);
|
|
5128
|
+
if (Number.isNaN(retryAt)) return void 0;
|
|
5129
|
+
return Math.max(0, retryAt - now());
|
|
5130
|
+
}
|
|
5131
|
+
async function mapResponseErrorWithBody(response, path2) {
|
|
5132
|
+
return mapResponseError(response, path2, await readErrorBodyExcerpt(response));
|
|
5133
|
+
}
|
|
5134
|
+
function mapResponseError(response, path2, bodyExcerpt) {
|
|
5135
|
+
const details = {
|
|
5136
|
+
path: path2,
|
|
5137
|
+
status: response.status,
|
|
5138
|
+
statusText: response.statusText
|
|
5139
|
+
};
|
|
5140
|
+
if (bodyExcerpt !== void 0) details["body"] = bodyExcerpt;
|
|
5141
|
+
if (response.status === 429 || response.status === 503) {
|
|
5142
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
5143
|
+
if (retryAfterMs !== void 0) details["retryAfterMs"] = retryAfterMs;
|
|
5144
|
+
return new ConnectionError({
|
|
5145
|
+
details,
|
|
5146
|
+
message: response.status === 429 ? `HTTP request for ${path2} was rate limited (429)` : `HTTP service unavailable for ${path2} (503)`,
|
|
5147
|
+
retryable: true
|
|
5148
|
+
});
|
|
5149
|
+
}
|
|
4921
5150
|
if (response.status === 401) {
|
|
4922
5151
|
return new AuthenticationError({
|
|
4923
5152
|
details,
|
|
@@ -4949,18 +5178,58 @@ async function* webStreamToAsyncIterable(body) {
|
|
|
4949
5178
|
const reader = body.getReader();
|
|
4950
5179
|
try {
|
|
4951
5180
|
while (true) {
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
5181
|
+
let result;
|
|
5182
|
+
try {
|
|
5183
|
+
result = await reader.read();
|
|
5184
|
+
} catch (error) {
|
|
5185
|
+
if (error instanceof ZeroTransferError) throw error;
|
|
5186
|
+
throw new ConnectionError({
|
|
5187
|
+
cause: error,
|
|
5188
|
+
message: "HTTP response stream was interrupted before completion",
|
|
5189
|
+
retryable: true
|
|
5190
|
+
});
|
|
5191
|
+
}
|
|
5192
|
+
if (result.done) break;
|
|
5193
|
+
if (result.value !== void 0) yield result.value;
|
|
4955
5194
|
}
|
|
4956
5195
|
} finally {
|
|
4957
5196
|
reader.releaseLock();
|
|
4958
5197
|
}
|
|
4959
5198
|
}
|
|
5199
|
+
function asyncIterableToReadableStream(source, onChunk) {
|
|
5200
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
5201
|
+
return new ReadableStream({
|
|
5202
|
+
async pull(controller) {
|
|
5203
|
+
try {
|
|
5204
|
+
const next = await iterator.next();
|
|
5205
|
+
if (next.done === true) {
|
|
5206
|
+
controller.close();
|
|
5207
|
+
return;
|
|
5208
|
+
}
|
|
5209
|
+
const chunk = next.value;
|
|
5210
|
+
if (chunk.byteLength === 0) {
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
controller.enqueue(chunk);
|
|
5214
|
+
onChunk(chunk);
|
|
5215
|
+
} catch (error) {
|
|
5216
|
+
controller.error(error);
|
|
5217
|
+
}
|
|
5218
|
+
},
|
|
5219
|
+
async cancel(reason) {
|
|
5220
|
+
if (typeof iterator.return === "function") {
|
|
5221
|
+
try {
|
|
5222
|
+
await iterator.return(reason);
|
|
5223
|
+
} catch {
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
}
|
|
5227
|
+
});
|
|
5228
|
+
}
|
|
4960
5229
|
function secretToString(value) {
|
|
4961
5230
|
if (typeof value === "string") return value;
|
|
4962
|
-
if (value instanceof Uint8Array ||
|
|
4963
|
-
return
|
|
5231
|
+
if (value instanceof Uint8Array || Buffer6.isBuffer(value)) {
|
|
5232
|
+
return Buffer6.from(value).toString("utf8");
|
|
4964
5233
|
}
|
|
4965
5234
|
return String(value);
|
|
4966
5235
|
}
|
|
@@ -5172,7 +5441,7 @@ var S3FileSystem = class {
|
|
|
5172
5441
|
url.searchParams.set("delimiter", "/");
|
|
5173
5442
|
if (prefix.length > 0) url.searchParams.set("prefix", prefix);
|
|
5174
5443
|
const response = await s3Fetch(this.options, "GET", url);
|
|
5175
|
-
if (!response.ok) throw
|
|
5444
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5176
5445
|
const body = await response.text();
|
|
5177
5446
|
return parseListObjectsV2(body, prefix);
|
|
5178
5447
|
}
|
|
@@ -5180,7 +5449,7 @@ var S3FileSystem = class {
|
|
|
5180
5449
|
const normalized = normalizeRemotePath(path2);
|
|
5181
5450
|
const url = buildObjectUrl(this.options, normalized);
|
|
5182
5451
|
const response = await s3Fetch(this.options, "HEAD", url);
|
|
5183
|
-
if (!response.ok) throw
|
|
5452
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5184
5453
|
const stat = {
|
|
5185
5454
|
exists: true,
|
|
5186
5455
|
name: basenameRemotePath(normalized),
|
|
@@ -5220,12 +5489,12 @@ var S3TransferOperations = class {
|
|
|
5220
5489
|
extraHeaders: headers
|
|
5221
5490
|
});
|
|
5222
5491
|
if (!response.ok && response.status !== 206) {
|
|
5223
|
-
throw
|
|
5492
|
+
throw await mapResponseErrorWithBody(response, normalized);
|
|
5224
5493
|
}
|
|
5225
5494
|
const body = response.body;
|
|
5226
5495
|
if (body === null) {
|
|
5227
5496
|
throw new ConnectionError({
|
|
5228
|
-
message: `S3 response had no body for ${url
|
|
5497
|
+
message: `S3 response had no body for ${redactUrlForLogging(url)}`,
|
|
5229
5498
|
retryable: true
|
|
5230
5499
|
});
|
|
5231
5500
|
}
|
|
@@ -5261,19 +5530,33 @@ var S3TransferOperations = class {
|
|
|
5261
5530
|
}
|
|
5262
5531
|
return this.writeSingleShot(request, normalized);
|
|
5263
5532
|
}
|
|
5533
|
+
/**
|
|
5534
|
+
* Single PUT upload used when multipart is disabled. Streams the body with
|
|
5535
|
+
* a declared `Content-Length` (signed as `UNSIGNED-PAYLOAD`) when the
|
|
5536
|
+
* caller knows the total size; S3 requires a length up front, so only
|
|
5537
|
+
* unknown-size payloads fall back to buffering the content in memory.
|
|
5538
|
+
*/
|
|
5264
5539
|
async writeSingleShot(request, normalized) {
|
|
5265
5540
|
const url = buildObjectUrl(this.options, normalized);
|
|
5266
|
-
const
|
|
5541
|
+
const totalBytes = request.totalBytes;
|
|
5542
|
+
if (typeof totalBytes !== "number" || totalBytes < 0) {
|
|
5543
|
+
const buffered = await collectChunks(request.content);
|
|
5544
|
+
return this.singleShotFromBuffer(request, normalized, buffered);
|
|
5545
|
+
}
|
|
5546
|
+
let bytesTransferred = 0;
|
|
5547
|
+
const stream = asyncIterableToReadableStream(request.content, (chunk) => {
|
|
5548
|
+
bytesTransferred += chunk.byteLength;
|
|
5549
|
+
request.reportProgress(bytesTransferred, totalBytes);
|
|
5550
|
+
});
|
|
5267
5551
|
const response = await s3Fetch(this.options, "PUT", url, {
|
|
5268
5552
|
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
5269
|
-
|
|
5270
|
-
|
|
5553
|
+
extraHeaders: { "content-type": "application/octet-stream" },
|
|
5554
|
+
streamBody: { content: stream, contentLength: totalBytes }
|
|
5271
5555
|
});
|
|
5272
|
-
if (!response.ok) throw
|
|
5273
|
-
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
5556
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5274
5557
|
const result = {
|
|
5275
|
-
bytesTransferred
|
|
5276
|
-
totalBytes
|
|
5558
|
+
bytesTransferred,
|
|
5559
|
+
totalBytes
|
|
5277
5560
|
};
|
|
5278
5561
|
const etag = response.headers.get("etag");
|
|
5279
5562
|
if (etag !== null) result.checksum = etag;
|
|
@@ -5337,7 +5620,7 @@ var S3TransferOperations = class {
|
|
|
5337
5620
|
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
5338
5621
|
extraHeaders: { "content-type": "application/octet-stream" }
|
|
5339
5622
|
});
|
|
5340
|
-
if (!initiateResponse.ok) throw
|
|
5623
|
+
if (!initiateResponse.ok) throw await mapResponseErrorWithBody(initiateResponse, normalized);
|
|
5341
5624
|
const initiateBody = await initiateResponse.text();
|
|
5342
5625
|
const initiated = innerText(initiateBody, "UploadId");
|
|
5343
5626
|
if (initiated === void 0 || initiated === "") {
|
|
@@ -5376,7 +5659,7 @@ var S3TransferOperations = class {
|
|
|
5376
5659
|
body: partBytes.bytes
|
|
5377
5660
|
});
|
|
5378
5661
|
if (!partResponse.ok) {
|
|
5379
|
-
throw
|
|
5662
|
+
throw await mapResponseErrorWithBody(partResponse, normalized);
|
|
5380
5663
|
}
|
|
5381
5664
|
const partEtag = partResponse.headers.get("etag");
|
|
5382
5665
|
if (partEtag === null) {
|
|
@@ -5432,7 +5715,7 @@ var S3TransferOperations = class {
|
|
|
5432
5715
|
if (resumeStore === void 0) {
|
|
5433
5716
|
await abortMultipart(this.options, objectUrl, uploadId).catch(() => void 0);
|
|
5434
5717
|
}
|
|
5435
|
-
throw
|
|
5718
|
+
throw await mapResponseErrorWithBody(completeResponse, normalized);
|
|
5436
5719
|
}
|
|
5437
5720
|
if (resumeStore !== void 0) await resumeStore.clear(resumeKey);
|
|
5438
5721
|
const completeBody = await completeResponse.text();
|
|
@@ -5451,7 +5734,7 @@ var S3TransferOperations = class {
|
|
|
5451
5734
|
body: buffered,
|
|
5452
5735
|
extraHeaders: { "content-type": "application/octet-stream" }
|
|
5453
5736
|
});
|
|
5454
|
-
if (!response.ok) throw
|
|
5737
|
+
if (!response.ok) throw await mapResponseErrorWithBody(response, normalized);
|
|
5455
5738
|
request.reportProgress(buffered.byteLength, buffered.byteLength);
|
|
5456
5739
|
const result = {
|
|
5457
5740
|
bytesTransferred: buffered.byteLength,
|
|
@@ -5469,6 +5752,8 @@ async function s3Fetch(options, method, url, fetchOptions = {}) {
|
|
|
5469
5752
|
};
|
|
5470
5753
|
if (fetchOptions.body !== void 0) {
|
|
5471
5754
|
headers["content-length"] = String(fetchOptions.body.byteLength);
|
|
5755
|
+
} else if (fetchOptions.streamBody !== void 0) {
|
|
5756
|
+
headers["content-length"] = String(fetchOptions.streamBody.contentLength);
|
|
5472
5757
|
}
|
|
5473
5758
|
signSigV4({
|
|
5474
5759
|
accessKeyId: options.accessKeyId,
|
|
@@ -5479,10 +5764,16 @@ async function s3Fetch(options, method, url, fetchOptions = {}) {
|
|
|
5479
5764
|
service: options.service,
|
|
5480
5765
|
url,
|
|
5481
5766
|
...fetchOptions.body !== void 0 ? { body: fetchOptions.body } : {},
|
|
5767
|
+
...fetchOptions.streamBody !== void 0 ? { unsignedPayload: true } : {},
|
|
5482
5768
|
...options.sessionToken !== void 0 ? { sessionToken: options.sessionToken } : {}
|
|
5483
5769
|
});
|
|
5484
5770
|
const init = { headers, method };
|
|
5485
|
-
if (fetchOptions.body !== void 0)
|
|
5771
|
+
if (fetchOptions.body !== void 0) {
|
|
5772
|
+
init.body = fetchOptions.body;
|
|
5773
|
+
} else if (fetchOptions.streamBody !== void 0) {
|
|
5774
|
+
init.body = fetchOptions.streamBody.content;
|
|
5775
|
+
init.duplex = "half";
|
|
5776
|
+
}
|
|
5486
5777
|
if (fetchOptions.signal !== void 0) init.signal = fetchOptions.signal;
|
|
5487
5778
|
const controller = new AbortController();
|
|
5488
5779
|
const upstreamSignal = init.signal ?? null;
|
|
@@ -5500,10 +5791,11 @@ async function s3Fetch(options, method, url, fetchOptions = {}) {
|
|
|
5500
5791
|
try {
|
|
5501
5792
|
return await options.fetch(url.toString(), { ...init, signal: controller.signal });
|
|
5502
5793
|
} catch (error) {
|
|
5794
|
+
const safeUrl = redactUrlForLogging(url);
|
|
5503
5795
|
throw new ConnectionError({
|
|
5504
5796
|
cause: error,
|
|
5505
|
-
details: { url:
|
|
5506
|
-
message: `S3 request to ${
|
|
5797
|
+
details: { url: safeUrl },
|
|
5798
|
+
message: `S3 request to ${safeUrl} failed`,
|
|
5507
5799
|
retryable: true
|
|
5508
5800
|
});
|
|
5509
5801
|
} finally {
|
|
@@ -5675,6 +5967,7 @@ export {
|
|
|
5675
5967
|
copyBetween,
|
|
5676
5968
|
createAtomicDeployPlan,
|
|
5677
5969
|
createBandwidthThrottle,
|
|
5970
|
+
createDefaultRetryPolicy,
|
|
5678
5971
|
createFileSystemS3MultipartResumeStore,
|
|
5679
5972
|
createLocalProviderFactory,
|
|
5680
5973
|
createMemoryProviderFactory,
|
|
@@ -5713,8 +6006,10 @@ export {
|
|
|
5713
6006
|
parseRemoteManifest,
|
|
5714
6007
|
redactCommand,
|
|
5715
6008
|
redactConnectionProfile,
|
|
6009
|
+
redactErrorForLogging,
|
|
5716
6010
|
redactObject,
|
|
5717
6011
|
redactSecretSource,
|
|
6012
|
+
redactUrlForLogging,
|
|
5718
6013
|
redactValue,
|
|
5719
6014
|
resolveConnectionProfileSecrets,
|
|
5720
6015
|
resolveOpenSshHost,
|