@air/api-rest 0.1.2 → 0.1.3
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.cjs +100 -44
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +100 -44
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -420,22 +420,23 @@ var Uploads = class {
|
|
|
420
420
|
});
|
|
421
421
|
}
|
|
422
422
|
async uploadFile(params, options = {}) {
|
|
423
|
-
const
|
|
423
|
+
const meta = await resolveFileMeta(params);
|
|
424
424
|
const uploadResponse = await this.create({
|
|
425
|
-
fileName,
|
|
426
|
-
ext,
|
|
427
|
-
size,
|
|
428
|
-
mime,
|
|
425
|
+
fileName: meta.fileName,
|
|
426
|
+
ext: meta.ext,
|
|
427
|
+
size: meta.size,
|
|
428
|
+
mime: meta.mime,
|
|
429
429
|
parentBoardId: options.parentBoardId,
|
|
430
430
|
assetId: options.assetId,
|
|
431
431
|
customFields: options.customFields,
|
|
432
432
|
tags: options.tags
|
|
433
433
|
});
|
|
434
434
|
if (isSmallUpload(uploadResponse)) {
|
|
435
|
-
|
|
435
|
+
const data = await materializeData(meta);
|
|
436
|
+
await this._uploadSmallFile(uploadResponse.uploadUrl, data, options.onProgress, meta.size);
|
|
436
437
|
return { assetId: uploadResponse.assetId, versionId: uploadResponse.versionId };
|
|
437
438
|
}
|
|
438
|
-
await this._uploadLargeFile(uploadResponse,
|
|
439
|
+
await this._uploadLargeFile(uploadResponse, meta, options.onProgress);
|
|
439
440
|
return { assetId: uploadResponse.assetId, versionId: uploadResponse.versionId };
|
|
440
441
|
}
|
|
441
442
|
async _uploadSmallFile(url, data, onProgress, totalSize) {
|
|
@@ -451,42 +452,73 @@ var Uploads = class {
|
|
|
451
452
|
onProgress({ percentage: 100, uploadedBytes: totalSize, totalBytes: totalSize });
|
|
452
453
|
}
|
|
453
454
|
}
|
|
454
|
-
async _uploadLargeFile(uploadInfo,
|
|
455
|
+
async _uploadLargeFile(uploadInfo, meta, onProgress) {
|
|
455
456
|
const { multiPartUploadId, key } = uploadInfo;
|
|
456
|
-
const
|
|
457
|
+
const { size: totalSize } = meta;
|
|
457
458
|
const partCount = Math.ceil(totalSize / DEFAULT_PART_SIZE);
|
|
458
459
|
const parts = [];
|
|
459
460
|
let uploadedBytes = 0;
|
|
460
461
|
const fetchFn = this.client.fetchFn;
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
462
|
+
if (meta.source.type === "path") {
|
|
463
|
+
const fs = await import("fs");
|
|
464
|
+
const fd = fs.openSync(meta.source.filePath, "r");
|
|
465
|
+
try {
|
|
466
|
+
for (let i = 0; i < partCount; i++) {
|
|
467
|
+
const partNumber = i + 1;
|
|
468
|
+
const start = i * DEFAULT_PART_SIZE;
|
|
469
|
+
const end = Math.min(start + DEFAULT_PART_SIZE, totalSize);
|
|
470
|
+
const length = end - start;
|
|
471
|
+
const chunk = Buffer.alloc(length);
|
|
472
|
+
const bytesRead = fs.readSync(fd, chunk, 0, length, start);
|
|
473
|
+
const body = bytesRead < length ? chunk.subarray(0, bytesRead) : chunk;
|
|
474
|
+
const { url } = await this.getPartUploadUrl({ multiPartUploadId, key, partNumber });
|
|
475
|
+
const response = await fetchFn(url, { method: "PUT", body });
|
|
476
|
+
if (!response.ok) {
|
|
477
|
+
throw new Error(`Part ${partNumber} upload failed with status ${response.status}`);
|
|
478
|
+
}
|
|
479
|
+
const etag = response.headers.get("etag");
|
|
480
|
+
if (!etag) {
|
|
481
|
+
throw new Error(`Part ${partNumber} upload did not return an ETag`);
|
|
482
|
+
}
|
|
483
|
+
parts.push({ etag, partNumber });
|
|
484
|
+
uploadedBytes += bytesRead;
|
|
485
|
+
if (onProgress) {
|
|
486
|
+
onProgress({
|
|
487
|
+
percentage: Math.round(uploadedBytes / totalSize * 100),
|
|
488
|
+
uploadedBytes,
|
|
489
|
+
totalBytes: totalSize
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
} finally {
|
|
494
|
+
fs.closeSync(fd);
|
|
477
495
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
496
|
+
} else {
|
|
497
|
+
const buffer = await materializeData(meta);
|
|
498
|
+
const buf = buffer instanceof Buffer ? buffer : Buffer.from(buffer);
|
|
499
|
+
for (let i = 0; i < partCount; i++) {
|
|
500
|
+
const partNumber = i + 1;
|
|
501
|
+
const start = i * DEFAULT_PART_SIZE;
|
|
502
|
+
const end = Math.min(start + DEFAULT_PART_SIZE, totalSize);
|
|
503
|
+
const chunk = buf.subarray(start, end);
|
|
504
|
+
const { url } = await this.getPartUploadUrl({ multiPartUploadId, key, partNumber });
|
|
505
|
+
const response = await fetchFn(url, { method: "PUT", body: chunk });
|
|
506
|
+
if (!response.ok) {
|
|
507
|
+
throw new Error(`Part ${partNumber} upload failed with status ${response.status}`);
|
|
508
|
+
}
|
|
509
|
+
const etag = response.headers.get("etag");
|
|
510
|
+
if (!etag) {
|
|
511
|
+
throw new Error(`Part ${partNumber} upload did not return an ETag`);
|
|
512
|
+
}
|
|
513
|
+
parts.push({ etag, partNumber });
|
|
514
|
+
uploadedBytes += chunk.byteLength;
|
|
515
|
+
if (onProgress) {
|
|
516
|
+
onProgress({
|
|
517
|
+
percentage: Math.round(uploadedBytes / totalSize * 100),
|
|
518
|
+
uploadedBytes,
|
|
519
|
+
totalBytes: totalSize
|
|
520
|
+
});
|
|
521
|
+
}
|
|
490
522
|
}
|
|
491
523
|
}
|
|
492
524
|
await this.completeMultipart({ multiPartUploadId, key, parts });
|
|
@@ -495,16 +527,22 @@ var Uploads = class {
|
|
|
495
527
|
function isSmallUpload(response) {
|
|
496
528
|
return "uploadUrl" in response;
|
|
497
529
|
}
|
|
498
|
-
async function
|
|
530
|
+
async function resolveFileMeta(params) {
|
|
499
531
|
if (params.filePath) {
|
|
500
532
|
const fs = await import("fs");
|
|
501
533
|
const path = await import("path");
|
|
502
|
-
const
|
|
534
|
+
const stat = fs.statSync(params.filePath);
|
|
503
535
|
const parsed = path.parse(params.filePath);
|
|
504
536
|
const fileName = params.fileName ?? parsed.name;
|
|
505
537
|
const ext = params.ext ?? parsed.ext.replace(/^\./, "");
|
|
506
538
|
const mime = params.mime ?? guessMime(ext);
|
|
507
|
-
return {
|
|
539
|
+
return {
|
|
540
|
+
source: { type: "path", filePath: params.filePath },
|
|
541
|
+
fileName,
|
|
542
|
+
ext,
|
|
543
|
+
mime,
|
|
544
|
+
size: stat.size
|
|
545
|
+
};
|
|
508
546
|
}
|
|
509
547
|
if (params.buffer) {
|
|
510
548
|
const data = params.buffer instanceof Buffer ? params.buffer : Buffer.from(params.buffer);
|
|
@@ -513,7 +551,7 @@ async function resolveFileData(params) {
|
|
|
513
551
|
}
|
|
514
552
|
const mime = params.mime ?? guessMime(params.ext);
|
|
515
553
|
return {
|
|
516
|
-
|
|
554
|
+
source: { type: "buffer", data },
|
|
517
555
|
fileName: params.fileName,
|
|
518
556
|
ext: params.ext,
|
|
519
557
|
mime,
|
|
@@ -521,17 +559,35 @@ async function resolveFileData(params) {
|
|
|
521
559
|
};
|
|
522
560
|
}
|
|
523
561
|
if (params.file) {
|
|
524
|
-
const arrayBuffer = await params.file.arrayBuffer();
|
|
525
|
-
const data = new Uint8Array(arrayBuffer);
|
|
526
562
|
const name = params.file.name;
|
|
527
563
|
const dotIndex = name.lastIndexOf(".");
|
|
528
564
|
const fileName = params.fileName ?? (dotIndex > 0 ? name.substring(0, dotIndex) : name);
|
|
529
565
|
const ext = params.ext ?? (dotIndex > 0 ? name.substring(dotIndex + 1) : "");
|
|
530
566
|
const mime = params.mime ?? (params.file.type || guessMime(ext));
|
|
531
|
-
return {
|
|
567
|
+
return {
|
|
568
|
+
source: { type: "file", file: params.file },
|
|
569
|
+
fileName,
|
|
570
|
+
ext,
|
|
571
|
+
mime,
|
|
572
|
+
size: params.file.size
|
|
573
|
+
};
|
|
532
574
|
}
|
|
533
575
|
throw new Error("One of filePath, buffer, or file must be provided");
|
|
534
576
|
}
|
|
577
|
+
async function materializeData(meta) {
|
|
578
|
+
switch (meta.source.type) {
|
|
579
|
+
case "path": {
|
|
580
|
+
const fs = await import("fs");
|
|
581
|
+
return fs.readFileSync(meta.source.filePath);
|
|
582
|
+
}
|
|
583
|
+
case "buffer":
|
|
584
|
+
return meta.source.data;
|
|
585
|
+
case "file": {
|
|
586
|
+
const arrayBuffer = await meta.source.file.arrayBuffer();
|
|
587
|
+
return new Uint8Array(arrayBuffer);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
535
591
|
function guessMime(ext) {
|
|
536
592
|
const mimeMap = {
|
|
537
593
|
jpg: "image/jpeg",
|
package/dist/index.d.cts
CHANGED
|
@@ -341,6 +341,23 @@ interface CompleteMultipartParams {
|
|
|
341
341
|
partNumber: number;
|
|
342
342
|
}[];
|
|
343
343
|
}
|
|
344
|
+
type FileSource = {
|
|
345
|
+
type: "path";
|
|
346
|
+
filePath: string;
|
|
347
|
+
} | {
|
|
348
|
+
type: "buffer";
|
|
349
|
+
data: Buffer;
|
|
350
|
+
} | {
|
|
351
|
+
type: "file";
|
|
352
|
+
file: File;
|
|
353
|
+
};
|
|
354
|
+
interface FileMeta {
|
|
355
|
+
source: FileSource;
|
|
356
|
+
fileName: string;
|
|
357
|
+
ext: string;
|
|
358
|
+
mime: string;
|
|
359
|
+
size: number;
|
|
360
|
+
}
|
|
344
361
|
interface UploadFileParams {
|
|
345
362
|
filePath?: string;
|
|
346
363
|
buffer?: Buffer | ArrayBuffer;
|
|
@@ -397,4 +414,4 @@ declare class AirApi extends AirBase {
|
|
|
397
414
|
constructor(options?: AirApiOptions);
|
|
398
415
|
}
|
|
399
416
|
|
|
400
|
-
export { type AddBoardAssetsParams, type AddGuestParams, type AddVersionTagParams, AirApi, type AirApiOptions, type Asset, type AssetBoardListParams, type AssetCustomFieldValue, type AssetListParams, type AssetVersion, type AssetVersionDownloadUrl, type AssetVersionListResponse, type AssetVersionUpdateParams, type AssetVersionUrls, Assets, type Board, type BoardCreateParams, type BoardListParams, type BoardUpdateParams, Boards, type CompleteMultipartParams, type CustomField, type CustomFieldCreateParams, type CustomFieldListParams, type CustomFieldType, type CustomFieldUpdateParams, type CustomFieldValue, type CustomFieldValueCreateParams, type CustomFieldValueUpdateParams, CustomFields, type GetPartUploadUrlParams, type GetPartUploadUrlResponse, type Guest, type GuestListParams, type GuestListResponse, type ImportCreateParams, type ImportCreateResponse, type ImportStatus, type ImportStatusResponse, Imports, type LargeUploadResponse, type Role, type RoleListParams, type RoleListResponse, Roles, type SetBoardCustomFieldParams, type SetCustomFieldParams, type SmallUploadResponse, type SmartTag, type Tag, type TagCreateParams, type TagListParams, type TagUpdateParams, Tags, type UpdateGuestParams, type UploadCreateParams, type UploadCreateResponse, type UploadFileOptions, type UploadFileParams, type UploadFileResponse, Uploads };
|
|
417
|
+
export { type AddBoardAssetsParams, type AddGuestParams, type AddVersionTagParams, AirApi, type AirApiOptions, type Asset, type AssetBoardListParams, type AssetCustomFieldValue, type AssetListParams, type AssetVersion, type AssetVersionDownloadUrl, type AssetVersionListResponse, type AssetVersionUpdateParams, type AssetVersionUrls, Assets, type Board, type BoardCreateParams, type BoardListParams, type BoardUpdateParams, Boards, type CompleteMultipartParams, type CustomField, type CustomFieldCreateParams, type CustomFieldListParams, type CustomFieldType, type CustomFieldUpdateParams, type CustomFieldValue, type CustomFieldValueCreateParams, type CustomFieldValueUpdateParams, CustomFields, type FileMeta, type FileSource, type GetPartUploadUrlParams, type GetPartUploadUrlResponse, type Guest, type GuestListParams, type GuestListResponse, type ImportCreateParams, type ImportCreateResponse, type ImportStatus, type ImportStatusResponse, Imports, type LargeUploadResponse, type Role, type RoleListParams, type RoleListResponse, Roles, type SetBoardCustomFieldParams, type SetCustomFieldParams, type SmallUploadResponse, type SmartTag, type Tag, type TagCreateParams, type TagListParams, type TagUpdateParams, Tags, type UpdateGuestParams, type UploadCreateParams, type UploadCreateResponse, type UploadFileOptions, type UploadFileParams, type UploadFileResponse, Uploads };
|
package/dist/index.d.ts
CHANGED
|
@@ -341,6 +341,23 @@ interface CompleteMultipartParams {
|
|
|
341
341
|
partNumber: number;
|
|
342
342
|
}[];
|
|
343
343
|
}
|
|
344
|
+
type FileSource = {
|
|
345
|
+
type: "path";
|
|
346
|
+
filePath: string;
|
|
347
|
+
} | {
|
|
348
|
+
type: "buffer";
|
|
349
|
+
data: Buffer;
|
|
350
|
+
} | {
|
|
351
|
+
type: "file";
|
|
352
|
+
file: File;
|
|
353
|
+
};
|
|
354
|
+
interface FileMeta {
|
|
355
|
+
source: FileSource;
|
|
356
|
+
fileName: string;
|
|
357
|
+
ext: string;
|
|
358
|
+
mime: string;
|
|
359
|
+
size: number;
|
|
360
|
+
}
|
|
344
361
|
interface UploadFileParams {
|
|
345
362
|
filePath?: string;
|
|
346
363
|
buffer?: Buffer | ArrayBuffer;
|
|
@@ -397,4 +414,4 @@ declare class AirApi extends AirBase {
|
|
|
397
414
|
constructor(options?: AirApiOptions);
|
|
398
415
|
}
|
|
399
416
|
|
|
400
|
-
export { type AddBoardAssetsParams, type AddGuestParams, type AddVersionTagParams, AirApi, type AirApiOptions, type Asset, type AssetBoardListParams, type AssetCustomFieldValue, type AssetListParams, type AssetVersion, type AssetVersionDownloadUrl, type AssetVersionListResponse, type AssetVersionUpdateParams, type AssetVersionUrls, Assets, type Board, type BoardCreateParams, type BoardListParams, type BoardUpdateParams, Boards, type CompleteMultipartParams, type CustomField, type CustomFieldCreateParams, type CustomFieldListParams, type CustomFieldType, type CustomFieldUpdateParams, type CustomFieldValue, type CustomFieldValueCreateParams, type CustomFieldValueUpdateParams, CustomFields, type GetPartUploadUrlParams, type GetPartUploadUrlResponse, type Guest, type GuestListParams, type GuestListResponse, type ImportCreateParams, type ImportCreateResponse, type ImportStatus, type ImportStatusResponse, Imports, type LargeUploadResponse, type Role, type RoleListParams, type RoleListResponse, Roles, type SetBoardCustomFieldParams, type SetCustomFieldParams, type SmallUploadResponse, type SmartTag, type Tag, type TagCreateParams, type TagListParams, type TagUpdateParams, Tags, type UpdateGuestParams, type UploadCreateParams, type UploadCreateResponse, type UploadFileOptions, type UploadFileParams, type UploadFileResponse, Uploads };
|
|
417
|
+
export { type AddBoardAssetsParams, type AddGuestParams, type AddVersionTagParams, AirApi, type AirApiOptions, type Asset, type AssetBoardListParams, type AssetCustomFieldValue, type AssetListParams, type AssetVersion, type AssetVersionDownloadUrl, type AssetVersionListResponse, type AssetVersionUpdateParams, type AssetVersionUrls, Assets, type Board, type BoardCreateParams, type BoardListParams, type BoardUpdateParams, Boards, type CompleteMultipartParams, type CustomField, type CustomFieldCreateParams, type CustomFieldListParams, type CustomFieldType, type CustomFieldUpdateParams, type CustomFieldValue, type CustomFieldValueCreateParams, type CustomFieldValueUpdateParams, CustomFields, type FileMeta, type FileSource, type GetPartUploadUrlParams, type GetPartUploadUrlResponse, type Guest, type GuestListParams, type GuestListResponse, type ImportCreateParams, type ImportCreateResponse, type ImportStatus, type ImportStatusResponse, Imports, type LargeUploadResponse, type Role, type RoleListParams, type RoleListResponse, Roles, type SetBoardCustomFieldParams, type SetCustomFieldParams, type SmallUploadResponse, type SmartTag, type Tag, type TagCreateParams, type TagListParams, type TagUpdateParams, Tags, type UpdateGuestParams, type UploadCreateParams, type UploadCreateResponse, type UploadFileOptions, type UploadFileParams, type UploadFileResponse, Uploads };
|
package/dist/index.js
CHANGED
|
@@ -364,22 +364,23 @@ var Uploads = class {
|
|
|
364
364
|
});
|
|
365
365
|
}
|
|
366
366
|
async uploadFile(params, options = {}) {
|
|
367
|
-
const
|
|
367
|
+
const meta = await resolveFileMeta(params);
|
|
368
368
|
const uploadResponse = await this.create({
|
|
369
|
-
fileName,
|
|
370
|
-
ext,
|
|
371
|
-
size,
|
|
372
|
-
mime,
|
|
369
|
+
fileName: meta.fileName,
|
|
370
|
+
ext: meta.ext,
|
|
371
|
+
size: meta.size,
|
|
372
|
+
mime: meta.mime,
|
|
373
373
|
parentBoardId: options.parentBoardId,
|
|
374
374
|
assetId: options.assetId,
|
|
375
375
|
customFields: options.customFields,
|
|
376
376
|
tags: options.tags
|
|
377
377
|
});
|
|
378
378
|
if (isSmallUpload(uploadResponse)) {
|
|
379
|
-
|
|
379
|
+
const data = await materializeData(meta);
|
|
380
|
+
await this._uploadSmallFile(uploadResponse.uploadUrl, data, options.onProgress, meta.size);
|
|
380
381
|
return { assetId: uploadResponse.assetId, versionId: uploadResponse.versionId };
|
|
381
382
|
}
|
|
382
|
-
await this._uploadLargeFile(uploadResponse,
|
|
383
|
+
await this._uploadLargeFile(uploadResponse, meta, options.onProgress);
|
|
383
384
|
return { assetId: uploadResponse.assetId, versionId: uploadResponse.versionId };
|
|
384
385
|
}
|
|
385
386
|
async _uploadSmallFile(url, data, onProgress, totalSize) {
|
|
@@ -395,42 +396,73 @@ var Uploads = class {
|
|
|
395
396
|
onProgress({ percentage: 100, uploadedBytes: totalSize, totalBytes: totalSize });
|
|
396
397
|
}
|
|
397
398
|
}
|
|
398
|
-
async _uploadLargeFile(uploadInfo,
|
|
399
|
+
async _uploadLargeFile(uploadInfo, meta, onProgress) {
|
|
399
400
|
const { multiPartUploadId, key } = uploadInfo;
|
|
400
|
-
const
|
|
401
|
+
const { size: totalSize } = meta;
|
|
401
402
|
const partCount = Math.ceil(totalSize / DEFAULT_PART_SIZE);
|
|
402
403
|
const parts = [];
|
|
403
404
|
let uploadedBytes = 0;
|
|
404
405
|
const fetchFn = this.client.fetchFn;
|
|
405
|
-
|
|
406
|
-
const
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
406
|
+
if (meta.source.type === "path") {
|
|
407
|
+
const fs = await import("fs");
|
|
408
|
+
const fd = fs.openSync(meta.source.filePath, "r");
|
|
409
|
+
try {
|
|
410
|
+
for (let i = 0; i < partCount; i++) {
|
|
411
|
+
const partNumber = i + 1;
|
|
412
|
+
const start = i * DEFAULT_PART_SIZE;
|
|
413
|
+
const end = Math.min(start + DEFAULT_PART_SIZE, totalSize);
|
|
414
|
+
const length = end - start;
|
|
415
|
+
const chunk = Buffer.alloc(length);
|
|
416
|
+
const bytesRead = fs.readSync(fd, chunk, 0, length, start);
|
|
417
|
+
const body = bytesRead < length ? chunk.subarray(0, bytesRead) : chunk;
|
|
418
|
+
const { url } = await this.getPartUploadUrl({ multiPartUploadId, key, partNumber });
|
|
419
|
+
const response = await fetchFn(url, { method: "PUT", body });
|
|
420
|
+
if (!response.ok) {
|
|
421
|
+
throw new Error(`Part ${partNumber} upload failed with status ${response.status}`);
|
|
422
|
+
}
|
|
423
|
+
const etag = response.headers.get("etag");
|
|
424
|
+
if (!etag) {
|
|
425
|
+
throw new Error(`Part ${partNumber} upload did not return an ETag`);
|
|
426
|
+
}
|
|
427
|
+
parts.push({ etag, partNumber });
|
|
428
|
+
uploadedBytes += bytesRead;
|
|
429
|
+
if (onProgress) {
|
|
430
|
+
onProgress({
|
|
431
|
+
percentage: Math.round(uploadedBytes / totalSize * 100),
|
|
432
|
+
uploadedBytes,
|
|
433
|
+
totalBytes: totalSize
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} finally {
|
|
438
|
+
fs.closeSync(fd);
|
|
421
439
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
440
|
+
} else {
|
|
441
|
+
const buffer = await materializeData(meta);
|
|
442
|
+
const buf = buffer instanceof Buffer ? buffer : Buffer.from(buffer);
|
|
443
|
+
for (let i = 0; i < partCount; i++) {
|
|
444
|
+
const partNumber = i + 1;
|
|
445
|
+
const start = i * DEFAULT_PART_SIZE;
|
|
446
|
+
const end = Math.min(start + DEFAULT_PART_SIZE, totalSize);
|
|
447
|
+
const chunk = buf.subarray(start, end);
|
|
448
|
+
const { url } = await this.getPartUploadUrl({ multiPartUploadId, key, partNumber });
|
|
449
|
+
const response = await fetchFn(url, { method: "PUT", body: chunk });
|
|
450
|
+
if (!response.ok) {
|
|
451
|
+
throw new Error(`Part ${partNumber} upload failed with status ${response.status}`);
|
|
452
|
+
}
|
|
453
|
+
const etag = response.headers.get("etag");
|
|
454
|
+
if (!etag) {
|
|
455
|
+
throw new Error(`Part ${partNumber} upload did not return an ETag`);
|
|
456
|
+
}
|
|
457
|
+
parts.push({ etag, partNumber });
|
|
458
|
+
uploadedBytes += chunk.byteLength;
|
|
459
|
+
if (onProgress) {
|
|
460
|
+
onProgress({
|
|
461
|
+
percentage: Math.round(uploadedBytes / totalSize * 100),
|
|
462
|
+
uploadedBytes,
|
|
463
|
+
totalBytes: totalSize
|
|
464
|
+
});
|
|
465
|
+
}
|
|
434
466
|
}
|
|
435
467
|
}
|
|
436
468
|
await this.completeMultipart({ multiPartUploadId, key, parts });
|
|
@@ -439,16 +471,22 @@ var Uploads = class {
|
|
|
439
471
|
function isSmallUpload(response) {
|
|
440
472
|
return "uploadUrl" in response;
|
|
441
473
|
}
|
|
442
|
-
async function
|
|
474
|
+
async function resolveFileMeta(params) {
|
|
443
475
|
if (params.filePath) {
|
|
444
476
|
const fs = await import("fs");
|
|
445
477
|
const path = await import("path");
|
|
446
|
-
const
|
|
478
|
+
const stat = fs.statSync(params.filePath);
|
|
447
479
|
const parsed = path.parse(params.filePath);
|
|
448
480
|
const fileName = params.fileName ?? parsed.name;
|
|
449
481
|
const ext = params.ext ?? parsed.ext.replace(/^\./, "");
|
|
450
482
|
const mime = params.mime ?? guessMime(ext);
|
|
451
|
-
return {
|
|
483
|
+
return {
|
|
484
|
+
source: { type: "path", filePath: params.filePath },
|
|
485
|
+
fileName,
|
|
486
|
+
ext,
|
|
487
|
+
mime,
|
|
488
|
+
size: stat.size
|
|
489
|
+
};
|
|
452
490
|
}
|
|
453
491
|
if (params.buffer) {
|
|
454
492
|
const data = params.buffer instanceof Buffer ? params.buffer : Buffer.from(params.buffer);
|
|
@@ -457,7 +495,7 @@ async function resolveFileData(params) {
|
|
|
457
495
|
}
|
|
458
496
|
const mime = params.mime ?? guessMime(params.ext);
|
|
459
497
|
return {
|
|
460
|
-
|
|
498
|
+
source: { type: "buffer", data },
|
|
461
499
|
fileName: params.fileName,
|
|
462
500
|
ext: params.ext,
|
|
463
501
|
mime,
|
|
@@ -465,17 +503,35 @@ async function resolveFileData(params) {
|
|
|
465
503
|
};
|
|
466
504
|
}
|
|
467
505
|
if (params.file) {
|
|
468
|
-
const arrayBuffer = await params.file.arrayBuffer();
|
|
469
|
-
const data = new Uint8Array(arrayBuffer);
|
|
470
506
|
const name = params.file.name;
|
|
471
507
|
const dotIndex = name.lastIndexOf(".");
|
|
472
508
|
const fileName = params.fileName ?? (dotIndex > 0 ? name.substring(0, dotIndex) : name);
|
|
473
509
|
const ext = params.ext ?? (dotIndex > 0 ? name.substring(dotIndex + 1) : "");
|
|
474
510
|
const mime = params.mime ?? (params.file.type || guessMime(ext));
|
|
475
|
-
return {
|
|
511
|
+
return {
|
|
512
|
+
source: { type: "file", file: params.file },
|
|
513
|
+
fileName,
|
|
514
|
+
ext,
|
|
515
|
+
mime,
|
|
516
|
+
size: params.file.size
|
|
517
|
+
};
|
|
476
518
|
}
|
|
477
519
|
throw new Error("One of filePath, buffer, or file must be provided");
|
|
478
520
|
}
|
|
521
|
+
async function materializeData(meta) {
|
|
522
|
+
switch (meta.source.type) {
|
|
523
|
+
case "path": {
|
|
524
|
+
const fs = await import("fs");
|
|
525
|
+
return fs.readFileSync(meta.source.filePath);
|
|
526
|
+
}
|
|
527
|
+
case "buffer":
|
|
528
|
+
return meta.source.data;
|
|
529
|
+
case "file": {
|
|
530
|
+
const arrayBuffer = await meta.source.file.arrayBuffer();
|
|
531
|
+
return new Uint8Array(arrayBuffer);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
479
535
|
function guessMime(ext) {
|
|
480
536
|
const mimeMap = {
|
|
481
537
|
jpg: "image/jpeg",
|