@finueva/drive 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -327,6 +327,101 @@ const MAX_UPLOAD_BYTE_LENGTH$1 = 33554432e4;
327
327
  const UPLOAD_PART_BYTE_LENGTH$2 = 32 * 1024 * 1024;
328
328
  const CONTENT_DIGEST$1 = /^sha256:[0-9a-f]{64}$/;
329
329
  const UTF8 = new TextEncoder();
330
+ function isWorkspaceUsage(value) {
331
+ if (!isExactRecord(value, [
332
+ "workspaceId",
333
+ "provider",
334
+ "usedBytes",
335
+ "reservedBytes",
336
+ "limitBytes",
337
+ "availableBytes",
338
+ "accounting",
339
+ "reservedBytesIncludeUnresolvedUploads",
340
+ "unresolvedReservedBytes",
341
+ "warnings"
342
+ ]) || typeof value.workspaceId !== "string" || !WORKSPACE_ID$1.test(value.workspaceId) || value.provider !== "managed" || value.accounting !== "current-versions-and-reservations" || value.reservedBytesIncludeUnresolvedUploads !== true || !Array.isArray(value.warnings) || value.warnings.length > 1 || value.warnings.some((warning) => warning !== "unresolved-archive-ownership") || ![
343
+ value.usedBytes,
344
+ value.reservedBytes,
345
+ value.limitBytes,
346
+ value.availableBytes,
347
+ value.unresolvedReservedBytes
348
+ ].every((bytes) => typeof bytes === "string" && /^(0|[1-9][0-9]{0,18})$/.test(bytes))) return false;
349
+ const available = BigInt(value.limitBytes) - BigInt(value.usedBytes) - BigInt(value.reservedBytes);
350
+ const valid = BigInt(value.availableBytes) === (available > 0n ? available : 0n) && BigInt(value.unresolvedReservedBytes) <= BigInt(value.reservedBytes) && (value.unresolvedReservedBytes === "0" || value.warnings.length === 1);
351
+ if (valid) Object.freeze(value.warnings);
352
+ return valid;
353
+ }
354
+ function isWorkspaceDiscoveryPage(value) {
355
+ if (!isExactRecord(value, ["items", "nextCursor"]) || !Array.isArray(value.items) || value.items.length > 50 || !(value.nextCursor === null || typeof value.nextCursor === "string" && value.nextCursor.length > 0 && value.nextCursor.length <= 2048 && /^[A-Za-z0-9_.-]+$/.test(value.nextCursor)) || value.items.length === 0 && value.nextCursor !== null) return false;
356
+ const ids = new Set();
357
+ const valid = value.items.every((item) => {
358
+ if (!isExactRecord(item, [
359
+ "workspaceId",
360
+ "itemId",
361
+ "parentId",
362
+ "type",
363
+ "name",
364
+ "lifecycleState",
365
+ "revision",
366
+ "currentVersion",
367
+ "volumeId",
368
+ "provider",
369
+ "createdAtEpochMilliseconds",
370
+ "modifiedAtEpochMilliseconds",
371
+ "creatorUserId",
372
+ "etag",
373
+ "trashedAtEpochMilliseconds",
374
+ "purgeAfterEpochMilliseconds"
375
+ ]) || item.lifecycleState !== "active" && item.lifecycleState !== "trashed" || typeof item.volumeId !== "string" || !VOLUME_ID.test(item.volumeId) || item.provider !== "managed" || item.creatorUserId !== null && (typeof item.creatorUserId !== "string" || !AUTH_ID$1.test(item.creatorUserId)) || ![
376
+ item.createdAtEpochMilliseconds,
377
+ item.modifiedAtEpochMilliseconds,
378
+ item.trashedAtEpochMilliseconds,
379
+ item.purgeAfterEpochMilliseconds
380
+ ].every((date) => date === null || isSafeInteger$1(date))) return false;
381
+ const metadata = {
382
+ workspaceId: item.workspaceId,
383
+ itemId: item.itemId,
384
+ parentId: item.parentId,
385
+ type: item.type,
386
+ name: item.name,
387
+ lifecycleState: "active",
388
+ revision: item.revision,
389
+ currentVersion: item.currentVersion
390
+ };
391
+ if (typeof metadata.workspaceId !== "string" || !WORKSPACE_ID$1.test(metadata.workspaceId) || typeof metadata.itemId !== "string" || !ITEM_ID$1.test(metadata.itemId) || metadata.parentId === null || metadata.parentId === metadata.itemId || !isManagedItemMetadata(metadata, metadata.workspaceId, metadata.itemId) || item.etag !== `"drive.item.v1.${metadata.revision}"` || ids.has(metadata.itemId)) return false;
392
+ if (item.lifecycleState === "active" ? item.trashedAtEpochMilliseconds !== null || item.purgeAfterEpochMilliseconds !== null : !isSafeInteger$1(item.trashedAtEpochMilliseconds) || !isSafeInteger$1(item.purgeAfterEpochMilliseconds) || item.purgeAfterEpochMilliseconds <= item.trashedAtEpochMilliseconds) return false;
393
+ ids.add(metadata.itemId);
394
+ return true;
395
+ });
396
+ if (valid) {
397
+ for (const item of value.items) Object.freeze(item);
398
+ Object.freeze(value.items);
399
+ Object.freeze(value);
400
+ }
401
+ return valid;
402
+ }
403
+ function isCurrentWorkspaceSources(value) {
404
+ if (!isExactRecord(value, [
405
+ "workspace",
406
+ "volumes",
407
+ "knownProviders",
408
+ "externalConnectionsSupported"
409
+ ]) || !isCurrentWorkspace(value.workspace) || !Array.isArray(value.volumes) || value.volumes.length !== 1 || !Array.isArray(value.knownProviders) || value.externalConnectionsSupported !== false) return false;
410
+ const volume = value.volumes[0];
411
+ const providers = [
412
+ "managed",
413
+ "google-drive",
414
+ "onedrive",
415
+ "dropbox",
416
+ "s3-compatible"
417
+ ];
418
+ return isExactRecord(volume, [
419
+ "volumeId",
420
+ "rootItemId",
421
+ "provider",
422
+ "name"
423
+ ]) && volume.volumeId === value.workspace.rootVolumeId && volume.rootItemId === value.workspace.rootItemId && volume.provider === "managed" && volume.name === "Files" && value.knownProviders.length === providers.length && value.knownProviders.every((provider, index) => provider === providers[index]);
424
+ }
330
425
  function isCurrentWorkspace(value) {
331
426
  if (!isExactRecord(value, [
332
427
  "workspaceId",
@@ -584,12 +679,14 @@ function isUploadStatus(value, afterPartNumber, limit) {
584
679
  "nextAfterPartNumber",
585
680
  "expiresAtEpochMilliseconds",
586
681
  "ready"
587
- ]) || typeof value.itemId !== "string" || !ITEM_ID$1.test(value.itemId) || typeof value.versionId !== "string" || !VERSION_ID$1.test(value.versionId) || typeof value.uploadId !== "string" || !UPLOAD_ID$1.test(value.uploadId) || value.mode !== "presigned-multipart" && value.mode !== "server-mediated-zero-byte" || ![
682
+ ], ["verification"]) || typeof value.itemId !== "string" || !ITEM_ID$1.test(value.itemId) || typeof value.versionId !== "string" || !VERSION_ID$1.test(value.versionId) || typeof value.uploadId !== "string" || !UPLOAD_ID$1.test(value.uploadId) || value.mode !== "presigned-multipart" && value.mode !== "server-mediated-zero-byte" || ![
588
683
  "initiating",
589
684
  "recovery-required",
590
685
  "uploading",
591
686
  "completing",
592
687
  "provider-completed",
688
+ "verifying",
689
+ "failed",
593
690
  "ready",
594
691
  "aborting",
595
692
  "aborted"
@@ -599,18 +696,38 @@ function isUploadStatus(value, afterPartNumber, limit) {
599
696
  "maxParts"
600
697
  ]) || 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;
601
698
  let previousPartNumber = afterPartNumber;
699
+ if (value.verification !== void 0) {
700
+ const progress = value.verification;
701
+ if (!isExactRecord(progress, [
702
+ "state",
703
+ "verifiedByteLength",
704
+ "totalByteLength",
705
+ "revision"
706
+ ]) || ![
707
+ "pending",
708
+ "verified",
709
+ "failed"
710
+ ].includes(String(progress.state)) || !isSafeInteger$1(progress.verifiedByteLength) || !isSafeInteger$1(progress.totalByteLength) || progress.totalByteLength > MAX_UPLOAD_BYTE_LENGTH$1 || progress.verifiedByteLength > progress.totalByteLength || !isSafeInteger$1(progress.revision) || progress.state === "verified" && progress.verifiedByteLength !== progress.totalByteLength) return false;
711
+ Object.freeze(progress);
712
+ }
713
+ if (["verifying", "failed"].includes(String(value.lifecycle)) && value.verification === void 0) return false;
602
714
  for (const part of value.confirmedParts) {
603
715
  if (!isExactRecord(part, ["partNumber", "checksumSha256"]) || !isSafeInteger$1(part.partNumber) || part.partNumber <= previousPartNumber || part.partNumber > value.partPolicy.partCount || !isPartChecksum$1(part.checksumSha256)) return false;
604
716
  previousPartNumber = part.partNumber;
605
717
  }
606
- 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;
718
+ 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 || !isExactRecord(value.verification, [
719
+ "state",
720
+ "verifiedByteLength",
721
+ "totalByteLength",
722
+ "revision"
723
+ ]) || value.verification.state !== "verified" || value.verification.totalByteLength !== value.ready.byteLength)) return false;
607
724
  Object.freeze(value.partPolicy);
608
725
  for (const part of value.confirmedParts) Object.freeze(part);
609
726
  Object.freeze(value.confirmedParts);
610
727
  Object.freeze(value);
611
728
  return true;
612
729
  }
613
- function isUploadPartDescriptor(value, expectedPartNumber, checksumSha256, allowInsecureHttp) {
730
+ function isUploadPartDescriptor(value, expectedPartNumber, allowInsecureHttp, contentMd5) {
614
731
  if (!isExactRecord(value, [
615
732
  "url",
616
733
  "method",
@@ -618,7 +735,7 @@ function isUploadPartDescriptor(value, expectedPartNumber, checksumSha256, allow
618
735
  "partNumber",
619
736
  "expectedByteLength",
620
737
  "expiresAtEpochMilliseconds"
621
- ]) || !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;
738
+ ]) || !isSignedTransferUrl(value.url, allowInsecureHttp) || value.method !== "PUT" || !contentMd5 || !isExactRecord(value.headers, ["content-md5"]) || value.headers["content-md5"] !== contentMd5 || 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;
622
739
  Object.freeze(value.headers);
623
740
  Object.freeze(value);
624
741
  return true;
@@ -642,6 +759,19 @@ function isReadyUpload(value, expected) {
642
759
  Object.freeze(value);
643
760
  return true;
644
761
  }
762
+ function isPendingUpload(value, uploadId) {
763
+ if (!isExactRecord(value, [
764
+ "itemId",
765
+ "versionId",
766
+ "uploadId",
767
+ "state",
768
+ "verifiedByteLength",
769
+ "totalByteLength",
770
+ "verificationRevision"
771
+ ]) || typeof value.itemId !== "string" || !ITEM_ID$1.test(value.itemId) || typeof value.versionId !== "string" || !VERSION_ID$1.test(value.versionId) || value.uploadId !== uploadId || value.state !== "verifying" || !isSafeInteger$1(value.verifiedByteLength) || !isSafeInteger$1(value.totalByteLength) || value.totalByteLength > MAX_UPLOAD_BYTE_LENGTH$1 || value.verifiedByteLength > value.totalByteLength || !isSafeInteger$1(value.verificationRevision)) return false;
772
+ Object.freeze(value);
773
+ return true;
774
+ }
645
775
  function isPartChecksum$1(value) {
646
776
  return typeof value === "string" && /^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$/.test(value);
647
777
  }
@@ -781,9 +911,9 @@ function isSafeCapabilityUrl(value) {
781
911
  return false;
782
912
  }
783
913
  }
784
- function isExactRecord(value, keys) {
914
+ function isExactRecord(value, keys, optional = []) {
785
915
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
786
- return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
916
+ return Object.keys(value).every((key) => keys.includes(key) || optional.includes(key)) && keys.every((key) => Object.hasOwn(value, key));
787
917
  }
788
918
  //#endregion
789
919
  //#region src/internal/client/request.ts
@@ -811,6 +941,29 @@ function validateCurrentWorkspaceInput(value) {
811
941
  const signal = validateSignal(captureRecord(value === void 0 ? {} : value, [], ["signal"]).signal);
812
942
  return signal === void 0 ? {} : { signal };
813
943
  }
944
+ function validateWorkspaceDiscoveryInput(value, list) {
945
+ const input = captureRecord(value, ["workspaceId"], list ? [
946
+ "signal",
947
+ "view",
948
+ "kind",
949
+ "creatorUserId",
950
+ "limit",
951
+ "cursor"
952
+ ] : ["signal"]);
953
+ const signal = validateSignal(input.signal);
954
+ if (typeof input.workspaceId !== "string" || !WORKSPACE_ID.test(input.workspaceId) || input.view !== void 0 && input.view !== "recent" && input.view !== "shared" && input.view !== "trash" || input.kind !== void 0 && input.kind !== "file" && input.kind !== "folder" || input.creatorUserId !== void 0 && (typeof input.creatorUserId !== "string" || !AUTH_ID.test(input.creatorUserId)) || input.limit !== void 0 && (typeof input.limit !== "number" || !Number.isInteger(input.limit) || input.limit < 1 || input.limit > 50) || input.cursor !== void 0 && (typeof input.cursor !== "string" || input.cursor.length < 1 || input.cursor.length > 2048 || !/^[A-Za-z0-9_.-]+$/.test(input.cursor))) throw new DriveError("invalid_input");
955
+ return {
956
+ workspaceId: input.workspaceId,
957
+ signal,
958
+ query: {
959
+ ...input.view === void 0 ? {} : { view: input.view },
960
+ ...input.kind === void 0 ? {} : { kind: input.kind },
961
+ ...input.creatorUserId === void 0 ? {} : { creatorUserId: input.creatorUserId },
962
+ ...input.limit === void 0 ? {} : { limit: input.limit },
963
+ ...input.cursor === void 0 ? {} : { cursor: input.cursor }
964
+ }
965
+ };
966
+ }
814
967
  function validateResourceInput(value) {
815
968
  const input = captureRecord(value, ["workspaceId", "itemId"], ["signal"]);
816
969
  const workspaceId = input.workspaceId;
@@ -1112,14 +1265,17 @@ function validateCreateUploadPartDescriptorInput(value) {
1112
1265
  "workspaceId",
1113
1266
  "uploadId",
1114
1267
  "partNumber",
1115
- "checksumSha256"
1268
+ "checksumSha256",
1269
+ "contentMd5"
1116
1270
  ], ["signal"]);
1117
1271
  const upload = validateUploadPath(input.workspaceId, input.uploadId, input.signal);
1118
1272
  if (!isPartNumber(input.partNumber) || !isPartChecksum(input.checksumSha256)) throw new DriveError("invalid_input");
1273
+ if (typeof input.contentMd5 !== "string" || !/^[A-Za-z0-9+/]{21}[AQgw]==$/.test(input.contentMd5)) throw new DriveError("invalid_input");
1119
1274
  return {
1120
1275
  ...upload,
1121
1276
  partNumber: input.partNumber,
1122
- checksumSha256: input.checksumSha256
1277
+ checksumSha256: input.checksumSha256,
1278
+ contentMd5: input.contentMd5
1123
1279
  };
1124
1280
  }
1125
1281
  function validateRecordUploadPartInput(value) {
@@ -1984,6 +2140,9 @@ const DOWNLOAD_ERROR_CODES = Object.freeze([
1984
2140
  function createDriveOperations(runtime) {
1985
2141
  return Object.freeze({
1986
2142
  getCurrentWorkspace: (input) => getCurrentWorkspace(runtime, input),
2143
+ listWorkspaceItems: (input) => listWorkspaceItems(runtime, input),
2144
+ getWorkspaceUsage: (input) => getWorkspaceUsage(runtime, input),
2145
+ getCurrentWorkspaceSources: (input) => getCurrentWorkspaceSources(runtime, input),
1987
2146
  getItem: (input) => getItem(runtime, input),
1988
2147
  searchWorkspaceItems: (input) => searchWorkspaceItems(runtime, input),
1989
2148
  listFolderChildren: (input) => listFolderChildren(runtime, input),
@@ -2182,6 +2341,29 @@ function streamWithOperationLifetime(source, expectedLength, signal) {
2182
2341
  }
2183
2342
  });
2184
2343
  }
2344
+ async function getCurrentWorkspaceSources(runtime, input) {
2345
+ return execute(runtime, validateCurrentWorkspaceInput(input).signal, null, (api, requestOptions) => api.GET("/api/v1/workspaces/current/sources", {
2346
+ ...operationOptions("getCurrentWorkspaceSources", { params: {} }),
2347
+ ...requestOptions
2348
+ }), [200], isCurrentWorkspaceSources, false, CURRENT_WORKSPACE_ERROR_CODES);
2349
+ }
2350
+ async function listWorkspaceItems(runtime, input) {
2351
+ const validated = validateWorkspaceDiscoveryInput(input, true);
2352
+ return execute(runtime, validated.signal, null, (api, options) => api.GET("/api/v1/workspaces/{workspaceId}/items", {
2353
+ ...operationOptions("listWorkspaceItems", { params: {
2354
+ path: { workspaceId: validated.workspaceId },
2355
+ query: validated.query
2356
+ } }),
2357
+ ...options
2358
+ }), [200], (value) => isWorkspaceDiscoveryPage(value) && value.items.length <= (validated.query.limit ?? 25) && value.items.every((item, index, items) => item.workspaceId === validated.workspaceId && item.lifecycleState === (validated.query.view === "trash" ? "trashed" : "active") && ((validated.query.view ?? "recent") !== "recent" || item.modifiedAtEpochMilliseconds !== null) && (validated.query.kind === void 0 || item.type === validated.query.kind) && (validated.query.creatorUserId === void 0 || item.creatorUserId === validated.query.creatorUserId) && (index === 0 || ((validated.query.view ?? "recent") === "recent" ? items[index - 1].modifiedAtEpochMilliseconds > item.modifiedAtEpochMilliseconds || items[index - 1].modifiedAtEpochMilliseconds === item.modifiedAtEpochMilliseconds && items[index - 1].itemId < item.itemId : items[index - 1].itemId < item.itemId))), false, SEARCH_WORKSPACE_ITEMS_ERROR_CODES);
2359
+ }
2360
+ async function getWorkspaceUsage(runtime, input) {
2361
+ const validated = validateWorkspaceDiscoveryInput(input, false);
2362
+ return execute(runtime, validated.signal, null, (api, options) => api.GET("/api/v1/workspaces/{workspaceId}/usage", {
2363
+ ...operationOptions("getWorkspaceUsage", { params: { path: { workspaceId: validated.workspaceId } } }),
2364
+ ...options
2365
+ }), [200], (value) => isWorkspaceUsage(value) && value.workspaceId === validated.workspaceId, false, SEARCH_WORKSPACE_ITEMS_ERROR_CODES);
2366
+ }
2185
2367
  async function getCurrentWorkspace(runtime, input) {
2186
2368
  return execute(runtime, validateCurrentWorkspaceInput(input).signal, null, (api, requestOptions) => api.GET("/api/v1/workspaces/current", {
2187
2369
  ...operationOptions("getCurrentWorkspace", { params: {} }),
@@ -2437,7 +2619,7 @@ async function getUploadStatus(runtime, input) {
2437
2619
  }
2438
2620
  } }),
2439
2621
  ...requestOptions
2440
- }), [200], (value) => isUploadStatus(value, afterPartNumber, limit), false, STATUS_ERROR_CODES);
2622
+ }), [200], (value) => isUploadStatus(value, afterPartNumber, limit) && value.uploadId === validated.uploadId, false, STATUS_ERROR_CODES);
2441
2623
  }
2442
2624
  async function createUploadPartDescriptor(runtime, input) {
2443
2625
  const validated = validateCreateUploadPartDescriptorInput(input);
@@ -2449,11 +2631,12 @@ async function createUploadPartDescriptor(runtime, input) {
2449
2631
  } },
2450
2632
  body: {
2451
2633
  partNumber: validated.partNumber,
2452
- checksumSha256: validated.checksumSha256
2634
+ checksumSha256: validated.checksumSha256,
2635
+ contentMd5: validated.contentMd5
2453
2636
  }
2454
2637
  }),
2455
2638
  ...requestOptions
2456
- }), [200], (value) => isUploadPartDescriptor(value, validated.partNumber, validated.checksumSha256, new URL(runtime.baseUrl).protocol === "http:"), false, TRANSFER_ERROR_CODES);
2639
+ }), [200], (value) => isUploadPartDescriptor(value, validated.partNumber, new URL(runtime.baseUrl).protocol === "http:", validated.contentMd5), false, TRANSFER_ERROR_CODES);
2457
2640
  }
2458
2641
  async function recordUploadPart(runtime, input) {
2459
2642
  const validated = validateRecordUploadPartInput(input);
@@ -2485,7 +2668,7 @@ async function completeUpload(runtime, input) {
2485
2668
  header: { "Idempotency-Key": validated.idempotencyKey }
2486
2669
  } }),
2487
2670
  ...requestOptions
2488
- }), [200], (value) => isReadyUpload(value, { uploadId: validated.uploadId }), false, COMPLETE_UPLOAD_ERROR_CODES);
2671
+ }), [200, 202], (value, status) => status === 202 ? isPendingUpload(value, validated.uploadId) : isReadyUpload(value, { uploadId: validated.uploadId }), false, COMPLETE_UPLOAD_ERROR_CODES);
2489
2672
  }
2490
2673
  async function abortUpload(runtime, input) {
2491
2674
  const validated = validateAbortUploadInput(input);
@@ -2549,6 +2732,9 @@ function createView(arr) {
2549
2732
  function rotr(word, shift) {
2550
2733
  return word << 32 - shift | word >>> shift;
2551
2734
  }
2735
+ function rotl(word, shift) {
2736
+ return word << shift | word >>> 32 - shift >>> 0;
2737
+ }
2552
2738
  const hasHexBuiltin = (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")();
2553
2739
  const hexes = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
2554
2740
  function bytesToHex(bytes) {
@@ -2860,6 +3046,115 @@ var _SHA256 = class extends SHA2_32B {
2860
3046
  };
2861
3047
  const sha256 = createHasher(() => new _SHA256(), oidNist(1));
2862
3048
  //#endregion
3049
+ //#region ../../node_modules/.pnpm/@noble+hashes@2.3.0/node_modules/@noble/hashes/legacy.js
3050
+ const SHA1_IV = Uint32Array.from([
3051
+ 1732584193,
3052
+ 4023233417,
3053
+ 2562383102,
3054
+ 271733878,
3055
+ 3285377520
3056
+ ]);
3057
+ const p32 = Math.pow(2, 32);
3058
+ const K = Array.from({ length: 64 }, (_, i) => Math.floor(p32 * Math.abs(Math.sin(i + 1))));
3059
+ const MD5_IV = SHA1_IV.slice(0, 4);
3060
+ const MD5_W = new Uint32Array(16);
3061
+ const MD5_SHIFTS = (() => {
3062
+ const S = [
3063
+ [
3064
+ 7,
3065
+ 12,
3066
+ 17,
3067
+ 22
3068
+ ],
3069
+ [
3070
+ 5,
3071
+ 9,
3072
+ 14,
3073
+ 20
3074
+ ],
3075
+ [
3076
+ 4,
3077
+ 11,
3078
+ 16,
3079
+ 23
3080
+ ],
3081
+ [
3082
+ 6,
3083
+ 10,
3084
+ 15,
3085
+ 21
3086
+ ]
3087
+ ];
3088
+ return Uint8Array.from({ length: 64 }, (_, i) => S[Math.floor(i / 16)][i % 4]);
3089
+ })();
3090
+ var _MD5 = class extends HashMD {
3091
+ A = MD5_IV[0] | 0;
3092
+ B = MD5_IV[1] | 0;
3093
+ C = MD5_IV[2] | 0;
3094
+ D = MD5_IV[3] | 0;
3095
+ constructor() {
3096
+ super(64, 16, 8, true);
3097
+ }
3098
+ get() {
3099
+ const { A, B, C, D } = this;
3100
+ return [
3101
+ A,
3102
+ B,
3103
+ C,
3104
+ D
3105
+ ];
3106
+ }
3107
+ set(A, B, C, D) {
3108
+ this.A = A | 0;
3109
+ this.B = B | 0;
3110
+ this.C = C | 0;
3111
+ this.D = D | 0;
3112
+ }
3113
+ _cloneInto(to) {
3114
+ (to ||= new this.constructor()).set(...this.get());
3115
+ return this._cloneIntoMeta(to);
3116
+ }
3117
+ process(view, offset) {
3118
+ for (let i = 0; i < 16; i++, offset += 4) MD5_W[i] = view.getUint32(offset, true);
3119
+ let { A, B, C, D } = this;
3120
+ for (let i = 0; i < 64; i++) {
3121
+ let F, g;
3122
+ if (i < 16) {
3123
+ F = Chi(B, C, D);
3124
+ g = i;
3125
+ } else if (i < 32) {
3126
+ F = Chi(D, B, C);
3127
+ g = (5 * i + 1) % 16;
3128
+ } else if (i < 48) {
3129
+ F = B ^ C ^ D;
3130
+ g = (3 * i + 5) % 16;
3131
+ } else {
3132
+ F = C ^ (B | ~D);
3133
+ g = 7 * i % 16;
3134
+ }
3135
+ F = F + A + K[i] + MD5_W[g];
3136
+ A = D;
3137
+ D = C;
3138
+ C = B;
3139
+ B = B + rotl(F, MD5_SHIFTS[i]);
3140
+ }
3141
+ A = A + this.A | 0;
3142
+ B = B + this.B | 0;
3143
+ C = C + this.C | 0;
3144
+ D = D + this.D | 0;
3145
+ this.set(A, B, C, D);
3146
+ }
3147
+ roundClean() {
3148
+ clean(MD5_W);
3149
+ }
3150
+ destroy() {
3151
+ this.destroyed = true;
3152
+ this.set(0, 0, 0, 0);
3153
+ clean(this.buffer);
3154
+ }
3155
+ };
3156
+ const md5 = createHasher(() => new _MD5());
3157
+ //#endregion
2863
3158
  //#region src/internal/client/upload.ts
2864
3159
  const UPLOAD_PART_BYTE_LENGTH = 32 * 1024 * 1024;
2865
3160
  const STATUS_PAGE_LIMIT = 100;
@@ -2907,6 +3202,7 @@ async function uploadFileInternal(runtime, input, controls) {
2907
3202
  uploadId: session.uploadId,
2908
3203
  partNumber,
2909
3204
  checksumSha256,
3205
+ contentMd5: digests.partMd5s[index],
2910
3206
  ...validated.signal === void 0 ? {} : { signal: validated.signal }
2911
3207
  });
2912
3208
  const descriptor = descriptorResult.data;
@@ -2914,14 +3210,16 @@ async function uploadFileInternal(runtime, input, controls) {
2914
3210
  status: descriptorResult.status,
2915
3211
  requestId: descriptorResult.requestId
2916
3212
  });
2917
- const etag = await putPart(runtime, descriptor, slicePart(validated.source, start, end, expectedByteLength), checksumSha256, validated.signal);
3213
+ const etag = await putPart(runtime, descriptor, slicePart(validated.source, start, end, expectedByteLength), validated.signal);
2918
3214
  revision = await recordPartWithReconciliation(controls, validated, session, partNumber, revision, expectedByteLength, etag, checksumSha256);
2919
3215
  confirmedByteLength += expectedByteLength;
2920
3216
  confirmedPartCount += 1;
2921
3217
  emitProgress(validated.onProgress, "uploading", confirmedByteLength, validated.byteLength, confirmedPartCount, totalPartCount);
2922
3218
  }
2923
3219
  emitProgress(validated.onProgress, "completing", confirmedByteLength, validated.byteLength, confirmedPartCount, totalPartCount);
2924
- return await completeWithReconciliation(controls, validated, session, digests);
3220
+ const completed = await completeWithReconciliation(controls, validated, session, digests);
3221
+ if (completed.data.state === "verifying") emitProgress(validated.onProgress, "verifying", confirmedByteLength, validated.byteLength, confirmedPartCount, totalPartCount);
3222
+ return completed;
2925
3223
  } catch (error) {
2926
3224
  if (validated.signal?.aborted && session) await bestEffortAbort(controls, validated.workspaceId, session.uploadId);
2927
3225
  if (error instanceof DriveError) throw error;
@@ -2977,6 +3275,7 @@ async function readResumeState(controls, input, session, digests) {
2977
3275
  if (session.mode === "presigned-multipart" && [
2978
3276
  "completing",
2979
3277
  "provider-completed",
3278
+ "verifying",
2980
3279
  "ready"
2981
3280
  ].includes(baseline.lifecycle) && confirmedParts.size !== session.partCount) throw new DriveError("invalid_response");
2982
3281
  failForTerminalStatus(baseline.lifecycle, baselineRequestId);
@@ -3033,7 +3332,8 @@ async function completeWithReconciliation(controls, input, session, digests) {
3033
3332
  idempotencyKey: input.completionIdempotencyKey,
3034
3333
  ...input.signal === void 0 ? {} : { signal: input.signal }
3035
3334
  });
3036
- assertReadyMatchesInput(completed.data, session, input, digests, completed.requestId);
3335
+ if (completed.data.state === "ready") assertReadyMatchesInput(completed.data, session, input, digests, completed.requestId);
3336
+ else if (completed.data.itemId !== session.itemId || completed.data.versionId !== session.versionId || completed.data.uploadId !== session.uploadId || completed.data.totalByteLength !== input.byteLength) throw new DriveError("invalid_response");
3037
3337
  return completed;
3038
3338
  } catch (error) {
3039
3339
  if (!isAmbiguousControlFailure(error) || input.signal?.aborted) throw error;
@@ -3054,6 +3354,7 @@ async function completeWithReconciliation(controls, input, session, digests) {
3054
3354
  async function hashSource(source, byteLength, partCount, signal) {
3055
3355
  const fullHash = sha256.create();
3056
3356
  const partChecksums = [];
3357
+ const partMd5s = [];
3057
3358
  for (let index = 0; index < partCount; index += 1) {
3058
3359
  throwIfCallerAborted(signal);
3059
3360
  const start = index * UPLOAD_PART_BYTE_LENGTH;
@@ -3070,13 +3371,15 @@ async function hashSource(source, byteLength, partCount, signal) {
3070
3371
  const bytes = new Uint8Array(buffer);
3071
3372
  fullHash.update(bytes);
3072
3373
  partChecksums.push(toBase64(sha256(bytes)));
3374
+ partMd5s.push(toBase64(md5(bytes)));
3073
3375
  }
3074
3376
  return Object.freeze({
3075
3377
  callerDeclaredContentDigest: `sha256:${bytesToHex(fullHash.digest())}`,
3076
- partChecksums: Object.freeze(partChecksums)
3378
+ partChecksums: Object.freeze(partChecksums),
3379
+ partMd5s: Object.freeze(partMd5s)
3077
3380
  });
3078
3381
  }
3079
- async function putPart(runtime, descriptor, part, checksumSha256, callerSignal) {
3382
+ async function putPart(runtime, descriptor, part, callerSignal) {
3080
3383
  const remainingMilliseconds = descriptor.expiresAtEpochMilliseconds - Date.now();
3081
3384
  if (remainingMilliseconds < 1) throw new DriveError("invalid_response");
3082
3385
  const operation = createOperationSignal(callerSignal, Math.min(runtime.timeoutMilliseconds, remainingMilliseconds));
@@ -3094,8 +3397,9 @@ async function putPart(runtime, descriptor, part, checksumSha256, callerSignal)
3094
3397
  }), operation.signal);
3095
3398
  if (!(response instanceof Response)) throw new DriveError("invalid_response");
3096
3399
  const etag = response.headers.get("etag");
3097
- const responseChecksum = response.headers.get("x-amz-checksum-sha256");
3098
- if (response.status < 200 || response.status > 299 || !isEtag(etag) || responseChecksum !== checksumSha256) throw new DriveError("invalid_response", { status: response.status });
3400
+ const md5Response = response.headers.get("content-md5");
3401
+ const checksumMatches = md5Response === null || md5Response === descriptor.headers["content-md5"];
3402
+ if (response.status < 200 || response.status > 299 || !isEtag(etag) || !checksumMatches) throw new DriveError("invalid_response", { status: response.status });
3099
3403
  return etag;
3100
3404
  } catch (error) {
3101
3405
  if (operation.didTimeout()) throw new DriveError("timeout");
@@ -3131,7 +3435,7 @@ function failForTerminalStatus(lifecycle, requestId) {
3131
3435
  status: 409,
3132
3436
  ...requestId === void 0 ? {} : { requestId }
3133
3437
  });
3134
- if (lifecycle === "aborting" || lifecycle === "aborted") throw new DriveError("precondition_failed", {
3438
+ if (lifecycle === "aborting" || lifecycle === "aborted" || lifecycle === "failed") throw new DriveError("precondition_failed", {
3135
3439
  status: 409,
3136
3440
  ...requestId === void 0 ? {} : { requestId }
3137
3441
  });
package/dist/server.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as UpdateItemInput, A as ListItemVersionsInput, C as FolderPage, D as GetUploadStatusInput, E as GetItemVersionInput, F as ManagedItemVersionPage, I as ManagedItemVersionResource, J as RestoreItemInput, K as ReadyUpload, L as ManagedItemVersionRestoreResult, M as ManagedDownloadRestorePending, N as ManagedItemMetadata, O as ListFolderChildrenInput, P as ManagedItemVersion, Q as TrashItemInput, R as ManagedLifecycleItem, S as FolderItem, T as GetItemInput, W as ProtectionClassification, Y as RestoreItemVersionInput, Z as SearchWorkspaceItemsInput, c as CurrentWorkspace, ct as UploadSession, dt as WorkspaceItemSearchPage, ft as operations, h as DriveOperationResult, j as ManagedDownloadDescriptor, l as DeleteItemInput, lt as UploadStatus, m as DriveCredentialProvider, n as CompleteUploadInput, o as CreateUploadInput, ot as UploadPartDescriptor, q as RecordUploadPartInput, r as CreateDownloadInput, s as CreateUploadPartDescriptorInput, st as UploadPartResult, t as AbortUploadInput, tt as UploadConfirmedPart, u as DownloadItemResult, ut as WorkspaceItemSearchItem, v as DriveServerClient, w as GetCurrentWorkspaceInput, y as DriveServerClientConfig } from "./contract.js";
1
+ import { $ as RestoreItemVersionInput, A as ListFolderChildrenInput, B as ManagedItemVersionRestoreResult, C as FolderItem, D as GetItemVersionInput, E as GetItemInput, F as ManagedDownloadRestorePending, H as PendingUpload, I as ManagedItemMetadata, J as ProtectionClassification, L as ManagedItemVersion, M as ListItemVersionsInput, N as ListWorkspaceItemsInput, O as GetUploadStatusInput, P as ManagedDownloadDescriptor, Q as RestoreItemInput, R as ManagedItemVersionPage, T as GetCurrentWorkspaceInput, V as ManagedLifecycleItem, X as ReadyUpload, Z as RecordUploadPartInput, _t as WorkspaceItemSearchItem, at as UploadCompletion, b as DriveServerClientConfig, bt as operations, c as CurrentWorkspace, d as DownloadItemResult, dt as UploadPartDescriptor, ft as UploadPartResult, g as DriveOperationResult, gt as WorkspaceDiscoveryPage, h as DriveCredentialProvider, ht as WorkspaceDiscoveryItem, k as GetWorkspaceUsageInput, l as CurrentWorkspaceSources, mt as UploadStatus, n as CompleteUploadInput, nt as TrashItemInput, o as CreateUploadInput, ot as UploadConfirmedPart, pt as UploadSession, r as CreateDownloadInput, rt as UpdateItemInput, s as CreateUploadPartDescriptorInput, t as AbortUploadInput, tt as SearchWorkspaceItemsInput, u as DeleteItemInput, vt as WorkspaceItemSearchPage, w as FolderPage, y as DriveServerClient, yt as WorkspaceUsage, z as ManagedItemVersionResource } from "./contract.js";
2
2
  //#region src/internal/provisioning.d.ts
3
3
  type Operation = operations["provisionManagedWorkspace"];
4
4
  type ProvisionWorkspaceCommand = Operation["requestBody"]["content"]["application/json"];
@@ -20,4 +20,4 @@ declare function createDriveProvisioningClient(config: DriveProvisioningClientCo
20
20
  //#region src/server.d.ts
21
21
  declare function createDriveServerClient(config: DriveServerClientConfig): DriveServerClient;
22
22
  //#endregion
23
- export { type AbortUploadInput, type CompleteUploadInput, type CreateDownloadInput, type CreateUploadInput, type CreateUploadPartDescriptorInput, type CurrentWorkspace, type DeleteItemInput, type DownloadItemResult, type DriveProvisioningClient, type DriveProvisioningClientConfig, type DriveServerClient, type DriveServerClientConfig, type FolderItem, type FolderPage, type GetCurrentWorkspaceInput, type GetItemInput, type GetItemVersionInput, type GetUploadStatusInput, type ListFolderChildrenInput, type ListItemVersionsInput, type ManagedDownloadDescriptor, type ManagedDownloadRestorePending, type ManagedItemMetadata, type ManagedItemVersion, type ManagedItemVersionPage, type ManagedItemVersionResource, type ManagedItemVersionRestoreResult, type ManagedLifecycleItem, type ProtectionClassification, type ProvisionWorkspaceCommand, type ProvisionWorkspaceResult, type ReadyUpload, type RecordUploadPartInput, type RestoreItemInput, type RestoreItemVersionInput, type SearchWorkspaceItemsInput, type TrashItemInput, type UpdateItemInput, type UploadConfirmedPart, type UploadPartDescriptor, type UploadPartResult, type UploadSession, type UploadStatus, type WorkspaceItemSearchItem, type WorkspaceItemSearchPage, createDriveProvisioningClient, createDriveServerClient };
23
+ export { type AbortUploadInput, type CompleteUploadInput, type CreateDownloadInput, type CreateUploadInput, type CreateUploadPartDescriptorInput, type CurrentWorkspace, type CurrentWorkspaceSources, type DeleteItemInput, type DownloadItemResult, type DriveProvisioningClient, type DriveProvisioningClientConfig, type DriveServerClient, type DriveServerClientConfig, type FolderItem, type FolderPage, type GetCurrentWorkspaceInput, type GetItemInput, type GetItemVersionInput, type GetUploadStatusInput, type GetWorkspaceUsageInput, type ListFolderChildrenInput, type ListItemVersionsInput, type ListWorkspaceItemsInput, type ManagedDownloadDescriptor, type ManagedDownloadRestorePending, type ManagedItemMetadata, type ManagedItemVersion, type ManagedItemVersionPage, type ManagedItemVersionResource, type ManagedItemVersionRestoreResult, type ManagedLifecycleItem, type PendingUpload, type ProtectionClassification, type ProvisionWorkspaceCommand, type ProvisionWorkspaceResult, type ReadyUpload, type RecordUploadPartInput, type RestoreItemInput, type RestoreItemVersionInput, type SearchWorkspaceItemsInput, type TrashItemInput, type UpdateItemInput, type UploadCompletion, type UploadConfirmedPart, type UploadPartDescriptor, type UploadPartResult, type UploadSession, type UploadStatus, type WorkspaceDiscoveryItem, type WorkspaceDiscoveryPage, type WorkspaceItemSearchItem, type WorkspaceItemSearchPage, type WorkspaceUsage, createDriveProvisioningClient, createDriveServerClient };