@zero-transfer/sdk 0.4.2 → 0.4.7
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 +27 -22
- package/dist/index.cjs +727 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +146 -20
- package/dist/index.d.ts +146 -20
- package/dist/index.mjs +717 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -94,7 +94,6 @@ __export(index_exports, {
|
|
|
94
94
|
createLocalProviderFactory: () => createLocalProviderFactory,
|
|
95
95
|
createMemoryProviderFactory: () => createMemoryProviderFactory,
|
|
96
96
|
createMemoryS3MultipartResumeStore: () => createMemoryS3MultipartResumeStore,
|
|
97
|
-
createNativeSftpProviderFactory: () => createNativeSftpProviderFactory,
|
|
98
97
|
createOAuthTokenSecretSource: () => createOAuthTokenSecretSource,
|
|
99
98
|
createOneDriveProviderFactory: () => createOneDriveProviderFactory,
|
|
100
99
|
createOutboxRoute: () => createOutboxRoute,
|
|
@@ -104,7 +103,7 @@ __export(index_exports, {
|
|
|
104
103
|
createRemoteBrowser: () => createRemoteBrowser,
|
|
105
104
|
createRemoteManifest: () => createRemoteManifest,
|
|
106
105
|
createS3ProviderFactory: () => createS3ProviderFactory,
|
|
107
|
-
createSftpProviderFactory: () =>
|
|
106
|
+
createSftpProviderFactory: () => createSftpProviderFactory,
|
|
108
107
|
createSyncPlan: () => createSyncPlan,
|
|
109
108
|
createTransferClient: () => createTransferClient,
|
|
110
109
|
createTransferJobsFromPlan: () => createTransferJobsFromPlan,
|
|
@@ -128,6 +127,7 @@ __export(index_exports, {
|
|
|
128
127
|
inboxFailedPath: () => inboxFailedPath,
|
|
129
128
|
inboxProcessedPath: () => inboxProcessedPath,
|
|
130
129
|
isClassicProviderId: () => isClassicProviderId,
|
|
130
|
+
isMainModule: () => isMainModule,
|
|
131
131
|
isSensitiveKey: () => isSensitiveKey,
|
|
132
132
|
joinRemotePath: () => joinRemotePath,
|
|
133
133
|
matchKnownHosts: () => matchKnownHosts,
|
|
@@ -160,6 +160,7 @@ __export(index_exports, {
|
|
|
160
160
|
resolveSecret: () => resolveSecret,
|
|
161
161
|
runConnectionDiagnostics: () => runConnectionDiagnostics,
|
|
162
162
|
runRoute: () => runRoute,
|
|
163
|
+
runSshCommand: () => runSshCommand,
|
|
163
164
|
serializeRemoteManifest: () => serializeRemoteManifest,
|
|
164
165
|
signWebhookPayload: () => signWebhookPayload,
|
|
165
166
|
sortRemoteEntries: () => sortRemoteEntries,
|
|
@@ -6273,7 +6274,7 @@ var SshTransportPacketUnprotector = class {
|
|
|
6273
6274
|
}
|
|
6274
6275
|
/**
|
|
6275
6276
|
* Feeds raw encrypted bytes from the socket and returns any fully decoded payloads.
|
|
6276
|
-
* Maintains internal framing state across calls
|
|
6277
|
+
* Maintains internal framing state across calls - pass each `data` event chunk directly.
|
|
6277
6278
|
*/
|
|
6278
6279
|
pushBytes(chunk) {
|
|
6279
6280
|
this.framePendingRaw = import_node_buffer15.Buffer.concat([this.framePendingRaw, chunk]);
|
|
@@ -6781,7 +6782,7 @@ var SshTransportConnection = class {
|
|
|
6781
6782
|
assertConnected() {
|
|
6782
6783
|
if (!this.connected) {
|
|
6783
6784
|
throw new ProtocolError({
|
|
6784
|
-
message: "SshTransportConnection is not yet connected
|
|
6785
|
+
message: "SshTransportConnection is not yet connected - call connect() first",
|
|
6785
6786
|
protocol: "sftp",
|
|
6786
6787
|
retryable: false
|
|
6787
6788
|
});
|
|
@@ -7121,14 +7122,14 @@ function sftpStatusToError(status, path2) {
|
|
|
7121
7122
|
case SFTP_STATUS.NO_SUCH_FILE:
|
|
7122
7123
|
return new PathNotFoundError({
|
|
7123
7124
|
details: { path: path2, sftpMessage: status.errorMessage },
|
|
7124
|
-
message: `SFTP: no such file or directory${path2 !== void 0 ? `
|
|
7125
|
+
message: `SFTP: no such file or directory${path2 !== void 0 ? ` - ${path2}` : ""}`,
|
|
7125
7126
|
protocol: "sftp",
|
|
7126
7127
|
retryable: false
|
|
7127
7128
|
});
|
|
7128
7129
|
case SFTP_STATUS.PERMISSION_DENIED:
|
|
7129
7130
|
return new PermissionDeniedError({
|
|
7130
7131
|
details: { path: path2, sftpMessage: status.errorMessage },
|
|
7131
|
-
message: `SFTP: permission denied${path2 !== void 0 ? `
|
|
7132
|
+
message: `SFTP: permission denied${path2 !== void 0 ? ` - ${path2}` : ""}`,
|
|
7132
7133
|
protocol: "sftp",
|
|
7133
7134
|
retryable: false
|
|
7134
7135
|
});
|
|
@@ -7136,21 +7137,21 @@ function sftpStatusToError(status, path2) {
|
|
|
7136
7137
|
case SFTP_STATUS.CONNECTION_LOST:
|
|
7137
7138
|
return new ConnectionError({
|
|
7138
7139
|
details: { sftpMessage: status.errorMessage, statusCode: status.statusCode },
|
|
7139
|
-
message: `SFTP: connection error
|
|
7140
|
+
message: `SFTP: connection error - ${status.errorMessage}`,
|
|
7140
7141
|
protocol: "sftp",
|
|
7141
7142
|
retryable: true
|
|
7142
7143
|
});
|
|
7143
7144
|
case SFTP_STATUS.OP_UNSUPPORTED:
|
|
7144
7145
|
return new UnsupportedFeatureError({
|
|
7145
7146
|
details: { sftpMessage: status.errorMessage },
|
|
7146
|
-
message: `SFTP: operation unsupported
|
|
7147
|
+
message: `SFTP: operation unsupported - ${status.errorMessage}`,
|
|
7147
7148
|
protocol: "sftp",
|
|
7148
7149
|
retryable: false
|
|
7149
7150
|
});
|
|
7150
7151
|
case SFTP_STATUS.BAD_MESSAGE:
|
|
7151
7152
|
return new ProtocolError({
|
|
7152
7153
|
details: { sftpMessage: status.errorMessage },
|
|
7153
|
-
message: `SFTP: bad message
|
|
7154
|
+
message: `SFTP: bad message - ${status.errorMessage}`,
|
|
7154
7155
|
protocol: "sftp",
|
|
7155
7156
|
retryable: false
|
|
7156
7157
|
});
|
|
@@ -7158,7 +7159,7 @@ function sftpStatusToError(status, path2) {
|
|
|
7158
7159
|
return new ZeroTransferError({
|
|
7159
7160
|
code: "SFTP_FAILURE",
|
|
7160
7161
|
details: { sftpMessage: status.errorMessage, statusCode: status.statusCode },
|
|
7161
|
-
message: `SFTP: operation failed (status ${status.statusCode})
|
|
7162
|
+
message: `SFTP: operation failed (status ${status.statusCode}) - ${status.errorMessage}`,
|
|
7162
7163
|
protocol: "sftp",
|
|
7163
7164
|
retryable: false
|
|
7164
7165
|
});
|
|
@@ -7568,7 +7569,7 @@ function buildNativeSftpCapabilities(maxConcurrency) {
|
|
|
7568
7569
|
var NATIVE_SFTP_PROVIDER_CAPABILITIES = buildNativeSftpCapabilities(
|
|
7569
7570
|
NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
7570
7571
|
);
|
|
7571
|
-
function
|
|
7572
|
+
function createSftpProviderFactory(options = {}) {
|
|
7572
7573
|
validateNativeSftpOptions(options);
|
|
7573
7574
|
const capabilities = buildNativeSftpCapabilities(
|
|
7574
7575
|
options.maxConcurrency ?? NATIVE_SFTP_DEFAULT_MAX_CONCURRENCY
|
|
@@ -9261,6 +9262,9 @@ function concatChunks(chunks, totalSize) {
|
|
|
9261
9262
|
// src/providers/cloud/OneDriveProvider.ts
|
|
9262
9263
|
var ONEDRIVE_DRIVE_BASE = "https://graph.microsoft.com/v1.0/me/drive";
|
|
9263
9264
|
var ONEDRIVE_CHECKSUM_CAPABILITIES = ["sha1", "sha256", "quickxorhash"];
|
|
9265
|
+
var ONEDRIVE_CHUNK_ALIGNMENT = 320 * 1024;
|
|
9266
|
+
var DEFAULT_ONEDRIVE_PART_SIZE = 10 * 1024 * 1024;
|
|
9267
|
+
var DEFAULT_ONEDRIVE_THRESHOLD = 4 * 1024 * 1024;
|
|
9264
9268
|
function createOneDriveProviderFactory(options = {}) {
|
|
9265
9269
|
const id = options.id ?? "one-drive";
|
|
9266
9270
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -9271,6 +9275,37 @@ function createOneDriveProviderFactory(options = {}) {
|
|
|
9271
9275
|
retryable: false
|
|
9272
9276
|
});
|
|
9273
9277
|
}
|
|
9278
|
+
const multipartEnabled = options.multipart?.enabled ?? true;
|
|
9279
|
+
const partSizeBytes = options.multipart?.partSizeBytes ?? DEFAULT_ONEDRIVE_PART_SIZE;
|
|
9280
|
+
const thresholdBytes = options.multipart?.thresholdBytes ?? DEFAULT_ONEDRIVE_THRESHOLD;
|
|
9281
|
+
if (multipartEnabled) {
|
|
9282
|
+
if (!Number.isInteger(partSizeBytes) || partSizeBytes <= 0) {
|
|
9283
|
+
throw new ConfigurationError({
|
|
9284
|
+
details: { partSizeBytes },
|
|
9285
|
+
message: "OneDriveMultipartOptions.partSizeBytes must be a positive integer",
|
|
9286
|
+
retryable: false
|
|
9287
|
+
});
|
|
9288
|
+
}
|
|
9289
|
+
if (partSizeBytes % ONEDRIVE_CHUNK_ALIGNMENT !== 0) {
|
|
9290
|
+
throw new ConfigurationError({
|
|
9291
|
+
details: { partSizeBytes },
|
|
9292
|
+
message: `OneDrive multipart partSizeBytes must be a multiple of ${String(ONEDRIVE_CHUNK_ALIGNMENT)} bytes (320 KiB)`,
|
|
9293
|
+
retryable: false
|
|
9294
|
+
});
|
|
9295
|
+
}
|
|
9296
|
+
if (!Number.isInteger(thresholdBytes) || thresholdBytes < 0) {
|
|
9297
|
+
throw new ConfigurationError({
|
|
9298
|
+
details: { thresholdBytes },
|
|
9299
|
+
message: "OneDriveMultipartOptions.thresholdBytes must be a non-negative integer",
|
|
9300
|
+
retryable: false
|
|
9301
|
+
});
|
|
9302
|
+
}
|
|
9303
|
+
}
|
|
9304
|
+
const multipart = {
|
|
9305
|
+
enabled: multipartEnabled,
|
|
9306
|
+
partSizeBytes,
|
|
9307
|
+
thresholdBytes
|
|
9308
|
+
};
|
|
9274
9309
|
const capabilities = {
|
|
9275
9310
|
atomicRename: false,
|
|
9276
9311
|
authentication: ["token", "oauth"],
|
|
@@ -9280,13 +9315,17 @@ function createOneDriveProviderFactory(options = {}) {
|
|
|
9280
9315
|
list: true,
|
|
9281
9316
|
maxConcurrency: 4,
|
|
9282
9317
|
metadata: ["modifiedAt", "createdAt", "uniqueId"],
|
|
9283
|
-
notes: [
|
|
9284
|
-
|
|
9318
|
+
notes: multipartEnabled ? [
|
|
9319
|
+
`OneDrive upload session enabled by default (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B).`,
|
|
9320
|
+
"Payloads at or below the threshold automatically fall back to single-shot PUT /content.",
|
|
9321
|
+
"Pass `multipart: { enabled: false }` to force the legacy single-shot behaviour."
|
|
9322
|
+
] : [
|
|
9323
|
+
"OneDrive provider performs single-shot uploads via PUT /content; resumable upload sessions are disabled."
|
|
9285
9324
|
],
|
|
9286
9325
|
provider: id,
|
|
9287
9326
|
readStream: true,
|
|
9288
9327
|
resumeDownload: true,
|
|
9289
|
-
resumeUpload:
|
|
9328
|
+
resumeUpload: multipartEnabled,
|
|
9290
9329
|
serverSideCopy: false,
|
|
9291
9330
|
serverSideMove: false,
|
|
9292
9331
|
stat: true,
|
|
@@ -9300,7 +9339,8 @@ function createOneDriveProviderFactory(options = {}) {
|
|
|
9300
9339
|
defaultHeaders: { ...options.defaultHeaders ?? {} },
|
|
9301
9340
|
driveBaseUrl,
|
|
9302
9341
|
fetch: fetchImpl,
|
|
9303
|
-
id
|
|
9342
|
+
id,
|
|
9343
|
+
multipart
|
|
9304
9344
|
}),
|
|
9305
9345
|
id
|
|
9306
9346
|
};
|
|
@@ -9334,6 +9374,7 @@ var OneDriveProvider = class {
|
|
|
9334
9374
|
driveBaseUrl: this.internals.driveBaseUrl,
|
|
9335
9375
|
fetch: this.internals.fetch,
|
|
9336
9376
|
id: this.internals.id,
|
|
9377
|
+
multipart: this.internals.multipart,
|
|
9337
9378
|
token
|
|
9338
9379
|
};
|
|
9339
9380
|
if (profile.timeoutMs !== void 0) sessionOptions.timeoutMs = profile.timeoutMs;
|
|
@@ -9437,12 +9478,19 @@ var OneDriveTransferOperations = class {
|
|
|
9437
9478
|
if (request.offset !== void 0 && request.offset > 0) {
|
|
9438
9479
|
throw new UnsupportedFeatureError({
|
|
9439
9480
|
details: { offset: request.offset },
|
|
9440
|
-
message: "OneDrive provider does not yet support
|
|
9481
|
+
message: "OneDrive provider does not yet support cross-attempt resume of upload sessions",
|
|
9441
9482
|
retryable: false
|
|
9442
9483
|
});
|
|
9443
9484
|
}
|
|
9444
9485
|
const normalized = normalizeRemotePath(request.endpoint.path);
|
|
9486
|
+
const multipart = this.options.multipart;
|
|
9445
9487
|
const buffered = await collectChunks3(request.content);
|
|
9488
|
+
if (!multipart.enabled || buffered.byteLength <= multipart.thresholdBytes) {
|
|
9489
|
+
return this.singleShotPut(request, normalized, buffered);
|
|
9490
|
+
}
|
|
9491
|
+
return this.writeUploadSession(request, normalized, buffered);
|
|
9492
|
+
}
|
|
9493
|
+
async singleShotPut(request, normalized, buffered) {
|
|
9446
9494
|
const url = `${this.options.driveBaseUrl}${itemSegment(normalized)}/content`;
|
|
9447
9495
|
const response = await graphFetch(this.options, "PUT", url, {
|
|
9448
9496
|
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
@@ -9462,6 +9510,77 @@ var OneDriveTransferOperations = class {
|
|
|
9462
9510
|
if (checksum !== void 0) result.checksum = checksum;
|
|
9463
9511
|
return result;
|
|
9464
9512
|
}
|
|
9513
|
+
async writeUploadSession(request, normalized, buffered) {
|
|
9514
|
+
const partSize = this.options.multipart.partSizeBytes;
|
|
9515
|
+
const total = buffered.byteLength;
|
|
9516
|
+
const sessionUrl = `${this.options.driveBaseUrl}${itemSegment(normalized)}/createUploadSession`;
|
|
9517
|
+
const initiate = await graphFetch(this.options, "POST", sessionUrl, {
|
|
9518
|
+
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
9519
|
+
body: new TextEncoder().encode(
|
|
9520
|
+
JSON.stringify({ item: { "@microsoft.graph.conflictBehavior": "replace" } })
|
|
9521
|
+
),
|
|
9522
|
+
extraHeaders: { "content-type": "application/json" }
|
|
9523
|
+
});
|
|
9524
|
+
if (!initiate.ok) {
|
|
9525
|
+
throw mapOneDriveResponseError(initiate, normalized, await safeReadText3(initiate));
|
|
9526
|
+
}
|
|
9527
|
+
const initiateBody = await initiate.json();
|
|
9528
|
+
const uploadUrl = initiateBody.uploadUrl;
|
|
9529
|
+
if (typeof uploadUrl !== "string" || uploadUrl === "") {
|
|
9530
|
+
throw new ConnectionError({
|
|
9531
|
+
details: { path: normalized },
|
|
9532
|
+
message: "OneDrive createUploadSession returned no uploadUrl",
|
|
9533
|
+
retryable: true
|
|
9534
|
+
});
|
|
9535
|
+
}
|
|
9536
|
+
let bytesTransferred = 0;
|
|
9537
|
+
let finalItem;
|
|
9538
|
+
try {
|
|
9539
|
+
while (bytesTransferred < total) {
|
|
9540
|
+
request.throwIfAborted();
|
|
9541
|
+
const chunkEnd = Math.min(bytesTransferred + partSize, total);
|
|
9542
|
+
const chunk = buffered.subarray(bytesTransferred, chunkEnd);
|
|
9543
|
+
const response = await graphSessionFetch(this.options, uploadUrl, {
|
|
9544
|
+
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
9545
|
+
body: chunk,
|
|
9546
|
+
extraHeaders: {
|
|
9547
|
+
"content-length": String(chunk.byteLength),
|
|
9548
|
+
"content-range": `bytes ${String(bytesTransferred)}-${String(chunkEnd - 1)}/${String(total)}`,
|
|
9549
|
+
"content-type": "application/octet-stream"
|
|
9550
|
+
}
|
|
9551
|
+
});
|
|
9552
|
+
if (response.status === 202) {
|
|
9553
|
+
bytesTransferred = chunkEnd;
|
|
9554
|
+
request.reportProgress(bytesTransferred, total);
|
|
9555
|
+
continue;
|
|
9556
|
+
}
|
|
9557
|
+
if (response.status === 200 || response.status === 201) {
|
|
9558
|
+
bytesTransferred = chunkEnd;
|
|
9559
|
+
request.reportProgress(bytesTransferred, total);
|
|
9560
|
+
finalItem = await response.json();
|
|
9561
|
+
break;
|
|
9562
|
+
}
|
|
9563
|
+
throw mapOneDriveResponseError(response, normalized, await safeReadText3(response));
|
|
9564
|
+
}
|
|
9565
|
+
} catch (error) {
|
|
9566
|
+
void graphSessionFetch(this.options, uploadUrl, { method: "DELETE" }).catch(() => void 0);
|
|
9567
|
+
throw error;
|
|
9568
|
+
}
|
|
9569
|
+
if (finalItem === void 0) {
|
|
9570
|
+
throw new ConnectionError({
|
|
9571
|
+
details: { path: normalized },
|
|
9572
|
+
message: "OneDrive upload session did not return a final DriveItem",
|
|
9573
|
+
retryable: true
|
|
9574
|
+
});
|
|
9575
|
+
}
|
|
9576
|
+
const result = {
|
|
9577
|
+
bytesTransferred,
|
|
9578
|
+
totalBytes: bytesTransferred
|
|
9579
|
+
};
|
|
9580
|
+
const checksum = preferHash(finalItem.file?.hashes);
|
|
9581
|
+
if (checksum !== void 0) result.checksum = checksum;
|
|
9582
|
+
return result;
|
|
9583
|
+
}
|
|
9465
9584
|
async fetchItem(normalized) {
|
|
9466
9585
|
const url = `${this.options.driveBaseUrl}${itemSegment(normalized)}`;
|
|
9467
9586
|
const response = await graphFetch(this.options, "GET", url);
|
|
@@ -9508,6 +9627,45 @@ async function graphFetch(options, method, url, fetchOptions = {}) {
|
|
|
9508
9627
|
if (timer !== void 0) clearTimeout(timer);
|
|
9509
9628
|
}
|
|
9510
9629
|
}
|
|
9630
|
+
async function graphSessionFetch(options, uploadUrl, fetchOptions = {}) {
|
|
9631
|
+
const headers = {
|
|
9632
|
+
...fetchOptions.extraHeaders ?? {}
|
|
9633
|
+
};
|
|
9634
|
+
const init = { headers, method: fetchOptions.method ?? "PUT" };
|
|
9635
|
+
if (fetchOptions.body !== void 0) {
|
|
9636
|
+
init.body = fetchOptions.body;
|
|
9637
|
+
}
|
|
9638
|
+
const controller = new AbortController();
|
|
9639
|
+
const upstream = fetchOptions.signal ?? null;
|
|
9640
|
+
if (upstream !== null) {
|
|
9641
|
+
if (upstream.aborted) controller.abort(upstream.reason);
|
|
9642
|
+
else upstream.addEventListener("abort", () => controller.abort(upstream.reason));
|
|
9643
|
+
}
|
|
9644
|
+
let timer;
|
|
9645
|
+
if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
|
|
9646
|
+
timer = setTimeout(
|
|
9647
|
+
() => controller.abort(new Error("OneDrive upload session request timed out")),
|
|
9648
|
+
options.timeoutMs
|
|
9649
|
+
);
|
|
9650
|
+
}
|
|
9651
|
+
try {
|
|
9652
|
+
return await options.fetch(uploadUrl, { ...init, signal: controller.signal });
|
|
9653
|
+
} catch (error) {
|
|
9654
|
+
const safeUrl = redactSessionUrl(uploadUrl);
|
|
9655
|
+
throw new ConnectionError({
|
|
9656
|
+
cause: error,
|
|
9657
|
+
details: { url: safeUrl },
|
|
9658
|
+
message: `OneDrive upload session request to ${safeUrl} failed`,
|
|
9659
|
+
retryable: true
|
|
9660
|
+
});
|
|
9661
|
+
} finally {
|
|
9662
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
9663
|
+
}
|
|
9664
|
+
}
|
|
9665
|
+
function redactSessionUrl(url) {
|
|
9666
|
+
const queryStart = url.indexOf("?");
|
|
9667
|
+
return queryStart === -1 ? url : `${url.slice(0, queryStart)}?<redacted>`;
|
|
9668
|
+
}
|
|
9511
9669
|
function mapOneDriveResponseError(response, contextPath, bodyText) {
|
|
9512
9670
|
const details = {
|
|
9513
9671
|
bodyText: bodyText.slice(0, 500),
|
|
@@ -9629,8 +9787,12 @@ async function collectChunks3(source) {
|
|
|
9629
9787
|
}
|
|
9630
9788
|
|
|
9631
9789
|
// src/providers/cloud/AzureBlobProvider.ts
|
|
9790
|
+
var import_node_crypto11 = require("crypto");
|
|
9632
9791
|
var AZURE_BLOB_API_VERSION = "2023-11-03";
|
|
9633
9792
|
var AZURE_CHECKSUM_CAPABILITIES = ["md5"];
|
|
9793
|
+
var DEFAULT_AZURE_PART_SIZE = 8 * 1024 * 1024;
|
|
9794
|
+
var DEFAULT_AZURE_THRESHOLD = 8 * 1024 * 1024;
|
|
9795
|
+
var AZURE_MAX_PART_SIZE = 4e3 * 1024 * 1024;
|
|
9634
9796
|
function createAzureBlobProviderFactory(options) {
|
|
9635
9797
|
if (typeof options.container !== "string" || options.container === "") {
|
|
9636
9798
|
throw new ConfigurationError({
|
|
@@ -9648,6 +9810,37 @@ function createAzureBlobProviderFactory(options) {
|
|
|
9648
9810
|
}
|
|
9649
9811
|
const endpoint = resolveAzureEndpoint(options);
|
|
9650
9812
|
const apiVersion = options.apiVersion ?? AZURE_BLOB_API_VERSION;
|
|
9813
|
+
const multipartEnabled = options.multipart?.enabled ?? true;
|
|
9814
|
+
const partSizeBytes = options.multipart?.partSizeBytes ?? DEFAULT_AZURE_PART_SIZE;
|
|
9815
|
+
const thresholdBytes = options.multipart?.thresholdBytes ?? DEFAULT_AZURE_THRESHOLD;
|
|
9816
|
+
if (multipartEnabled) {
|
|
9817
|
+
if (!Number.isInteger(partSizeBytes) || partSizeBytes <= 0) {
|
|
9818
|
+
throw new ConfigurationError({
|
|
9819
|
+
details: { partSizeBytes },
|
|
9820
|
+
message: "AzureBlobMultipartOptions.partSizeBytes must be a positive integer",
|
|
9821
|
+
retryable: false
|
|
9822
|
+
});
|
|
9823
|
+
}
|
|
9824
|
+
if (partSizeBytes > AZURE_MAX_PART_SIZE) {
|
|
9825
|
+
throw new ConfigurationError({
|
|
9826
|
+
details: { maxBytes: AZURE_MAX_PART_SIZE, partSizeBytes },
|
|
9827
|
+
message: `AzureBlobMultipartOptions.partSizeBytes must not exceed ${String(AZURE_MAX_PART_SIZE)} bytes (4000 MiB)`,
|
|
9828
|
+
retryable: false
|
|
9829
|
+
});
|
|
9830
|
+
}
|
|
9831
|
+
if (!Number.isInteger(thresholdBytes) || thresholdBytes < 0) {
|
|
9832
|
+
throw new ConfigurationError({
|
|
9833
|
+
details: { thresholdBytes },
|
|
9834
|
+
message: "AzureBlobMultipartOptions.thresholdBytes must be a non-negative integer",
|
|
9835
|
+
retryable: false
|
|
9836
|
+
});
|
|
9837
|
+
}
|
|
9838
|
+
}
|
|
9839
|
+
const multipart = {
|
|
9840
|
+
enabled: multipartEnabled,
|
|
9841
|
+
partSizeBytes,
|
|
9842
|
+
thresholdBytes
|
|
9843
|
+
};
|
|
9651
9844
|
const capabilities = {
|
|
9652
9845
|
atomicRename: false,
|
|
9653
9846
|
authentication: ["token", "oauth"],
|
|
@@ -9657,13 +9850,17 @@ function createAzureBlobProviderFactory(options) {
|
|
|
9657
9850
|
list: true,
|
|
9658
9851
|
maxConcurrency: 4,
|
|
9659
9852
|
metadata: ["modifiedAt", "uniqueId"],
|
|
9660
|
-
notes: [
|
|
9661
|
-
|
|
9853
|
+
notes: multipartEnabled ? [
|
|
9854
|
+
`Azure Blob staged-block upload enabled by default (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B).`,
|
|
9855
|
+
"Payloads at or below the threshold automatically fall back to single-shot block-blob PUT.",
|
|
9856
|
+
"Pass `multipart: { enabled: false }` to force the legacy single-shot behaviour."
|
|
9857
|
+
] : [
|
|
9858
|
+
"Azure Blob provider performs single-shot block-blob uploads via PUT; entire object is buffered in memory before transmission."
|
|
9662
9859
|
],
|
|
9663
9860
|
provider: id,
|
|
9664
9861
|
readStream: true,
|
|
9665
9862
|
resumeDownload: true,
|
|
9666
|
-
resumeUpload:
|
|
9863
|
+
resumeUpload: multipartEnabled,
|
|
9667
9864
|
serverSideCopy: false,
|
|
9668
9865
|
serverSideMove: false,
|
|
9669
9866
|
stat: true,
|
|
@@ -9680,6 +9877,7 @@ function createAzureBlobProviderFactory(options) {
|
|
|
9680
9877
|
endpoint,
|
|
9681
9878
|
fetch: fetchImpl,
|
|
9682
9879
|
id,
|
|
9880
|
+
multipart,
|
|
9683
9881
|
...options.sasToken !== void 0 ? { sasToken: options.sasToken } : {}
|
|
9684
9882
|
}),
|
|
9685
9883
|
id
|
|
@@ -9725,7 +9923,8 @@ var AzureBlobProvider = class {
|
|
|
9725
9923
|
defaultHeaders: this.internals.defaultHeaders,
|
|
9726
9924
|
endpoint: this.internals.endpoint,
|
|
9727
9925
|
fetch: this.internals.fetch,
|
|
9728
|
-
id: this.internals.id
|
|
9926
|
+
id: this.internals.id,
|
|
9927
|
+
multipart: this.internals.multipart
|
|
9729
9928
|
};
|
|
9730
9929
|
if (bearerToken !== void 0) sessionOptions.bearerToken = bearerToken;
|
|
9731
9930
|
if (this.internals.sasToken !== void 0) sessionOptions.sasToken = this.internals.sasToken;
|
|
@@ -9860,12 +10059,19 @@ var AzureBlobTransferOperations = class {
|
|
|
9860
10059
|
if (request.offset !== void 0 && request.offset > 0) {
|
|
9861
10060
|
throw new UnsupportedFeatureError({
|
|
9862
10061
|
details: { offset: request.offset },
|
|
9863
|
-
message: "Azure Blob provider does not yet support staged-block
|
|
10062
|
+
message: "Azure Blob provider does not yet support cross-attempt resume of staged-block uploads",
|
|
9864
10063
|
retryable: false
|
|
9865
10064
|
});
|
|
9866
10065
|
}
|
|
9867
10066
|
const normalized = normalizeRemotePath(request.endpoint.path);
|
|
9868
|
-
const
|
|
10067
|
+
const multipart = this.options.multipart;
|
|
10068
|
+
if (!multipart.enabled) {
|
|
10069
|
+
const buffered = await collectChunks4(request.content);
|
|
10070
|
+
return this.singleShotPut(request, normalized, buffered);
|
|
10071
|
+
}
|
|
10072
|
+
return this.writeStagedBlocks(request, normalized);
|
|
10073
|
+
}
|
|
10074
|
+
async singleShotPut(request, normalized, buffered) {
|
|
9869
10075
|
const url = buildBlobUrl(this.options, normalized);
|
|
9870
10076
|
const response = await azureFetch(this.options, "PUT", url, {
|
|
9871
10077
|
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
@@ -9887,6 +10093,92 @@ var AzureBlobTransferOperations = class {
|
|
|
9887
10093
|
if (md5 !== null && md5 !== "") result.checksum = md5;
|
|
9888
10094
|
return result;
|
|
9889
10095
|
}
|
|
10096
|
+
async writeStagedBlocks(request, normalized) {
|
|
10097
|
+
const multipart = this.options.multipart;
|
|
10098
|
+
const partSize = multipart.partSizeBytes;
|
|
10099
|
+
const iterator = request.content[Symbol.asyncIterator]();
|
|
10100
|
+
const uploadNonce = generateUploadNonce();
|
|
10101
|
+
const initialBuffer = [];
|
|
10102
|
+
let initialSize = 0;
|
|
10103
|
+
while (initialSize <= multipart.thresholdBytes) {
|
|
10104
|
+
const next = await iterator.next();
|
|
10105
|
+
if (next.done === true) break;
|
|
10106
|
+
const chunk = next.value;
|
|
10107
|
+
if (chunk.byteLength === 0) continue;
|
|
10108
|
+
initialBuffer.push(chunk);
|
|
10109
|
+
initialSize += chunk.byteLength;
|
|
10110
|
+
}
|
|
10111
|
+
if (initialSize <= multipart.thresholdBytes) {
|
|
10112
|
+
return this.singleShotPut(request, normalized, concatChunks2(initialBuffer, initialSize));
|
|
10113
|
+
}
|
|
10114
|
+
const blockIds = [];
|
|
10115
|
+
let bytesTransferred = 0;
|
|
10116
|
+
let partNumber = 1;
|
|
10117
|
+
let buffer = [...initialBuffer];
|
|
10118
|
+
let bufferSize = initialSize;
|
|
10119
|
+
const flushBlocks = async (final) => {
|
|
10120
|
+
while (bufferSize >= partSize || final && bufferSize > 0) {
|
|
10121
|
+
const take = Math.min(bufferSize, partSize);
|
|
10122
|
+
const sliced = sliceFromBuffers(buffer, take);
|
|
10123
|
+
buffer = sliced.remaining;
|
|
10124
|
+
bufferSize -= sliced.bytes.byteLength;
|
|
10125
|
+
const blockId = encodeBlockId(uploadNonce, partNumber);
|
|
10126
|
+
const blockUrl = buildBlobUrl(this.options, normalized, {
|
|
10127
|
+
blockid: blockId,
|
|
10128
|
+
comp: "block"
|
|
10129
|
+
});
|
|
10130
|
+
const response = await azureFetch(this.options, "PUT", blockUrl, {
|
|
10131
|
+
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
10132
|
+
body: sliced.bytes,
|
|
10133
|
+
extraHeaders: { "content-type": "application/octet-stream" }
|
|
10134
|
+
});
|
|
10135
|
+
if (!response.ok) {
|
|
10136
|
+
throw mapAzureResponseError(response, normalized, await safeReadText4(response));
|
|
10137
|
+
}
|
|
10138
|
+
blockIds.push(blockId);
|
|
10139
|
+
bytesTransferred += sliced.bytes.byteLength;
|
|
10140
|
+
request.reportProgress(bytesTransferred, void 0);
|
|
10141
|
+
partNumber += 1;
|
|
10142
|
+
}
|
|
10143
|
+
};
|
|
10144
|
+
await flushBlocks(false);
|
|
10145
|
+
while (true) {
|
|
10146
|
+
request.throwIfAborted();
|
|
10147
|
+
const next = await iterator.next();
|
|
10148
|
+
if (next.done === true) break;
|
|
10149
|
+
if (next.value.byteLength === 0) continue;
|
|
10150
|
+
buffer.push(next.value);
|
|
10151
|
+
bufferSize += next.value.byteLength;
|
|
10152
|
+
await flushBlocks(false);
|
|
10153
|
+
}
|
|
10154
|
+
await flushBlocks(true);
|
|
10155
|
+
if (blockIds.length === 0) {
|
|
10156
|
+
throw new ConnectionError({
|
|
10157
|
+
message: "Azure Blob staged-block upload completed with zero blocks",
|
|
10158
|
+
retryable: false
|
|
10159
|
+
});
|
|
10160
|
+
}
|
|
10161
|
+
const commitUrl = buildBlobUrl(this.options, normalized, { comp: "blocklist" });
|
|
10162
|
+
const xmlBody = buildBlockListXml(blockIds);
|
|
10163
|
+
const commitResponse = await azureFetch(this.options, "PUT", commitUrl, {
|
|
10164
|
+
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
10165
|
+
body: new TextEncoder().encode(xmlBody),
|
|
10166
|
+
extraHeaders: {
|
|
10167
|
+
"content-type": "application/xml",
|
|
10168
|
+
"x-ms-blob-content-type": "application/octet-stream"
|
|
10169
|
+
}
|
|
10170
|
+
});
|
|
10171
|
+
if (!commitResponse.ok) {
|
|
10172
|
+
throw mapAzureResponseError(commitResponse, normalized, await safeReadText4(commitResponse));
|
|
10173
|
+
}
|
|
10174
|
+
const result = {
|
|
10175
|
+
bytesTransferred,
|
|
10176
|
+
totalBytes: bytesTransferred
|
|
10177
|
+
};
|
|
10178
|
+
const md5 = commitResponse.headers.get("content-md5");
|
|
10179
|
+
if (md5 !== null && md5 !== "") result.checksum = md5;
|
|
10180
|
+
return result;
|
|
10181
|
+
}
|
|
9890
10182
|
};
|
|
9891
10183
|
async function azureFetch(options, method, url, fetchOptions = {}) {
|
|
9892
10184
|
const headers = {
|
|
@@ -9932,14 +10224,17 @@ function buildContainerUrl(options, params) {
|
|
|
9932
10224
|
appendSas(search, options.sasToken);
|
|
9933
10225
|
return `${options.endpoint}/${encodeURIComponent(options.container)}?${search.toString()}`;
|
|
9934
10226
|
}
|
|
9935
|
-
function buildBlobUrl(options, normalized) {
|
|
10227
|
+
function buildBlobUrl(options, normalized, extraParams) {
|
|
9936
10228
|
const blobPath = normalized.replace(/^\/+/u, "");
|
|
9937
10229
|
const encoded = blobPath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
9938
10230
|
const base = `${options.endpoint}/${encodeURIComponent(options.container)}/${encoded}`;
|
|
9939
|
-
|
|
9940
|
-
|
|
10231
|
+
const search = new URLSearchParams();
|
|
10232
|
+
if (extraParams !== void 0) {
|
|
10233
|
+
for (const [k, v] of Object.entries(extraParams)) search.set(k, v);
|
|
9941
10234
|
}
|
|
9942
|
-
|
|
10235
|
+
appendSas(search, options.sasToken);
|
|
10236
|
+
const query = search.toString();
|
|
10237
|
+
return query === "" ? base : `${base}?${query}`;
|
|
9943
10238
|
}
|
|
9944
10239
|
function appendSas(search, sasToken) {
|
|
9945
10240
|
if (sasToken === void 0 || sasToken === "") return;
|
|
@@ -10091,11 +10386,63 @@ async function collectChunks4(source) {
|
|
|
10091
10386
|
}
|
|
10092
10387
|
return out;
|
|
10093
10388
|
}
|
|
10389
|
+
function concatChunks2(chunks, totalSize) {
|
|
10390
|
+
const out = new Uint8Array(totalSize);
|
|
10391
|
+
let offset = 0;
|
|
10392
|
+
for (const chunk of chunks) {
|
|
10393
|
+
out.set(chunk, offset);
|
|
10394
|
+
offset += chunk.byteLength;
|
|
10395
|
+
}
|
|
10396
|
+
return out;
|
|
10397
|
+
}
|
|
10398
|
+
function sliceFromBuffers(buffers, size) {
|
|
10399
|
+
const out = new Uint8Array(size);
|
|
10400
|
+
let offset = 0;
|
|
10401
|
+
let i = 0;
|
|
10402
|
+
while (offset < size && i < buffers.length) {
|
|
10403
|
+
const chunk = buffers[i];
|
|
10404
|
+
if (chunk === void 0) {
|
|
10405
|
+
i += 1;
|
|
10406
|
+
continue;
|
|
10407
|
+
}
|
|
10408
|
+
const remaining = size - offset;
|
|
10409
|
+
if (chunk.byteLength <= remaining) {
|
|
10410
|
+
out.set(chunk, offset);
|
|
10411
|
+
offset += chunk.byteLength;
|
|
10412
|
+
i += 1;
|
|
10413
|
+
} else {
|
|
10414
|
+
out.set(chunk.subarray(0, remaining), offset);
|
|
10415
|
+
const leftover = chunk.subarray(remaining);
|
|
10416
|
+
const next = buffers.slice(i + 1);
|
|
10417
|
+
next.unshift(leftover);
|
|
10418
|
+
return { bytes: out, remaining: next };
|
|
10419
|
+
}
|
|
10420
|
+
}
|
|
10421
|
+
return { bytes: out.subarray(0, offset), remaining: buffers.slice(i) };
|
|
10422
|
+
}
|
|
10423
|
+
function encodeBlockId(nonce, partNumber) {
|
|
10424
|
+
const padded = String(partNumber).padStart(9, "0");
|
|
10425
|
+
const raw = `${nonce}-${padded}`;
|
|
10426
|
+
return Buffer.from(raw, "utf8").toString("base64");
|
|
10427
|
+
}
|
|
10428
|
+
function generateUploadNonce() {
|
|
10429
|
+
return (0, import_node_crypto11.randomBytes)(4).toString("hex");
|
|
10430
|
+
}
|
|
10431
|
+
function buildBlockListXml(blockIds) {
|
|
10432
|
+
const items = blockIds.map((id) => `<Latest>${escapeXml(id)}</Latest>`).join("");
|
|
10433
|
+
return `<?xml version="1.0" encoding="utf-8"?><BlockList>${items}</BlockList>`;
|
|
10434
|
+
}
|
|
10435
|
+
function escapeXml(value) {
|
|
10436
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
10437
|
+
}
|
|
10094
10438
|
|
|
10095
10439
|
// src/providers/cloud/GcsProvider.ts
|
|
10096
10440
|
var GCS_JSON_API_BASE = "https://storage.googleapis.com/storage/v1";
|
|
10097
10441
|
var GCS_UPLOAD_API_BASE = "https://storage.googleapis.com/upload/storage/v1";
|
|
10098
10442
|
var GCS_CHECKSUM_CAPABILITIES = ["md5", "crc32c"];
|
|
10443
|
+
var GCS_CHUNK_ALIGNMENT = 256 * 1024;
|
|
10444
|
+
var DEFAULT_GCS_PART_SIZE = 8 * 1024 * 1024;
|
|
10445
|
+
var DEFAULT_GCS_THRESHOLD = 8 * 1024 * 1024;
|
|
10099
10446
|
function createGcsProviderFactory(options) {
|
|
10100
10447
|
if (typeof options.bucket !== "string" || options.bucket === "") {
|
|
10101
10448
|
throw new ConfigurationError({
|
|
@@ -10113,6 +10460,37 @@ function createGcsProviderFactory(options) {
|
|
|
10113
10460
|
}
|
|
10114
10461
|
const apiBaseUrl = (options.apiBaseUrl ?? GCS_JSON_API_BASE).replace(/\/+$/u, "");
|
|
10115
10462
|
const uploadBaseUrl = (options.uploadBaseUrl ?? GCS_UPLOAD_API_BASE).replace(/\/+$/u, "");
|
|
10463
|
+
const multipartEnabled = options.multipart?.enabled ?? true;
|
|
10464
|
+
const partSizeBytes = options.multipart?.partSizeBytes ?? DEFAULT_GCS_PART_SIZE;
|
|
10465
|
+
const thresholdBytes = options.multipart?.thresholdBytes ?? DEFAULT_GCS_THRESHOLD;
|
|
10466
|
+
if (multipartEnabled) {
|
|
10467
|
+
if (!Number.isInteger(partSizeBytes) || partSizeBytes <= 0) {
|
|
10468
|
+
throw new ConfigurationError({
|
|
10469
|
+
details: { partSizeBytes },
|
|
10470
|
+
message: "GcsMultipartOptions.partSizeBytes must be a positive integer",
|
|
10471
|
+
retryable: false
|
|
10472
|
+
});
|
|
10473
|
+
}
|
|
10474
|
+
if (partSizeBytes % GCS_CHUNK_ALIGNMENT !== 0) {
|
|
10475
|
+
throw new ConfigurationError({
|
|
10476
|
+
details: { partSizeBytes },
|
|
10477
|
+
message: `GCS multipart partSizeBytes must be a multiple of ${String(GCS_CHUNK_ALIGNMENT)} bytes (256 KiB)`,
|
|
10478
|
+
retryable: false
|
|
10479
|
+
});
|
|
10480
|
+
}
|
|
10481
|
+
if (!Number.isInteger(thresholdBytes) || thresholdBytes < 0) {
|
|
10482
|
+
throw new ConfigurationError({
|
|
10483
|
+
details: { thresholdBytes },
|
|
10484
|
+
message: "GcsMultipartOptions.thresholdBytes must be a non-negative integer",
|
|
10485
|
+
retryable: false
|
|
10486
|
+
});
|
|
10487
|
+
}
|
|
10488
|
+
}
|
|
10489
|
+
const multipart = {
|
|
10490
|
+
enabled: multipartEnabled,
|
|
10491
|
+
partSizeBytes,
|
|
10492
|
+
thresholdBytes
|
|
10493
|
+
};
|
|
10116
10494
|
const capabilities = {
|
|
10117
10495
|
atomicRename: false,
|
|
10118
10496
|
authentication: ["token", "oauth"],
|
|
@@ -10122,13 +10500,17 @@ function createGcsProviderFactory(options) {
|
|
|
10122
10500
|
list: true,
|
|
10123
10501
|
maxConcurrency: 4,
|
|
10124
10502
|
metadata: ["modifiedAt", "createdAt", "uniqueId"],
|
|
10125
|
-
notes: [
|
|
10126
|
-
|
|
10503
|
+
notes: multipartEnabled ? [
|
|
10504
|
+
`GCS resumable-upload session enabled by default (partSize=${String(multipart.partSizeBytes)}B, threshold=${String(multipart.thresholdBytes)}B).`,
|
|
10505
|
+
"Payloads at or below the threshold automatically fall back to single-shot uploadType=media POST.",
|
|
10506
|
+
"Pass `multipart: { enabled: false }` to force the legacy single-shot behaviour."
|
|
10507
|
+
] : [
|
|
10508
|
+
"GCS provider performs single-shot media uploads via /upload?uploadType=media; resumable upload sessions are disabled."
|
|
10127
10509
|
],
|
|
10128
10510
|
provider: id,
|
|
10129
10511
|
readStream: true,
|
|
10130
10512
|
resumeDownload: true,
|
|
10131
|
-
resumeUpload:
|
|
10513
|
+
resumeUpload: multipartEnabled,
|
|
10132
10514
|
serverSideCopy: false,
|
|
10133
10515
|
serverSideMove: false,
|
|
10134
10516
|
stat: true,
|
|
@@ -10144,6 +10526,7 @@ function createGcsProviderFactory(options) {
|
|
|
10144
10526
|
defaultHeaders: { ...options.defaultHeaders ?? {} },
|
|
10145
10527
|
fetch: fetchImpl,
|
|
10146
10528
|
id,
|
|
10529
|
+
multipart,
|
|
10147
10530
|
uploadBaseUrl
|
|
10148
10531
|
}),
|
|
10149
10532
|
id
|
|
@@ -10179,6 +10562,7 @@ var GcsProvider = class {
|
|
|
10179
10562
|
defaultHeaders: this.internals.defaultHeaders,
|
|
10180
10563
|
fetch: this.internals.fetch,
|
|
10181
10564
|
id: this.internals.id,
|
|
10565
|
+
multipart: this.internals.multipart,
|
|
10182
10566
|
token,
|
|
10183
10567
|
uploadBaseUrl: this.internals.uploadBaseUrl
|
|
10184
10568
|
};
|
|
@@ -10300,13 +10684,20 @@ var GcsTransferOperations = class {
|
|
|
10300
10684
|
if (request.offset !== void 0 && request.offset > 0) {
|
|
10301
10685
|
throw new UnsupportedFeatureError({
|
|
10302
10686
|
details: { offset: request.offset },
|
|
10303
|
-
message: "GCS provider does not yet support
|
|
10687
|
+
message: "GCS provider does not yet support cross-attempt resume of upload sessions",
|
|
10304
10688
|
retryable: false
|
|
10305
10689
|
});
|
|
10306
10690
|
}
|
|
10307
10691
|
const normalized = normalizeRemotePath(request.endpoint.path);
|
|
10308
10692
|
const objectName = toGcsObjectName(normalized);
|
|
10309
|
-
const
|
|
10693
|
+
const multipart = this.options.multipart;
|
|
10694
|
+
if (!multipart.enabled) {
|
|
10695
|
+
const buffered = await collectChunks5(request.content);
|
|
10696
|
+
return this.singleShotMedia(request, normalized, objectName, buffered);
|
|
10697
|
+
}
|
|
10698
|
+
return this.writeResumableSession(request, normalized, objectName);
|
|
10699
|
+
}
|
|
10700
|
+
async singleShotMedia(request, normalized, objectName, buffered) {
|
|
10310
10701
|
const url = `${this.options.uploadBaseUrl}/b/${encodeURIComponent(this.options.bucket)}/o?uploadType=media&name=${encodeURIComponent(objectName)}`;
|
|
10311
10702
|
const response = await gcsFetch(this.options, "POST", url, {
|
|
10312
10703
|
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
@@ -10325,6 +10716,124 @@ var GcsTransferOperations = class {
|
|
|
10325
10716
|
if (typeof item.md5Hash === "string" && item.md5Hash !== "") result.checksum = item.md5Hash;
|
|
10326
10717
|
return result;
|
|
10327
10718
|
}
|
|
10719
|
+
async writeResumableSession(request, normalized, objectName) {
|
|
10720
|
+
const multipart = this.options.multipart;
|
|
10721
|
+
const partSize = multipart.partSizeBytes;
|
|
10722
|
+
const iterator = request.content[Symbol.asyncIterator]();
|
|
10723
|
+
const initialBuffer = [];
|
|
10724
|
+
let initialSize = 0;
|
|
10725
|
+
while (initialSize <= multipart.thresholdBytes) {
|
|
10726
|
+
const next = await iterator.next();
|
|
10727
|
+
if (next.done === true) break;
|
|
10728
|
+
const chunk = next.value;
|
|
10729
|
+
if (chunk.byteLength === 0) continue;
|
|
10730
|
+
initialBuffer.push(chunk);
|
|
10731
|
+
initialSize += chunk.byteLength;
|
|
10732
|
+
}
|
|
10733
|
+
if (initialSize <= multipart.thresholdBytes) {
|
|
10734
|
+
return this.singleShotMedia(
|
|
10735
|
+
request,
|
|
10736
|
+
normalized,
|
|
10737
|
+
objectName,
|
|
10738
|
+
concatChunks3(initialBuffer, initialSize)
|
|
10739
|
+
);
|
|
10740
|
+
}
|
|
10741
|
+
const initiateUrl = `${this.options.uploadBaseUrl}/b/${encodeURIComponent(this.options.bucket)}/o?uploadType=resumable&name=${encodeURIComponent(objectName)}`;
|
|
10742
|
+
const initiateResponse = await gcsFetch(this.options, "POST", initiateUrl, {
|
|
10743
|
+
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
10744
|
+
body: new TextEncoder().encode("{}"),
|
|
10745
|
+
extraHeaders: {
|
|
10746
|
+
"content-type": "application/json; charset=UTF-8",
|
|
10747
|
+
"x-upload-content-type": "application/octet-stream"
|
|
10748
|
+
}
|
|
10749
|
+
});
|
|
10750
|
+
if (!initiateResponse.ok) {
|
|
10751
|
+
throw mapGcsResponseError(initiateResponse, normalized, await safeReadText5(initiateResponse));
|
|
10752
|
+
}
|
|
10753
|
+
const sessionUri = initiateResponse.headers.get("location");
|
|
10754
|
+
if (sessionUri === null || sessionUri === "") {
|
|
10755
|
+
throw new ConnectionError({
|
|
10756
|
+
details: { path: normalized },
|
|
10757
|
+
message: "GCS resumable session initiation returned no Location header",
|
|
10758
|
+
retryable: true
|
|
10759
|
+
});
|
|
10760
|
+
}
|
|
10761
|
+
let bytesTransferred = 0;
|
|
10762
|
+
let buffer = [...initialBuffer];
|
|
10763
|
+
let bufferSize = initialSize;
|
|
10764
|
+
let sourceExhausted = false;
|
|
10765
|
+
let finalItem;
|
|
10766
|
+
const flushChunks = async (final) => {
|
|
10767
|
+
while (bufferSize >= partSize || final && bufferSize > 0) {
|
|
10768
|
+
const take = final ? bufferSize : partSize;
|
|
10769
|
+
const sliced = sliceFromBuffers2(buffer, take);
|
|
10770
|
+
buffer = sliced.remaining;
|
|
10771
|
+
bufferSize -= sliced.bytes.byteLength;
|
|
10772
|
+
const chunkStart = bytesTransferred;
|
|
10773
|
+
const chunkEnd = chunkStart + sliced.bytes.byteLength - 1;
|
|
10774
|
+
const totalRange = final ? String(chunkEnd + 1) : "*";
|
|
10775
|
+
const headers = {
|
|
10776
|
+
"content-length": String(sliced.bytes.byteLength),
|
|
10777
|
+
"content-range": `bytes ${String(chunkStart)}-${String(chunkEnd)}/${totalRange}`,
|
|
10778
|
+
"content-type": "application/octet-stream"
|
|
10779
|
+
};
|
|
10780
|
+
const response = await gcsSessionFetch(this.options, sessionUri, {
|
|
10781
|
+
...request.signal !== void 0 ? { signal: request.signal } : {},
|
|
10782
|
+
body: sliced.bytes,
|
|
10783
|
+
extraHeaders: headers
|
|
10784
|
+
});
|
|
10785
|
+
if (response.status === 308) {
|
|
10786
|
+
bytesTransferred += sliced.bytes.byteLength;
|
|
10787
|
+
request.reportProgress(bytesTransferred, void 0);
|
|
10788
|
+
continue;
|
|
10789
|
+
}
|
|
10790
|
+
if (response.status === 200 || response.status === 201) {
|
|
10791
|
+
bytesTransferred += sliced.bytes.byteLength;
|
|
10792
|
+
request.reportProgress(bytesTransferred, bytesTransferred);
|
|
10793
|
+
finalItem = await response.json();
|
|
10794
|
+
return;
|
|
10795
|
+
}
|
|
10796
|
+
throw mapGcsResponseError(response, normalized, await safeReadText5(response));
|
|
10797
|
+
}
|
|
10798
|
+
};
|
|
10799
|
+
try {
|
|
10800
|
+
await flushChunks(false);
|
|
10801
|
+
while (!sourceExhausted) {
|
|
10802
|
+
request.throwIfAborted();
|
|
10803
|
+
const next = await iterator.next();
|
|
10804
|
+
if (next.done === true) {
|
|
10805
|
+
sourceExhausted = true;
|
|
10806
|
+
break;
|
|
10807
|
+
}
|
|
10808
|
+
if (next.value.byteLength === 0) continue;
|
|
10809
|
+
buffer.push(next.value);
|
|
10810
|
+
bufferSize += next.value.byteLength;
|
|
10811
|
+
await flushChunks(false);
|
|
10812
|
+
if (finalItem !== void 0) break;
|
|
10813
|
+
}
|
|
10814
|
+
if (finalItem === void 0) {
|
|
10815
|
+
await flushChunks(true);
|
|
10816
|
+
}
|
|
10817
|
+
} catch (error) {
|
|
10818
|
+
void gcsSessionFetch(this.options, sessionUri, { method: "DELETE" }).catch(() => void 0);
|
|
10819
|
+
throw error;
|
|
10820
|
+
}
|
|
10821
|
+
if (finalItem === void 0) {
|
|
10822
|
+
throw new ConnectionError({
|
|
10823
|
+
details: { path: normalized },
|
|
10824
|
+
message: "GCS resumable upload did not return a final object",
|
|
10825
|
+
retryable: true
|
|
10826
|
+
});
|
|
10827
|
+
}
|
|
10828
|
+
const result = {
|
|
10829
|
+
bytesTransferred,
|
|
10830
|
+
totalBytes: bytesTransferred
|
|
10831
|
+
};
|
|
10832
|
+
if (typeof finalItem.md5Hash === "string" && finalItem.md5Hash !== "") {
|
|
10833
|
+
result.checksum = finalItem.md5Hash;
|
|
10834
|
+
}
|
|
10835
|
+
return result;
|
|
10836
|
+
}
|
|
10328
10837
|
};
|
|
10329
10838
|
async function gcsFetch(options, method, url, fetchOptions = {}) {
|
|
10330
10839
|
const headers = {
|
|
@@ -10483,6 +10992,81 @@ async function collectChunks5(source) {
|
|
|
10483
10992
|
}
|
|
10484
10993
|
return out;
|
|
10485
10994
|
}
|
|
10995
|
+
function concatChunks3(chunks, totalSize) {
|
|
10996
|
+
const out = new Uint8Array(totalSize);
|
|
10997
|
+
let offset = 0;
|
|
10998
|
+
for (const chunk of chunks) {
|
|
10999
|
+
out.set(chunk, offset);
|
|
11000
|
+
offset += chunk.byteLength;
|
|
11001
|
+
}
|
|
11002
|
+
return out;
|
|
11003
|
+
}
|
|
11004
|
+
function sliceFromBuffers2(buffers, size) {
|
|
11005
|
+
const out = new Uint8Array(size);
|
|
11006
|
+
let offset = 0;
|
|
11007
|
+
let i = 0;
|
|
11008
|
+
while (offset < size && i < buffers.length) {
|
|
11009
|
+
const chunk = buffers[i];
|
|
11010
|
+
if (chunk === void 0) {
|
|
11011
|
+
i += 1;
|
|
11012
|
+
continue;
|
|
11013
|
+
}
|
|
11014
|
+
const remaining = size - offset;
|
|
11015
|
+
if (chunk.byteLength <= remaining) {
|
|
11016
|
+
out.set(chunk, offset);
|
|
11017
|
+
offset += chunk.byteLength;
|
|
11018
|
+
i += 1;
|
|
11019
|
+
} else {
|
|
11020
|
+
out.set(chunk.subarray(0, remaining), offset);
|
|
11021
|
+
const leftover = chunk.subarray(remaining);
|
|
11022
|
+
const next = buffers.slice(i + 1);
|
|
11023
|
+
next.unshift(leftover);
|
|
11024
|
+
return { bytes: out, remaining: next };
|
|
11025
|
+
}
|
|
11026
|
+
}
|
|
11027
|
+
return { bytes: out.subarray(0, offset), remaining: buffers.slice(i) };
|
|
11028
|
+
}
|
|
11029
|
+
async function gcsSessionFetch(options, sessionUri, fetchOptions = {}) {
|
|
11030
|
+
const headers = {
|
|
11031
|
+
...options.defaultHeaders,
|
|
11032
|
+
...fetchOptions.extraHeaders ?? {},
|
|
11033
|
+
authorization: `Bearer ${options.token}`
|
|
11034
|
+
};
|
|
11035
|
+
const init = { headers, method: fetchOptions.method ?? "PUT" };
|
|
11036
|
+
if (fetchOptions.body !== void 0) {
|
|
11037
|
+
init.body = fetchOptions.body;
|
|
11038
|
+
}
|
|
11039
|
+
const controller = new AbortController();
|
|
11040
|
+
const upstream = fetchOptions.signal ?? null;
|
|
11041
|
+
if (upstream !== null) {
|
|
11042
|
+
if (upstream.aborted) controller.abort(upstream.reason);
|
|
11043
|
+
else upstream.addEventListener("abort", () => controller.abort(upstream.reason));
|
|
11044
|
+
}
|
|
11045
|
+
let timer;
|
|
11046
|
+
if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
|
|
11047
|
+
timer = setTimeout(
|
|
11048
|
+
() => controller.abort(new Error("GCS resumable session request timed out")),
|
|
11049
|
+
options.timeoutMs
|
|
11050
|
+
);
|
|
11051
|
+
}
|
|
11052
|
+
try {
|
|
11053
|
+
return await options.fetch(sessionUri, { ...init, signal: controller.signal });
|
|
11054
|
+
} catch (error) {
|
|
11055
|
+
const safeUrl = redactSessionUrl2(sessionUri);
|
|
11056
|
+
throw new ConnectionError({
|
|
11057
|
+
cause: error,
|
|
11058
|
+
details: { url: safeUrl },
|
|
11059
|
+
message: `GCS resumable session request to ${safeUrl} failed`,
|
|
11060
|
+
retryable: true
|
|
11061
|
+
});
|
|
11062
|
+
} finally {
|
|
11063
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
11064
|
+
}
|
|
11065
|
+
}
|
|
11066
|
+
function redactSessionUrl2(url) {
|
|
11067
|
+
const queryStart = url.indexOf("?");
|
|
11068
|
+
return queryStart === -1 ? url : `${url.slice(0, queryStart)}?<redacted>`;
|
|
11069
|
+
}
|
|
10486
11070
|
|
|
10487
11071
|
// src/providers/local/LocalProvider.ts
|
|
10488
11072
|
var import_node_fs = require("fs");
|
|
@@ -10693,9 +11277,9 @@ async function collectTransferContent(request) {
|
|
|
10693
11277
|
byteLength += clonedChunk.byteLength;
|
|
10694
11278
|
request.reportProgress(byteLength, request.totalBytes);
|
|
10695
11279
|
}
|
|
10696
|
-
return
|
|
11280
|
+
return concatChunks4(chunks, byteLength);
|
|
10697
11281
|
}
|
|
10698
|
-
function
|
|
11282
|
+
function concatChunks4(chunks, byteLength) {
|
|
10699
11283
|
const content = new Uint8Array(byteLength);
|
|
10700
11284
|
let offset = 0;
|
|
10701
11285
|
for (const chunk of chunks) {
|
|
@@ -11269,9 +11853,9 @@ async function collectTransferContent2(request) {
|
|
|
11269
11853
|
byteLength += clonedChunk.byteLength;
|
|
11270
11854
|
request.reportProgress(byteLength, request.totalBytes);
|
|
11271
11855
|
}
|
|
11272
|
-
return
|
|
11856
|
+
return concatChunks5(chunks, byteLength);
|
|
11273
11857
|
}
|
|
11274
|
-
function
|
|
11858
|
+
function concatChunks5(chunks, byteLength) {
|
|
11275
11859
|
const content = new Uint8Array(byteLength);
|
|
11276
11860
|
let offset = 0;
|
|
11277
11861
|
for (const chunk of chunks) {
|
|
@@ -11954,12 +12538,12 @@ function parentOf(path2) {
|
|
|
11954
12538
|
}
|
|
11955
12539
|
|
|
11956
12540
|
// src/providers/web/S3Provider.ts
|
|
11957
|
-
var
|
|
12541
|
+
var import_node_crypto13 = require("crypto");
|
|
11958
12542
|
var import_promises3 = require("fs/promises");
|
|
11959
12543
|
var import_node_path3 = require("path");
|
|
11960
12544
|
|
|
11961
12545
|
// src/providers/web/awsSigv4.ts
|
|
11962
|
-
var
|
|
12546
|
+
var import_node_crypto12 = require("crypto");
|
|
11963
12547
|
function signSigV4(input) {
|
|
11964
12548
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
11965
12549
|
const amzDate = formatAmzDate(now);
|
|
@@ -12029,13 +12613,13 @@ function encodeRfc3986(value) {
|
|
|
12029
12613
|
);
|
|
12030
12614
|
}
|
|
12031
12615
|
function sha256Hex(data) {
|
|
12032
|
-
return (0,
|
|
12616
|
+
return (0, import_node_crypto12.createHash)("sha256").update(data).digest("hex");
|
|
12033
12617
|
}
|
|
12034
12618
|
function hmac(key, data) {
|
|
12035
|
-
return (0,
|
|
12619
|
+
return (0, import_node_crypto12.createHmac)("sha256", key).update(data, "utf8").digest();
|
|
12036
12620
|
}
|
|
12037
12621
|
function hmacHex(key, data) {
|
|
12038
|
-
return (0,
|
|
12622
|
+
return (0, import_node_crypto12.createHmac)("sha256", key).update(data, "utf8").digest("hex");
|
|
12039
12623
|
}
|
|
12040
12624
|
|
|
12041
12625
|
// src/providers/web/S3Provider.ts
|
|
@@ -12061,7 +12645,7 @@ function createFileSystemS3MultipartResumeStore(options) {
|
|
|
12061
12645
|
});
|
|
12062
12646
|
}
|
|
12063
12647
|
const fileFor = (key) => {
|
|
12064
|
-
const hash = (0,
|
|
12648
|
+
const hash = (0, import_node_crypto13.createHash)("sha256").update(`${key.bucket}\0${key.jobId}\0${key.path}`).digest("hex");
|
|
12065
12649
|
return (0, import_node_path3.join)(directory, `${hash}.json`);
|
|
12066
12650
|
};
|
|
12067
12651
|
return {
|
|
@@ -12438,7 +13022,7 @@ var S3TransferOperations = class {
|
|
|
12438
13022
|
const flushPart = async (final) => {
|
|
12439
13023
|
while (bufferSize >= partSize || final && bufferSize > 0) {
|
|
12440
13024
|
const take = final ? bufferSize : partSize;
|
|
12441
|
-
const partBytes =
|
|
13025
|
+
const partBytes = sliceFromBuffers3(buffer, take);
|
|
12442
13026
|
buffer = partBytes.remaining;
|
|
12443
13027
|
bufferSize -= partBytes.bytes.byteLength;
|
|
12444
13028
|
const partUrl = new URL(objectUrl.toString());
|
|
@@ -12627,7 +13211,7 @@ function concat(chunks, totalSize) {
|
|
|
12627
13211
|
}
|
|
12628
13212
|
return out;
|
|
12629
13213
|
}
|
|
12630
|
-
function
|
|
13214
|
+
function sliceFromBuffers3(buffers, size) {
|
|
12631
13215
|
const out = new Uint8Array(size);
|
|
12632
13216
|
let offset = 0;
|
|
12633
13217
|
let i = 0;
|
|
@@ -12659,11 +13243,11 @@ async function abortMultipart(options, objectUrl, uploadId) {
|
|
|
12659
13243
|
}
|
|
12660
13244
|
function buildCompleteMultipartBody(parts) {
|
|
12661
13245
|
const partsXml = parts.map(
|
|
12662
|
-
(part) => `<Part><PartNumber>${String(part.partNumber)}</PartNumber><ETag>${
|
|
13246
|
+
(part) => `<Part><PartNumber>${String(part.partNumber)}</PartNumber><ETag>${escapeXml2(part.etag)}</ETag></Part>`
|
|
12663
13247
|
).join("");
|
|
12664
13248
|
return `<?xml version="1.0" encoding="UTF-8"?><CompleteMultipartUpload>${partsXml}</CompleteMultipartUpload>`;
|
|
12665
13249
|
}
|
|
12666
|
-
function
|
|
13250
|
+
function escapeXml2(value) {
|
|
12667
13251
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
12668
13252
|
}
|
|
12669
13253
|
function parseListObjectsV2(xml, prefix) {
|
|
@@ -12743,7 +13327,7 @@ function getBuiltinCapabilityMatrix() {
|
|
|
12743
13327
|
label: "FTPS"
|
|
12744
13328
|
},
|
|
12745
13329
|
{
|
|
12746
|
-
capabilities:
|
|
13330
|
+
capabilities: createSftpProviderFactory().capabilities,
|
|
12747
13331
|
id: "sftp",
|
|
12748
13332
|
label: "SFTP"
|
|
12749
13333
|
},
|
|
@@ -12812,8 +13396,8 @@ function formatCapabilityMatrixMarkdown(matrix = getBuiltinCapabilityMatrix()) {
|
|
|
12812
13396
|
const c = entry.capabilities;
|
|
12813
13397
|
const yesNo = (value) => value ? "\u2705" : "\u274C";
|
|
12814
13398
|
const sideways = `${yesNo(c.serverSideCopy)} / ${yesNo(c.serverSideMove)}`;
|
|
12815
|
-
const checksums = c.checksum.length === 0 ? "
|
|
12816
|
-
const auth = c.authentication.length === 0 ? "
|
|
13399
|
+
const checksums = c.checksum.length === 0 ? "-" : c.checksum.join(", ");
|
|
13400
|
+
const auth = c.authentication.length === 0 ? "-" : c.authentication.join(", ");
|
|
12817
13401
|
return `| ${entry.label} | ${yesNo(c.list)} | ${yesNo(c.stat)} | ${yesNo(c.readStream)} | ${yesNo(c.writeStream)} | ${yesNo(c.resumeDownload)} | ${yesNo(c.resumeUpload)} | ${sideways} | ${checksums} | ${auth} |`;
|
|
12818
13402
|
});
|
|
12819
13403
|
return [header, divider, ...rows].join("\n");
|
|
@@ -13399,6 +13983,84 @@ function mapFtp550(details) {
|
|
|
13399
13983
|
return new PermissionDeniedError(details);
|
|
13400
13984
|
}
|
|
13401
13985
|
|
|
13986
|
+
// src/protocols/ssh/runSshCommand.ts
|
|
13987
|
+
var import_node_net3 = require("net");
|
|
13988
|
+
var DEFAULT_PORT = 22;
|
|
13989
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
|
|
13990
|
+
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
13991
|
+
var DEFAULT_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
13992
|
+
async function runSshCommand(options) {
|
|
13993
|
+
const {
|
|
13994
|
+
host,
|
|
13995
|
+
port = DEFAULT_PORT,
|
|
13996
|
+
command,
|
|
13997
|
+
auth,
|
|
13998
|
+
transport: transportOptions,
|
|
13999
|
+
connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS,
|
|
14000
|
+
maxOutputBytes = DEFAULT_MAX_OUTPUT_BYTES
|
|
14001
|
+
} = options;
|
|
14002
|
+
const socket = await openTcpSocket(host, port, connectTimeoutMs);
|
|
14003
|
+
const transport = new SshTransportConnection({
|
|
14004
|
+
handshakeTimeoutMs: DEFAULT_HANDSHAKE_TIMEOUT_MS,
|
|
14005
|
+
...transportOptions
|
|
14006
|
+
});
|
|
14007
|
+
try {
|
|
14008
|
+
const handshake = await transport.connect(socket);
|
|
14009
|
+
const authSession = new SshAuthSession(transport);
|
|
14010
|
+
await authSession.authenticate({
|
|
14011
|
+
credential: auth,
|
|
14012
|
+
sessionId: handshake.keyExchange.sessionId
|
|
14013
|
+
});
|
|
14014
|
+
const conn = new SshConnectionManager(transport);
|
|
14015
|
+
const channel = await conn.openExecChannel(command);
|
|
14016
|
+
const pump = conn.start();
|
|
14017
|
+
pump.catch(() => {
|
|
14018
|
+
});
|
|
14019
|
+
const chunks = [];
|
|
14020
|
+
let bytesReceived = 0;
|
|
14021
|
+
try {
|
|
14022
|
+
for await (const chunk of channel.receiveData()) {
|
|
14023
|
+
bytesReceived += chunk.length;
|
|
14024
|
+
if (bytesReceived > maxOutputBytes) {
|
|
14025
|
+
throw new Error(
|
|
14026
|
+
`runSshCommand: stdout exceeded ${maxOutputBytes} bytes (set maxOutputBytes to allow more)`
|
|
14027
|
+
);
|
|
14028
|
+
}
|
|
14029
|
+
chunks.push(chunk);
|
|
14030
|
+
}
|
|
14031
|
+
} finally {
|
|
14032
|
+
channel.close();
|
|
14033
|
+
}
|
|
14034
|
+
const stdout = Buffer.concat(chunks);
|
|
14035
|
+
return {
|
|
14036
|
+
stdout,
|
|
14037
|
+
stdoutText: stdout.toString("utf8"),
|
|
14038
|
+
bytesReceived
|
|
14039
|
+
};
|
|
14040
|
+
} finally {
|
|
14041
|
+
transport.disconnect();
|
|
14042
|
+
}
|
|
14043
|
+
}
|
|
14044
|
+
function openTcpSocket(host, port, timeoutMs) {
|
|
14045
|
+
return new Promise((resolve, reject) => {
|
|
14046
|
+
const socket = (0, import_node_net3.connect)({ host, port });
|
|
14047
|
+
const timer = setTimeout(() => {
|
|
14048
|
+
socket.destroy();
|
|
14049
|
+
reject(
|
|
14050
|
+
new Error(`runSshCommand: TCP connect to ${host}:${port} timed out after ${timeoutMs}ms`)
|
|
14051
|
+
);
|
|
14052
|
+
}, timeoutMs);
|
|
14053
|
+
socket.once("connect", () => {
|
|
14054
|
+
clearTimeout(timer);
|
|
14055
|
+
resolve(socket);
|
|
14056
|
+
});
|
|
14057
|
+
socket.once("error", (error) => {
|
|
14058
|
+
clearTimeout(timer);
|
|
14059
|
+
reject(error);
|
|
14060
|
+
});
|
|
14061
|
+
});
|
|
14062
|
+
}
|
|
14063
|
+
|
|
13402
14064
|
// src/transfers/TransferPlan.ts
|
|
13403
14065
|
function createTransferPlan(input) {
|
|
13404
14066
|
const plan = {
|
|
@@ -14856,9 +15518,9 @@ function deepFreeze(value) {
|
|
|
14856
15518
|
}
|
|
14857
15519
|
|
|
14858
15520
|
// src/mft/webhooks.ts
|
|
14859
|
-
var
|
|
15521
|
+
var import_node_crypto14 = require("crypto");
|
|
14860
15522
|
function signWebhookPayload(payload, secret, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
14861
|
-
const mac = (0,
|
|
15523
|
+
const mac = (0, import_node_crypto14.createHmac)("sha256", secret);
|
|
14862
15524
|
mac.update(`${timestamp}.${payload}`);
|
|
14863
15525
|
return { digest: mac.digest("hex"), timestamp };
|
|
14864
15526
|
}
|
|
@@ -15437,6 +16099,19 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15437
16099
|
const options = { client, route, signal };
|
|
15438
16100
|
return runRoute(options);
|
|
15439
16101
|
};
|
|
16102
|
+
|
|
16103
|
+
// src/utils/mainModule.ts
|
|
16104
|
+
var import_node_url = require("url");
|
|
16105
|
+
function isMainModule(importMetaUrl) {
|
|
16106
|
+
if (typeof process === "undefined" || !process.argv || process.argv.length < 2) {
|
|
16107
|
+
return false;
|
|
16108
|
+
}
|
|
16109
|
+
try {
|
|
16110
|
+
return process.argv[1] === (0, import_node_url.fileURLToPath)(importMetaUrl);
|
|
16111
|
+
} catch {
|
|
16112
|
+
return false;
|
|
16113
|
+
}
|
|
16114
|
+
}
|
|
15440
16115
|
// Annotate the CommonJS export names for ESM import in node:
|
|
15441
16116
|
0 && (module.exports = {
|
|
15442
16117
|
AbortError,
|
|
@@ -15503,7 +16178,6 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15503
16178
|
createLocalProviderFactory,
|
|
15504
16179
|
createMemoryProviderFactory,
|
|
15505
16180
|
createMemoryS3MultipartResumeStore,
|
|
15506
|
-
createNativeSftpProviderFactory,
|
|
15507
16181
|
createOAuthTokenSecretSource,
|
|
15508
16182
|
createOneDriveProviderFactory,
|
|
15509
16183
|
createOutboxRoute,
|
|
@@ -15537,6 +16211,7 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15537
16211
|
inboxFailedPath,
|
|
15538
16212
|
inboxProcessedPath,
|
|
15539
16213
|
isClassicProviderId,
|
|
16214
|
+
isMainModule,
|
|
15540
16215
|
isSensitiveKey,
|
|
15541
16216
|
joinRemotePath,
|
|
15542
16217
|
matchKnownHosts,
|
|
@@ -15569,6 +16244,7 @@ var defaultRunner = ({ client, route, signal }) => {
|
|
|
15569
16244
|
resolveSecret,
|
|
15570
16245
|
runConnectionDiagnostics,
|
|
15571
16246
|
runRoute,
|
|
16247
|
+
runSshCommand,
|
|
15572
16248
|
serializeRemoteManifest,
|
|
15573
16249
|
signWebhookPayload,
|
|
15574
16250
|
sortRemoteEntries,
|