@amaster.ai/s3-client 1.1.14-beta.1 → 1.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -55,6 +55,9 @@ Creates a new instance of the S3 client. Optionally accepts a custom HTTP client
|
|
|
55
55
|
|
|
56
56
|
Uploads a file to the storage.
|
|
57
57
|
|
|
58
|
+
Large files automatically switch to multipart upload inside the SDK. The caller
|
|
59
|
+
still uses the same `upload(file)` entrypoint.
|
|
60
|
+
|
|
58
61
|
- **Parameters**:
|
|
59
62
|
- `file`: `File` or `Blob` object to upload.
|
|
60
63
|
- **Returns**: `Promise<ClientResult<UploadRes>>`
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var httpClient=require('@amaster.ai/http-client');var
|
|
1
|
+
'use strict';var httpClient=require('@amaster.ai/http-client');var c="/api/storage",P=1024*1024,S=32*P,w=8*P,F=3;function f(t,e,r){return {data:null,error:{message:t,status:e,details:r},status:e}}function h(){let t=new Error("Upload aborted");return t.name="AbortError",t}function $(t,e){return t instanceof Error&&t.message?t.message:typeof t=="string"&&t?t:e}function B(t){return "name"in t&&typeof t.name=="string"&&t.name.trim()?t.name.trim():"blob"}function L(t){return typeof t.type=="string"&&t.type?t.type:void 0}function I(t){return t.size>=S}function M(t,e){if(!t)return null;if(typeof Headers<"u"&&t instanceof Headers)return t.get(e);if(typeof t=="object"){for(let[r,a]of Object.entries(t))if(r.toLowerCase()===e.toLowerCase())return a==null?null:String(a)}return null}function x(t){return (typeof t=="object"||typeof t=="function")&&t!==null&&"then"in t&&typeof t.then=="function"}function E(t){return typeof t=="object"&&t!==null&&("statusCode"in t||"status"in t||"data"in t||"header"in t||"headers"in t)}function y(t){return E(t)?t:{}}async function H(t,e,r){return await new Promise((a,o)=>{let s=false,u=null,m=()=>{r?.removeEventListener("abort",i);},d=n=>{s||(s=true,m(),a(n));},l=n=>{s||(s=true,m(),o(n));},i=()=>{try{u?.abort?.();}catch{}l(h());};if(r?.aborted){l(h());return}r?.addEventListener("abort",i,{once:true});try{let n=t({...e,success:p=>{d(y(p));},fail:l});if(typeof n=="object"&&n!==null&&(u=n),x(n)){n.then(p=>{d(y(p));},l);return}E(n)&&d(n);}catch(n){l(n);}})}async function j(t,e,r){if(typeof fetch!="function")throw new Error("Fetch API is not available for multipart upload.");let a=await fetch(t,{method:"PUT",body:e,signal:r});if(!a.ok){let s="";try{s=await a.text();}catch{}throw new Error(`Multipart part upload failed (${a.status})${s?`: ${s}`:""}`)}let o=M(a.headers,"etag");if(!o)throw new Error("Multipart upload response missing ETag header. Check object storage CORS expose headers.");return o}async function O(t,e,r){let a=httpClient.getMiniProgramRequest();if(!a)throw new Error("Mini-program request API is not available for multipart upload.");let o=await H(a,{url:t,method:"PUT",data:await e.arrayBuffer(),header:{}},r),s=Number(o?.statusCode??o?.status??0);if(s<200||s>=300){let m=typeof o?.data=="string"&&o.data?`: ${o.data}`:"";throw new Error(`Multipart part upload failed (${s||"unknown"})${m}`)}let u=M(o?.header??o?.headers,"etag");if(!u)throw new Error("Multipart upload response missing ETag header. Check object storage CORS expose headers.");return u}async function _(t,e,r){return httpClient.detectMiniProgramPlatform()?await O(t,e,r):await j(t,e,r)}async function b(t,e,r){try{await t.request({url:`${c}/upload/multipart/abort`,method:"post",data:{filePath:e,uploadId:r}});}catch{}}async function D(t,e){let r=await t.request({url:`${c}/upload/multipart/init`,method:"post",data:{filename:B(e),contentType:L(e)}});if(r.error)return f(r.error.message,r.status||500,r.error.details);if(!r.data?.uploadId||!r.data?.filePath)return f("Multipart upload init returned an invalid response.",r.status||500);let{uploadId:a,filePath:o}=r.data,s=Math.max(1,Math.ceil(e.size/w)),u=new Array(s),m=Math.min(F,s),d=new AbortController,l=0;try{if(await Promise.all(Array.from({length:m},async()=>{for(;;){let n=l;if(l+=1,n>=s)return;let p=n+1,R=n*w,C=Math.min(R+w,e.size),T=e.slice(R,C),g=await t.request({url:`${c}/upload/multipart/url`,method:"post",data:{filePath:o,uploadId:a,partNumber:p},signal:d.signal});if(g.error)throw new Error(g.error.message);if(!g.data?.url)throw new Error(`Multipart upload URL request returned an invalid response for part ${p}.`);let k=await _(g.data.url,T,d.signal);u[n]={partNumber:p,eTag:k};}})),u.some(n=>!n?.eTag))throw new Error("Multipart upload did not collect all uploaded part ETags.");let i=await t.request({url:`${c}/upload/multipart/complete`,method:"post",data:{filePath:o,uploadId:a,parts:u}});return i.error?(await b(t,o,a),f(i.error.message,i.status||500,i.error.details)):i.data?i:(await b(t,o,a),f("Multipart upload complete returned an empty response.",i.status||500))}catch(i){return d.abort(),await b(t,o,a),f($(i,"Multipart upload failed"),i instanceof Error&&i.name==="AbortError"?499:500,i)}}function N(t=httpClient.createHttpClient()){return {async download(e){return e?await t.request({url:`${c}/download`,method:"get",params:{filename:e},responseType:"blob"}):{data:null,error:{message:"Filename is required",status:400},status:400}},async getMetadata(e){return e?t.request({url:`${c}/metadata`,method:"get",params:{key:e}}):{data:null,error:{message:"Key is required",status:400},status:400}},async upload(e){if(!e)return {data:null,error:{message:"File is required",status:400},status:400};if(I(e))return await D(t,e);let r=new FormData;return r.append("file",e),t.request({url:`${c}/upload`,method:"post",data:r,headers:{"Content-Type":"multipart/form-data"}})}}}exports.createS3Client=N;//# sourceMappingURL=index.cjs.map
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/s3-client.ts"],"names":["BASE_URL","createS3Client","http","createHttpClient","filename","key","file","formData"],"mappings":"+DAGA,IAAMA,CAAAA,CAAW,cAAA,CAEV,SAASC,CAAAA,CAAeC,CAAAA,CAAmBC,2BAAAA,EAAiB,CAAa,CAC9E,OAAO,CACL,MAAM,QAAA,CAASC,CAAAA,CAA+C,CAC5D,OAAKA,CAAAA,CAQU,MAAMF,EAAK,OAAA,CAAc,CACtC,GAAA,CAAK,CAAA,EAAGF,CAAQ,CAAA,SAAA,CAAA,CAChB,MAAA,CAAQ,KAAA,CACR,OAAQ,CAAE,QAAA,CAAAI,CAAS,CAAA,CACnB,YAAA,CAAc,MAChB,CAAC,CAAA,CAZQ,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,OAAA,CAAS,sBAAA,CAAwB,MAAA,CAAQ,GAAI,EACtD,MAAA,CAAQ,GACV,CAWJ,CAAA,CAEA,MAAM,WAAA,CAAYC,CAAAA,CAAgD,CAChE,OAAKA,CAAAA,CAQEH,CAAAA,CAAK,OAAA,CAAoB,CAC9B,GAAA,CAAK,CAAA,EAAGF,CAAQ,CAAA,SAAA,CAAA,CAChB,OAAQ,KAAA,CACR,MAAA,CAAQ,CAAE,GAAA,CAAAK,CAAI,CAChB,CAAC,CAAA,CAXQ,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,OAAA,CAAS,iBAAA,CAAmB,MAAA,CAAQ,GAAI,EACjD,MAAA,CAAQ,GACV,CAQJ,CAAA,CAEA,MAAM,MAAA,CAAOC,CAAAA,CAAqD,CAChE,GAAI,CAACA,CAAAA,CACH,OAAO,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,QAAS,kBAAA,CAAoB,MAAA,CAAQ,GAAI,CAAA,CAClD,MAAA,CAAQ,GACV,CAAA,CAGF,IAAMC,EAAW,IAAI,QAAA,CACrB,OAAAA,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAQD,CAAI,CAAA,CAErBJ,EAAK,OAAA,CAAmB,CAC7B,GAAA,CAAK,CAAA,EAAGF,CAAQ,CAAA,OAAA,CAAA,CAChB,MAAA,CAAQ,MAAA,CACR,KAAMO,CAAAA,CACN,OAAA,CAAS,CACP,cAAA,CAAgB,qBAClB,CACF,CAAC,CACH,CACF,CACF","file":"index.cjs","sourcesContent":["import { type ClientResult, createHttpClient, type HttpClient } from \"@amaster.ai/http-client\";\nimport type { S3Client, S3Metadata, UploadRes } from \"./types\";\n\nconst BASE_URL = \"/api/storage\";\n\nexport function createS3Client(http: HttpClient = createHttpClient()): S3Client {\n return {\n async download(filename: string): Promise<ClientResult<Blob>> {\n if (!filename) {\n return {\n data: null,\n error: { message: \"Filename is required\", status: 400 },\n status: 400,\n };\n }\n\n const result = await http.request<Blob>({\n url: `${BASE_URL}/download`,\n method: \"get\",\n params: { filename },\n responseType: \"blob\",\n });\n\n return result;\n },\n\n async getMetadata(key: string): Promise<ClientResult<S3Metadata>> {\n if (!key) {\n return {\n data: null,\n error: { message: \"Key is required\", status: 400 },\n status: 400,\n };\n }\n\n return http.request<S3Metadata>({\n url: `${BASE_URL}/metadata`,\n method: \"get\",\n params: { key },\n });\n },\n\n async upload(file: File | Blob): Promise<ClientResult<UploadRes>> {\n if (!file) {\n return {\n data: null,\n error: { message: \"File is required\", status: 400 },\n status: 400,\n };\n }\n\n const formData = new FormData();\n formData.append(\"file\", file);\n\n return http.request<UploadRes>({\n url: `${BASE_URL}/upload`,\n method: \"post\",\n data: formData,\n headers: {\n \"Content-Type\": \"multipart/form-data\",\n },\n });\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/s3-client.ts"],"names":["BASE_URL","MB","DEFAULT_MULTIPART_THRESHOLD","DEFAULT_PART_SIZE","DEFAULT_CONCURRENCY","createErrorResult","message","status","details","createAbortError","error","getErrorMessage","fallback","resolveFilename","file","resolveContentType","shouldUseMultipartUpload","getHeaderValue","headers","key","headerKey","headerValue","isPromiseLike","value","isMiniProgramRequestResponse","normalizeMiniProgramRequestResponse","callMiniProgramRequest","request","options","signal","resolve","reject","settled","task","cleanup","onAbort","resolveOnce","rejectOnce","taskResult","uploadPartWithFetch","url","blob","response","eTag","uploadPartWithMiniProgram","getMiniProgramRequest","uploadPartToPresignedUrl","detectMiniProgramPlatform","abortMultipartUpload","http","filePath","uploadId","uploadWithMultipart","initResult","totalParts","parts","workerCount","uploadAbortController","nextPartIndex","currentIndex","partNumber","start","end","urlResult","part","completeResult","createS3Client","createHttpClient","filename","formData"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n createHttpClient,\n detectMiniProgramPlatform,\n getMiniProgramRequest,\n type ClientResult,\n type HttpClient,\n} from \"@amaster.ai/http-client\";\nimport type { S3Client, S3Metadata, UploadRes } from \"./types\";\n\nconst BASE_URL = \"/api/storage\";\nconst MB = 1024 * 1024;\nconst DEFAULT_MULTIPART_THRESHOLD = 32 * MB;\nconst DEFAULT_PART_SIZE = 8 * MB;\nconst DEFAULT_CONCURRENCY = 3;\n\ntype MultipartInitRes = {\n uploadId: string;\n filePath: string;\n};\n\ntype MultipartUrlRes = {\n url: string;\n};\n\ntype PartETag = {\n partNumber: number;\n eTag: string;\n};\n\ntype MiniProgramRequestResponse = {\n statusCode?: unknown;\n status?: unknown;\n data?: unknown;\n header?: unknown;\n headers?: unknown;\n};\n\ntype AbortableTask = {\n abort?: () => void;\n};\n\nfunction createErrorResult<T>(\n message: string,\n status: number,\n details?: unknown,\n): ClientResult<T> {\n return {\n data: null,\n error: {\n message,\n status,\n details,\n },\n status,\n };\n}\n\nfunction createAbortError(): Error {\n const error = new Error(\"Upload aborted\");\n error.name = \"AbortError\";\n return error;\n}\n\nfunction getErrorMessage(error: unknown, fallback: string): string {\n if (error instanceof Error && error.message) {\n return error.message;\n }\n if (typeof error === \"string\" && error) {\n return error;\n }\n return fallback;\n}\n\nfunction resolveFilename(file: File | Blob): string {\n if (\"name\" in file && typeof file.name === \"string\" && file.name.trim()) {\n return file.name.trim();\n }\n return \"blob\";\n}\n\nfunction resolveContentType(file: File | Blob): string | undefined {\n return typeof file.type === \"string\" && file.type ? file.type : undefined;\n}\n\nfunction shouldUseMultipartUpload(file: File | Blob): boolean {\n return file.size >= DEFAULT_MULTIPART_THRESHOLD;\n}\n\nfunction getHeaderValue(headers: unknown, key: string): string | null {\n if (!headers) {\n return null;\n }\n\n if (typeof Headers !== \"undefined\" && headers instanceof Headers) {\n return headers.get(key);\n }\n\n if (typeof headers === \"object\") {\n for (const [headerKey, headerValue] of Object.entries(\n headers as Record<string, unknown>,\n )) {\n if (headerKey.toLowerCase() === key.toLowerCase()) {\n return headerValue == null ? null : String(headerValue);\n }\n }\n }\n\n return null;\n}\n\nfunction isPromiseLike<T = unknown>(value: unknown): value is PromiseLike<T> {\n return (\n (typeof value === \"object\" || typeof value === \"function\") &&\n value !== null &&\n \"then\" in value &&\n typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\nfunction isMiniProgramRequestResponse(\n value: unknown,\n): value is MiniProgramRequestResponse {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (\"statusCode\" in value ||\n \"status\" in value ||\n \"data\" in value ||\n \"header\" in value ||\n \"headers\" in value)\n );\n}\n\nfunction normalizeMiniProgramRequestResponse(\n value: unknown,\n): MiniProgramRequestResponse {\n return isMiniProgramRequestResponse(value) ? value : {};\n}\n\nasync function callMiniProgramRequest(\n request: (_options: Record<string, unknown>) => unknown,\n options: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<MiniProgramRequestResponse> {\n return await new Promise<MiniProgramRequestResponse>((resolve, reject) => {\n let settled = false;\n let task: AbortableTask | null = null;\n\n const cleanup = () => {\n signal?.removeEventListener(\"abort\", onAbort);\n };\n\n const resolveOnce = (value: MiniProgramRequestResponse) => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n resolve(value);\n };\n\n const rejectOnce = (error: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n reject(error);\n };\n\n const onAbort = () => {\n try {\n task?.abort?.();\n } catch {\n // ignore abort failures from runtime-specific request tasks\n }\n rejectOnce(createAbortError());\n };\n\n if (signal?.aborted) {\n rejectOnce(createAbortError());\n return;\n }\n\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const taskResult = request({\n ...options,\n success: (value: unknown) => {\n resolveOnce(normalizeMiniProgramRequestResponse(value));\n },\n fail: rejectOnce,\n });\n\n if (typeof taskResult === \"object\" && taskResult !== null) {\n task = taskResult as AbortableTask;\n }\n\n if (isPromiseLike(taskResult)) {\n void taskResult.then(\n (value) => {\n resolveOnce(normalizeMiniProgramRequestResponse(value));\n },\n rejectOnce,\n );\n return;\n }\n\n if (isMiniProgramRequestResponse(taskResult)) {\n resolveOnce(taskResult);\n }\n } catch (error) {\n rejectOnce(error);\n }\n });\n}\n\nasync function uploadPartWithFetch(\n url: string,\n blob: Blob,\n signal?: AbortSignal,\n): Promise<string> {\n if (typeof fetch !== \"function\") {\n throw new Error(\"Fetch API is not available for multipart upload.\");\n }\n\n const response = await fetch(url, {\n method: \"PUT\",\n body: blob,\n signal,\n });\n\n if (!response.ok) {\n let details = \"\";\n try {\n details = await response.text();\n } catch {\n // ignore response body parse failures\n }\n throw new Error(\n `Multipart part upload failed (${response.status})${details ? `: ${details}` : \"\"}`,\n );\n }\n\n const eTag = getHeaderValue(response.headers, \"etag\");\n if (!eTag) {\n throw new Error(\n \"Multipart upload response missing ETag header. Check object storage CORS expose headers.\",\n );\n }\n\n return eTag;\n}\n\nasync function uploadPartWithMiniProgram(\n url: string,\n blob: Blob,\n signal?: AbortSignal,\n): Promise<string> {\n const request = getMiniProgramRequest();\n if (!request) {\n throw new Error(\"Mini-program request API is not available for multipart upload.\");\n }\n\n const response = await callMiniProgramRequest(\n request,\n {\n url,\n method: \"PUT\",\n data: await blob.arrayBuffer(),\n header: {},\n },\n signal,\n );\n\n const status = Number(response?.statusCode ?? response?.status ?? 0);\n if (status < 200 || status >= 300) {\n const details =\n typeof response?.data === \"string\" && response.data\n ? `: ${response.data}`\n : \"\";\n throw new Error(\n `Multipart part upload failed (${status || \"unknown\"})${details}`,\n );\n }\n\n const eTag = getHeaderValue(response?.header ?? response?.headers, \"etag\");\n if (!eTag) {\n throw new Error(\n \"Multipart upload response missing ETag header. Check object storage CORS expose headers.\",\n );\n }\n\n return eTag;\n}\n\nasync function uploadPartToPresignedUrl(\n url: string,\n blob: Blob,\n signal?: AbortSignal,\n): Promise<string> {\n if (detectMiniProgramPlatform()) {\n return await uploadPartWithMiniProgram(url, blob, signal);\n }\n\n return await uploadPartWithFetch(url, blob, signal);\n}\n\nasync function abortMultipartUpload(\n http: HttpClient,\n filePath: string,\n uploadId: string,\n): Promise<void> {\n try {\n await http.request<void>({\n url: `${BASE_URL}/upload/multipart/abort`,\n method: \"post\",\n data: {\n filePath,\n uploadId,\n },\n });\n } catch {\n // Best-effort cleanup only.\n }\n}\n\nasync function uploadWithMultipart(\n http: HttpClient,\n file: File | Blob,\n): Promise<ClientResult<UploadRes>> {\n const initResult = await http.request<MultipartInitRes>({\n url: `${BASE_URL}/upload/multipart/init`,\n method: \"post\",\n data: {\n filename: resolveFilename(file),\n contentType: resolveContentType(file),\n },\n });\n\n if (initResult.error) {\n return createErrorResult(\n initResult.error.message,\n initResult.status || 500,\n initResult.error.details,\n );\n }\n\n if (!initResult.data?.uploadId || !initResult.data?.filePath) {\n return createErrorResult(\n \"Multipart upload init returned an invalid response.\",\n initResult.status || 500,\n );\n }\n\n const { uploadId, filePath } = initResult.data;\n const totalParts = Math.max(1, Math.ceil(file.size / DEFAULT_PART_SIZE));\n const parts: PartETag[] = new Array(totalParts);\n const workerCount = Math.min(DEFAULT_CONCURRENCY, totalParts);\n const uploadAbortController = new AbortController();\n let nextPartIndex = 0;\n\n try {\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n while (true) {\n const currentIndex = nextPartIndex;\n nextPartIndex += 1;\n\n if (currentIndex >= totalParts) {\n return;\n }\n\n const partNumber = currentIndex + 1;\n const start = currentIndex * DEFAULT_PART_SIZE;\n const end = Math.min(start + DEFAULT_PART_SIZE, file.size);\n const blob = file.slice(start, end);\n\n const urlResult = await http.request<MultipartUrlRes>({\n url: `${BASE_URL}/upload/multipart/url`,\n method: \"post\",\n data: {\n filePath,\n uploadId,\n partNumber,\n },\n signal: uploadAbortController.signal,\n });\n\n if (urlResult.error) {\n throw new Error(urlResult.error.message);\n }\n\n if (!urlResult.data?.url) {\n throw new Error(\n `Multipart upload URL request returned an invalid response for part ${partNumber}.`,\n );\n }\n\n const eTag = await uploadPartToPresignedUrl(\n urlResult.data.url,\n blob,\n uploadAbortController.signal,\n );\n\n parts[currentIndex] = {\n partNumber,\n eTag,\n };\n }\n }),\n );\n\n if (parts.some((part) => !part?.eTag)) {\n throw new Error(\"Multipart upload did not collect all uploaded part ETags.\");\n }\n\n const completeResult = await http.request<UploadRes>({\n url: `${BASE_URL}/upload/multipart/complete`,\n method: \"post\",\n data: {\n filePath,\n uploadId,\n parts,\n },\n });\n\n if (completeResult.error) {\n await abortMultipartUpload(http, filePath, uploadId);\n return createErrorResult(\n completeResult.error.message,\n completeResult.status || 500,\n completeResult.error.details,\n );\n }\n\n if (!completeResult.data) {\n await abortMultipartUpload(http, filePath, uploadId);\n return createErrorResult(\n \"Multipart upload complete returned an empty response.\",\n completeResult.status || 500,\n );\n }\n\n return completeResult;\n } catch (error) {\n uploadAbortController.abort();\n await abortMultipartUpload(http, filePath, uploadId);\n return createErrorResult(\n getErrorMessage(error, \"Multipart upload failed\"),\n error instanceof Error && error.name === \"AbortError\" ? 499 : 500,\n error,\n );\n }\n}\n\nexport function createS3Client(http: HttpClient = createHttpClient()): S3Client {\n return {\n async download(filename: string): Promise<ClientResult<Blob>> {\n if (!filename) {\n return {\n data: null,\n error: { message: \"Filename is required\", status: 400 },\n status: 400,\n };\n }\n\n const result = await http.request<Blob>({\n url: `${BASE_URL}/download`,\n method: \"get\",\n params: { filename },\n responseType: \"blob\",\n });\n\n return result;\n },\n\n async getMetadata(key: string): Promise<ClientResult<S3Metadata>> {\n if (!key) {\n return {\n data: null,\n error: { message: \"Key is required\", status: 400 },\n status: 400,\n };\n }\n\n return http.request<S3Metadata>({\n url: `${BASE_URL}/metadata`,\n method: \"get\",\n params: { key },\n });\n },\n\n async upload(file: File | Blob): Promise<ClientResult<UploadRes>> {\n if (!file) {\n return {\n data: null,\n error: { message: \"File is required\", status: 400 },\n status: 400,\n };\n }\n\n if (shouldUseMultipartUpload(file)) {\n return await uploadWithMultipart(http, file);\n }\n\n const formData = new FormData();\n formData.append(\"file\", file);\n\n return http.request<UploadRes>({\n url: `${BASE_URL}/upload`,\n method: \"post\",\n data: formData,\n headers: {\n \"Content-Type\": \"multipart/form-data\",\n },\n });\n },\n };\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {createHttpClient}from'@amaster.ai/http-client';var
|
|
1
|
+
import {createHttpClient,detectMiniProgramPlatform,getMiniProgramRequest}from'@amaster.ai/http-client';var c="/api/storage",P=1024*1024,S=32*P,w=8*P,F=3;function f(t,e,r){return {data:null,error:{message:t,status:e,details:r},status:e}}function h(){let t=new Error("Upload aborted");return t.name="AbortError",t}function $(t,e){return t instanceof Error&&t.message?t.message:typeof t=="string"&&t?t:e}function B(t){return "name"in t&&typeof t.name=="string"&&t.name.trim()?t.name.trim():"blob"}function L(t){return typeof t.type=="string"&&t.type?t.type:void 0}function I(t){return t.size>=S}function M(t,e){if(!t)return null;if(typeof Headers<"u"&&t instanceof Headers)return t.get(e);if(typeof t=="object"){for(let[r,a]of Object.entries(t))if(r.toLowerCase()===e.toLowerCase())return a==null?null:String(a)}return null}function x(t){return (typeof t=="object"||typeof t=="function")&&t!==null&&"then"in t&&typeof t.then=="function"}function E(t){return typeof t=="object"&&t!==null&&("statusCode"in t||"status"in t||"data"in t||"header"in t||"headers"in t)}function y(t){return E(t)?t:{}}async function H(t,e,r){return await new Promise((a,o)=>{let s=false,u=null,m=()=>{r?.removeEventListener("abort",i);},d=n=>{s||(s=true,m(),a(n));},l=n=>{s||(s=true,m(),o(n));},i=()=>{try{u?.abort?.();}catch{}l(h());};if(r?.aborted){l(h());return}r?.addEventListener("abort",i,{once:true});try{let n=t({...e,success:p=>{d(y(p));},fail:l});if(typeof n=="object"&&n!==null&&(u=n),x(n)){n.then(p=>{d(y(p));},l);return}E(n)&&d(n);}catch(n){l(n);}})}async function j(t,e,r){if(typeof fetch!="function")throw new Error("Fetch API is not available for multipart upload.");let a=await fetch(t,{method:"PUT",body:e,signal:r});if(!a.ok){let s="";try{s=await a.text();}catch{}throw new Error(`Multipart part upload failed (${a.status})${s?`: ${s}`:""}`)}let o=M(a.headers,"etag");if(!o)throw new Error("Multipart upload response missing ETag header. Check object storage CORS expose headers.");return o}async function O(t,e,r){let a=getMiniProgramRequest();if(!a)throw new Error("Mini-program request API is not available for multipart upload.");let o=await H(a,{url:t,method:"PUT",data:await e.arrayBuffer(),header:{}},r),s=Number(o?.statusCode??o?.status??0);if(s<200||s>=300){let m=typeof o?.data=="string"&&o.data?`: ${o.data}`:"";throw new Error(`Multipart part upload failed (${s||"unknown"})${m}`)}let u=M(o?.header??o?.headers,"etag");if(!u)throw new Error("Multipart upload response missing ETag header. Check object storage CORS expose headers.");return u}async function _(t,e,r){return detectMiniProgramPlatform()?await O(t,e,r):await j(t,e,r)}async function b(t,e,r){try{await t.request({url:`${c}/upload/multipart/abort`,method:"post",data:{filePath:e,uploadId:r}});}catch{}}async function D(t,e){let r=await t.request({url:`${c}/upload/multipart/init`,method:"post",data:{filename:B(e),contentType:L(e)}});if(r.error)return f(r.error.message,r.status||500,r.error.details);if(!r.data?.uploadId||!r.data?.filePath)return f("Multipart upload init returned an invalid response.",r.status||500);let{uploadId:a,filePath:o}=r.data,s=Math.max(1,Math.ceil(e.size/w)),u=new Array(s),m=Math.min(F,s),d=new AbortController,l=0;try{if(await Promise.all(Array.from({length:m},async()=>{for(;;){let n=l;if(l+=1,n>=s)return;let p=n+1,R=n*w,C=Math.min(R+w,e.size),T=e.slice(R,C),g=await t.request({url:`${c}/upload/multipart/url`,method:"post",data:{filePath:o,uploadId:a,partNumber:p},signal:d.signal});if(g.error)throw new Error(g.error.message);if(!g.data?.url)throw new Error(`Multipart upload URL request returned an invalid response for part ${p}.`);let k=await _(g.data.url,T,d.signal);u[n]={partNumber:p,eTag:k};}})),u.some(n=>!n?.eTag))throw new Error("Multipart upload did not collect all uploaded part ETags.");let i=await t.request({url:`${c}/upload/multipart/complete`,method:"post",data:{filePath:o,uploadId:a,parts:u}});return i.error?(await b(t,o,a),f(i.error.message,i.status||500,i.error.details)):i.data?i:(await b(t,o,a),f("Multipart upload complete returned an empty response.",i.status||500))}catch(i){return d.abort(),await b(t,o,a),f($(i,"Multipart upload failed"),i instanceof Error&&i.name==="AbortError"?499:500,i)}}function N(t=createHttpClient()){return {async download(e){return e?await t.request({url:`${c}/download`,method:"get",params:{filename:e},responseType:"blob"}):{data:null,error:{message:"Filename is required",status:400},status:400}},async getMetadata(e){return e?t.request({url:`${c}/metadata`,method:"get",params:{key:e}}):{data:null,error:{message:"Key is required",status:400},status:400}},async upload(e){if(!e)return {data:null,error:{message:"File is required",status:400},status:400};if(I(e))return await D(t,e);let r=new FormData;return r.append("file",e),t.request({url:`${c}/upload`,method:"post",data:r,headers:{"Content-Type":"multipart/form-data"}})}}}export{N as createS3Client};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/s3-client.ts"],"names":["BASE_URL","createS3Client","http","createHttpClient","filename","key","file","formData"],"mappings":"uDAGA,IAAMA,CAAAA,CAAW,cAAA,CAEV,SAASC,CAAAA,CAAeC,CAAAA,CAAmBC,gBAAAA,EAAiB,CAAa,CAC9E,OAAO,CACL,MAAM,QAAA,CAASC,CAAAA,CAA+C,CAC5D,OAAKA,CAAAA,CAQU,MAAMF,EAAK,OAAA,CAAc,CACtC,GAAA,CAAK,CAAA,EAAGF,CAAQ,CAAA,SAAA,CAAA,CAChB,MAAA,CAAQ,KAAA,CACR,OAAQ,CAAE,QAAA,CAAAI,CAAS,CAAA,CACnB,YAAA,CAAc,MAChB,CAAC,CAAA,CAZQ,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,OAAA,CAAS,sBAAA,CAAwB,MAAA,CAAQ,GAAI,EACtD,MAAA,CAAQ,GACV,CAWJ,CAAA,CAEA,MAAM,WAAA,CAAYC,CAAAA,CAAgD,CAChE,OAAKA,CAAAA,CAQEH,CAAAA,CAAK,OAAA,CAAoB,CAC9B,GAAA,CAAK,CAAA,EAAGF,CAAQ,CAAA,SAAA,CAAA,CAChB,OAAQ,KAAA,CACR,MAAA,CAAQ,CAAE,GAAA,CAAAK,CAAI,CAChB,CAAC,CAAA,CAXQ,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,OAAA,CAAS,iBAAA,CAAmB,MAAA,CAAQ,GAAI,EACjD,MAAA,CAAQ,GACV,CAQJ,CAAA,CAEA,MAAM,MAAA,CAAOC,CAAAA,CAAqD,CAChE,GAAI,CAACA,CAAAA,CACH,OAAO,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,QAAS,kBAAA,CAAoB,MAAA,CAAQ,GAAI,CAAA,CAClD,MAAA,CAAQ,GACV,CAAA,CAGF,IAAMC,EAAW,IAAI,QAAA,CACrB,OAAAA,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAQD,CAAI,CAAA,CAErBJ,EAAK,OAAA,CAAmB,CAC7B,GAAA,CAAK,CAAA,EAAGF,CAAQ,CAAA,OAAA,CAAA,CAChB,MAAA,CAAQ,MAAA,CACR,KAAMO,CAAAA,CACN,OAAA,CAAS,CACP,cAAA,CAAgB,qBAClB,CACF,CAAC,CACH,CACF,CACF","file":"index.js","sourcesContent":["import { type ClientResult, createHttpClient, type HttpClient } from \"@amaster.ai/http-client\";\nimport type { S3Client, S3Metadata, UploadRes } from \"./types\";\n\nconst BASE_URL = \"/api/storage\";\n\nexport function createS3Client(http: HttpClient = createHttpClient()): S3Client {\n return {\n async download(filename: string): Promise<ClientResult<Blob>> {\n if (!filename) {\n return {\n data: null,\n error: { message: \"Filename is required\", status: 400 },\n status: 400,\n };\n }\n\n const result = await http.request<Blob>({\n url: `${BASE_URL}/download`,\n method: \"get\",\n params: { filename },\n responseType: \"blob\",\n });\n\n return result;\n },\n\n async getMetadata(key: string): Promise<ClientResult<S3Metadata>> {\n if (!key) {\n return {\n data: null,\n error: { message: \"Key is required\", status: 400 },\n status: 400,\n };\n }\n\n return http.request<S3Metadata>({\n url: `${BASE_URL}/metadata`,\n method: \"get\",\n params: { key },\n });\n },\n\n async upload(file: File | Blob): Promise<ClientResult<UploadRes>> {\n if (!file) {\n return {\n data: null,\n error: { message: \"File is required\", status: 400 },\n status: 400,\n };\n }\n\n const formData = new FormData();\n formData.append(\"file\", file);\n\n return http.request<UploadRes>({\n url: `${BASE_URL}/upload`,\n method: \"post\",\n data: formData,\n headers: {\n \"Content-Type\": \"multipart/form-data\",\n },\n });\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/s3-client.ts"],"names":["BASE_URL","MB","DEFAULT_MULTIPART_THRESHOLD","DEFAULT_PART_SIZE","DEFAULT_CONCURRENCY","createErrorResult","message","status","details","createAbortError","error","getErrorMessage","fallback","resolveFilename","file","resolveContentType","shouldUseMultipartUpload","getHeaderValue","headers","key","headerKey","headerValue","isPromiseLike","value","isMiniProgramRequestResponse","normalizeMiniProgramRequestResponse","callMiniProgramRequest","request","options","signal","resolve","reject","settled","task","cleanup","onAbort","resolveOnce","rejectOnce","taskResult","uploadPartWithFetch","url","blob","response","eTag","uploadPartWithMiniProgram","getMiniProgramRequest","uploadPartToPresignedUrl","detectMiniProgramPlatform","abortMultipartUpload","http","filePath","uploadId","uploadWithMultipart","initResult","totalParts","parts","workerCount","uploadAbortController","nextPartIndex","currentIndex","partNumber","start","end","urlResult","part","completeResult","createS3Client","createHttpClient","filename","formData"],"mappings":"uGASA,IAAMA,CAAAA,CAAW,cAAA,CACXC,CAAAA,CAAK,IAAA,CAAO,IAAA,CACZC,CAAAA,CAA8B,EAAA,CAAKD,CAAAA,CACnCE,CAAAA,CAAoB,CAAA,CAAIF,CAAAA,CACxBG,CAAAA,CAAsB,CAAA,CA4B5B,SAASC,CAAAA,CACPC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACiB,CACjB,OAAO,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CACL,OAAA,CAAAF,CAAAA,CACA,MAAA,CAAAC,CAAAA,CACA,OAAA,CAAAC,CACF,CAAA,CACA,MAAA,CAAAD,CACF,CACF,CAEA,SAASE,CAAAA,EAA0B,CACjC,IAAMC,CAAAA,CAAQ,IAAI,KAAA,CAAM,gBAAgB,CAAA,CACxC,OAAAA,CAAAA,CAAM,IAAA,CAAO,YAAA,CACNA,CACT,CAEA,SAASC,CAAAA,CAAgBD,EAAgBE,CAAAA,CAA0B,CACjE,OAAIF,CAAAA,YAAiB,KAAA,EAASA,CAAAA,CAAM,OAAA,CAC3BA,CAAAA,CAAM,OAAA,CAEX,OAAOA,CAAAA,EAAU,QAAA,EAAYA,CAAAA,CACxBA,CAAAA,CAEFE,CACT,CAEA,SAASC,CAAAA,CAAgBC,CAAAA,CAA2B,CAClD,OAAI,MAAA,GAAUA,CAAAA,EAAQ,OAAOA,CAAAA,CAAK,IAAA,EAAS,QAAA,EAAYA,CAAAA,CAAK,IAAA,CAAK,IAAA,EAAK,CAC7DA,CAAAA,CAAK,KAAK,IAAA,EAAK,CAEjB,MACT,CAEA,SAASC,CAAAA,CAAmBD,CAAAA,CAAuC,CACjE,OAAO,OAAOA,CAAAA,CAAK,IAAA,EAAS,QAAA,EAAYA,CAAAA,CAAK,IAAA,CAAOA,CAAAA,CAAK,KAAO,MAClE,CAEA,SAASE,CAAAA,CAAyBF,CAAAA,CAA4B,CAC5D,OAAOA,CAAAA,CAAK,IAAA,EAAQZ,CACtB,CAEA,SAASe,CAAAA,CAAeC,CAAAA,CAAkBC,CAAAA,CAA4B,CACpE,GAAI,CAACD,CAAAA,CACH,OAAO,IAAA,CAGT,GAAI,OAAO,OAAA,CAAY,GAAA,EAAeA,CAAAA,YAAmB,OAAA,CACvD,OAAOA,CAAAA,CAAQ,GAAA,CAAIC,CAAG,CAAA,CAGxB,GAAI,OAAOD,CAAAA,EAAY,QAAA,CAAA,CACrB,IAAA,GAAW,CAACE,CAAAA,CAAWC,CAAW,CAAA,GAAK,MAAA,CAAO,OAAA,CAC5CH,CACF,CAAA,CACE,GAAIE,CAAAA,CAAU,WAAA,EAAY,GAAMD,EAAI,WAAA,EAAY,CAC9C,OAAOE,CAAAA,EAAe,IAAA,CAAO,IAAA,CAAO,MAAA,CAAOA,CAAW,CAAA,CAK5D,OAAO,IACT,CAEA,SAASC,CAAAA,CAA2BC,CAAAA,CAAyC,CAC3E,OAAA,CACG,OAAOA,CAAAA,EAAU,QAAA,EAAY,OAAOA,CAAAA,EAAU,UAAA,GAC/CA,CAAAA,GAAU,IAAA,EACV,MAAA,GAAUA,CAAAA,EACV,OAAQA,CAAAA,CAA6B,IAAA,EAAS,UAElD,CAEA,SAASC,CAAAA,CACPD,CAAAA,CACqC,CACrC,OACE,OAAOA,CAAAA,EAAU,QAAA,EACjBA,CAAAA,GAAU,IAAA,GACT,YAAA,GAAgBA,CAAAA,EACf,QAAA,GAAYA,CAAAA,EACZ,MAAA,GAAUA,CAAAA,EACV,QAAA,GAAYA,CAAAA,EACZ,SAAA,GAAaA,CAAAA,CAEnB,CAEA,SAASE,CAAAA,CACPF,CAAAA,CAC4B,CAC5B,OAAOC,CAAAA,CAA6BD,CAAK,CAAA,CAAIA,CAAAA,CAAQ,EACvD,CAEA,eAAeG,CAAAA,CACbC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACqC,CACrC,OAAO,MAAM,IAAI,OAAA,CAAoC,CAACC,CAAAA,CAASC,CAAAA,GAAW,CACxE,IAAIC,CAAAA,CAAU,KAAA,CACVC,EAA6B,IAAA,CAE3BC,CAAAA,CAAU,IAAM,CACpBL,CAAAA,EAAQ,mBAAA,CAAoB,OAAA,CAASM,CAAO,EAC9C,CAAA,CAEMC,CAAAA,CAAeb,CAAAA,EAAsC,CACrDS,CAAAA,GAGJA,CAAAA,CAAU,IAAA,CACVE,GAAQ,CACRJ,CAAAA,CAAQP,CAAK,CAAA,EACf,CAAA,CAEMc,CAAAA,CAAc3B,CAAAA,EAAmB,CACjCsB,CAAAA,GAGJA,CAAAA,CAAU,IAAA,CACVE,CAAAA,EAAQ,CACRH,CAAAA,CAAOrB,CAAK,CAAA,EACd,CAAA,CAEMyB,CAAAA,CAAU,IAAM,CACpB,GAAI,CACFF,CAAAA,EAAM,KAAA,KACR,CAAA,KAAQ,CAER,CACAI,CAAAA,CAAW5B,CAAAA,EAAkB,EAC/B,EAEA,GAAIoB,CAAAA,EAAQ,OAAA,CAAS,CACnBQ,CAAAA,CAAW5B,CAAAA,EAAkB,CAAA,CAC7B,MACF,CAEAoB,CAAAA,EAAQ,gBAAA,CAAiB,OAAA,CAASM,CAAAA,CAAS,CAAE,IAAA,CAAM,IAAK,CAAC,CAAA,CAEzD,GAAI,CACF,IAAMG,CAAAA,CAAaX,CAAAA,CAAQ,CACzB,GAAGC,CAAAA,CACH,OAAA,CAAUL,CAAAA,EAAmB,CAC3Ba,CAAAA,CAAYX,CAAAA,CAAoCF,CAAK,CAAC,EACxD,CAAA,CACA,IAAA,CAAMc,CACR,CAAC,CAAA,CAMD,GAJI,OAAOC,CAAAA,EAAe,QAAA,EAAYA,CAAAA,GAAe,IAAA,GACnDL,CAAAA,CAAOK,CAAAA,CAAAA,CAGLhB,CAAAA,CAAcgB,CAAU,CAAA,CAAG,CACxBA,CAAAA,CAAW,IAAA,CACbf,CAAAA,EAAU,CACTa,CAAAA,CAAYX,CAAAA,CAAoCF,CAAK,CAAC,EACxD,CAAA,CACAc,CACF,CAAA,CACA,MACF,CAEIb,CAAAA,CAA6Bc,CAAU,CAAA,EACzCF,CAAAA,CAAYE,CAAU,EAE1B,CAAA,MAAS5B,CAAAA,CAAO,CACd2B,CAAAA,CAAW3B,CAAK,EAClB,CACF,CAAC,CACH,CAEA,eAAe6B,CAAAA,CACbC,CAAAA,CACAC,CAAAA,CACAZ,CAAAA,CACiB,CACjB,GAAI,OAAO,KAAA,EAAU,UAAA,CACnB,MAAM,IAAI,KAAA,CAAM,kDAAkD,CAAA,CAGpE,IAAMa,CAAAA,CAAW,MAAM,KAAA,CAAMF,CAAAA,CAAK,CAChC,MAAA,CAAQ,KAAA,CACR,IAAA,CAAMC,CAAAA,CACN,MAAA,CAAAZ,CACF,CAAC,CAAA,CAED,GAAI,CAACa,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAIlC,CAAAA,CAAU,EAAA,CACd,GAAI,CACFA,CAAAA,CAAU,MAAMkC,CAAAA,CAAS,IAAA,GAC3B,CAAA,KAAQ,CAER,CACA,MAAM,IAAI,KAAA,CACR,iCAAiCA,CAAAA,CAAS,MAAM,CAAA,CAAA,EAAIlC,CAAAA,CAAU,CAAA,EAAA,EAAKA,CAAO,CAAA,CAAA,CAAK,EAAE,CAAA,CACnF,CACF,CAEA,IAAMmC,CAAAA,CAAO1B,CAAAA,CAAeyB,CAAAA,CAAS,OAAA,CAAS,MAAM,CAAA,CACpD,GAAI,CAACC,CAAAA,CACH,MAAM,IAAI,KAAA,CACR,0FACF,CAAA,CAGF,OAAOA,CACT,CAEA,eAAeC,CAAAA,CACbJ,CAAAA,CACAC,CAAAA,CACAZ,EACiB,CACjB,IAAMF,CAAAA,CAAUkB,qBAAAA,EAAsB,CACtC,GAAI,CAAClB,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,iEAAiE,CAAA,CAGnF,IAAMe,CAAAA,CAAW,MAAMhB,CAAAA,CACrBC,CAAAA,CACA,CACE,GAAA,CAAAa,CAAAA,CACA,MAAA,CAAQ,KAAA,CACR,IAAA,CAAM,MAAMC,CAAAA,CAAK,WAAA,EAAY,CAC7B,MAAA,CAAQ,EACV,CAAA,CACAZ,CACF,CAAA,CAEMtB,CAAAA,CAAS,MAAA,CAAOmC,CAAAA,EAAU,UAAA,EAAcA,CAAAA,EAAU,MAAA,EAAU,CAAC,CAAA,CACnE,GAAInC,CAAAA,CAAS,GAAA,EAAOA,CAAAA,EAAU,GAAA,CAAK,CACjC,IAAMC,EACJ,OAAOkC,CAAAA,EAAU,IAAA,EAAS,QAAA,EAAYA,CAAAA,CAAS,IAAA,CAC3C,CAAA,EAAA,EAAKA,CAAAA,CAAS,IAAI,CAAA,CAAA,CAClB,EAAA,CACN,MAAM,IAAI,KAAA,CACR,CAAA,8BAAA,EAAiCnC,CAAAA,EAAU,SAAS,CAAA,CAAA,EAAIC,CAAO,CAAA,CACjE,CACF,CAEA,IAAMmC,CAAAA,CAAO1B,CAAAA,CAAeyB,CAAAA,EAAU,MAAA,EAAUA,CAAAA,EAAU,OAAA,CAAS,MAAM,CAAA,CACzE,GAAI,CAACC,CAAAA,CACH,MAAM,IAAI,KAAA,CACR,0FACF,CAAA,CAGF,OAAOA,CACT,CAEA,eAAeG,CAAAA,CACbN,CAAAA,CACAC,CAAAA,CACAZ,CAAAA,CACiB,CACjB,OAAIkB,2BAA0B,CACrB,MAAMH,CAAAA,CAA0BJ,CAAAA,CAAKC,CAAAA,CAAMZ,CAAM,CAAA,CAGnD,MAAMU,CAAAA,CAAoBC,CAAAA,CAAKC,CAAAA,CAAMZ,CAAM,CACpD,CAEA,eAAemB,CAAAA,CACbC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACe,CACf,GAAI,CACF,MAAMF,CAAAA,CAAK,OAAA,CAAc,CACvB,GAAA,CAAK,CAAA,EAAGjD,CAAQ,CAAA,uBAAA,CAAA,CAChB,MAAA,CAAQ,MAAA,CACR,KAAM,CACJ,QAAA,CAAAkD,CAAAA,CACA,QAAA,CAAAC,CACF,CACF,CAAC,EACH,CAAA,KAAQ,CAER,CACF,CAEA,eAAeC,CAAAA,CACbH,CAAAA,CACAnC,CAAAA,CACkC,CAClC,IAAMuC,CAAAA,CAAa,MAAMJ,CAAAA,CAAK,OAAA,CAA0B,CACtD,GAAA,CAAK,CAAA,EAAGjD,CAAQ,CAAA,sBAAA,CAAA,CAChB,MAAA,CAAQ,MAAA,CACR,IAAA,CAAM,CACJ,QAAA,CAAUa,EAAgBC,CAAI,CAAA,CAC9B,WAAA,CAAaC,CAAAA,CAAmBD,CAAI,CACtC,CACF,CAAC,CAAA,CAED,GAAIuC,CAAAA,CAAW,KAAA,CACb,OAAOhD,CAAAA,CACLgD,CAAAA,CAAW,KAAA,CAAM,QACjBA,CAAAA,CAAW,MAAA,EAAU,GAAA,CACrBA,CAAAA,CAAW,KAAA,CAAM,OACnB,CAAA,CAGF,GAAI,CAACA,CAAAA,CAAW,IAAA,EAAM,QAAA,EAAY,CAACA,CAAAA,CAAW,IAAA,EAAM,QAAA,CAClD,OAAOhD,CAAAA,CACL,qDAAA,CACAgD,CAAAA,CAAW,MAAA,EAAU,GACvB,CAAA,CAGF,GAAM,CAAE,QAAA,CAAAF,CAAAA,CAAU,QAAA,CAAAD,CAAS,CAAA,CAAIG,CAAAA,CAAW,IAAA,CACpCC,CAAAA,CAAa,IAAA,CAAK,GAAA,CAAI,CAAA,CAAG,IAAA,CAAK,IAAA,CAAKxC,CAAAA,CAAK,IAAA,CAAOX,CAAiB,CAAC,CAAA,CACjEoD,CAAAA,CAAoB,IAAI,KAAA,CAAMD,CAAU,CAAA,CACxCE,CAAAA,CAAc,KAAK,GAAA,CAAIpD,CAAAA,CAAqBkD,CAAU,CAAA,CACtDG,CAAAA,CAAwB,IAAI,eAAA,CAC9BC,CAAAA,CAAgB,CAAA,CAEpB,GAAI,CAmDF,GAlDA,MAAM,OAAA,CAAQ,GAAA,CACZ,KAAA,CAAM,KAAK,CAAE,MAAA,CAAQF,CAAY,CAAA,CAAG,SAAY,CAC9C,OAAa,CACX,IAAMG,CAAAA,CAAeD,CAAAA,CAGrB,GAFAA,CAAAA,EAAiB,CAAA,CAEbC,CAAAA,EAAgBL,CAAAA,CAClB,OAGF,IAAMM,CAAAA,CAAaD,CAAAA,CAAe,CAAA,CAC5BE,CAAAA,CAAQF,CAAAA,CAAexD,CAAAA,CACvB2D,CAAAA,CAAM,IAAA,CAAK,GAAA,CAAID,CAAAA,CAAQ1D,CAAAA,CAAmBW,CAAAA,CAAK,IAAI,CAAA,CACnD2B,CAAAA,CAAO3B,CAAAA,CAAK,KAAA,CAAM+C,CAAAA,CAAOC,CAAG,CAAA,CAE5BC,CAAAA,CAAY,MAAMd,CAAAA,CAAK,OAAA,CAAyB,CACpD,GAAA,CAAK,CAAA,EAAGjD,CAAQ,CAAA,qBAAA,CAAA,CAChB,MAAA,CAAQ,MAAA,CACR,KAAM,CACJ,QAAA,CAAAkD,CAAAA,CACA,QAAA,CAAAC,CAAAA,CACA,UAAA,CAAAS,CACF,CAAA,CACA,MAAA,CAAQH,CAAAA,CAAsB,MAChC,CAAC,CAAA,CAED,GAAIM,CAAAA,CAAU,KAAA,CACZ,MAAM,IAAI,KAAA,CAAMA,CAAAA,CAAU,KAAA,CAAM,OAAO,CAAA,CAGzC,GAAI,CAACA,CAAAA,CAAU,IAAA,EAAM,GAAA,CACnB,MAAM,IAAI,KAAA,CACR,CAAA,mEAAA,EAAsEH,CAAU,GAClF,CAAA,CAGF,IAAMjB,CAAAA,CAAO,MAAMG,CAAAA,CACjBiB,CAAAA,CAAU,IAAA,CAAK,GAAA,CACftB,CAAAA,CACAgB,CAAAA,CAAsB,MACxB,CAAA,CAEAF,CAAAA,CAAMI,CAAY,CAAA,CAAI,CACpB,UAAA,CAAAC,CAAAA,CACA,IAAA,CAAAjB,CACF,EACF,CACF,CAAC,CACH,CAAA,CAEIY,CAAAA,CAAM,IAAA,CAAMS,CAAAA,EAAS,CAACA,CAAAA,EAAM,IAAI,CAAA,CAClC,MAAM,IAAI,KAAA,CAAM,2DAA2D,CAAA,CAG7E,IAAMC,CAAAA,CAAiB,MAAMhB,CAAAA,CAAK,OAAA,CAAmB,CACnD,GAAA,CAAK,CAAA,EAAGjD,CAAQ,CAAA,0BAAA,CAAA,CAChB,MAAA,CAAQ,MAAA,CACR,IAAA,CAAM,CACJ,QAAA,CAAAkD,CAAAA,CACA,QAAA,CAAAC,CAAAA,CACA,KAAA,CAAAI,CACF,CACF,CAAC,CAAA,CAED,OAAIU,CAAAA,CAAe,KAAA,EACjB,MAAMjB,CAAAA,CAAqBC,EAAMC,CAAAA,CAAUC,CAAQ,CAAA,CAC5C9C,CAAAA,CACL4D,CAAAA,CAAe,KAAA,CAAM,OAAA,CACrBA,CAAAA,CAAe,MAAA,EAAU,GAAA,CACzBA,CAAAA,CAAe,KAAA,CAAM,OACvB,CAAA,EAGGA,CAAAA,CAAe,IAAA,CAQbA,CAAAA,EAPL,MAAMjB,CAAAA,CAAqBC,CAAAA,CAAMC,CAAAA,CAAUC,CAAQ,CAAA,CAC5C9C,CAAAA,CACL,uDAAA,CACA4D,CAAAA,CAAe,MAAA,EAAU,GAC3B,CAAA,CAIJ,CAAA,MAASvD,CAAAA,CAAO,CACd,OAAA+C,CAAAA,CAAsB,KAAA,EAAM,CAC5B,MAAMT,CAAAA,CAAqBC,CAAAA,CAAMC,CAAAA,CAAUC,CAAQ,CAAA,CAC5C9C,CAAAA,CACLM,CAAAA,CAAgBD,CAAAA,CAAO,yBAAyB,CAAA,CAChDA,CAAAA,YAAiB,KAAA,EAASA,EAAM,IAAA,GAAS,YAAA,CAAe,GAAA,CAAM,GAAA,CAC9DA,CACF,CACF,CACF,CAEO,SAASwD,CAAAA,CAAejB,CAAAA,CAAmBkB,gBAAAA,EAAiB,CAAa,CAC9E,OAAO,CACL,MAAM,QAAA,CAASC,CAAAA,CAA+C,CAC5D,OAAKA,CAAAA,CAQU,MAAMnB,CAAAA,CAAK,OAAA,CAAc,CACtC,GAAA,CAAK,CAAA,EAAGjD,CAAQ,CAAA,SAAA,CAAA,CAChB,MAAA,CAAQ,KAAA,CACR,MAAA,CAAQ,CAAE,QAAA,CAAAoE,CAAS,CAAA,CACnB,YAAA,CAAc,MAChB,CAAC,CAAA,CAZQ,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,OAAA,CAAS,sBAAA,CAAwB,MAAA,CAAQ,GAAI,CAAA,CACtD,MAAA,CAAQ,GACV,CAWJ,CAAA,CAEA,MAAM,WAAA,CAAYjD,CAAAA,CAAgD,CAChE,OAAKA,CAAAA,CAQE8B,CAAAA,CAAK,OAAA,CAAoB,CAC9B,GAAA,CAAK,CAAA,EAAGjD,CAAQ,CAAA,SAAA,CAAA,CAChB,MAAA,CAAQ,KAAA,CACR,MAAA,CAAQ,CAAE,GAAA,CAAAmB,CAAI,CAChB,CAAC,CAAA,CAXQ,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,OAAA,CAAS,kBAAmB,MAAA,CAAQ,GAAI,CAAA,CACjD,MAAA,CAAQ,GACV,CAQJ,CAAA,CAEA,MAAM,MAAA,CAAOL,CAAAA,CAAqD,CAChE,GAAI,CAACA,CAAAA,CACH,OAAO,CACL,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,CAAE,OAAA,CAAS,kBAAA,CAAoB,MAAA,CAAQ,GAAI,CAAA,CAClD,MAAA,CAAQ,GACV,CAAA,CAGF,GAAIE,CAAAA,CAAyBF,CAAI,CAAA,CAC/B,OAAO,MAAMsC,CAAAA,CAAoBH,CAAAA,CAAMnC,CAAI,CAAA,CAG7C,IAAMuD,CAAAA,CAAW,IAAI,QAAA,CACrB,OAAAA,CAAAA,CAAS,MAAA,CAAO,MAAA,CAAQvD,CAAI,CAAA,CAErBmC,CAAAA,CAAK,QAAmB,CAC7B,GAAA,CAAK,CAAA,EAAGjD,CAAQ,CAAA,OAAA,CAAA,CAChB,MAAA,CAAQ,MAAA,CACR,IAAA,CAAMqE,CAAAA,CACN,OAAA,CAAS,CACP,cAAA,CAAgB,qBAClB,CACF,CAAC,CACH,CACF,CACF","file":"index.js","sourcesContent":["import {\n createHttpClient,\n detectMiniProgramPlatform,\n getMiniProgramRequest,\n type ClientResult,\n type HttpClient,\n} from \"@amaster.ai/http-client\";\nimport type { S3Client, S3Metadata, UploadRes } from \"./types\";\n\nconst BASE_URL = \"/api/storage\";\nconst MB = 1024 * 1024;\nconst DEFAULT_MULTIPART_THRESHOLD = 32 * MB;\nconst DEFAULT_PART_SIZE = 8 * MB;\nconst DEFAULT_CONCURRENCY = 3;\n\ntype MultipartInitRes = {\n uploadId: string;\n filePath: string;\n};\n\ntype MultipartUrlRes = {\n url: string;\n};\n\ntype PartETag = {\n partNumber: number;\n eTag: string;\n};\n\ntype MiniProgramRequestResponse = {\n statusCode?: unknown;\n status?: unknown;\n data?: unknown;\n header?: unknown;\n headers?: unknown;\n};\n\ntype AbortableTask = {\n abort?: () => void;\n};\n\nfunction createErrorResult<T>(\n message: string,\n status: number,\n details?: unknown,\n): ClientResult<T> {\n return {\n data: null,\n error: {\n message,\n status,\n details,\n },\n status,\n };\n}\n\nfunction createAbortError(): Error {\n const error = new Error(\"Upload aborted\");\n error.name = \"AbortError\";\n return error;\n}\n\nfunction getErrorMessage(error: unknown, fallback: string): string {\n if (error instanceof Error && error.message) {\n return error.message;\n }\n if (typeof error === \"string\" && error) {\n return error;\n }\n return fallback;\n}\n\nfunction resolveFilename(file: File | Blob): string {\n if (\"name\" in file && typeof file.name === \"string\" && file.name.trim()) {\n return file.name.trim();\n }\n return \"blob\";\n}\n\nfunction resolveContentType(file: File | Blob): string | undefined {\n return typeof file.type === \"string\" && file.type ? file.type : undefined;\n}\n\nfunction shouldUseMultipartUpload(file: File | Blob): boolean {\n return file.size >= DEFAULT_MULTIPART_THRESHOLD;\n}\n\nfunction getHeaderValue(headers: unknown, key: string): string | null {\n if (!headers) {\n return null;\n }\n\n if (typeof Headers !== \"undefined\" && headers instanceof Headers) {\n return headers.get(key);\n }\n\n if (typeof headers === \"object\") {\n for (const [headerKey, headerValue] of Object.entries(\n headers as Record<string, unknown>,\n )) {\n if (headerKey.toLowerCase() === key.toLowerCase()) {\n return headerValue == null ? null : String(headerValue);\n }\n }\n }\n\n return null;\n}\n\nfunction isPromiseLike<T = unknown>(value: unknown): value is PromiseLike<T> {\n return (\n (typeof value === \"object\" || typeof value === \"function\") &&\n value !== null &&\n \"then\" in value &&\n typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\nfunction isMiniProgramRequestResponse(\n value: unknown,\n): value is MiniProgramRequestResponse {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (\"statusCode\" in value ||\n \"status\" in value ||\n \"data\" in value ||\n \"header\" in value ||\n \"headers\" in value)\n );\n}\n\nfunction normalizeMiniProgramRequestResponse(\n value: unknown,\n): MiniProgramRequestResponse {\n return isMiniProgramRequestResponse(value) ? value : {};\n}\n\nasync function callMiniProgramRequest(\n request: (_options: Record<string, unknown>) => unknown,\n options: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<MiniProgramRequestResponse> {\n return await new Promise<MiniProgramRequestResponse>((resolve, reject) => {\n let settled = false;\n let task: AbortableTask | null = null;\n\n const cleanup = () => {\n signal?.removeEventListener(\"abort\", onAbort);\n };\n\n const resolveOnce = (value: MiniProgramRequestResponse) => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n resolve(value);\n };\n\n const rejectOnce = (error: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n reject(error);\n };\n\n const onAbort = () => {\n try {\n task?.abort?.();\n } catch {\n // ignore abort failures from runtime-specific request tasks\n }\n rejectOnce(createAbortError());\n };\n\n if (signal?.aborted) {\n rejectOnce(createAbortError());\n return;\n }\n\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const taskResult = request({\n ...options,\n success: (value: unknown) => {\n resolveOnce(normalizeMiniProgramRequestResponse(value));\n },\n fail: rejectOnce,\n });\n\n if (typeof taskResult === \"object\" && taskResult !== null) {\n task = taskResult as AbortableTask;\n }\n\n if (isPromiseLike(taskResult)) {\n void taskResult.then(\n (value) => {\n resolveOnce(normalizeMiniProgramRequestResponse(value));\n },\n rejectOnce,\n );\n return;\n }\n\n if (isMiniProgramRequestResponse(taskResult)) {\n resolveOnce(taskResult);\n }\n } catch (error) {\n rejectOnce(error);\n }\n });\n}\n\nasync function uploadPartWithFetch(\n url: string,\n blob: Blob,\n signal?: AbortSignal,\n): Promise<string> {\n if (typeof fetch !== \"function\") {\n throw new Error(\"Fetch API is not available for multipart upload.\");\n }\n\n const response = await fetch(url, {\n method: \"PUT\",\n body: blob,\n signal,\n });\n\n if (!response.ok) {\n let details = \"\";\n try {\n details = await response.text();\n } catch {\n // ignore response body parse failures\n }\n throw new Error(\n `Multipart part upload failed (${response.status})${details ? `: ${details}` : \"\"}`,\n );\n }\n\n const eTag = getHeaderValue(response.headers, \"etag\");\n if (!eTag) {\n throw new Error(\n \"Multipart upload response missing ETag header. Check object storage CORS expose headers.\",\n );\n }\n\n return eTag;\n}\n\nasync function uploadPartWithMiniProgram(\n url: string,\n blob: Blob,\n signal?: AbortSignal,\n): Promise<string> {\n const request = getMiniProgramRequest();\n if (!request) {\n throw new Error(\"Mini-program request API is not available for multipart upload.\");\n }\n\n const response = await callMiniProgramRequest(\n request,\n {\n url,\n method: \"PUT\",\n data: await blob.arrayBuffer(),\n header: {},\n },\n signal,\n );\n\n const status = Number(response?.statusCode ?? response?.status ?? 0);\n if (status < 200 || status >= 300) {\n const details =\n typeof response?.data === \"string\" && response.data\n ? `: ${response.data}`\n : \"\";\n throw new Error(\n `Multipart part upload failed (${status || \"unknown\"})${details}`,\n );\n }\n\n const eTag = getHeaderValue(response?.header ?? response?.headers, \"etag\");\n if (!eTag) {\n throw new Error(\n \"Multipart upload response missing ETag header. Check object storage CORS expose headers.\",\n );\n }\n\n return eTag;\n}\n\nasync function uploadPartToPresignedUrl(\n url: string,\n blob: Blob,\n signal?: AbortSignal,\n): Promise<string> {\n if (detectMiniProgramPlatform()) {\n return await uploadPartWithMiniProgram(url, blob, signal);\n }\n\n return await uploadPartWithFetch(url, blob, signal);\n}\n\nasync function abortMultipartUpload(\n http: HttpClient,\n filePath: string,\n uploadId: string,\n): Promise<void> {\n try {\n await http.request<void>({\n url: `${BASE_URL}/upload/multipart/abort`,\n method: \"post\",\n data: {\n filePath,\n uploadId,\n },\n });\n } catch {\n // Best-effort cleanup only.\n }\n}\n\nasync function uploadWithMultipart(\n http: HttpClient,\n file: File | Blob,\n): Promise<ClientResult<UploadRes>> {\n const initResult = await http.request<MultipartInitRes>({\n url: `${BASE_URL}/upload/multipart/init`,\n method: \"post\",\n data: {\n filename: resolveFilename(file),\n contentType: resolveContentType(file),\n },\n });\n\n if (initResult.error) {\n return createErrorResult(\n initResult.error.message,\n initResult.status || 500,\n initResult.error.details,\n );\n }\n\n if (!initResult.data?.uploadId || !initResult.data?.filePath) {\n return createErrorResult(\n \"Multipart upload init returned an invalid response.\",\n initResult.status || 500,\n );\n }\n\n const { uploadId, filePath } = initResult.data;\n const totalParts = Math.max(1, Math.ceil(file.size / DEFAULT_PART_SIZE));\n const parts: PartETag[] = new Array(totalParts);\n const workerCount = Math.min(DEFAULT_CONCURRENCY, totalParts);\n const uploadAbortController = new AbortController();\n let nextPartIndex = 0;\n\n try {\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n while (true) {\n const currentIndex = nextPartIndex;\n nextPartIndex += 1;\n\n if (currentIndex >= totalParts) {\n return;\n }\n\n const partNumber = currentIndex + 1;\n const start = currentIndex * DEFAULT_PART_SIZE;\n const end = Math.min(start + DEFAULT_PART_SIZE, file.size);\n const blob = file.slice(start, end);\n\n const urlResult = await http.request<MultipartUrlRes>({\n url: `${BASE_URL}/upload/multipart/url`,\n method: \"post\",\n data: {\n filePath,\n uploadId,\n partNumber,\n },\n signal: uploadAbortController.signal,\n });\n\n if (urlResult.error) {\n throw new Error(urlResult.error.message);\n }\n\n if (!urlResult.data?.url) {\n throw new Error(\n `Multipart upload URL request returned an invalid response for part ${partNumber}.`,\n );\n }\n\n const eTag = await uploadPartToPresignedUrl(\n urlResult.data.url,\n blob,\n uploadAbortController.signal,\n );\n\n parts[currentIndex] = {\n partNumber,\n eTag,\n };\n }\n }),\n );\n\n if (parts.some((part) => !part?.eTag)) {\n throw new Error(\"Multipart upload did not collect all uploaded part ETags.\");\n }\n\n const completeResult = await http.request<UploadRes>({\n url: `${BASE_URL}/upload/multipart/complete`,\n method: \"post\",\n data: {\n filePath,\n uploadId,\n parts,\n },\n });\n\n if (completeResult.error) {\n await abortMultipartUpload(http, filePath, uploadId);\n return createErrorResult(\n completeResult.error.message,\n completeResult.status || 500,\n completeResult.error.details,\n );\n }\n\n if (!completeResult.data) {\n await abortMultipartUpload(http, filePath, uploadId);\n return createErrorResult(\n \"Multipart upload complete returned an empty response.\",\n completeResult.status || 500,\n );\n }\n\n return completeResult;\n } catch (error) {\n uploadAbortController.abort();\n await abortMultipartUpload(http, filePath, uploadId);\n return createErrorResult(\n getErrorMessage(error, \"Multipart upload failed\"),\n error instanceof Error && error.name === \"AbortError\" ? 499 : 500,\n error,\n );\n }\n}\n\nexport function createS3Client(http: HttpClient = createHttpClient()): S3Client {\n return {\n async download(filename: string): Promise<ClientResult<Blob>> {\n if (!filename) {\n return {\n data: null,\n error: { message: \"Filename is required\", status: 400 },\n status: 400,\n };\n }\n\n const result = await http.request<Blob>({\n url: `${BASE_URL}/download`,\n method: \"get\",\n params: { filename },\n responseType: \"blob\",\n });\n\n return result;\n },\n\n async getMetadata(key: string): Promise<ClientResult<S3Metadata>> {\n if (!key) {\n return {\n data: null,\n error: { message: \"Key is required\", status: 400 },\n status: 400,\n };\n }\n\n return http.request<S3Metadata>({\n url: `${BASE_URL}/metadata`,\n method: \"get\",\n params: { key },\n });\n },\n\n async upload(file: File | Blob): Promise<ClientResult<UploadRes>> {\n if (!file) {\n return {\n data: null,\n error: { message: \"File is required\", status: 400 },\n status: 400,\n };\n }\n\n if (shouldUseMultipartUpload(file)) {\n return await uploadWithMultipart(http, file);\n }\n\n const formData = new FormData();\n formData.append(\"file\", file);\n\n return http.request<UploadRes>({\n url: `${BASE_URL}/upload`,\n method: \"post\",\n data: formData,\n headers: {\n \"Content-Type\": \"multipart/form-data\",\n },\n });\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amaster.ai/s3-client",
|
|
3
|
-
"version": "1.1.14
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"description": "S3 storage client for file upload, download and management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"registry": "https://registry.npmjs.org/"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@amaster.ai/http-client": "1.1.14
|
|
35
|
+
"@amaster.ai/http-client": "1.1.14"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"axios": "^1.11.0"
|