@finueva/drive 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +49 -13
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1108 -29
- package/dist/contract.d.ts +925 -58
- package/dist/core.d.ts +2 -1
- package/dist/core.js +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1108 -29
- package/dist/server.d.ts +3 -3
- package/dist/server.js +366 -31
- package/dist/types.d.ts +2 -2
- package/package.json +6 -2
package/dist/client.js
CHANGED
|
@@ -10,6 +10,7 @@ const DRIVE_API_ERROR_CODES = Object.freeze([
|
|
|
10
10
|
"precondition_failed",
|
|
11
11
|
"idempotency_conflict",
|
|
12
12
|
"quota_exceeded",
|
|
13
|
+
"upload_recovery_required",
|
|
13
14
|
"service_unavailable",
|
|
14
15
|
"internal_error"
|
|
15
16
|
]);
|
|
@@ -24,6 +25,7 @@ const DRIVE_API_ERROR_MESSAGES = Object.freeze({
|
|
|
24
25
|
precondition_failed: "The request precondition failed.",
|
|
25
26
|
idempotency_conflict: "The idempotency key conflicts with an earlier request.",
|
|
26
27
|
quota_exceeded: "The workspace storage quota would be exceeded.",
|
|
28
|
+
upload_recovery_required: "The upload requires recovery before it can continue.",
|
|
27
29
|
service_unavailable: "The service is temporarily unavailable.",
|
|
28
30
|
internal_error: "The request could not be completed."
|
|
29
31
|
});
|
|
@@ -59,13 +61,13 @@ const REQUEST_ID = /^[A-Za-z0-9_-]{16,128}$/;
|
|
|
59
61
|
const COLLECTION_ETAG = /^"drive\.permission-collection\.v1\.(0|[1-9][0-9]*)"$/;
|
|
60
62
|
const GRANT_ETAG$1 = /^"drive\.permission-grant\.v1\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"$/;
|
|
61
63
|
const LIST_ETAG$1 = /^"drive\.permission-list\.v1\.[0-9a-f]{64}"$/;
|
|
62
|
-
async function parseResponse(response, operationSignal, preconditionStatus, successStatuses, guard, allowsQuotaExceeded) {
|
|
64
|
+
async function parseResponse(response, operationSignal, preconditionStatus, successStatuses, guard, allowsQuotaExceeded, allowedErrorCodes) {
|
|
63
65
|
const requestId = safeRequestId(response.headers.get("x-request-id"));
|
|
64
66
|
if (requestId === null) {
|
|
65
67
|
cancelResponse(response);
|
|
66
68
|
throw new DriveError("invalid_response", { status: response.status });
|
|
67
69
|
}
|
|
68
|
-
if (!successStatuses.includes(response.status)) throw await normalizeError(response, requestId, operationSignal, preconditionStatus, allowsQuotaExceeded);
|
|
70
|
+
if (!successStatuses.includes(response.status)) throw await normalizeError(response, requestId, operationSignal, preconditionStatus, allowsQuotaExceeded, allowedErrorCodes);
|
|
69
71
|
const { value } = await parseBoundedJson(response, SUCCESS_BODY_LIMIT, response.status, requestId, operationSignal);
|
|
70
72
|
const status = response.status;
|
|
71
73
|
if (!guard(value, status, response.headers)) throw new DriveError("invalid_response", {
|
|
@@ -79,6 +81,28 @@ async function parseResponse(response, operationSignal, preconditionStatus, succ
|
|
|
79
81
|
validators: readValidators(response.headers)
|
|
80
82
|
});
|
|
81
83
|
}
|
|
84
|
+
async function parseNoContentResponse(response, operationSignal, preconditionStatus, successStatuses, allowedErrorCodes) {
|
|
85
|
+
const requestId = safeRequestId(response.headers.get("x-request-id"));
|
|
86
|
+
if (requestId === null) {
|
|
87
|
+
cancelResponse(response);
|
|
88
|
+
throw new DriveError("invalid_response", { status: response.status });
|
|
89
|
+
}
|
|
90
|
+
if (!successStatuses.includes(response.status)) throw await normalizeError(response, requestId, operationSignal, preconditionStatus, false, allowedErrorCodes);
|
|
91
|
+
const contentLength = response.headers.get("content-length");
|
|
92
|
+
if (response.body !== null || contentLength !== null && contentLength !== "0" || response.headers.has("content-type")) {
|
|
93
|
+
cancelResponse(response);
|
|
94
|
+
throw new DriveError("invalid_response", {
|
|
95
|
+
status: response.status,
|
|
96
|
+
requestId
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return Object.freeze({
|
|
100
|
+
data: void 0,
|
|
101
|
+
status: response.status,
|
|
102
|
+
requestId,
|
|
103
|
+
validators: Object.freeze({})
|
|
104
|
+
});
|
|
105
|
+
}
|
|
82
106
|
function parseCollectionEtag(value) {
|
|
83
107
|
const match = COLLECTION_ETAG.exec(value);
|
|
84
108
|
if (!match) return void 0;
|
|
@@ -92,7 +116,7 @@ function parseGrantEtag(value) {
|
|
|
92
116
|
const grantRevision = Number(match[2]);
|
|
93
117
|
return Number.isSafeInteger(collectionRevision) && Number.isSafeInteger(grantRevision) ? [collectionRevision, grantRevision] : void 0;
|
|
94
118
|
}
|
|
95
|
-
async function normalizeError(response, headerRequestId, signal, preconditionStatus, allowsQuotaExceeded) {
|
|
119
|
+
async function normalizeError(response, headerRequestId, signal, preconditionStatus, allowsQuotaExceeded, allowedErrorCodes) {
|
|
96
120
|
let parsed;
|
|
97
121
|
try {
|
|
98
122
|
parsed = await parseBoundedJson(response, ERROR_BODY_LIMIT, response.status, headerRequestId, signal);
|
|
@@ -114,7 +138,7 @@ async function normalizeError(response, headerRequestId, signal, preconditionSta
|
|
|
114
138
|
requestId: headerRequestId
|
|
115
139
|
});
|
|
116
140
|
const envelope = value.error;
|
|
117
|
-
if (typeof envelope.code !== "string" || !DRIVE_API_ERROR_CODES.includes(envelope.code) || envelope.message !== DRIVE_API_ERROR_MESSAGES[envelope.code] || !REQUEST_ID.test(String(envelope.requestId)) || envelope.requestId !== headerRequestId || !isExactRecord$1(envelope.details, []) || !errorStatusMatches(envelope.code, response.status, preconditionStatus, allowsQuotaExceeded) || parsed.text !== JSON.stringify(value)) return new DriveError("invalid_response", {
|
|
141
|
+
if (typeof envelope.code !== "string" || !DRIVE_API_ERROR_CODES.includes(envelope.code) || envelope.message !== DRIVE_API_ERROR_MESSAGES[envelope.code] || !REQUEST_ID.test(String(envelope.requestId)) || envelope.requestId !== headerRequestId || !isExactRecord$1(envelope.details, []) || !errorStatusMatches(envelope.code, response.status, preconditionStatus, allowsQuotaExceeded, allowedErrorCodes) || parsed.text !== JSON.stringify(value)) return new DriveError("invalid_response", {
|
|
118
142
|
status: response.status,
|
|
119
143
|
requestId: headerRequestId
|
|
120
144
|
});
|
|
@@ -123,7 +147,8 @@ async function normalizeError(response, headerRequestId, signal, preconditionSta
|
|
|
123
147
|
requestId: envelope.requestId
|
|
124
148
|
});
|
|
125
149
|
}
|
|
126
|
-
function errorStatusMatches(code, status, preconditionStatus, allowsQuotaExceeded) {
|
|
150
|
+
function errorStatusMatches(code, status, preconditionStatus, allowsQuotaExceeded, allowedErrorCodes) {
|
|
151
|
+
if (allowedErrorCodes && !allowedErrorCodes.includes(code)) return false;
|
|
127
152
|
if (code === "quota_exceeded" && !allowsQuotaExceeded) return false;
|
|
128
153
|
return {
|
|
129
154
|
authentication_required: [401],
|
|
@@ -133,9 +158,10 @@ function errorStatusMatches(code, status, preconditionStatus, allowsQuotaExceede
|
|
|
133
158
|
invalid_request: [422],
|
|
134
159
|
invalid_pagination_cursor: [422],
|
|
135
160
|
invalid_idempotency_key: [422],
|
|
136
|
-
precondition_failed: [preconditionStatus],
|
|
161
|
+
precondition_failed: preconditionStatus === null ? [] : [preconditionStatus],
|
|
137
162
|
idempotency_conflict: [409],
|
|
138
163
|
quota_exceeded: [409],
|
|
164
|
+
upload_recovery_required: [409],
|
|
139
165
|
service_unavailable: [503],
|
|
140
166
|
internal_error: [500]
|
|
141
167
|
}[code].includes(status);
|
|
@@ -246,6 +272,7 @@ const WORKSPACE_ID$1 = /^drive\.workspace\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-
|
|
|
246
272
|
const VOLUME_ID = /^drive\.volume\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
247
273
|
const GRANT_ID$1 = /^drive\.grant\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
248
274
|
const UPLOAD_ID$1 = /^drive\.upload\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
275
|
+
const VERSION_ID = /^drive\.version\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
249
276
|
const CURSOR$1 = /^drive\.cursor\.v1\.[A-Za-z0-9_-]+$/;
|
|
250
277
|
const AUTH_ID$1 = /^[A-Za-z0-9_-]{1,128}$/;
|
|
251
278
|
const GRANT_ETAG = /^"drive\.permission-grant\.v1\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"$/;
|
|
@@ -268,7 +295,18 @@ const EFFECTIVE_ACTIONS = Object.freeze([
|
|
|
268
295
|
"items:update"
|
|
269
296
|
]);
|
|
270
297
|
const MAX_UPLOAD_BYTE_LENGTH$1 = 33554432e4;
|
|
271
|
-
const
|
|
298
|
+
const UPLOAD_PART_BYTE_LENGTH$2 = 32 * 1024 * 1024;
|
|
299
|
+
const CONTENT_DIGEST$1 = /^sha256:[0-9a-f]{64}$/;
|
|
300
|
+
function isCurrentWorkspace(value) {
|
|
301
|
+
if (!isExactRecord(value, [
|
|
302
|
+
"workspaceId",
|
|
303
|
+
"rootVolumeId",
|
|
304
|
+
"rootItemId",
|
|
305
|
+
"workspaceReference"
|
|
306
|
+
]) || typeof value.workspaceId !== "string" || !WORKSPACE_ID$1.test(value.workspaceId) || typeof value.rootVolumeId !== "string" || !VOLUME_ID.test(value.rootVolumeId) || typeof value.rootItemId !== "string" || !ITEM_ID$1.test(value.rootItemId)) return false;
|
|
307
|
+
const reference = value.workspaceReference;
|
|
308
|
+
return isExactRecord(reference, ["type", "userId"]) && reference.type === "personal" && typeof reference.userId === "string" && AUTH_ID$1.test(reference.userId) || isExactRecord(reference, ["type", "organizationId"]) && reference.type === "organization" && typeof reference.organizationId === "string" && AUTH_ID$1.test(reference.organizationId);
|
|
309
|
+
}
|
|
272
310
|
function isFolderPage(value, workspaceId, parentItemId) {
|
|
273
311
|
if (!(isExactRecord(value, ["items", "nextCursor"]) && Array.isArray(value.items) && value.items.length <= 50 && value.items.every(isFolderItem) && (value.nextCursor === null || typeof value.nextCursor === "string" && value.nextCursor.length <= 699 && CURSOR$1.test(value.nextCursor)))) return false;
|
|
274
312
|
return value.items.every((item) => item.workspaceId === workspaceId && item.parentItemId === parentItemId) && isStrictlyOrdered(value.items, (item) => item.itemId);
|
|
@@ -339,18 +377,93 @@ function isEffectivePermissionResponse(value, targetItemId) {
|
|
|
339
377
|
}
|
|
340
378
|
return true;
|
|
341
379
|
}
|
|
342
|
-
function
|
|
343
|
-
|
|
344
|
-
"
|
|
380
|
+
function isUploadSession(value, expectedByteLength) {
|
|
381
|
+
if (!isExactRecord(value, [
|
|
382
|
+
"itemId",
|
|
383
|
+
"versionId",
|
|
345
384
|
"uploadId",
|
|
346
|
-
"
|
|
347
|
-
"
|
|
348
|
-
"
|
|
349
|
-
"
|
|
350
|
-
"
|
|
385
|
+
"mode",
|
|
386
|
+
"partByteLength",
|
|
387
|
+
"partCount",
|
|
388
|
+
"revision",
|
|
389
|
+
"expiresAtEpochMilliseconds"
|
|
390
|
+
]) || typeof value.itemId !== "string" || !ITEM_ID$1.test(value.itemId) || typeof value.versionId !== "string" || !VERSION_ID.test(value.versionId) || typeof value.uploadId !== "string" || !UPLOAD_ID$1.test(value.uploadId) || value.mode !== "presigned-multipart" && value.mode !== "server-mediated-zero-byte" || value.partByteLength !== UPLOAD_PART_BYTE_LENGTH$2 || !isSafeInteger$1(value.partCount) || value.partCount > 1e4 || value.partCount !== Math.ceil(expectedByteLength / UPLOAD_PART_BYTE_LENGTH$2) || (expectedByteLength === 0 ? value.mode !== "server-mediated-zero-byte" : value.mode !== "presigned-multipart") || !isSafeInteger$1(value.revision) || !isSafeInteger$1(value.expiresAtEpochMilliseconds) || value.expiresAtEpochMilliseconds <= Date.now()) return false;
|
|
391
|
+
Object.freeze(value);
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
function isUploadStatus(value, afterPartNumber, limit) {
|
|
395
|
+
if (!isExactRecord(value, [
|
|
396
|
+
"itemId",
|
|
397
|
+
"versionId",
|
|
398
|
+
"uploadId",
|
|
399
|
+
"mode",
|
|
400
|
+
"lifecycle",
|
|
401
|
+
"revision",
|
|
402
|
+
"partPolicy",
|
|
403
|
+
"confirmedParts",
|
|
404
|
+
"nextAfterPartNumber",
|
|
351
405
|
"expiresAtEpochMilliseconds",
|
|
352
|
-
"
|
|
353
|
-
])
|
|
406
|
+
"ready"
|
|
407
|
+
]) || typeof value.itemId !== "string" || !ITEM_ID$1.test(value.itemId) || typeof value.versionId !== "string" || !VERSION_ID.test(value.versionId) || typeof value.uploadId !== "string" || !UPLOAD_ID$1.test(value.uploadId) || value.mode !== "presigned-multipart" && value.mode !== "server-mediated-zero-byte" || ![
|
|
408
|
+
"initiating",
|
|
409
|
+
"recovery-required",
|
|
410
|
+
"uploading",
|
|
411
|
+
"completing",
|
|
412
|
+
"provider-completed",
|
|
413
|
+
"ready",
|
|
414
|
+
"aborting",
|
|
415
|
+
"aborted"
|
|
416
|
+
].includes(String(value.lifecycle)) || !isSafeInteger$1(value.revision) || !isExactRecord(value.partPolicy, [
|
|
417
|
+
"partByteLength",
|
|
418
|
+
"partCount",
|
|
419
|
+
"maxParts"
|
|
420
|
+
]) || value.partPolicy.partByteLength !== UPLOAD_PART_BYTE_LENGTH$2 || !isSafeInteger$1(value.partPolicy.partCount) || value.partPolicy.partCount > 1e4 || value.partPolicy.maxParts !== 1e4 || (value.mode === "server-mediated-zero-byte" ? value.partPolicy.partCount !== 0 : value.partPolicy.partCount < 1) || !Array.isArray(value.confirmedParts) || value.confirmedParts.length > limit || !isSafeInteger$1(value.expiresAtEpochMilliseconds)) return false;
|
|
421
|
+
let previousPartNumber = afterPartNumber;
|
|
422
|
+
for (const part of value.confirmedParts) {
|
|
423
|
+
if (!isExactRecord(part, ["partNumber", "checksumSha256"]) || !isSafeInteger$1(part.partNumber) || part.partNumber <= previousPartNumber || part.partNumber > value.partPolicy.partCount || !isPartChecksum$1(part.checksumSha256)) return false;
|
|
424
|
+
previousPartNumber = part.partNumber;
|
|
425
|
+
}
|
|
426
|
+
if (!(value.nextAfterPartNumber === null || isSafeInteger$1(value.nextAfterPartNumber) && value.confirmedParts.length === limit && value.nextAfterPartNumber === previousPartNumber) || value.mode === "server-mediated-zero-byte" && (value.confirmedParts.length !== 0 || value.nextAfterPartNumber !== null) || value.lifecycle === "ready" !== (value.ready !== null) || value.ready !== null && (!isReadyUpload(value.ready, { uploadId: value.uploadId }) || value.ready.itemId !== value.itemId || value.ready.versionId !== value.versionId)) return false;
|
|
427
|
+
Object.freeze(value.partPolicy);
|
|
428
|
+
for (const part of value.confirmedParts) Object.freeze(part);
|
|
429
|
+
Object.freeze(value.confirmedParts);
|
|
430
|
+
Object.freeze(value);
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
function isUploadPartDescriptor(value, expectedPartNumber, checksumSha256, allowInsecureHttp) {
|
|
434
|
+
if (!isExactRecord(value, [
|
|
435
|
+
"url",
|
|
436
|
+
"method",
|
|
437
|
+
"headers",
|
|
438
|
+
"partNumber",
|
|
439
|
+
"expectedByteLength",
|
|
440
|
+
"expiresAtEpochMilliseconds"
|
|
441
|
+
]) || !isSignedTransferUrl(value.url, allowInsecureHttp) || value.method !== "PUT" || !isExactRecord(value.headers, ["x-amz-checksum-sha256"]) || value.headers["x-amz-checksum-sha256"] !== checksumSha256 || value.partNumber !== expectedPartNumber || !isSafeInteger$1(value.expectedByteLength) || value.expectedByteLength < 1 || value.expectedByteLength > UPLOAD_PART_BYTE_LENGTH$2 || !isSafeInteger$1(value.expiresAtEpochMilliseconds) || value.expiresAtEpochMilliseconds <= Date.now()) return false;
|
|
442
|
+
Object.freeze(value.headers);
|
|
443
|
+
Object.freeze(value);
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
function isUploadPartResult(value, expectedPartNumber, expectedUploadRevision) {
|
|
447
|
+
if (!isExactRecord(value, ["partNumber", "revision"]) || value.partNumber !== expectedPartNumber || value.revision !== expectedUploadRevision + 1 || !isSafeInteger$1(value.revision)) return false;
|
|
448
|
+
Object.freeze(value);
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
function isReadyUpload(value, expected) {
|
|
452
|
+
if (!isExactRecord(value, [
|
|
453
|
+
"itemId",
|
|
454
|
+
"versionId",
|
|
455
|
+
"uploadId",
|
|
456
|
+
"byteLength",
|
|
457
|
+
"callerDeclaredContentDigest",
|
|
458
|
+
"displayFilename",
|
|
459
|
+
"mediaType",
|
|
460
|
+
"state"
|
|
461
|
+
]) || typeof value.itemId !== "string" || !ITEM_ID$1.test(value.itemId) || typeof value.versionId !== "string" || !VERSION_ID.test(value.versionId) || value.uploadId !== expected.uploadId || !isSafeInteger$1(value.byteLength) || value.byteLength < 0 || value.byteLength > MAX_UPLOAD_BYTE_LENGTH$1 || typeof value.callerDeclaredContentDigest !== "string" || !CONTENT_DIGEST$1.test(value.callerDeclaredContentDigest) || !isSafeFilename$1(value.displayFilename) || !isMediaType$1(value.mediaType) || value.state !== "ready" || expected.byteLength !== void 0 && value.byteLength !== expected.byteLength || expected.callerDeclaredContentDigest !== void 0 && value.callerDeclaredContentDigest !== expected.callerDeclaredContentDigest || expected.displayFilename !== void 0 && value.displayFilename !== expected.displayFilename || expected.mediaType !== void 0 && value.mediaType !== expected.mediaType) return false;
|
|
462
|
+
Object.freeze(value);
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
function isPartChecksum$1(value) {
|
|
466
|
+
return typeof value === "string" && /^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$/.test(value);
|
|
354
467
|
}
|
|
355
468
|
function mutationValidatorsAreCoherent(headers) {
|
|
356
469
|
const collection = headers.get("drive-permission-collection-etag");
|
|
@@ -407,6 +520,19 @@ function isSafeFilename$1(value) {
|
|
|
407
520
|
}
|
|
408
521
|
return true;
|
|
409
522
|
}
|
|
523
|
+
function isMediaType$1(value) {
|
|
524
|
+
return typeof value === "string" && value.length <= 255 && /^[a-z0-9](?:[a-z0-9!#$&^_.+-]{0,125}[a-z0-9])?\/[a-z0-9](?:[a-z0-9!#$&^_.+-]{0,125}[a-z0-9])?$/.test(value);
|
|
525
|
+
}
|
|
526
|
+
function isSignedTransferUrl(value, allowInsecureHttp) {
|
|
527
|
+
if (typeof value !== "string" || value.length < 1 || value.length > 8192 || value !== value.trim()) return false;
|
|
528
|
+
try {
|
|
529
|
+
const url = new URL(value);
|
|
530
|
+
const loopbackHost = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
531
|
+
return (url.protocol === "https:" || url.protocol === "http:" && allowInsecureHttp && loopbackHost) && url.username === "" && url.password === "" && url.hash === "" && url.host !== "";
|
|
532
|
+
} catch {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
410
536
|
function filenameIsReserved$1(value) {
|
|
411
537
|
const base = value.split(".", 1)[0].toUpperCase();
|
|
412
538
|
return value === "." || value === ".." || [
|
|
@@ -446,7 +572,14 @@ const PERMISSION_ACTIONS = Object.freeze([
|
|
|
446
572
|
"items:update"
|
|
447
573
|
]);
|
|
448
574
|
const IDEMPOTENCY_KEY = /^[A-Za-z0-9_-]{16,128}$/;
|
|
575
|
+
const CONTENT_DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
576
|
+
const PART_CHECKSUM = /^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$/;
|
|
449
577
|
const MAX_UPLOAD_BYTE_LENGTH = 33554432e4;
|
|
578
|
+
const UPLOAD_PART_BYTE_LENGTH$1 = 32 * 1024 * 1024;
|
|
579
|
+
function validateCurrentWorkspaceInput(value) {
|
|
580
|
+
const signal = validateSignal(captureRecord(value === void 0 ? {} : value, [], ["signal"]).signal);
|
|
581
|
+
return signal === void 0 ? {} : { signal };
|
|
582
|
+
}
|
|
450
583
|
function validateResourceInput(value) {
|
|
451
584
|
const input = captureRecord(value, ["workspaceId", "itemId"], ["signal"]);
|
|
452
585
|
const workspaceId = input.workspaceId;
|
|
@@ -581,26 +714,133 @@ function validateCreateUploadInput(value) {
|
|
|
581
714
|
"workspaceId",
|
|
582
715
|
"parentItemId",
|
|
583
716
|
"expectedByteLength",
|
|
717
|
+
"displayFilename",
|
|
718
|
+
"mediaType",
|
|
719
|
+
"callerDeclaredContentDigest",
|
|
720
|
+
"protectionClassification",
|
|
584
721
|
"idempotencyKey"
|
|
585
722
|
], ["signal"]);
|
|
586
723
|
const signal = validateSignal(input.signal);
|
|
587
|
-
if (typeof input.workspaceId !== "string" || !WORKSPACE_ID.test(input.workspaceId) || typeof input.parentItemId !== "string" || !ITEM_ID.test(input.parentItemId) || typeof input.expectedByteLength !== "number" || !Number.isSafeInteger(input.expectedByteLength) || input.expectedByteLength < 0 || input.expectedByteLength > MAX_UPLOAD_BYTE_LENGTH || !isIdempotencyKey(input.idempotencyKey)) throw new DriveError("invalid_input");
|
|
724
|
+
if (typeof input.workspaceId !== "string" || !WORKSPACE_ID.test(input.workspaceId) || typeof input.parentItemId !== "string" || !ITEM_ID.test(input.parentItemId) || typeof input.expectedByteLength !== "number" || !Number.isSafeInteger(input.expectedByteLength) || input.expectedByteLength < 0 || input.expectedByteLength > MAX_UPLOAD_BYTE_LENGTH || !isSafeFilename(input.displayFilename) || !isMediaType(input.mediaType) || typeof input.callerDeclaredContentDigest !== "string" || !CONTENT_DIGEST.test(input.callerDeclaredContentDigest) || input.protectionClassification !== "ordinary" && input.protectionClassification !== "opaque-encrypted" || !isIdempotencyKey(input.idempotencyKey)) throw new DriveError("invalid_input");
|
|
588
725
|
return {
|
|
589
726
|
workspaceId: input.workspaceId,
|
|
590
727
|
parentItemId: input.parentItemId,
|
|
591
728
|
expectedByteLength: input.expectedByteLength,
|
|
729
|
+
displayFilename: input.displayFilename,
|
|
730
|
+
mediaType: input.mediaType,
|
|
731
|
+
callerDeclaredContentDigest: input.callerDeclaredContentDigest,
|
|
732
|
+
protectionClassification: input.protectionClassification,
|
|
592
733
|
idempotencyKey: input.idempotencyKey,
|
|
593
734
|
...signal === void 0 ? {} : { signal }
|
|
594
735
|
};
|
|
595
736
|
}
|
|
737
|
+
function validateGetUploadStatusInput(value) {
|
|
738
|
+
const input = captureRecord(value, ["workspaceId", "uploadId"], [
|
|
739
|
+
"afterPartNumber",
|
|
740
|
+
"limit",
|
|
741
|
+
"signal"
|
|
742
|
+
]);
|
|
743
|
+
const upload = validateUploadPath(input.workspaceId, input.uploadId, input.signal);
|
|
744
|
+
if (input.afterPartNumber !== void 0 && (!isSafeInteger(input.afterPartNumber) || input.afterPartNumber > 1e4) || input.limit !== void 0 && (!isSafeInteger(input.limit) || input.limit < 1 || input.limit > 100)) throw new DriveError("invalid_input");
|
|
745
|
+
return {
|
|
746
|
+
...upload,
|
|
747
|
+
...input.afterPartNumber === void 0 ? {} : { afterPartNumber: input.afterPartNumber },
|
|
748
|
+
...input.limit === void 0 ? {} : { limit: input.limit }
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
function validateCreateUploadPartDescriptorInput(value) {
|
|
752
|
+
const input = captureRecord(value, [
|
|
753
|
+
"workspaceId",
|
|
754
|
+
"uploadId",
|
|
755
|
+
"partNumber",
|
|
756
|
+
"checksumSha256"
|
|
757
|
+
], ["signal"]);
|
|
758
|
+
const upload = validateUploadPath(input.workspaceId, input.uploadId, input.signal);
|
|
759
|
+
if (!isPartNumber(input.partNumber) || !isPartChecksum(input.checksumSha256)) throw new DriveError("invalid_input");
|
|
760
|
+
return {
|
|
761
|
+
...upload,
|
|
762
|
+
partNumber: input.partNumber,
|
|
763
|
+
checksumSha256: input.checksumSha256
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function validateRecordUploadPartInput(value) {
|
|
767
|
+
const input = captureRecord(value, [
|
|
768
|
+
"workspaceId",
|
|
769
|
+
"uploadId",
|
|
770
|
+
"partNumber",
|
|
771
|
+
"expectedUploadRevision",
|
|
772
|
+
"byteLength",
|
|
773
|
+
"etag",
|
|
774
|
+
"checksumSha256"
|
|
775
|
+
], ["signal"]);
|
|
776
|
+
const upload = validateUploadPath(input.workspaceId, input.uploadId, input.signal);
|
|
777
|
+
if (!isPartNumber(input.partNumber) || !isSafeInteger(input.expectedUploadRevision) || input.expectedUploadRevision >= Number.MAX_SAFE_INTEGER || !isSafeInteger(input.byteLength) || input.byteLength < 1 || input.byteLength > UPLOAD_PART_BYTE_LENGTH$1 || !isEtag$1(input.etag) || !isPartChecksum(input.checksumSha256)) throw new DriveError("invalid_input");
|
|
778
|
+
return {
|
|
779
|
+
...upload,
|
|
780
|
+
partNumber: input.partNumber,
|
|
781
|
+
expectedUploadRevision: input.expectedUploadRevision,
|
|
782
|
+
byteLength: input.byteLength,
|
|
783
|
+
etag: input.etag,
|
|
784
|
+
checksumSha256: input.checksumSha256
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function validateCompleteUploadInput(value) {
|
|
788
|
+
const input = captureRecord(value, [
|
|
789
|
+
"workspaceId",
|
|
790
|
+
"uploadId",
|
|
791
|
+
"idempotencyKey"
|
|
792
|
+
], ["signal"]);
|
|
793
|
+
const upload = validateUploadPath(input.workspaceId, input.uploadId, input.signal);
|
|
794
|
+
if (!isIdempotencyKey(input.idempotencyKey)) throw new DriveError("invalid_input");
|
|
795
|
+
return {
|
|
796
|
+
...upload,
|
|
797
|
+
idempotencyKey: input.idempotencyKey
|
|
798
|
+
};
|
|
799
|
+
}
|
|
596
800
|
function validateAbortUploadInput(value) {
|
|
597
801
|
const input = captureRecord(value, ["workspaceId", "uploadId"], ["signal"]);
|
|
802
|
+
return validateUploadPath(input.workspaceId, input.uploadId, input.signal);
|
|
803
|
+
}
|
|
804
|
+
function validateUploadFileInput(value) {
|
|
805
|
+
const input = captureRecord(value, [
|
|
806
|
+
"workspaceId",
|
|
807
|
+
"parentItemId",
|
|
808
|
+
"source",
|
|
809
|
+
"displayFilename",
|
|
810
|
+
"idempotencyKey",
|
|
811
|
+
"completionIdempotencyKey"
|
|
812
|
+
], [
|
|
813
|
+
"mediaType",
|
|
814
|
+
"protectionClassification",
|
|
815
|
+
"signal",
|
|
816
|
+
"onProgress"
|
|
817
|
+
]);
|
|
598
818
|
const signal = validateSignal(input.signal);
|
|
599
|
-
|
|
819
|
+
const mediaType = input.mediaType ?? "application/octet-stream";
|
|
820
|
+
const protectionClassification = input.protectionClassification ?? "ordinary";
|
|
821
|
+
let source;
|
|
822
|
+
let byteLength;
|
|
823
|
+
try {
|
|
824
|
+
if (!(input.source instanceof Blob)) throw new DriveError("invalid_input");
|
|
825
|
+
source = input.source;
|
|
826
|
+
byteLength = source.size;
|
|
827
|
+
} catch (error) {
|
|
828
|
+
if (error instanceof DriveError) throw error;
|
|
829
|
+
throw new DriveError("invalid_input");
|
|
830
|
+
}
|
|
831
|
+
if (typeof input.workspaceId !== "string" || !WORKSPACE_ID.test(input.workspaceId) || typeof input.parentItemId !== "string" || !ITEM_ID.test(input.parentItemId) || !Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength > MAX_UPLOAD_BYTE_LENGTH || !isSafeFilename(input.displayFilename) || !isMediaType(mediaType) || protectionClassification !== "ordinary" && protectionClassification !== "opaque-encrypted" || !isIdempotencyKey(input.idempotencyKey) || !isIdempotencyKey(input.completionIdempotencyKey) || input.onProgress !== void 0 && typeof input.onProgress !== "function") throw new DriveError("invalid_input");
|
|
600
832
|
return {
|
|
601
833
|
workspaceId: input.workspaceId,
|
|
602
|
-
|
|
603
|
-
|
|
834
|
+
parentItemId: input.parentItemId,
|
|
835
|
+
source,
|
|
836
|
+
byteLength,
|
|
837
|
+
displayFilename: input.displayFilename,
|
|
838
|
+
mediaType,
|
|
839
|
+
protectionClassification,
|
|
840
|
+
idempotencyKey: input.idempotencyKey,
|
|
841
|
+
completionIdempotencyKey: input.completionIdempotencyKey,
|
|
842
|
+
...signal === void 0 ? {} : { signal },
|
|
843
|
+
...input.onProgress === void 0 ? {} : { onProgress: input.onProgress }
|
|
604
844
|
};
|
|
605
845
|
}
|
|
606
846
|
function resourcePath(input) {
|
|
@@ -687,6 +927,27 @@ function captureArray(value) {
|
|
|
687
927
|
function isIdempotencyKey(value) {
|
|
688
928
|
return typeof value === "string" && IDEMPOTENCY_KEY.test(value);
|
|
689
929
|
}
|
|
930
|
+
function validateUploadPath(workspaceId, uploadId, signalValue) {
|
|
931
|
+
const signal = validateSignal(signalValue);
|
|
932
|
+
if (typeof workspaceId !== "string" || !WORKSPACE_ID.test(workspaceId) || typeof uploadId !== "string" || !UPLOAD_ID.test(uploadId)) throw new DriveError("invalid_input");
|
|
933
|
+
return {
|
|
934
|
+
workspaceId,
|
|
935
|
+
uploadId,
|
|
936
|
+
...signal === void 0 ? {} : { signal }
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
function isPartNumber(value) {
|
|
940
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 && value <= 1e4;
|
|
941
|
+
}
|
|
942
|
+
function isPartChecksum(value) {
|
|
943
|
+
return typeof value === "string" && PART_CHECKSUM.test(value);
|
|
944
|
+
}
|
|
945
|
+
function isEtag$1(value) {
|
|
946
|
+
return typeof value === "string" && value.length >= 1 && value.length <= 1024 && /^[\x20-\x7e]+$/.test(value);
|
|
947
|
+
}
|
|
948
|
+
function isMediaType(value) {
|
|
949
|
+
return typeof value === "string" && value.length <= 255 && /^[a-z0-9](?:[a-z0-9!#$&^_.+-]{0,125}[a-z0-9])?\/[a-z0-9](?:[a-z0-9!#$&^_.+-]{0,125}[a-z0-9])?$/.test(value);
|
|
950
|
+
}
|
|
690
951
|
function isExpiry(value) {
|
|
691
952
|
return value === null || isSafeInteger(value);
|
|
692
953
|
}
|
|
@@ -1146,7 +1407,7 @@ function createClientRuntime(config) {
|
|
|
1146
1407
|
if (typeof runtime.fetchImplementation !== "function" || typeof runtime.getCredential !== "function") throw new DriveError("invalid_input");
|
|
1147
1408
|
return runtime;
|
|
1148
1409
|
}
|
|
1149
|
-
async function execute(runtime, signal, preconditionStatus, send, successStatuses, guard, allowsQuotaExceeded = false) {
|
|
1410
|
+
async function execute(runtime, signal, preconditionStatus, send, successStatuses, guard, allowsQuotaExceeded = false, allowedErrorCodes) {
|
|
1150
1411
|
const operation = createOperationSignal(signal, runtime.timeoutMilliseconds);
|
|
1151
1412
|
try {
|
|
1152
1413
|
const operationSignal = operation.signal;
|
|
@@ -1173,7 +1434,7 @@ async function execute(runtime, signal, preconditionStatus, send, successStatuse
|
|
|
1173
1434
|
signal: operationSignal
|
|
1174
1435
|
});
|
|
1175
1436
|
if (!capturedResponse) throw new DriveError("network_error");
|
|
1176
|
-
return await parseResponse(capturedResponse, operationSignal, preconditionStatus, successStatuses, guard, allowsQuotaExceeded);
|
|
1437
|
+
return guard === null ? await parseNoContentResponse(capturedResponse, operationSignal, preconditionStatus, successStatuses, allowedErrorCodes) : await parseResponse(capturedResponse, operationSignal, preconditionStatus, successStatuses, guard, allowsQuotaExceeded, allowedErrorCodes);
|
|
1177
1438
|
} catch (error) {
|
|
1178
1439
|
if (operation.didTimeout()) throw new DriveError("timeout");
|
|
1179
1440
|
if (error instanceof DriveError) throw error;
|
|
@@ -1253,9 +1514,42 @@ async function raceWithAbort(promise, signal) {
|
|
|
1253
1514
|
}
|
|
1254
1515
|
//#endregion
|
|
1255
1516
|
//#region src/internal/client/operations.ts
|
|
1256
|
-
|
|
1257
|
-
|
|
1517
|
+
const CURRENT_WORKSPACE_ERROR_CODES = Object.freeze([
|
|
1518
|
+
"authentication_required",
|
|
1519
|
+
"identity_authority_unavailable",
|
|
1520
|
+
"permission_denied",
|
|
1521
|
+
"resource_not_found",
|
|
1522
|
+
"invalid_request",
|
|
1523
|
+
"service_unavailable"
|
|
1524
|
+
]);
|
|
1525
|
+
const TRANSFER_ERROR_CODES = Object.freeze([
|
|
1526
|
+
"authentication_required",
|
|
1527
|
+
"identity_authority_unavailable",
|
|
1528
|
+
"permission_denied",
|
|
1529
|
+
"resource_not_found",
|
|
1530
|
+
"invalid_request",
|
|
1531
|
+
"precondition_failed",
|
|
1532
|
+
"upload_recovery_required",
|
|
1533
|
+
"service_unavailable"
|
|
1534
|
+
]);
|
|
1535
|
+
const STATUS_ERROR_CODES = Object.freeze([
|
|
1536
|
+
"authentication_required",
|
|
1537
|
+
"identity_authority_unavailable",
|
|
1538
|
+
"permission_denied",
|
|
1539
|
+
"resource_not_found",
|
|
1540
|
+
"invalid_request",
|
|
1541
|
+
"service_unavailable"
|
|
1542
|
+
]);
|
|
1543
|
+
const CREATE_UPLOAD_ERROR_CODES = Object.freeze([
|
|
1544
|
+
...TRANSFER_ERROR_CODES,
|
|
1545
|
+
"invalid_idempotency_key",
|
|
1546
|
+
"idempotency_conflict",
|
|
1547
|
+
"quota_exceeded"
|
|
1548
|
+
]);
|
|
1549
|
+
const COMPLETE_UPLOAD_ERROR_CODES = Object.freeze([...TRANSFER_ERROR_CODES, "invalid_idempotency_key"]);
|
|
1550
|
+
function createDriveOperations(runtime) {
|
|
1258
1551
|
return Object.freeze({
|
|
1552
|
+
getCurrentWorkspace: (input) => getCurrentWorkspace(runtime, input),
|
|
1259
1553
|
listFolderChildren: (input) => listFolderChildren(runtime, input),
|
|
1260
1554
|
createFolder: (input) => createFolder(runtime, input),
|
|
1261
1555
|
listItemPermissions: (input) => listItemPermissions(runtime, input),
|
|
@@ -1264,9 +1558,19 @@ function createDriveClientInternal(config) {
|
|
|
1264
1558
|
updateItemPermission: (input) => updateItemPermission(runtime, input),
|
|
1265
1559
|
revokeItemPermission: (input) => revokeItemPermission(runtime, input),
|
|
1266
1560
|
createUpload: (input) => createUpload(runtime, input),
|
|
1561
|
+
getUploadStatus: (input) => getUploadStatus(runtime, input),
|
|
1562
|
+
createUploadPartDescriptor: (input) => createUploadPartDescriptor(runtime, input),
|
|
1563
|
+
recordUploadPart: (input) => recordUploadPart(runtime, input),
|
|
1564
|
+
completeUpload: (input) => completeUpload(runtime, input),
|
|
1267
1565
|
abortUpload: (input) => abortUpload(runtime, input)
|
|
1268
1566
|
});
|
|
1269
1567
|
}
|
|
1568
|
+
async function getCurrentWorkspace(runtime, input) {
|
|
1569
|
+
return execute(runtime, validateCurrentWorkspaceInput(input).signal, null, (api, requestOptions) => api.GET("/api/v1/workspaces/current", {
|
|
1570
|
+
...operationOptions("getCurrentWorkspace", { params: {} }),
|
|
1571
|
+
...requestOptions
|
|
1572
|
+
}), [200], isCurrentWorkspace, false, CURRENT_WORKSPACE_ERROR_CODES);
|
|
1573
|
+
}
|
|
1270
1574
|
async function listFolderChildren(runtime, input) {
|
|
1271
1575
|
const validated = validatePageInput(input);
|
|
1272
1576
|
return execute(runtime, validated.signal, 409, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/items/{itemId}/children", {
|
|
@@ -1375,11 +1679,81 @@ async function createUpload(runtime, input) {
|
|
|
1375
1679
|
},
|
|
1376
1680
|
body: {
|
|
1377
1681
|
parentItemId: validated.parentItemId,
|
|
1378
|
-
expectedByteLength: validated.expectedByteLength
|
|
1682
|
+
expectedByteLength: validated.expectedByteLength,
|
|
1683
|
+
displayFilename: validated.displayFilename,
|
|
1684
|
+
mediaType: validated.mediaType,
|
|
1685
|
+
callerDeclaredContentDigest: validated.callerDeclaredContentDigest,
|
|
1686
|
+
protectionClassification: validated.protectionClassification
|
|
1379
1687
|
}
|
|
1380
1688
|
}),
|
|
1381
1689
|
...requestOptions
|
|
1382
|
-
}), [200, 201], (value) =>
|
|
1690
|
+
}), [200, 201], (value) => isUploadSession(value, validated.expectedByteLength), true, CREATE_UPLOAD_ERROR_CODES);
|
|
1691
|
+
}
|
|
1692
|
+
async function getUploadStatus(runtime, input) {
|
|
1693
|
+
const validated = validateGetUploadStatusInput(input);
|
|
1694
|
+
const afterPartNumber = validated.afterPartNumber ?? 0;
|
|
1695
|
+
const limit = validated.limit ?? 100;
|
|
1696
|
+
return execute(runtime, validated.signal, null, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/uploads/{uploadId}", {
|
|
1697
|
+
...operationOptions("getUploadStatus", { params: {
|
|
1698
|
+
path: {
|
|
1699
|
+
workspaceId: validated.workspaceId,
|
|
1700
|
+
uploadId: validated.uploadId
|
|
1701
|
+
},
|
|
1702
|
+
query: {
|
|
1703
|
+
...validated.afterPartNumber === void 0 ? {} : { afterPartNumber: String(validated.afterPartNumber) },
|
|
1704
|
+
...validated.limit === void 0 ? {} : { limit: String(validated.limit) }
|
|
1705
|
+
}
|
|
1706
|
+
} }),
|
|
1707
|
+
...requestOptions
|
|
1708
|
+
}), [200], (value) => isUploadStatus(value, afterPartNumber, limit), false, STATUS_ERROR_CODES);
|
|
1709
|
+
}
|
|
1710
|
+
async function createUploadPartDescriptor(runtime, input) {
|
|
1711
|
+
const validated = validateCreateUploadPartDescriptorInput(input);
|
|
1712
|
+
return execute(runtime, validated.signal, 409, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/uploads/{uploadId}/parts", {
|
|
1713
|
+
...operationOptions("createUploadPartDescriptor", {
|
|
1714
|
+
params: { path: {
|
|
1715
|
+
workspaceId: validated.workspaceId,
|
|
1716
|
+
uploadId: validated.uploadId
|
|
1717
|
+
} },
|
|
1718
|
+
body: {
|
|
1719
|
+
partNumber: validated.partNumber,
|
|
1720
|
+
checksumSha256: validated.checksumSha256
|
|
1721
|
+
}
|
|
1722
|
+
}),
|
|
1723
|
+
...requestOptions
|
|
1724
|
+
}), [200], (value) => isUploadPartDescriptor(value, validated.partNumber, validated.checksumSha256, new URL(runtime.baseUrl).protocol === "http:"), false, TRANSFER_ERROR_CODES);
|
|
1725
|
+
}
|
|
1726
|
+
async function recordUploadPart(runtime, input) {
|
|
1727
|
+
const validated = validateRecordUploadPartInput(input);
|
|
1728
|
+
return execute(runtime, validated.signal, 409, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/uploads/{uploadId}/parts/{partNumber}", {
|
|
1729
|
+
...operationOptions("recordUploadPart", {
|
|
1730
|
+
params: { path: {
|
|
1731
|
+
workspaceId: validated.workspaceId,
|
|
1732
|
+
uploadId: validated.uploadId,
|
|
1733
|
+
partNumber: String(validated.partNumber)
|
|
1734
|
+
} },
|
|
1735
|
+
body: {
|
|
1736
|
+
expectedUploadRevision: validated.expectedUploadRevision,
|
|
1737
|
+
byteLength: validated.byteLength,
|
|
1738
|
+
etag: validated.etag,
|
|
1739
|
+
checksumSha256: validated.checksumSha256
|
|
1740
|
+
}
|
|
1741
|
+
}),
|
|
1742
|
+
...requestOptions
|
|
1743
|
+
}), [200], (value) => isUploadPartResult(value, validated.partNumber, validated.expectedUploadRevision), false, TRANSFER_ERROR_CODES);
|
|
1744
|
+
}
|
|
1745
|
+
async function completeUpload(runtime, input) {
|
|
1746
|
+
const validated = validateCompleteUploadInput(input);
|
|
1747
|
+
return execute(runtime, validated.signal, 409, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/uploads/{uploadId}/complete", {
|
|
1748
|
+
...operationOptions("completeUpload", { params: {
|
|
1749
|
+
path: {
|
|
1750
|
+
workspaceId: validated.workspaceId,
|
|
1751
|
+
uploadId: validated.uploadId
|
|
1752
|
+
},
|
|
1753
|
+
header: { "Idempotency-Key": validated.idempotencyKey }
|
|
1754
|
+
} }),
|
|
1755
|
+
...requestOptions
|
|
1756
|
+
}), [200], (value) => isReadyUpload(value, { uploadId: validated.uploadId }), false, COMPLETE_UPLOAD_ERROR_CODES);
|
|
1383
1757
|
}
|
|
1384
1758
|
async function abortUpload(runtime, input) {
|
|
1385
1759
|
const validated = validateAbortUploadInput(input);
|
|
@@ -1389,12 +1763,717 @@ async function abortUpload(runtime, input) {
|
|
|
1389
1763
|
uploadId: validated.uploadId
|
|
1390
1764
|
} } }),
|
|
1391
1765
|
...requestOptions
|
|
1392
|
-
}), [
|
|
1766
|
+
}), [204], null, false, TRANSFER_ERROR_CODES);
|
|
1767
|
+
}
|
|
1768
|
+
//#endregion
|
|
1769
|
+
//#region ../../node_modules/.pnpm/@noble+hashes@2.3.0/node_modules/@noble/hashes/_u64.js
|
|
1770
|
+
const fromNumH = (n) => n / 2 ** 32 | 0;
|
|
1771
|
+
const fromNumL = (n) => n >>> 0;
|
|
1772
|
+
function setU64FromNum(view, byteOffset, n, isLE) {
|
|
1773
|
+
const h = fromNumH(n);
|
|
1774
|
+
const l = fromNumL(n);
|
|
1775
|
+
view.setUint32(byteOffset, isLE ? l : h, isLE);
|
|
1776
|
+
view.setUint32(byteOffset + 4, isLE ? h : l, isLE);
|
|
1777
|
+
}
|
|
1778
|
+
//#endregion
|
|
1779
|
+
//#region ../../node_modules/.pnpm/@noble+hashes@2.3.0/node_modules/@noble/hashes/utils.js
|
|
1780
|
+
function isBytes(a) {
|
|
1781
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
1782
|
+
}
|
|
1783
|
+
const atitle = (title) => title ? `"${title}" ` : "";
|
|
1784
|
+
function anumber(n, title = "") {
|
|
1785
|
+
if (typeof n !== "number") throw new TypeError(atitle(title) + "expected number, got " + typeof n);
|
|
1786
|
+
if (!Number.isSafeInteger(n) || n < 0) throw new RangeError(atitle(title) + "expected integer >= 0, got " + n);
|
|
1787
|
+
return n;
|
|
1788
|
+
}
|
|
1789
|
+
function abytes(value, length, title = "") {
|
|
1790
|
+
if (isBytes(value) && (length === void 0 || value.length === length)) return value;
|
|
1791
|
+
if (length !== void 0) anumber(length, "length");
|
|
1792
|
+
const bytes = isBytes(value);
|
|
1793
|
+
const ofLen = length !== void 0 ? ` of length ${length}` : "";
|
|
1794
|
+
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
1795
|
+
const message = atitle(title) + "expected Uint8Array" + ofLen + ", got " + got;
|
|
1796
|
+
if (!bytes) throw new TypeError(message);
|
|
1797
|
+
throw new RangeError(message);
|
|
1798
|
+
}
|
|
1799
|
+
const aobject = (value, label) => {
|
|
1800
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError((label === "object" ? "" : `"${label}" `) + "expected object, got type=" + typeof value);
|
|
1801
|
+
};
|
|
1802
|
+
function aexists(instance, checkFinished = true) {
|
|
1803
|
+
if (instance.destroyed) throw new Error("hash was destroyed");
|
|
1804
|
+
if (checkFinished && instance.finished) throw new Error("digest() was already called");
|
|
1805
|
+
}
|
|
1806
|
+
function aoutput(out, instance) {
|
|
1807
|
+
abytes(out, void 0, "output");
|
|
1808
|
+
const min = instance.outputLen;
|
|
1809
|
+
if (!(out.length >= min)) throw new RangeError("\"output\" expected length >= " + min);
|
|
1810
|
+
}
|
|
1811
|
+
function clean(...arrays) {
|
|
1812
|
+
for (let i = 0; i < arrays.length; i++) arrays[i].fill(0);
|
|
1813
|
+
}
|
|
1814
|
+
function createView(arr) {
|
|
1815
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
1816
|
+
}
|
|
1817
|
+
function rotr(word, shift) {
|
|
1818
|
+
return word << 32 - shift | word >>> shift;
|
|
1819
|
+
}
|
|
1820
|
+
const hasHexBuiltin = (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")();
|
|
1821
|
+
const hexes = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
1822
|
+
function bytesToHex(bytes) {
|
|
1823
|
+
abytes(bytes);
|
|
1824
|
+
if (hasHexBuiltin) return bytes.toHex();
|
|
1825
|
+
let hex = "";
|
|
1826
|
+
for (let i = 0; i < bytes.length; i++) hex += hexes[bytes[i]];
|
|
1827
|
+
return hex;
|
|
1828
|
+
}
|
|
1829
|
+
function checkOpts(defaults, opts, title = "opts") {
|
|
1830
|
+
aobject(defaults, "defaults");
|
|
1831
|
+
if (opts !== void 0) aobject(opts, title);
|
|
1832
|
+
return Object.assign(defaults, opts);
|
|
1833
|
+
}
|
|
1834
|
+
function createHasher(hashCons, info = {}) {
|
|
1835
|
+
if (typeof hashCons !== "function") throw new TypeError("\"hashCons\" expected function, got type=" + typeof hashCons);
|
|
1836
|
+
info = checkOpts({}, info, "info");
|
|
1837
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
1838
|
+
const tmp = hashCons(void 0);
|
|
1839
|
+
hashC.outputLen = tmp.outputLen;
|
|
1840
|
+
hashC.blockLen = tmp.blockLen;
|
|
1841
|
+
hashC.canXOF = tmp.canXOF;
|
|
1842
|
+
hashC.create = (opts) => hashCons(opts);
|
|
1843
|
+
Object.assign(hashC, info);
|
|
1844
|
+
return Object.freeze(hashC);
|
|
1845
|
+
}
|
|
1846
|
+
const oidNist = (suffix) => ({ oid: Uint8Array.from([
|
|
1847
|
+
6,
|
|
1848
|
+
9,
|
|
1849
|
+
96,
|
|
1850
|
+
134,
|
|
1851
|
+
72,
|
|
1852
|
+
1,
|
|
1853
|
+
101,
|
|
1854
|
+
3,
|
|
1855
|
+
4,
|
|
1856
|
+
2,
|
|
1857
|
+
suffix
|
|
1858
|
+
]) });
|
|
1859
|
+
//#endregion
|
|
1860
|
+
//#region ../../node_modules/.pnpm/@noble+hashes@2.3.0/node_modules/@noble/hashes/_md.js
|
|
1861
|
+
function Chi(a, b, c) {
|
|
1862
|
+
return a & b ^ ~a & c;
|
|
1863
|
+
}
|
|
1864
|
+
function Maj(a, b, c) {
|
|
1865
|
+
return a & b ^ a & c ^ b & c;
|
|
1866
|
+
}
|
|
1867
|
+
var HashMD = class {
|
|
1868
|
+
blockLen;
|
|
1869
|
+
outputLen;
|
|
1870
|
+
canXOF = false;
|
|
1871
|
+
padOffset;
|
|
1872
|
+
isLE;
|
|
1873
|
+
buffer;
|
|
1874
|
+
view;
|
|
1875
|
+
finished = false;
|
|
1876
|
+
length = 0;
|
|
1877
|
+
pos = 0;
|
|
1878
|
+
destroyed = false;
|
|
1879
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
1880
|
+
this.blockLen = blockLen;
|
|
1881
|
+
this.outputLen = outputLen;
|
|
1882
|
+
this.padOffset = padOffset;
|
|
1883
|
+
this.isLE = isLE;
|
|
1884
|
+
this.buffer = new Uint8Array(blockLen);
|
|
1885
|
+
this.view = createView(this.buffer);
|
|
1886
|
+
}
|
|
1887
|
+
update(data) {
|
|
1888
|
+
aexists(this);
|
|
1889
|
+
abytes(data);
|
|
1890
|
+
const { view, buffer, blockLen } = this;
|
|
1891
|
+
const len = data.length;
|
|
1892
|
+
let processed = false;
|
|
1893
|
+
for (let pos = 0; pos < len;) {
|
|
1894
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
1895
|
+
if (take === blockLen) {
|
|
1896
|
+
const dataView = createView(data);
|
|
1897
|
+
for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
|
|
1898
|
+
processed = true;
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
buffer.set(pos === 0 && take === len ? data : data.subarray(pos, pos + take), this.pos);
|
|
1902
|
+
this.pos += take;
|
|
1903
|
+
pos += take;
|
|
1904
|
+
if (this.pos === blockLen) {
|
|
1905
|
+
this.process(view, 0);
|
|
1906
|
+
this.pos = 0;
|
|
1907
|
+
processed = true;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
this.length += data.length;
|
|
1911
|
+
if (processed) this.roundClean();
|
|
1912
|
+
return this;
|
|
1913
|
+
}
|
|
1914
|
+
digestInto(out) {
|
|
1915
|
+
aexists(this);
|
|
1916
|
+
aoutput(out, this);
|
|
1917
|
+
this.finished = true;
|
|
1918
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
1919
|
+
let { pos } = this;
|
|
1920
|
+
buffer[pos++] = 128;
|
|
1921
|
+
buffer.fill(0, pos);
|
|
1922
|
+
if (this.padOffset > blockLen - pos) {
|
|
1923
|
+
this.process(view, 0);
|
|
1924
|
+
buffer.fill(0);
|
|
1925
|
+
}
|
|
1926
|
+
setU64FromNum(view, blockLen - 8, this.length * 8, isLE);
|
|
1927
|
+
this.process(view, 0);
|
|
1928
|
+
this.roundClean();
|
|
1929
|
+
const oview = out === buffer ? view : createView(out);
|
|
1930
|
+
const len = this.outputLen;
|
|
1931
|
+
const outLen = len / 4;
|
|
1932
|
+
const state = this.get();
|
|
1933
|
+
if (len % 4 || outLen > state.length) throw new Error("invalid outputLen");
|
|
1934
|
+
for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);
|
|
1935
|
+
}
|
|
1936
|
+
digest() {
|
|
1937
|
+
const { buffer, outputLen } = this;
|
|
1938
|
+
this.digestInto(buffer);
|
|
1939
|
+
const res = buffer.slice(0, outputLen);
|
|
1940
|
+
this.destroy();
|
|
1941
|
+
return res;
|
|
1942
|
+
}
|
|
1943
|
+
_cloneIntoMeta(to) {
|
|
1944
|
+
const { buffer, length, finished, destroyed, pos } = this;
|
|
1945
|
+
to.destroyed = destroyed;
|
|
1946
|
+
to.finished = finished;
|
|
1947
|
+
to.length = length;
|
|
1948
|
+
to.pos = pos;
|
|
1949
|
+
if (pos) to.buffer.set(buffer);
|
|
1950
|
+
return to;
|
|
1951
|
+
}
|
|
1952
|
+
clone() {
|
|
1953
|
+
return this._cloneInto();
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
const SHA256_IV = Uint32Array.from([
|
|
1957
|
+
1779033703,
|
|
1958
|
+
3144134277,
|
|
1959
|
+
1013904242,
|
|
1960
|
+
2773480762,
|
|
1961
|
+
1359893119,
|
|
1962
|
+
2600822924,
|
|
1963
|
+
528734635,
|
|
1964
|
+
1541459225
|
|
1965
|
+
]);
|
|
1966
|
+
//#endregion
|
|
1967
|
+
//#region ../../node_modules/.pnpm/@noble+hashes@2.3.0/node_modules/@noble/hashes/sha2.js
|
|
1968
|
+
const SHA256_K = Uint32Array.from([
|
|
1969
|
+
1116352408,
|
|
1970
|
+
1899447441,
|
|
1971
|
+
3049323471,
|
|
1972
|
+
3921009573,
|
|
1973
|
+
961987163,
|
|
1974
|
+
1508970993,
|
|
1975
|
+
2453635748,
|
|
1976
|
+
2870763221,
|
|
1977
|
+
3624381080,
|
|
1978
|
+
310598401,
|
|
1979
|
+
607225278,
|
|
1980
|
+
1426881987,
|
|
1981
|
+
1925078388,
|
|
1982
|
+
2162078206,
|
|
1983
|
+
2614888103,
|
|
1984
|
+
3248222580,
|
|
1985
|
+
3835390401,
|
|
1986
|
+
4022224774,
|
|
1987
|
+
264347078,
|
|
1988
|
+
604807628,
|
|
1989
|
+
770255983,
|
|
1990
|
+
1249150122,
|
|
1991
|
+
1555081692,
|
|
1992
|
+
1996064986,
|
|
1993
|
+
2554220882,
|
|
1994
|
+
2821834349,
|
|
1995
|
+
2952996808,
|
|
1996
|
+
3210313671,
|
|
1997
|
+
3336571891,
|
|
1998
|
+
3584528711,
|
|
1999
|
+
113926993,
|
|
2000
|
+
338241895,
|
|
2001
|
+
666307205,
|
|
2002
|
+
773529912,
|
|
2003
|
+
1294757372,
|
|
2004
|
+
1396182291,
|
|
2005
|
+
1695183700,
|
|
2006
|
+
1986661051,
|
|
2007
|
+
2177026350,
|
|
2008
|
+
2456956037,
|
|
2009
|
+
2730485921,
|
|
2010
|
+
2820302411,
|
|
2011
|
+
3259730800,
|
|
2012
|
+
3345764771,
|
|
2013
|
+
3516065817,
|
|
2014
|
+
3600352804,
|
|
2015
|
+
4094571909,
|
|
2016
|
+
275423344,
|
|
2017
|
+
430227734,
|
|
2018
|
+
506948616,
|
|
2019
|
+
659060556,
|
|
2020
|
+
883997877,
|
|
2021
|
+
958139571,
|
|
2022
|
+
1322822218,
|
|
2023
|
+
1537002063,
|
|
2024
|
+
1747873779,
|
|
2025
|
+
1955562222,
|
|
2026
|
+
2024104815,
|
|
2027
|
+
2227730452,
|
|
2028
|
+
2361852424,
|
|
2029
|
+
2428436474,
|
|
2030
|
+
2756734187,
|
|
2031
|
+
3204031479,
|
|
2032
|
+
3329325298
|
|
2033
|
+
]);
|
|
2034
|
+
const SHA256_W = new Uint32Array(64);
|
|
2035
|
+
var SHA2_32B = class extends HashMD {
|
|
2036
|
+
A = 0;
|
|
2037
|
+
B = 0;
|
|
2038
|
+
C = 0;
|
|
2039
|
+
D = 0;
|
|
2040
|
+
E = 0;
|
|
2041
|
+
F = 0;
|
|
2042
|
+
G = 0;
|
|
2043
|
+
H = 0;
|
|
2044
|
+
constructor(outputLen, IV) {
|
|
2045
|
+
super(64, outputLen, 8, false);
|
|
2046
|
+
this.A = IV[0] | 0;
|
|
2047
|
+
this.B = IV[1] | 0;
|
|
2048
|
+
this.C = IV[2] | 0;
|
|
2049
|
+
this.D = IV[3] | 0;
|
|
2050
|
+
this.E = IV[4] | 0;
|
|
2051
|
+
this.F = IV[5] | 0;
|
|
2052
|
+
this.G = IV[6] | 0;
|
|
2053
|
+
this.H = IV[7] | 0;
|
|
2054
|
+
}
|
|
2055
|
+
get() {
|
|
2056
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
2057
|
+
return [
|
|
2058
|
+
A,
|
|
2059
|
+
B,
|
|
2060
|
+
C,
|
|
2061
|
+
D,
|
|
2062
|
+
E,
|
|
2063
|
+
F,
|
|
2064
|
+
G,
|
|
2065
|
+
H
|
|
2066
|
+
];
|
|
2067
|
+
}
|
|
2068
|
+
set(A, B, C, D, E, F, G, H) {
|
|
2069
|
+
this.A = A | 0;
|
|
2070
|
+
this.B = B | 0;
|
|
2071
|
+
this.C = C | 0;
|
|
2072
|
+
this.D = D | 0;
|
|
2073
|
+
this.E = E | 0;
|
|
2074
|
+
this.F = F | 0;
|
|
2075
|
+
this.G = G | 0;
|
|
2076
|
+
this.H = H | 0;
|
|
2077
|
+
}
|
|
2078
|
+
_cloneInto(to) {
|
|
2079
|
+
(to ||= new this.constructor()).set(...this.get());
|
|
2080
|
+
return this._cloneIntoMeta(to);
|
|
2081
|
+
}
|
|
2082
|
+
process(view, offset) {
|
|
2083
|
+
for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);
|
|
2084
|
+
for (let i = 16; i < 64; i++) {
|
|
2085
|
+
const W15 = SHA256_W[i - 15];
|
|
2086
|
+
const W2 = SHA256_W[i - 2];
|
|
2087
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|
2088
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|
2089
|
+
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
|
|
2090
|
+
}
|
|
2091
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
2092
|
+
for (let i = 0; i < 64; i++) {
|
|
2093
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
2094
|
+
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
2095
|
+
const T2 = (rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22)) + Maj(A, B, C) | 0;
|
|
2096
|
+
H = G;
|
|
2097
|
+
G = F;
|
|
2098
|
+
F = E;
|
|
2099
|
+
E = D + T1 | 0;
|
|
2100
|
+
D = C;
|
|
2101
|
+
C = B;
|
|
2102
|
+
B = A;
|
|
2103
|
+
A = T1 + T2 | 0;
|
|
2104
|
+
}
|
|
2105
|
+
A = A + this.A | 0;
|
|
2106
|
+
B = B + this.B | 0;
|
|
2107
|
+
C = C + this.C | 0;
|
|
2108
|
+
D = D + this.D | 0;
|
|
2109
|
+
E = E + this.E | 0;
|
|
2110
|
+
F = F + this.F | 0;
|
|
2111
|
+
G = G + this.G | 0;
|
|
2112
|
+
H = H + this.H | 0;
|
|
2113
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
2114
|
+
}
|
|
2115
|
+
roundClean() {
|
|
2116
|
+
clean(SHA256_W);
|
|
2117
|
+
}
|
|
2118
|
+
destroy() {
|
|
2119
|
+
this.destroyed = true;
|
|
2120
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
2121
|
+
clean(this.buffer);
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
var _SHA256 = class extends SHA2_32B {
|
|
2125
|
+
constructor() {
|
|
2126
|
+
super(32, SHA256_IV);
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
const sha256 = createHasher(() => new _SHA256(), oidNist(1));
|
|
2130
|
+
//#endregion
|
|
2131
|
+
//#region src/internal/client/upload.ts
|
|
2132
|
+
const UPLOAD_PART_BYTE_LENGTH = 32 * 1024 * 1024;
|
|
2133
|
+
const STATUS_PAGE_LIMIT = 100;
|
|
2134
|
+
const STATUS_PAGE_MAXIMUM = 100;
|
|
2135
|
+
const ABORT_DEADLINE_MILLISECONDS = 5e3;
|
|
2136
|
+
async function uploadFileInternal(runtime, input, controls) {
|
|
2137
|
+
const validated = validateUploadFileInput(input);
|
|
2138
|
+
const totalPartCount = Math.ceil(validated.byteLength / UPLOAD_PART_BYTE_LENGTH);
|
|
2139
|
+
emitProgress(validated.onProgress, "preparing", 0, validated.byteLength, 0, totalPartCount);
|
|
2140
|
+
let session;
|
|
2141
|
+
try {
|
|
2142
|
+
const digests = await hashSource(validated.source, validated.byteLength, totalPartCount, validated.signal);
|
|
2143
|
+
const created = await controls.createUpload({
|
|
2144
|
+
workspaceId: validated.workspaceId,
|
|
2145
|
+
parentItemId: validated.parentItemId,
|
|
2146
|
+
expectedByteLength: validated.byteLength,
|
|
2147
|
+
displayFilename: validated.displayFilename,
|
|
2148
|
+
mediaType: validated.mediaType,
|
|
2149
|
+
callerDeclaredContentDigest: digests.callerDeclaredContentDigest,
|
|
2150
|
+
protectionClassification: validated.protectionClassification,
|
|
2151
|
+
idempotencyKey: validated.idempotencyKey,
|
|
2152
|
+
...validated.signal === void 0 ? {} : { signal: validated.signal }
|
|
2153
|
+
});
|
|
2154
|
+
session = created.data;
|
|
2155
|
+
assertSessionPolicy(session, validated.byteLength, totalPartCount, created.status, created.requestId);
|
|
2156
|
+
const resumed = await readResumeState(controls, validated, session, digests);
|
|
2157
|
+
emitProgress(validated.onProgress, "uploading", resumed.confirmedByteLength, validated.byteLength, resumed.confirmedParts.size, totalPartCount);
|
|
2158
|
+
if (resumed.readyResult) {
|
|
2159
|
+
emitProgress(validated.onProgress, "completing", resumed.confirmedByteLength, validated.byteLength, resumed.confirmedParts.size, totalPartCount);
|
|
2160
|
+
return resumed.readyResult;
|
|
2161
|
+
}
|
|
2162
|
+
let revision = resumed.revision;
|
|
2163
|
+
let confirmedByteLength = resumed.confirmedByteLength;
|
|
2164
|
+
let confirmedPartCount = resumed.confirmedParts.size;
|
|
2165
|
+
if (resumed.lifecycle === "uploading") for (const partNumber of pendingUploadPartNumbers(session.partCount, resumed.confirmedParts)) {
|
|
2166
|
+
const index = partNumber - 1;
|
|
2167
|
+
throwIfCallerAborted(validated.signal);
|
|
2168
|
+
const start = index * session.partByteLength;
|
|
2169
|
+
const end = Math.min(start + session.partByteLength, validated.byteLength);
|
|
2170
|
+
const expectedByteLength = end - start;
|
|
2171
|
+
const checksumSha256 = digests.partChecksums[index];
|
|
2172
|
+
if (!checksumSha256) throw new DriveError("invalid_response");
|
|
2173
|
+
const descriptorResult = await controls.createUploadPartDescriptor({
|
|
2174
|
+
workspaceId: validated.workspaceId,
|
|
2175
|
+
uploadId: session.uploadId,
|
|
2176
|
+
partNumber,
|
|
2177
|
+
checksumSha256,
|
|
2178
|
+
...validated.signal === void 0 ? {} : { signal: validated.signal }
|
|
2179
|
+
});
|
|
2180
|
+
const descriptor = descriptorResult.data;
|
|
2181
|
+
if (descriptor.expectedByteLength !== expectedByteLength || descriptor.expiresAtEpochMilliseconds > session.expiresAtEpochMilliseconds) throw new DriveError("invalid_response", {
|
|
2182
|
+
status: descriptorResult.status,
|
|
2183
|
+
requestId: descriptorResult.requestId
|
|
2184
|
+
});
|
|
2185
|
+
const etag = await putPart(runtime, descriptor, slicePart(validated.source, start, end, expectedByteLength), checksumSha256, validated.signal);
|
|
2186
|
+
revision = await recordPartWithReconciliation(controls, validated, session, partNumber, revision, expectedByteLength, etag, checksumSha256);
|
|
2187
|
+
confirmedByteLength += expectedByteLength;
|
|
2188
|
+
confirmedPartCount += 1;
|
|
2189
|
+
emitProgress(validated.onProgress, "uploading", confirmedByteLength, validated.byteLength, confirmedPartCount, totalPartCount);
|
|
2190
|
+
}
|
|
2191
|
+
emitProgress(validated.onProgress, "completing", confirmedByteLength, validated.byteLength, confirmedPartCount, totalPartCount);
|
|
2192
|
+
return await completeWithReconciliation(controls, validated, session, digests);
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
if (validated.signal?.aborted && session) await bestEffortAbort(controls, validated.workspaceId, session.uploadId);
|
|
2195
|
+
if (error instanceof DriveError) throw error;
|
|
2196
|
+
throw validated.signal?.aborted ? new DriveError("aborted") : new DriveError("invalid_input");
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
function* pendingUploadPartNumbers(partCount, confirmedParts) {
|
|
2200
|
+
for (let partNumber = 1; partNumber <= partCount; partNumber += 1) if (!confirmedParts.has(partNumber)) yield partNumber;
|
|
2201
|
+
}
|
|
2202
|
+
async function readResumeState(controls, input, session, digests) {
|
|
2203
|
+
const confirmedParts = new Set();
|
|
2204
|
+
let confirmedByteLength = 0;
|
|
2205
|
+
let afterPartNumber = 0;
|
|
2206
|
+
let baseline;
|
|
2207
|
+
let baselineRequestId;
|
|
2208
|
+
let readyResult;
|
|
2209
|
+
for (let pageNumber = 0; pageNumber < STATUS_PAGE_MAXIMUM; pageNumber += 1) {
|
|
2210
|
+
const result = await controls.getUploadStatus({
|
|
2211
|
+
workspaceId: input.workspaceId,
|
|
2212
|
+
uploadId: session.uploadId,
|
|
2213
|
+
afterPartNumber,
|
|
2214
|
+
limit: STATUS_PAGE_LIMIT,
|
|
2215
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
2216
|
+
});
|
|
2217
|
+
const status = result.data;
|
|
2218
|
+
assertStatusMatchesSession(status, session);
|
|
2219
|
+
if (baseline) assertSameStatusSnapshot(status, baseline);
|
|
2220
|
+
else {
|
|
2221
|
+
baseline = status;
|
|
2222
|
+
baselineRequestId = result.requestId;
|
|
2223
|
+
}
|
|
2224
|
+
for (const part of status.confirmedParts) {
|
|
2225
|
+
const expectedChecksum = digests.partChecksums[part.partNumber - 1];
|
|
2226
|
+
if (!expectedChecksum || part.checksumSha256 !== expectedChecksum || confirmedParts.has(part.partNumber)) throw new DriveError("invalid_response", {
|
|
2227
|
+
status: result.status,
|
|
2228
|
+
requestId: result.requestId
|
|
2229
|
+
});
|
|
2230
|
+
confirmedParts.add(part.partNumber);
|
|
2231
|
+
confirmedByteLength += expectedPartByteLength(input.byteLength, part.partNumber);
|
|
2232
|
+
}
|
|
2233
|
+
if (status.lifecycle === "ready" && status.ready) {
|
|
2234
|
+
assertReadyMatchesInput(status.ready, session, input, digests, result.requestId);
|
|
2235
|
+
readyResult = resultFromStatus(result, status.ready);
|
|
2236
|
+
}
|
|
2237
|
+
if (status.nextAfterPartNumber === null) break;
|
|
2238
|
+
if (pageNumber === STATUS_PAGE_MAXIMUM - 1) throw new DriveError("invalid_response", {
|
|
2239
|
+
status: result.status,
|
|
2240
|
+
requestId: result.requestId
|
|
2241
|
+
});
|
|
2242
|
+
afterPartNumber = status.nextAfterPartNumber;
|
|
2243
|
+
}
|
|
2244
|
+
if (!baseline) throw new DriveError("invalid_response");
|
|
2245
|
+
if (session.mode === "presigned-multipart" && [
|
|
2246
|
+
"completing",
|
|
2247
|
+
"provider-completed",
|
|
2248
|
+
"ready"
|
|
2249
|
+
].includes(baseline.lifecycle) && confirmedParts.size !== session.partCount) throw new DriveError("invalid_response");
|
|
2250
|
+
failForTerminalStatus(baseline.lifecycle, baselineRequestId);
|
|
2251
|
+
return Object.freeze({
|
|
2252
|
+
revision: baseline.revision,
|
|
2253
|
+
confirmedParts,
|
|
2254
|
+
confirmedByteLength,
|
|
2255
|
+
lifecycle: baseline.lifecycle,
|
|
2256
|
+
...readyResult === void 0 ? {} : { readyResult }
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
async function recordPartWithReconciliation(controls, input, session, partNumber, expectedUploadRevision, byteLength, etag, checksumSha256) {
|
|
2260
|
+
const record = (revision) => controls.recordUploadPart({
|
|
2261
|
+
workspaceId: input.workspaceId,
|
|
2262
|
+
uploadId: session.uploadId,
|
|
2263
|
+
partNumber,
|
|
2264
|
+
expectedUploadRevision: revision,
|
|
2265
|
+
byteLength,
|
|
2266
|
+
etag,
|
|
2267
|
+
checksumSha256,
|
|
2268
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
2269
|
+
});
|
|
2270
|
+
try {
|
|
2271
|
+
return (await record(expectedUploadRevision)).data.revision;
|
|
2272
|
+
} catch (error) {
|
|
2273
|
+
if (!isAmbiguousControlFailure(error) || input.signal?.aborted) throw error;
|
|
2274
|
+
const statusResult = await controls.getUploadStatus({
|
|
2275
|
+
workspaceId: input.workspaceId,
|
|
2276
|
+
uploadId: session.uploadId,
|
|
2277
|
+
afterPartNumber: partNumber - 1,
|
|
2278
|
+
limit: 1,
|
|
2279
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
2280
|
+
});
|
|
2281
|
+
const status = statusResult.data;
|
|
2282
|
+
assertStatusMatchesSession(status, session);
|
|
2283
|
+
failForTerminalStatus(status.lifecycle, statusResult.requestId);
|
|
2284
|
+
const confirmed = status.confirmedParts.find((part) => part.partNumber === partNumber);
|
|
2285
|
+
if (confirmed) {
|
|
2286
|
+
if (confirmed.checksumSha256 !== checksumSha256) throw new DriveError("invalid_response", {
|
|
2287
|
+
status: statusResult.status,
|
|
2288
|
+
requestId: statusResult.requestId
|
|
2289
|
+
});
|
|
2290
|
+
return status.revision;
|
|
2291
|
+
}
|
|
2292
|
+
if (status.lifecycle !== "uploading") throw new DriveError("precondition_failed");
|
|
2293
|
+
return (await record(status.revision)).data.revision;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
async function completeWithReconciliation(controls, input, session, digests) {
|
|
2297
|
+
try {
|
|
2298
|
+
const completed = await controls.completeUpload({
|
|
2299
|
+
workspaceId: input.workspaceId,
|
|
2300
|
+
uploadId: session.uploadId,
|
|
2301
|
+
idempotencyKey: input.completionIdempotencyKey,
|
|
2302
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
2303
|
+
});
|
|
2304
|
+
assertReadyMatchesInput(completed.data, session, input, digests, completed.requestId);
|
|
2305
|
+
return completed;
|
|
2306
|
+
} catch (error) {
|
|
2307
|
+
if (!isAmbiguousControlFailure(error) || input.signal?.aborted) throw error;
|
|
2308
|
+
const statusResult = await controls.getUploadStatus({
|
|
2309
|
+
workspaceId: input.workspaceId,
|
|
2310
|
+
uploadId: session.uploadId,
|
|
2311
|
+
afterPartNumber: 0,
|
|
2312
|
+
limit: 1,
|
|
2313
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
2314
|
+
});
|
|
2315
|
+
const status = statusResult.data;
|
|
2316
|
+
assertStatusMatchesSession(status, session);
|
|
2317
|
+
if (status.lifecycle !== "ready" || !status.ready) throw error;
|
|
2318
|
+
assertReadyMatchesInput(status.ready, session, input, digests, statusResult.requestId);
|
|
2319
|
+
return resultFromStatus(statusResult, status.ready);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
async function hashSource(source, byteLength, partCount, signal) {
|
|
2323
|
+
const fullHash = sha256.create();
|
|
2324
|
+
const partChecksums = [];
|
|
2325
|
+
for (let index = 0; index < partCount; index += 1) {
|
|
2326
|
+
throwIfCallerAborted(signal);
|
|
2327
|
+
const start = index * UPLOAD_PART_BYTE_LENGTH;
|
|
2328
|
+
const end = Math.min(start + UPLOAD_PART_BYTE_LENGTH, byteLength);
|
|
2329
|
+
const part = slicePart(source, start, end, end - start);
|
|
2330
|
+
let buffer;
|
|
2331
|
+
try {
|
|
2332
|
+
buffer = signal ? await raceWithAbort(part.arrayBuffer(), signal) : await part.arrayBuffer();
|
|
2333
|
+
} catch (error) {
|
|
2334
|
+
if (error instanceof DriveError) throw error;
|
|
2335
|
+
throw signal?.aborted ? new DriveError("aborted") : new DriveError("invalid_input");
|
|
2336
|
+
}
|
|
2337
|
+
if (buffer.byteLength !== end - start) throw new DriveError("invalid_input");
|
|
2338
|
+
const bytes = new Uint8Array(buffer);
|
|
2339
|
+
fullHash.update(bytes);
|
|
2340
|
+
partChecksums.push(toBase64(sha256(bytes)));
|
|
2341
|
+
}
|
|
2342
|
+
return Object.freeze({
|
|
2343
|
+
callerDeclaredContentDigest: `sha256:${bytesToHex(fullHash.digest())}`,
|
|
2344
|
+
partChecksums: Object.freeze(partChecksums)
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
async function putPart(runtime, descriptor, part, checksumSha256, callerSignal) {
|
|
2348
|
+
const remainingMilliseconds = descriptor.expiresAtEpochMilliseconds - Date.now();
|
|
2349
|
+
if (remainingMilliseconds < 1) throw new DriveError("invalid_response");
|
|
2350
|
+
const operation = createOperationSignal(callerSignal, Math.min(runtime.timeoutMilliseconds, remainingMilliseconds));
|
|
2351
|
+
let response;
|
|
2352
|
+
try {
|
|
2353
|
+
response = await raceWithAbort(runtime.fetchImplementation(descriptor.url, {
|
|
2354
|
+
method: descriptor.method,
|
|
2355
|
+
body: part,
|
|
2356
|
+
headers: descriptor.headers,
|
|
2357
|
+
credentials: "omit",
|
|
2358
|
+
cache: "no-store",
|
|
2359
|
+
redirect: "error",
|
|
2360
|
+
referrerPolicy: "no-referrer",
|
|
2361
|
+
signal: operation.signal
|
|
2362
|
+
}), operation.signal);
|
|
2363
|
+
if (!(response instanceof Response)) throw new DriveError("invalid_response");
|
|
2364
|
+
const etag = response.headers.get("etag");
|
|
2365
|
+
const responseChecksum = response.headers.get("x-amz-checksum-sha256");
|
|
2366
|
+
if (response.status < 200 || response.status > 299 || !isEtag(etag) || responseChecksum !== checksumSha256) throw new DriveError("invalid_response", { status: response.status });
|
|
2367
|
+
return etag;
|
|
2368
|
+
} catch (error) {
|
|
2369
|
+
if (operation.didTimeout()) throw new DriveError("timeout");
|
|
2370
|
+
if (callerSignal?.aborted) throw new DriveError("aborted");
|
|
2371
|
+
if (error instanceof DriveError) throw error;
|
|
2372
|
+
throw new DriveError("network_error");
|
|
2373
|
+
} finally {
|
|
2374
|
+
operation.dispose();
|
|
2375
|
+
response?.body?.cancel().catch(() => {});
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
function assertSessionPolicy(session, byteLength, partCount, status, requestId) {
|
|
2379
|
+
const expectedMode = byteLength === 0 ? "server-mediated-zero-byte" : "presigned-multipart";
|
|
2380
|
+
if (session.mode !== expectedMode || session.partByteLength !== UPLOAD_PART_BYTE_LENGTH || session.partCount !== partCount || session.expiresAtEpochMilliseconds <= Date.now()) throw new DriveError("invalid_response", {
|
|
2381
|
+
status,
|
|
2382
|
+
requestId
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
function assertStatusMatchesSession(status, session) {
|
|
2386
|
+
if (status.itemId !== session.itemId || status.versionId !== session.versionId || status.uploadId !== session.uploadId || status.mode !== session.mode || status.revision < session.revision || status.partPolicy.partByteLength !== session.partByteLength || status.partPolicy.partCount !== session.partCount || status.partPolicy.maxParts !== 1e4 || status.expiresAtEpochMilliseconds !== session.expiresAtEpochMilliseconds) throw new DriveError("invalid_response");
|
|
2387
|
+
}
|
|
2388
|
+
function assertSameStatusSnapshot(status, baseline) {
|
|
2389
|
+
if (status.itemId !== baseline.itemId || status.versionId !== baseline.versionId || status.uploadId !== baseline.uploadId || status.mode !== baseline.mode || status.lifecycle !== baseline.lifecycle || status.revision !== baseline.revision || status.partPolicy.partByteLength !== baseline.partPolicy.partByteLength || status.partPolicy.partCount !== baseline.partPolicy.partCount || status.partPolicy.maxParts !== baseline.partPolicy.maxParts || status.expiresAtEpochMilliseconds !== baseline.expiresAtEpochMilliseconds || JSON.stringify(status.ready) !== JSON.stringify(baseline.ready)) throw new DriveError("invalid_response");
|
|
2390
|
+
}
|
|
2391
|
+
function assertReadyMatchesInput(ready, session, input, digests, requestId) {
|
|
2392
|
+
if (ready.itemId !== session.itemId || ready.versionId !== session.versionId || ready.uploadId !== session.uploadId || ready.byteLength !== input.byteLength || ready.callerDeclaredContentDigest !== digests.callerDeclaredContentDigest || ready.displayFilename !== input.displayFilename || ready.mediaType !== input.mediaType) throw new DriveError("invalid_response", {
|
|
2393
|
+
status: 200,
|
|
2394
|
+
requestId
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2397
|
+
function failForTerminalStatus(lifecycle, requestId) {
|
|
2398
|
+
if (lifecycle === "initiating" || lifecycle === "recovery-required") throw new DriveError("upload_recovery_required", {
|
|
2399
|
+
status: 409,
|
|
2400
|
+
...requestId === void 0 ? {} : { requestId }
|
|
2401
|
+
});
|
|
2402
|
+
if (lifecycle === "aborting" || lifecycle === "aborted") throw new DriveError("precondition_failed", {
|
|
2403
|
+
status: 409,
|
|
2404
|
+
...requestId === void 0 ? {} : { requestId }
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
function resultFromStatus(status, ready) {
|
|
2408
|
+
return Object.freeze({
|
|
2409
|
+
data: ready,
|
|
2410
|
+
status: status.status,
|
|
2411
|
+
requestId: status.requestId,
|
|
2412
|
+
validators: status.validators
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
function expectedPartByteLength(byteLength, partNumber) {
|
|
2416
|
+
return Math.min(UPLOAD_PART_BYTE_LENGTH, byteLength - (partNumber - 1) * UPLOAD_PART_BYTE_LENGTH);
|
|
2417
|
+
}
|
|
2418
|
+
function slicePart(source, start, end, expectedByteLength) {
|
|
2419
|
+
try {
|
|
2420
|
+
const part = source.slice(start, end);
|
|
2421
|
+
if (!(part instanceof Blob) || part.size !== expectedByteLength) throw new DriveError("invalid_input");
|
|
2422
|
+
return part;
|
|
2423
|
+
} catch (error) {
|
|
2424
|
+
if (error instanceof DriveError) throw error;
|
|
2425
|
+
throw new DriveError("invalid_input");
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
function emitProgress(callback, phase, confirmedByteLength, totalByteLength, confirmedPartCount, totalPartCount) {
|
|
2429
|
+
if (!callback) return;
|
|
2430
|
+
const progress = Object.freeze({
|
|
2431
|
+
phase,
|
|
2432
|
+
confirmedByteLength,
|
|
2433
|
+
totalByteLength,
|
|
2434
|
+
confirmedPartCount,
|
|
2435
|
+
totalPartCount
|
|
2436
|
+
});
|
|
2437
|
+
try {
|
|
2438
|
+
callback(progress);
|
|
2439
|
+
} catch {}
|
|
2440
|
+
}
|
|
2441
|
+
async function bestEffortAbort(controls, workspaceId, uploadId) {
|
|
2442
|
+
const controller = new AbortController();
|
|
2443
|
+
const timeout = setTimeout(() => controller.abort(), ABORT_DEADLINE_MILLISECONDS);
|
|
2444
|
+
try {
|
|
2445
|
+
await controls.abortUpload({
|
|
2446
|
+
workspaceId,
|
|
2447
|
+
uploadId,
|
|
2448
|
+
signal: controller.signal
|
|
2449
|
+
});
|
|
2450
|
+
} catch {} finally {
|
|
2451
|
+
clearTimeout(timeout);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
function isAmbiguousControlFailure(value) {
|
|
2455
|
+
return value instanceof DriveError && (value.code === "network_error" || value.code === "timeout" || value.code === "invalid_response");
|
|
2456
|
+
}
|
|
2457
|
+
function throwIfCallerAborted(signal) {
|
|
2458
|
+
if (signal?.aborted) throw new DriveError("aborted");
|
|
2459
|
+
}
|
|
2460
|
+
function toBase64(bytes) {
|
|
2461
|
+
let binary = "";
|
|
2462
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2463
|
+
return btoa(binary);
|
|
2464
|
+
}
|
|
2465
|
+
function isEtag(value) {
|
|
2466
|
+
return value !== null && value.length >= 1 && value.length <= 1024 && /^[\x20-\x7e]+$/.test(value);
|
|
1393
2467
|
}
|
|
1394
2468
|
//#endregion
|
|
1395
2469
|
//#region src/client.ts
|
|
1396
2470
|
function createDriveClient(config) {
|
|
1397
|
-
|
|
2471
|
+
const runtime = createClientRuntime(config);
|
|
2472
|
+
const operations = createDriveOperations(runtime);
|
|
2473
|
+
return Object.freeze({
|
|
2474
|
+
...operations,
|
|
2475
|
+
uploadFile: (input) => uploadFileInternal(runtime, input, operations)
|
|
2476
|
+
});
|
|
1398
2477
|
}
|
|
1399
2478
|
//#endregion
|
|
1400
2479
|
export { createDriveClient };
|