connectbase-client 3.49.0 → 3.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +7 -0
- package/dist/connect-base.umd.js +5 -5
- package/dist/index.d.mts +75 -3
- package/dist/index.d.ts +75 -3
- package/dist/index.js +110 -7
- package/dist/index.mjs +110 -7
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2251,18 +2251,27 @@ var StorageAPI = class {
|
|
|
2251
2251
|
* Presigned URL 로 직접 업로드. 호출 전에 URL 스킴(https)을 검증해
|
|
2252
2252
|
* 서버 응답을 그대로 신뢰하는 SSRF/오용 경로를 차단.
|
|
2253
2253
|
* 타임아웃과 외부 signal 을 모두 지원한다.
|
|
2254
|
+
*
|
|
2255
|
+
* `onProgress` 가 주어지고 `XMLHttpRequest` 를 쓸 수 있는(브라우저) 환경이면
|
|
2256
|
+
* XHR 의 `upload.onprogress` 로 바이트 단위 진행률을 보고한다. 그 외에는
|
|
2257
|
+
* fetch 로 업로드하고 시작(0%)/완료(100%)만 통지한다(Node/Edge 호환).
|
|
2254
2258
|
*/
|
|
2255
2259
|
async uploadToPresigned(rawUrl, file, options) {
|
|
2256
2260
|
const parsed = validateExternalUrl(rawUrl, {
|
|
2257
2261
|
allowLocalhost: isLocalhostOrigin(),
|
|
2258
2262
|
context: "storage.presigned-url"
|
|
2259
2263
|
});
|
|
2264
|
+
const url = parsed.toString();
|
|
2265
|
+
if (options?.onProgress && typeof XMLHttpRequest !== "undefined") {
|
|
2266
|
+
return this.putViaXhr(url, file, options.onProgress, options);
|
|
2267
|
+
}
|
|
2260
2268
|
const { signal, cleanup } = createTimeoutController({
|
|
2261
2269
|
timeout: options?.timeout,
|
|
2262
2270
|
signal: options?.signal
|
|
2263
2271
|
});
|
|
2264
2272
|
try {
|
|
2265
|
-
|
|
2273
|
+
options?.onProgress?.({ loaded: 0, total: file.size, percentage: 0 });
|
|
2274
|
+
const response = await fetch(url, {
|
|
2266
2275
|
method: "PUT",
|
|
2267
2276
|
body: file,
|
|
2268
2277
|
headers: {
|
|
@@ -2277,10 +2286,79 @@ var StorageAPI = class {
|
|
|
2277
2286
|
"PRESIGNED_UPLOAD_FAILED"
|
|
2278
2287
|
);
|
|
2279
2288
|
}
|
|
2289
|
+
options?.onProgress?.({ loaded: file.size, total: file.size, percentage: 100 });
|
|
2280
2290
|
} finally {
|
|
2281
2291
|
cleanup();
|
|
2282
2292
|
}
|
|
2283
2293
|
}
|
|
2294
|
+
/**
|
|
2295
|
+
* XMLHttpRequest 기반 PUT 업로드. `upload.onprogress` 로 바이트 진행률을
|
|
2296
|
+
* 보고하며, 외부 signal(취소)과 타임아웃을 모두 지원한다. URL 은 호출 전에
|
|
2297
|
+
* 이미 검증된 값을 받는다(uploadToPresigned 에서 스킴 검증 수행).
|
|
2298
|
+
*/
|
|
2299
|
+
putViaXhr(url, file, onProgress, options) {
|
|
2300
|
+
return new Promise((resolve, reject) => {
|
|
2301
|
+
const external = options?.signal;
|
|
2302
|
+
if (external?.aborted) {
|
|
2303
|
+
reject(external.reason ?? new DOMException("Aborted", "AbortError"));
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
const xhr = new XMLHttpRequest();
|
|
2307
|
+
xhr.open("PUT", url, true);
|
|
2308
|
+
xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream");
|
|
2309
|
+
const timeout = options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
2310
|
+
if (timeout > 0 && Number.isFinite(timeout)) {
|
|
2311
|
+
xhr.timeout = timeout;
|
|
2312
|
+
}
|
|
2313
|
+
let abortListener = null;
|
|
2314
|
+
const cleanup = () => {
|
|
2315
|
+
if (external && abortListener) {
|
|
2316
|
+
external.removeEventListener("abort", abortListener);
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
if (external) {
|
|
2320
|
+
abortListener = () => xhr.abort();
|
|
2321
|
+
external.addEventListener("abort", abortListener, { once: true });
|
|
2322
|
+
}
|
|
2323
|
+
xhr.upload.onprogress = (event) => {
|
|
2324
|
+
if (!event.lengthComputable) return;
|
|
2325
|
+
const total = event.total || file.size;
|
|
2326
|
+
onProgress({
|
|
2327
|
+
loaded: event.loaded,
|
|
2328
|
+
total,
|
|
2329
|
+
percentage: total > 0 ? Math.round(event.loaded / total * 100) : 0
|
|
2330
|
+
});
|
|
2331
|
+
};
|
|
2332
|
+
xhr.onload = () => {
|
|
2333
|
+
cleanup();
|
|
2334
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
2335
|
+
onProgress({ loaded: file.size, total: file.size, percentage: 100 });
|
|
2336
|
+
resolve();
|
|
2337
|
+
} else {
|
|
2338
|
+
reject(
|
|
2339
|
+
new ApiError(
|
|
2340
|
+
xhr.status,
|
|
2341
|
+
`Upload failed: ${xhr.statusText || "unknown error"}`,
|
|
2342
|
+
"PRESIGNED_UPLOAD_FAILED"
|
|
2343
|
+
)
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
xhr.onerror = () => {
|
|
2348
|
+
cleanup();
|
|
2349
|
+
reject(new ApiError(0, "Upload failed: network error", "PRESIGNED_UPLOAD_FAILED"));
|
|
2350
|
+
};
|
|
2351
|
+
xhr.ontimeout = () => {
|
|
2352
|
+
cleanup();
|
|
2353
|
+
reject(new DOMException(`Request timed out after ${timeout}ms`, "TimeoutError"));
|
|
2354
|
+
};
|
|
2355
|
+
xhr.onabort = () => {
|
|
2356
|
+
cleanup();
|
|
2357
|
+
reject(external?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
2358
|
+
};
|
|
2359
|
+
xhr.send(file);
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2284
2362
|
/**
|
|
2285
2363
|
* 파일 목록 조회
|
|
2286
2364
|
*
|
|
@@ -2310,14 +2388,28 @@ var StorageAPI = class {
|
|
|
2310
2388
|
*
|
|
2311
2389
|
* 서버를 거치지 않고 Object Storage에 직접 업로드합니다.
|
|
2312
2390
|
*
|
|
2391
|
+
* 3번째 인자는 부모 폴더 ID(문자열) 또는 옵션 객체를 받습니다. 옵션으로
|
|
2392
|
+
* `onProgress` 콜백을 넘기면 업로드 진행률(%)을 실시간으로 받을 수 있습니다
|
|
2393
|
+
* (브라우저 환경). 기존 `uploadFile(id, file, 'folder-id')` 호출과 하위 호환됩니다.
|
|
2394
|
+
*
|
|
2313
2395
|
* @example
|
|
2314
2396
|
* ```ts
|
|
2315
2397
|
* const fileInput = document.querySelector('input[type="file"]')
|
|
2316
2398
|
* const result = await cb.storage.uploadFile('storage-id', fileInput.files[0])
|
|
2317
2399
|
* console.log(result.url) // 업로드된 파일 URL
|
|
2400
|
+
*
|
|
2401
|
+
* // 특정 폴더에 업로드 (기존 방식 그대로)
|
|
2402
|
+
* await cb.storage.uploadFile('storage-id', file, 'folder-id')
|
|
2403
|
+
*
|
|
2404
|
+
* // 진행률 표시 + 특정 폴더
|
|
2405
|
+
* await cb.storage.uploadFile('storage-id', file, {
|
|
2406
|
+
* parentId: 'folder-id',
|
|
2407
|
+
* onProgress: (p) => console.log(`${p.percentage}%`), // { loaded, total, percentage }
|
|
2408
|
+
* })
|
|
2318
2409
|
* ```
|
|
2319
2410
|
*/
|
|
2320
|
-
async uploadFile(storageId, file,
|
|
2411
|
+
async uploadFile(storageId, file, parentIdOrOptions) {
|
|
2412
|
+
const options = typeof parentIdOrOptions === "string" ? { parentId: parentIdOrOptions } : parentIdOrOptions ?? {};
|
|
2321
2413
|
const prefix = this.getPublicPrefix();
|
|
2322
2414
|
const presignedResponse = await this.http.post(
|
|
2323
2415
|
`${prefix}/storages/files/${storageId}/presigned-url`,
|
|
@@ -2325,10 +2417,14 @@ var StorageAPI = class {
|
|
|
2325
2417
|
file_name: file.name,
|
|
2326
2418
|
file_size: file.size,
|
|
2327
2419
|
mime_type: file.type || "application/octet-stream",
|
|
2328
|
-
parent_id: parentId
|
|
2420
|
+
parent_id: options.parentId
|
|
2329
2421
|
}
|
|
2330
2422
|
);
|
|
2331
|
-
await this.uploadToPresigned(presignedResponse.upload_url, file
|
|
2423
|
+
await this.uploadToPresigned(presignedResponse.upload_url, file, {
|
|
2424
|
+
onProgress: options.onProgress,
|
|
2425
|
+
signal: options.signal,
|
|
2426
|
+
timeout: options.timeout
|
|
2427
|
+
});
|
|
2332
2428
|
const completeResponse = await this.http.post(
|
|
2333
2429
|
`${prefix}/storages/files/${storageId}/complete-upload`,
|
|
2334
2430
|
{ file_id: presignedResponse.file_id }
|
|
@@ -2347,11 +2443,14 @@ var StorageAPI = class {
|
|
|
2347
2443
|
}
|
|
2348
2444
|
/**
|
|
2349
2445
|
* 여러 파일 업로드
|
|
2446
|
+
*
|
|
2447
|
+
* `onProgress` 를 옵션으로 넘기면 **파일별로** 진행률 콜백이 호출됩니다
|
|
2448
|
+
* (각 파일마다 0→100%). 전체 진행률이 필요하면 완료된 파일 수를 직접 집계하세요.
|
|
2350
2449
|
*/
|
|
2351
|
-
async uploadFiles(storageId, files,
|
|
2450
|
+
async uploadFiles(storageId, files, parentIdOrOptions) {
|
|
2352
2451
|
const results = [];
|
|
2353
2452
|
for (const file of files) {
|
|
2354
|
-
const result = await this.uploadFile(storageId, file,
|
|
2453
|
+
const result = await this.uploadFile(storageId, file, parentIdOrOptions);
|
|
2355
2454
|
results.push(result);
|
|
2356
2455
|
}
|
|
2357
2456
|
return results;
|
|
@@ -2450,7 +2549,11 @@ var StorageAPI = class {
|
|
|
2450
2549
|
overwrite
|
|
2451
2550
|
}
|
|
2452
2551
|
);
|
|
2453
|
-
await this.uploadToPresigned(presignedResponse.upload_url, file
|
|
2552
|
+
await this.uploadToPresigned(presignedResponse.upload_url, file, {
|
|
2553
|
+
onProgress: options?.onProgress,
|
|
2554
|
+
signal: options?.signal,
|
|
2555
|
+
timeout: options?.timeout
|
|
2556
|
+
});
|
|
2454
2557
|
const completeResponse = await this.http.post(
|
|
2455
2558
|
`${prefix}/storages/files/${storageId}/complete-upload`,
|
|
2456
2559
|
{ file_id: presignedResponse.file_id }
|