deepspace 0.12.0 → 0.13.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 +153 -0
- package/dist/{chunk-ZJ43EK3N.js → chunk-233GQU2H.js} +492 -365
- package/dist/chunk-233GQU2H.js.map +1 -0
- package/dist/cli.js +441 -167
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.d.ts +2 -2
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +34 -34
- package/dist/documentation-react.d.ts +1 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +10 -10
- package/dist/documentation-server-core.d.ts +2 -2
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.js +2 -2
- package/dist/documentation.js.map +1 -1
- package/dist/index.d.ts +28 -3
- package/dist/index.js +86 -8
- package/dist/index.js.map +1 -1
- package/dist/{public-BGRX_Cjd.d.ts → public-BtArwbVM.d.ts} +8 -0
- package/dist/server.d.ts +94 -23
- package/dist/server.js +340 -87
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +95 -24
- package/dist/worker.js +345 -87
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZJ43EK3N.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -3636,6 +3636,12 @@ declare function toConnectionStatus(queryStatus: string): ConnectionStatus;
|
|
|
3636
3636
|
*
|
|
3637
3637
|
* Provides upload, download, list, and delete for files in R2.
|
|
3638
3638
|
*
|
|
3639
|
+
* Uploads pick their own transport. A small file goes in one request; a large
|
|
3640
|
+
* one is chunked into parts with `Blob.slice`, so peak memory is bounded by
|
|
3641
|
+
* the PART SIZE rather than by the file — a 1 GiB video costs one part, not a
|
|
3642
|
+
* gibibyte — and the file ceiling becomes a product decision rather than a
|
|
3643
|
+
* request-size accident. Callers do nothing differently either way.
|
|
3644
|
+
*
|
|
3639
3645
|
* Two scopes are supported via the `scope` option:
|
|
3640
3646
|
* - `'self'` (default): per-user storage. Reads require auth.
|
|
3641
3647
|
* - `'app'`: app-wide storage. Reads are public — the upload URL works
|
|
@@ -3668,10 +3674,29 @@ interface R2FileInfo {
|
|
|
3668
3674
|
originalName?: string;
|
|
3669
3675
|
uploadedBy?: string;
|
|
3670
3676
|
}
|
|
3677
|
+
/** Told after each part lands, so a long upload can drive a progress bar. */
|
|
3678
|
+
type UploadProgress = (sent: number, total: number) => void;
|
|
3679
|
+
interface UploadOptions {
|
|
3680
|
+
onProgress?: UploadProgress;
|
|
3681
|
+
}
|
|
3671
3682
|
interface UseR2FilesReturn {
|
|
3672
|
-
/**
|
|
3673
|
-
|
|
3674
|
-
|
|
3683
|
+
/**
|
|
3684
|
+
* Upload a File or Blob. Optionally provide a display name.
|
|
3685
|
+
*
|
|
3686
|
+
* A file above the single-request bound is chunked automatically and sent as
|
|
3687
|
+
* parts — nothing to opt into, and nothing held in memory beyond one part.
|
|
3688
|
+
* Pass `onProgress` to follow a large one.
|
|
3689
|
+
*/
|
|
3690
|
+
upload: (file: File | Blob, name?: string, options?: UploadOptions) => Promise<R2UploadResult>;
|
|
3691
|
+
/**
|
|
3692
|
+
* Upload raw base64 data.
|
|
3693
|
+
*
|
|
3694
|
+
* Single-request only: base64 has to be materialized whole to be decoded, so
|
|
3695
|
+
* this path keeps the one-request bound rather than the file ceiling — and
|
|
3696
|
+
* because the request carries the ENCODED string, its real budget is
|
|
3697
|
+
* `MAX_BASE64_UPLOAD_BYTES` (~18.75 MiB decoded), not the full 25 MiB. Send a
|
|
3698
|
+
* Blob through `upload` for anything larger; it chunks.
|
|
3699
|
+
*/
|
|
3675
3700
|
uploadBase64: (base64Data: string, name: string, mimeType?: string) => Promise<R2UploadResult>;
|
|
3676
3701
|
/**
|
|
3677
3702
|
* Delete a file. Accepts an R2FileInfo from `list()` or a raw key string.
|
package/dist/index.js
CHANGED
|
@@ -3521,8 +3521,25 @@ function toConnectionStatus(queryStatus) {
|
|
|
3521
3521
|
import { useCallback as useCallback15, useState as useState13, useMemo as useMemo11 } from "react";
|
|
3522
3522
|
|
|
3523
3523
|
// src/shared/app-files.ts
|
|
3524
|
-
var MAX_APP_FILE_BYTES =
|
|
3524
|
+
var MAX_APP_FILE_BYTES = 1024 * 1024 * 1024;
|
|
3525
|
+
var MAX_UPLOAD_REQUEST_BYTES = 25 * 1024 * 1024;
|
|
3526
|
+
var UPLOAD_PART_BYTES = 20 * 1024 * 1024;
|
|
3527
|
+
var MAX_UPLOAD_PARTS = Math.ceil(MAX_APP_FILE_BYTES / UPLOAD_PART_BYTES);
|
|
3528
|
+
var MAX_BASE64_UPLOAD_BYTES = Math.floor(MAX_UPLOAD_REQUEST_BYTES * 3 / 4);
|
|
3529
|
+
function planUploadParts(totalBytes, partSize) {
|
|
3530
|
+
const parts = [];
|
|
3531
|
+
for (let start = 0; start < totalBytes; start += partSize) {
|
|
3532
|
+
parts.push({
|
|
3533
|
+
partNumber: parts.length + 1,
|
|
3534
|
+
start,
|
|
3535
|
+
end: Math.min(start + partSize, totalBytes)
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
return parts;
|
|
3539
|
+
}
|
|
3540
|
+
var MAX_DEPLOY_ASSET_FILE_BYTES = 25 * 1024 * 1024;
|
|
3525
3541
|
function formatBytes(bytes) {
|
|
3542
|
+
if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GiB`;
|
|
3526
3543
|
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
3527
3544
|
if (bytes >= 1024) return `${Math.round(bytes / 1024)} KiB`;
|
|
3528
3545
|
return `${bytes} B`;
|
|
@@ -3530,8 +3547,8 @@ function formatBytes(bytes) {
|
|
|
3530
3547
|
function encodeKeyPath(key) {
|
|
3531
3548
|
return key.split("/").map(encodeURIComponent).join("/");
|
|
3532
3549
|
}
|
|
3533
|
-
function oversizeMessage(bytes) {
|
|
3534
|
-
return `That file is ${formatBytes(bytes)}; the limit is ${formatBytes(
|
|
3550
|
+
function oversizeMessage(bytes, limit = MAX_APP_FILE_BYTES) {
|
|
3551
|
+
return `That file is ${formatBytes(bytes)}; the limit is ${formatBytes(limit)} per file.`;
|
|
3535
3552
|
}
|
|
3536
3553
|
function describeFilesFailure(status, body) {
|
|
3537
3554
|
const trimmed = body.trim();
|
|
@@ -3543,8 +3560,9 @@ function describeFilesFailure(status, body) {
|
|
|
3543
3560
|
}
|
|
3544
3561
|
}
|
|
3545
3562
|
if (status === 413) {
|
|
3546
|
-
return `The
|
|
3563
|
+
return `The upload is too large. Files are limited to ${formatBytes(MAX_APP_FILE_BYTES)}, and one request may carry ${formatBytes(MAX_UPLOAD_REQUEST_BYTES)} \u2014 a larger request is rejected by the network before the app receives it.`;
|
|
3547
3564
|
}
|
|
3565
|
+
if (status === 411) return "The upload did not declare its length.";
|
|
3548
3566
|
if (status === 401 || status === 403) return "Not authorized to write these files.";
|
|
3549
3567
|
if (status === 415) return "That file type is not accepted.";
|
|
3550
3568
|
if (status >= 500) return `The files service failed (HTTP ${status}). Retry shortly.`;
|
|
@@ -3552,8 +3570,8 @@ function describeFilesFailure(status, body) {
|
|
|
3552
3570
|
}
|
|
3553
3571
|
|
|
3554
3572
|
// src/client/storage/useR2Files.ts
|
|
3555
|
-
function oversize(bytes) {
|
|
3556
|
-
return bytes >
|
|
3573
|
+
function oversize(bytes, limit = MAX_APP_FILE_BYTES) {
|
|
3574
|
+
return bytes > limit ? { success: false, error: oversizeMessage(bytes, limit) } : null;
|
|
3557
3575
|
}
|
|
3558
3576
|
async function readUploadResult(response) {
|
|
3559
3577
|
const text = await response.text();
|
|
@@ -3572,6 +3590,58 @@ function resolveFile(fileOrKey) {
|
|
|
3572
3590
|
}
|
|
3573
3591
|
return { key: fileOrKey.key, name: fileOrKey.originalName };
|
|
3574
3592
|
}
|
|
3593
|
+
async function uploadInParts(file, name, scopeParams, authHeaders, onProgress) {
|
|
3594
|
+
const headers = await authHeaders();
|
|
3595
|
+
const json = { ...headers, "Content-Type": "application/json" };
|
|
3596
|
+
const initResponse = await fetch(`/api/files/multipart?${scopeParams}`, {
|
|
3597
|
+
method: "POST",
|
|
3598
|
+
headers: json,
|
|
3599
|
+
body: JSON.stringify({ name, mimeType: file.type || "application/octet-stream", size: file.size })
|
|
3600
|
+
});
|
|
3601
|
+
if (!initResponse.ok) return await readUploadResult(initResponse);
|
|
3602
|
+
const session = await initResponse.json();
|
|
3603
|
+
if (!Number.isSafeInteger(session.partSize) || session.partSize < 1) {
|
|
3604
|
+
return {
|
|
3605
|
+
success: false,
|
|
3606
|
+
error: `The files service advertised an unusable part size (${session.partSize}).`
|
|
3607
|
+
};
|
|
3608
|
+
}
|
|
3609
|
+
const query = `${scopeParams}&uploadKey=${encodeKeyPath(session.uploadKey)}&uploadId=${encodeURIComponent(session.uploadId)}`;
|
|
3610
|
+
const plan = planUploadParts(file.size, session.partSize);
|
|
3611
|
+
const parts = [];
|
|
3612
|
+
let assembled = false;
|
|
3613
|
+
try {
|
|
3614
|
+
for (const part of plan) {
|
|
3615
|
+
const send = () => fetch(`/api/files/multipart/part?${query}&partNumber=${part.partNumber}`, {
|
|
3616
|
+
method: "PUT",
|
|
3617
|
+
headers,
|
|
3618
|
+
body: file.slice(part.start, part.end)
|
|
3619
|
+
});
|
|
3620
|
+
let response = await send().catch(() => null);
|
|
3621
|
+
if (!response || response.status === 429 || response.status >= 500) {
|
|
3622
|
+
const retried = await send().catch(() => null);
|
|
3623
|
+
if (retried) response = retried;
|
|
3624
|
+
}
|
|
3625
|
+
if (!response) return { success: false, error: "The files service could not be reached." };
|
|
3626
|
+
if (!response.ok) return await readUploadResult(response);
|
|
3627
|
+
parts.push(await response.json());
|
|
3628
|
+
onProgress?.(part.end, file.size);
|
|
3629
|
+
}
|
|
3630
|
+
const completed = await fetch(`/api/files/multipart/complete?${query}`, {
|
|
3631
|
+
method: "POST",
|
|
3632
|
+
headers: json,
|
|
3633
|
+
body: JSON.stringify({ parts })
|
|
3634
|
+
});
|
|
3635
|
+
assembled = completed.ok;
|
|
3636
|
+
return await readUploadResult(completed);
|
|
3637
|
+
} finally {
|
|
3638
|
+
if (!assembled) {
|
|
3639
|
+
await fetch(`/api/files/multipart?${query}`, { method: "DELETE", headers }).catch(
|
|
3640
|
+
() => void 0
|
|
3641
|
+
);
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3575
3645
|
function useR2Files(options) {
|
|
3576
3646
|
const [isUploading, setIsUploading] = useState13(false);
|
|
3577
3647
|
const scope = options?.scope ?? "self";
|
|
@@ -3588,11 +3658,15 @@ function useR2Files(options) {
|
|
|
3588
3658
|
return {};
|
|
3589
3659
|
}, []);
|
|
3590
3660
|
const upload = useCallback15(
|
|
3591
|
-
async (file, name) => {
|
|
3661
|
+
async (file, name, options2) => {
|
|
3592
3662
|
setIsUploading(true);
|
|
3593
3663
|
try {
|
|
3594
3664
|
const tooBig = oversize(file.size);
|
|
3595
3665
|
if (tooBig) return tooBig;
|
|
3666
|
+
const fileName = name ?? (file instanceof File ? file.name : "upload");
|
|
3667
|
+
if (file.size > UPLOAD_PART_BYTES) {
|
|
3668
|
+
return await uploadInParts(file, fileName, scopeParams, authHeaders, options2?.onProgress);
|
|
3669
|
+
}
|
|
3596
3670
|
const headers = await authHeaders();
|
|
3597
3671
|
const formData = new FormData();
|
|
3598
3672
|
formData.append("file", file);
|
|
@@ -3602,6 +3676,7 @@ function useR2Files(options) {
|
|
|
3602
3676
|
headers,
|
|
3603
3677
|
body: formData
|
|
3604
3678
|
});
|
|
3679
|
+
options2?.onProgress?.(file.size, file.size);
|
|
3605
3680
|
return await readUploadResult(response);
|
|
3606
3681
|
} catch (err) {
|
|
3607
3682
|
return { success: false, error: err instanceof Error ? err.message : "Upload failed" };
|
|
@@ -3615,7 +3690,10 @@ function useR2Files(options) {
|
|
|
3615
3690
|
async (base64Data, name, mimeType) => {
|
|
3616
3691
|
setIsUploading(true);
|
|
3617
3692
|
try {
|
|
3618
|
-
const tooBig = oversize(
|
|
3693
|
+
const tooBig = oversize(
|
|
3694
|
+
Math.floor(base64Data.length * 3 / 4),
|
|
3695
|
+
MAX_BASE64_UPLOAD_BYTES
|
|
3696
|
+
);
|
|
3619
3697
|
if (tooBig) return tooBig;
|
|
3620
3698
|
const headers = await authHeaders();
|
|
3621
3699
|
const response = await fetch(`/api/files/upload?${scopeParams}`, {
|