aspect-sync 0.1.11 → 0.1.13
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 +8 -6
- package/dist/index.js +12 -169
- package/package.json +11 -7
- package/dist/batchManager.d.ts +0 -21
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js +0 -121
- package/dist/batchManager.js.map +0 -1
- package/dist/cliDisplay.d.ts +0 -9
- package/dist/cliDisplay.d.ts.map +0 -1
- package/dist/cliDisplay.js +0 -302
- package/dist/cliDisplay.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/weightedAsyncQueue.d.ts +0 -153
- package/dist/lib/weightedAsyncQueue.d.ts.map +0 -1
- package/dist/lib/weightedAsyncQueue.js +0 -322
- package/dist/lib/weightedAsyncQueue.js.map +0 -1
- package/dist/rclone.d.ts +0 -28
- package/dist/rclone.d.ts.map +0 -1
- package/dist/rclone.js +0 -397
- package/dist/rclone.js.map +0 -1
- package/dist/statusTracker.d.ts +0 -46
- package/dist/statusTracker.d.ts.map +0 -1
- package/dist/statusTracker.js +0 -272
- package/dist/statusTracker.js.map +0 -1
- package/dist/sync.d.ts +0 -9
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js +0 -531
- package/dist/sync.js.map +0 -1
- package/dist/types.d.ts +0 -135
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -18
- package/dist/types.js.map +0 -1
- package/dist/uploader.d.ts +0 -7
- package/dist/uploader.d.ts.map +0 -1
- package/dist/uploader.js +0 -203
- package/dist/uploader.js.map +0 -1
- package/dist/util.d.ts +0 -4
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -41
- package/dist/util.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A CLI tool to sync files from external services (via rclone) to the Aspect platf
|
|
|
7
7
|
- **Rclone Integration**: Sync files from any rclone-supported remote (Dropbox, Google Drive, S3, etc.)
|
|
8
8
|
- **Directory Structure Preservation**: Maintains folder hierarchy from source to destination
|
|
9
9
|
- **Idempotent Syncs**: Safe to re-run - automatically skips already-uploaded files and reuses existing directories
|
|
10
|
-
- **
|
|
10
|
+
- **Upload-Core Uploads**: Efficient chunked uploads through Aspect's edge worker with retry logic
|
|
11
11
|
- **Dynamic CLI Progress**: Real-time upload progress display with in-place updates
|
|
12
12
|
- **Concurrent Uploads**: Configurable parallel chunk uploads for optimal performance
|
|
13
13
|
- **Session Isolation**: Auto-generated nanosecond-precision sessions for safe concurrent syncs
|
|
@@ -120,7 +120,8 @@ npx aspect-sync \
|
|
|
120
120
|
| `--project-id <id>` | Aspect project ID | Yes | - |
|
|
121
121
|
| `--api-key <key>` | Aspect API key (or set `ASPECT_API_KEY` env var) | Yes | - |
|
|
122
122
|
| `--api-url <url>` | Aspect API URL (or set `ASPECT_API_URL` env var) | No | `https://api.aspect.inc` |
|
|
123
|
-
| `--
|
|
123
|
+
| `--edge-worker-url <url>` | Aspect edge worker URL (or set `ASPECT_EDGE_WORKER_URL` env var) | No | `https://edge.aspect.inc` |
|
|
124
|
+
| `--upload-concurrent <number>` | Max concurrent chunk uploads | No | `16` |
|
|
124
125
|
| `--batch-size <size>` | Run the migration in download/upload batches (e.g., `500GB`, `1TB`) so data is uploaded as soon as each batch finishes downloading. | No | Disabled |
|
|
125
126
|
| `--keep-local` | Keep local files after upload (for debugging) | No | `false` |
|
|
126
127
|
| `--check` | Run in check-only mode (list remote files and compare with Aspect without downloading/uploading) | No | `false` |
|
|
@@ -137,6 +138,7 @@ You can set API credentials via environment variables:
|
|
|
137
138
|
```bash
|
|
138
139
|
export ASPECT_API_KEY=your-api-key
|
|
139
140
|
export ASPECT_API_URL=https://api.aspect.inc
|
|
141
|
+
export ASPECT_EDGE_WORKER_URL=https://edge.aspect.inc
|
|
140
142
|
|
|
141
143
|
aspect-sync \
|
|
142
144
|
--remote dropbox \
|
|
@@ -167,7 +169,7 @@ aspect-sync \
|
|
|
167
169
|
--directory-id abc-123-def \
|
|
168
170
|
--project-id xyz-789-uvw \
|
|
169
171
|
--api-key your-api-key \
|
|
170
|
-
--concurrent 8 \
|
|
172
|
+
--upload-concurrent 8 \
|
|
171
173
|
--rclone-extra --drive-shared-with-me
|
|
172
174
|
```
|
|
173
175
|
|
|
@@ -271,7 +273,7 @@ The tool shows dynamic, in-place progress updates:
|
|
|
271
273
|
↑ video4.mp4 (47%)
|
|
272
274
|
↑ video5.mp4 (23%)
|
|
273
275
|
|
|
274
|
-
Total: 10 | Success: 2 | Failed: 0 | Skipped: 1 | In Progress: 2 | Queued: 5 | Speed: 45.3
|
|
276
|
+
Total: 10 | Success: 2 | Failed: 0 | Skipped: 1 | In Progress: 2 | Queued: 5 | Speed: 45.3 Mbps | Time remaining: 2m 15s
|
|
275
277
|
```
|
|
276
278
|
|
|
277
279
|
**Status Indicators:**
|
|
@@ -328,7 +330,7 @@ aspect-sync \
|
|
|
328
330
|
|
|
329
331
|
### Increase concurrency
|
|
330
332
|
|
|
331
|
-
If you have high bandwidth, increase `--concurrent`:
|
|
333
|
+
If you have high bandwidth, increase `--upload-concurrent`:
|
|
332
334
|
|
|
333
335
|
```bash
|
|
334
336
|
aspect-sync \
|
|
@@ -337,7 +339,7 @@ aspect-sync \
|
|
|
337
339
|
--directory-id abc-123 \
|
|
338
340
|
--project-id xyz-789 \
|
|
339
341
|
--api-key your-key \
|
|
340
|
-
--concurrent 16
|
|
342
|
+
--upload-concurrent 16
|
|
341
343
|
```
|
|
342
344
|
|
|
343
345
|
### Check rclone configuration
|
package/dist/index.js
CHANGED
|
@@ -1,170 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { fileURLToPath } from "url";
|
|
7
|
-
import { SyncOrchestrator } from "./sync.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*/
|
|
15
|
-
function parseSizeString(sizeStr) {
|
|
16
|
-
const match = sizeStr.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);
|
|
17
|
-
if (!match) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
const value = parseFloat(match[1]);
|
|
21
|
-
const unit = match[2].toUpperCase();
|
|
22
|
-
const multipliers = {
|
|
23
|
-
'B': 1,
|
|
24
|
-
'KB': 1024,
|
|
25
|
-
'MB': 1024 * 1024,
|
|
26
|
-
'GB': 1024 * 1024 * 1024,
|
|
27
|
-
'TB': 1024 * 1024 * 1024 * 1024,
|
|
28
|
-
};
|
|
29
|
-
return value * (multipliers[unit] || 1);
|
|
30
|
-
}
|
|
31
|
-
const program = new Command();
|
|
32
|
-
program
|
|
33
|
-
.name("aspect-sync")
|
|
34
|
-
.description("Sync files from external services to Aspect via rclone")
|
|
35
|
-
.version(packageJson.version);
|
|
36
|
-
program
|
|
37
|
-
.requiredOption("--remote <remote>", "rclone remote name (e.g., dropbox)")
|
|
38
|
-
.requiredOption("--path <path>", "remote path to sync from")
|
|
39
|
-
.requiredOption("--directory-id <id>", "Aspect directory ID to upload to")
|
|
40
|
-
.requiredOption("--project-id <id>", "Aspect project ID")
|
|
41
|
-
.option("--api-url <url>", "Aspect API URL", process.env.ASPECT_API_URL || "https://api.aspect.inc")
|
|
42
|
-
.option("--api-key <key>", "Aspect API key", process.env.ASPECT_API_KEY || "")
|
|
43
|
-
.option("--upload-concurrent <number>", "Maximum concurrent chunk uploads to Aspect (default: 16)", "16")
|
|
44
|
-
.option("--keep-local", "Keep local files after upload (for debugging)", false)
|
|
45
|
-
.option("--temp-dir <path>", "Local temporary directory for synced files", path.join(os.homedir(), ".aspect", "sync"))
|
|
46
|
-
.option("--session <name>", "Session name for isolation (default: auto-generated timestamp)")
|
|
47
|
-
.option("--check", "Check remote files against Aspect without downloading/uploading", false)
|
|
48
|
-
.option("--batch-size <size>", "Enable batched mode with max batch size (e.g., 500GB, 1TB). Helps with large migrations.", "")
|
|
49
|
-
.option("--rclone-transfers <number>", "Number of parallel file transfers (default: 4)", "4")
|
|
50
|
-
.option("--rclone-multi-thread-streams <number>", "Streams per large file (default: 4, higher = faster large files)", "4")
|
|
51
|
-
.option("--rclone-multi-thread-chunk-size <size>", "Chunk size for multi-threading (default: 64MiB)", "64MiB")
|
|
52
|
-
.option("--rclone-multi-thread-cutoff <size>", "Minimum file size for multi-threading (default: 64MiB)", "64MiB")
|
|
53
|
-
.option("--rclone-buffer-size <size>", "Buffer size per transfer (default: 64M, memory = transfers × buffer-size)", "64M")
|
|
54
|
-
.option("--rclone-no-mmap", "Disable memory-mapped I/O (reduces memory efficiency)", false)
|
|
55
|
-
.option("--upload-chunk-size <size>", "Multipart upload chunk size in MB (default: 64)", "64")
|
|
56
|
-
.option("--rclone-extra <arg>", "Additional rclone argument to append (repeatable)", (value, previous) => (previous ?? []).concat(value), [])
|
|
57
|
-
.action(async (options) => {
|
|
58
|
-
// Validate required options
|
|
59
|
-
if (!options.apiKey) {
|
|
60
|
-
console.error("Error: --api-key is required (or set ASPECT_API_KEY env variable)");
|
|
61
|
-
process.exit(1);
|
|
62
|
-
}
|
|
63
|
-
// Build base temp directory
|
|
64
|
-
const baseTempDir = options.tempDir || path.join(os.homedir(), ".aspect", "sync");
|
|
65
|
-
// Generate or use provided session name
|
|
66
|
-
let sessionName;
|
|
67
|
-
if (options.session) {
|
|
68
|
-
sessionName = options.session;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
// Auto-generate: YYYY-MM-DD_HHh-MMm-SSs-NNNNNNNNNns (nanosecond precision)
|
|
72
|
-
const now = new Date();
|
|
73
|
-
const year = now.getFullYear();
|
|
74
|
-
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
75
|
-
const day = String(now.getDate()).padStart(2, '0');
|
|
76
|
-
const hours = String(now.getHours()).padStart(2, '0');
|
|
77
|
-
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
78
|
-
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
79
|
-
// Get nanoseconds from high-resolution time
|
|
80
|
-
const hrtime = process.hrtime.bigint();
|
|
81
|
-
// Extract the sub-second portion (last 9 digits = nanoseconds within the second)
|
|
82
|
-
const nanos = hrtime % 1000000000n;
|
|
83
|
-
const nanosStr = String(nanos).padStart(9, '0');
|
|
84
|
-
sessionName = `${year}-${month}-${day}_${hours}h-${minutes}m-${seconds}s-${nanosStr}ns`;
|
|
85
|
-
}
|
|
86
|
-
const localTempDir = path.join(baseTempDir, sessionName);
|
|
87
|
-
// Parse rclone options
|
|
88
|
-
const rcloneTransfers = parseInt(options.rcloneTransfers, 10);
|
|
89
|
-
const rcloneMultiThreadStreams = parseInt(options.rcloneMultiThreadStreams, 10);
|
|
90
|
-
const chunkSizeMb = parseFloat(options.uploadChunkSize);
|
|
91
|
-
// Auto-calculate checkers based on transfers
|
|
92
|
-
// If transfers >= 16: use 25% (1/4)
|
|
93
|
-
// If transfers < 16: keep them equal
|
|
94
|
-
const rcloneCheckers = rcloneTransfers >= 16
|
|
95
|
-
? Math.floor(rcloneTransfers / 4)
|
|
96
|
-
: rcloneTransfers;
|
|
97
|
-
// Parse batch size if provided
|
|
98
|
-
let batchSizeBytes = undefined;
|
|
99
|
-
if (options.batchSize) {
|
|
100
|
-
const parsedSize = parseSizeString(options.batchSize);
|
|
101
|
-
if (parsedSize === null) {
|
|
102
|
-
console.error(`Error: Invalid batch size format: ${options.batchSize}`);
|
|
103
|
-
console.error('Expected format: <number><unit> (e.g., "500GB", "1TB", "100MB")');
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
batchSizeBytes = parsedSize;
|
|
107
|
-
}
|
|
108
|
-
if (Number.isNaN(chunkSizeMb) || chunkSizeMb <= 0) {
|
|
109
|
-
console.error("Error: --upload-chunk-size must be a positive number");
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
const chunkSizeBytes = Math.round(chunkSizeMb * 1024 * 1024);
|
|
113
|
-
// Build config
|
|
114
|
-
const config = {
|
|
115
|
-
remote: options.remote,
|
|
116
|
-
remotePath: options.path,
|
|
117
|
-
directoryId: options.directoryId,
|
|
118
|
-
projectId: options.projectId,
|
|
119
|
-
apiUrl: options.apiUrl,
|
|
120
|
-
apiKey: options.apiKey,
|
|
121
|
-
maxConcurrent: parseInt(options.uploadConcurrent, 10),
|
|
122
|
-
keepLocal: options.keepLocal,
|
|
123
|
-
localTempDir: localTempDir,
|
|
124
|
-
sessionName: sessionName,
|
|
125
|
-
rcloneOptions: {
|
|
126
|
-
transfers: rcloneTransfers,
|
|
127
|
-
checkers: rcloneCheckers,
|
|
128
|
-
multiThreadStreams: rcloneMultiThreadStreams,
|
|
129
|
-
multiThreadChunkSize: options.rcloneMultiThreadChunkSize,
|
|
130
|
-
multiThreadCutoff: options.rcloneMultiThreadCutoff,
|
|
131
|
-
bufferSize: options.rcloneBufferSize,
|
|
132
|
-
useMmap: !options.rcloneNoMmap,
|
|
133
|
-
extraRcloneArgs: options.rcloneExtra,
|
|
134
|
-
},
|
|
135
|
-
batchSizeBytes: batchSizeBytes,
|
|
136
|
-
chunkSizeBytes,
|
|
137
|
-
};
|
|
138
|
-
// Validate config
|
|
139
|
-
if (isNaN(config.maxConcurrent) || config.maxConcurrent < 1) {
|
|
140
|
-
console.error("Error: --concurrent must be a positive integer");
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
if (isNaN(rcloneTransfers) || rcloneTransfers < 1) {
|
|
144
|
-
console.error("Error: --rclone-transfers must be a positive integer");
|
|
145
|
-
process.exit(1);
|
|
146
|
-
}
|
|
147
|
-
if (isNaN(rcloneMultiThreadStreams) || rcloneMultiThreadStreams < 1) {
|
|
148
|
-
console.error("Error: --rclone-multi-thread-streams must be a positive integer");
|
|
149
|
-
process.exit(1);
|
|
150
|
-
}
|
|
151
|
-
try {
|
|
152
|
-
const orchestrator = new SyncOrchestrator(config);
|
|
153
|
-
if (options.check) {
|
|
154
|
-
await orchestrator.runCheckOnly();
|
|
155
|
-
}
|
|
156
|
-
else if (batchSizeBytes !== undefined) {
|
|
157
|
-
await orchestrator.runBatched();
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
await orchestrator.run();
|
|
161
|
-
}
|
|
162
|
-
process.exit(0);
|
|
163
|
-
}
|
|
164
|
-
catch (error) {
|
|
165
|
-
console.error("Fatal error:", error);
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
program.parse();
|
|
170
|
-
//# sourceMappingURL=index.js.map
|
|
2
|
+
import{Command as ot}from"commander";import we from"node:path";import{readFileSync as at}from"node:fs";import{fileURLToPath as lt}from"node:url";import xe from"node:os";import me from"node:path";function Be(a){let e=a.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);if(!e)return null;let t=parseFloat(e[1]),s=e[2].toUpperCase(),n={B:1,KB:1024,MB:1024*1024,GB:1024*1024*1024,TB:1024*1024*1024*1024};return t*(n[s]||1)}function fe(a){let e=a.options.apiKey||a.environment.ASPECT_API_KEY||"";if(!e)throw new Error("Error: --api-key is required (or set ASPECT_API_KEY env variable)");let t=V({value:a.options.rcloneTransfers,optionName:"--rclone-transfers"}),s=V({value:a.options.rcloneMultiThreadStreams,optionName:"--rclone-multi-thread-streams"}),n=V({value:a.options.uploadConcurrent,optionName:"--upload-concurrent"}),r=parseFloat(a.options.uploadChunkSize);if(Number.isNaN(r)||r<=0)throw new Error("Error: --upload-chunk-size must be a positive number");let i=De(a.options.batchSize),o=a.options.session??Le({now:a.now??new Date,highResolutionTime:a.highResolutionTime??process.hrtime.bigint()}),l=a.options.tempDir||me.join(xe.homedir(),".aspect","sync");return{remote:a.options.remote,remotePath:a.options.path,directoryId:a.options.directoryId,projectId:a.options.projectId,apiUrl:a.options.apiUrl||a.environment.ASPECT_API_URL||"https://api.aspect.inc",edgeWorkerUrl:a.options.edgeWorkerUrl||a.environment.ASPECT_EDGE_WORKER_URL||"https://edge.aspect.inc",apiKey:e,maxConcurrent:n,keepLocal:a.options.keepLocal,localTempDir:me.join(l,o),sessionName:o,rcloneOptions:{transfers:t,checkers:t>=16?Math.floor(t/4):t,multiThreadStreams:s,multiThreadChunkSize:a.options.rcloneMultiThreadChunkSize,multiThreadCutoff:a.options.rcloneMultiThreadCutoff,bufferSize:a.options.rcloneBufferSize,useMmap:!a.options.rcloneNoMmap,extraRcloneArgs:a.options.rcloneExtra},batchSizeBytes:i,chunkSizeBytes:Math.round(r*1024*1024)}}function V(a){let e=parseInt(a.value,10);if(Number.isNaN(e)||e<1)throw new Error(`Error: ${a.optionName} must be a positive integer`);return e}function De(a){if(!a)return;let e=Be(a);if(e===null)throw new Error(`Error: Invalid batch size format: ${a}`);return e}function Le(a){let e=a.now.getFullYear(),t=String(a.now.getMonth()+1).padStart(2,"0"),s=String(a.now.getDate()).padStart(2,"0"),n=String(a.now.getHours()).padStart(2,"0"),r=String(a.now.getMinutes()).padStart(2,"0"),i=String(a.now.getSeconds()).padStart(2,"0"),o=a.highResolutionTime%1000000000n,l=String(o).padStart(9,"0");return`${e}-${t}-${s}_${n}h-${r}m-${i}s-${l}ns`}import M from"node:fs/promises";import he from"node:path";import{spawn as J}from"child_process";import*as Z from"fs";import*as b from"path";var k=class extends Error{constructor(t,s){super(t);this.stderr=s;this.name="RcloneError"}stderr};function Me(a){let e=["sync",a.remoteSource,a.localPath];return a.batchFilePath&&e.push("--files-from",a.batchFilePath),e.push("--progress","--stats","1s","--stats-one-line","-v","--transfers",String(a.rcloneOptions.transfers),"--checkers",String(a.rcloneOptions.checkers),"--multi-thread-streams",String(a.rcloneOptions.multiThreadStreams),"--multi-thread-chunk-size",a.rcloneOptions.multiThreadChunkSize,"--multi-thread-cutoff",a.rcloneOptions.multiThreadCutoff,"--fast-list","--buffer-size",a.rcloneOptions.bufferSize),a.rcloneOptions.useMmap&&e.push("--use-mmap"),a.rcloneOptions.extraRcloneArgs?.length&&e.push(...a.rcloneOptions.extraRcloneArgs),e}function U(a,e){let t=e.trim(),s={B:1,k:1024,Ki:1024,KiB:1024,kB:1e3,M:1024*1024,Mi:1024*1024,MiB:1024*1024,MB:1e3*1e3,G:1024*1024*1024,Gi:1024*1024*1024,GiB:1024*1024*1024,GB:1e3*1e3*1e3,T:1024*1024*1024*1024,Ti:1024*1024*1024*1024,TiB:1024*1024*1024*1024,TB:1e3*1e3*1e3*1e3};return a*(s[t]||1)}function ge(a){let e=0,t=a.match(/(\d+)h/),s=a.match(/(\d+)m/),n=a.match(/(\d+)s/);return t&&(e+=parseInt(t[1],10)*3600),s&&(e+=parseInt(s[1],10)*60),n&&(e+=parseInt(n[1],10)),e}function Oe(a){let e=a.trim(),t=/^([\d.]+)\s*([A-Za-z]+)\s*\/\s*([\d.]+)\s*([A-Za-z]+),\s*([\d.]+)%?,\s*([\d.]+)\s*([A-Za-z]+)\/s,\s*ETA\s+(.+)$/,s=e.match(t);if(s){let o=s[8].trim();return{bytesTransferred:U(parseFloat(s[1]),s[2]),totalBytes:U(parseFloat(s[3]),s[4]),percentComplete:parseFloat(s[5]),speed:U(parseFloat(s[6]),s[7]),eta:o==="-"?0:ge(o)}}let n=e.match(/Transferred:\s+([\d.]+)\s*([A-Za-z]+)\s*\/\s*([\d.]+)\s*([A-Za-z]+),\s*([\d.]+)%/);if(!n)return null;let r=e.match(/([\d.]+)\s*([A-Za-z]+)\/s/),i=e.match(/ETA\s+([^\s]+)$/);return{bytesTransferred:U(parseFloat(n[1]),n[2]),totalBytes:U(parseFloat(n[3]),n[4]),percentComplete:parseFloat(n[5]),speed:r?U(parseFloat(r[1]),r[2]):0,eta:i&&i[1]!=="-"?ge(i[1]):0}}function Ne(a){return new Promise((e,t)=>{let s=J("rclone",a.args,{stdio:["ignore","pipe","pipe"]}),n="",r=0,i=o=>{for(let l of o.split(`
|
|
3
|
+
`)){if(l.trim().length===0)continue;let u=Oe(l);if(!u)continue;let c=Date.now();!(u.percentComplete>=100)&&c-r<500||(r=c,a.onProgress?.(u))}};s.stdout.on("data",o=>{i(o.toString())}),s.stderr.on("data",o=>{let l=o.toString();n+=l,i(l)}),s.on("close",o=>{o===0?e():t(new k(`rclone exited with code ${o}`,n))}),s.on("error",o=>{t(new k(`Failed to start rclone: ${o.message}`,""))})})}async function ee(){return new Promise((a,e)=>{let t=J("rclone",["version"]);t.on("error",()=>{e(new Error("rclone is not installed or not in PATH. Please install rclone first: https://rclone.org/install/"))}),t.on("close",s=>{s===0?a():e(new Error("rclone is not installed or not in PATH. Please install rclone first: https://rclone.org/install/"))})})}async function Se(a,e,t,s,n,r){let i=`${a}:${e}`;return Ne({args:Me({remoteSource:i,localPath:t,rcloneOptions:n,batchFilePath:s}),remoteSource:i,onProgress:r})}async function O(a,e,t=[]){let s=`${a}:${e}`;return new Promise((n,r)=>{let i=J("rclone",["ls",s,"--fast-list",...t]),o="",l="";i.stdout.on("data",u=>{o+=u.toString()}),i.stderr.on("data",u=>{l+=u.toString()}),i.on("close",u=>{if(u===0){let c=o.split(`
|
|
4
|
+
`).map(d=>d.trim()).filter(d=>d.length>0).map(d=>{let h=d.match(/^(\d+)\s+(.+)$/);return h?{size:Number(h[1]),path:h[2]}:null}).filter(d=>d!==null);n(c)}else r(new k(`Failed to list files from ${s}`,l))}),i.on("error",u=>{r(new k(`Failed to start rclone: ${u.message}`,""))})})}async function ye(a){let e=[],t=new Set;async function s(n,r=""){let i=await Z.promises.readdir(n,{withFileTypes:!0});for(let o of i){let l=b.join(n,o.name),u=r?b.join(r,o.name):o.name;if(o.isDirectory())await s(l,u);else if(o.isFile()){let c=await Z.promises.stat(l);e.push({relativePath:u,absolutePath:l,fileName:o.name,size:c.size});let d=b.dirname(u);if(d!=="."){let h=d.split(b.sep);for(let p=0;p<h.length;p++){let f=h.slice(0,p+1).join(b.sep);t.add(f)}}}}}return await s(a),{files:e,directoryCount:t.size}}import Ce from"fs/promises";import be from"path";function Ae(a,e){let t=new Map;for(let u of a){let c=be.dirname(u.path);t.has(c)||t.set(c,[]),t.get(c).push(u)}let s=new Map;for(let[u,c]of t.entries()){let d=c.reduce((h,p)=>h+p.size,0);s.set(u,d)}let n=Array.from(t.keys()).sort(),r=[],i=[],o=0,l=1;for(let u of n){let c=t.get(u),d=s.get(u);if(d>e){if(i.length>0&&(r.push({batchNumber:l++,files:i,totalSize:o}),i=[],o=0),c.length===1)r.push({batchNumber:l++,files:c,totalSize:d});else for(let h of c)o+h.size>e&&i.length>0&&(r.push({batchNumber:l++,files:i,totalSize:o}),i=[],o=0),i.push(h),o+=h.size;continue}o+d>e&&i.length>0&&(r.push({batchNumber:l++,files:i,totalSize:o}),i=[],o=0),i.push(...c),o+=d}return i.length>0&&r.push({batchNumber:l++,files:i,totalSize:o}),r}async function Ee(a,e,t){let s=be.join(a,`batch${e}.txt`),n=`${t.map(r=>r.path).join(`
|
|
5
|
+
`)}
|
|
6
|
+
`;return await Ce.writeFile(s,n,"utf-8"),s}async function Te(a){try{await Ce.unlink(a)}catch(e){if(e.code!=="ENOENT")throw e}}import m from"chalk";import Ie from"log-update";function N(a,e){if(e.length===0)return"queued";if(a.isCreatingFolders)return"creating-folders";let t={queued:0,paused:0,inProgress:0,success:0,failed:0,cancelled:0};for(let n of e)switch(n.status){case"queued":t.queued++;break;case"paused":t.paused++;break;case"in-progress":t.inProgress++;break;case"success":t.success++;break;case"failed":t.failed++;break;case"cancelled":t.cancelled++;break}let s=e.length;return t.failed===s?"all-failed":t.failed>0?"some-failed":t.success===s?"success":t.paused>0&&t.queued===0&&t.inProgress===0?"paused":t.queued>0||t.inProgress>0?t.queued+t.paused===s?"queued":"in-progress":"cancelled"}function te(a,e){let t=N(a,e);return t==="creating-folders"||t==="in-progress"||t==="queued"||t==="paused"?"cancelled":t}function se(a){let e=[];for(let t of Object.values(a))t.chunkedState&&(t.status==="queued"||t.status==="in-progress"||t.status==="paused")&&e.push(t.chunkedState.assetId);return e}function ne(a){for(let e of Object.values(a))if(e.status==="queued"||e.status==="in-progress"||e.status==="paused")return!0;return!1}function P(a){let t=Object.values(a).reduce((s,n)=>{switch(n.status!=="failed"&&n.status!=="cancelled"&&(s.totalBytesToUpload+=n.totalBytesToUpload,s.totalBytesUploaded+=n.bytesUploaded),s.totalAssetCount+=1,n.status){case"queued":s.queuedAssetCount+=1;break;case"paused":s.pausedAssetCount+=1;break;case"in-progress":s.inProgressAssetCount+=1;break;case"success":s.successAssetCount+=1;break;case"failed":s.failedAssetCount+=1;break;case"cancelled":s.cancelledAssetCount+=1;break}return s},{totalBytesToUpload:0,totalBytesUploaded:0,totalAssetCount:0,queuedAssetCount:0,pausedAssetCount:0,inProgressAssetCount:0,successAssetCount:0,failedAssetCount:0,cancelledAssetCount:0,hasActiveUploads:!1,progress:0});return t.hasActiveUploads=t.totalAssetCount-t.successAssetCount-t.failedAssetCount-t.cancelledAssetCount>0,t.progress=t.hasActiveUploads?t.totalBytesUploaded/t.totalBytesToUpload:0,t}function re(a,e){if(e.length===0)return null;let t=e.reduce((s,n)=>(n.status!=="failed"&&n.status!=="cancelled"&&(s.totalBytes+=n.totalBytesToUpload,s.uploadedBytes+=n.bytesUploaded),n.status==="success"?s.filesSucceeded++:n.status==="failed"&&s.filesFailed++,(n.status==="success"||n.status==="failed"||n.status==="cancelled")&&s.filesCompleted++,s),{totalBytes:0,uploadedBytes:0,filesCompleted:0,filesSucceeded:0,filesTotal:e.length,filesFailed:0,percentage:0});return t.percentage=t.totalBytes>0?t.uploadedBytes/t.totalBytes*100:0,t}function ie(a){let e=Object.values(a).filter(t=>t.status==="queued"||t.status==="in-progress"||t.status==="paused");return e.length===0?!1:e.every(t=>t.status==="paused")}function oe(a){return Object.values(a).some(e=>e.status==="paused")}import Ut from"axios";var v=class{#t;#s;#e;#i;#o;#r=0;#a=0;#l=0;#n=0;#u=0;#A=0;constructor(e){this.#t=e?.alphaFast??.1,this.#s=e?.alphaMax??.5,this.#e=e?.consecutiveForBoost??3,this.#i=e?.minSamples??3,this.#o=e?.maxGapSeconds??2}sample(e){let t=Date.now();if(this.#n===0){this.#n=t,this.#u=e;return}let n=(t-this.#n)/1e3,r=e-this.#u;if(n<=0||r<0)return;if(n>this.#o){this.#n=t,this.#u=e;return}let i=r/n;if(this.#A===0)this.#r=i;else{let l=i-this.#r>=0?1:-1;this.#l!==0&&l===this.#l?this.#a++:this.#a=0,this.#l=l;let u=Math.min(1,this.#a/this.#e),c=this.#t+(this.#s-this.#t)*u;this.#r=c*i+(1-c)*this.#r}this.#n=t,this.#u=e,this.#A++}etaSeconds(e){return!this.isWarmedUp||this.#r<=0?0:e/this.#r}adjustTotalBytes(e){this.#u+=e}reset(){this.#r=0,this.#a=0,this.#l=0,this.#n=0,this.#u=0,this.#A=0}serialize(){return{displaySpeedBps:this.#r,lastSampleTimestamp:this.#n,lastSampleTotalBytes:this.#u,sampleCount:this.#A}}get speedBps(){return this.#r}get speedMbps(){return this.#r*8/1e6}get isWarmedUp(){return this.#A>=this.#i}};function R(a){let e={"X-Aspect-Share-Id":a.shareId};return a.shareAuthentication&&(e["X-Aspect-Share-Authentication"]=a.shareAuthentication),e}var _=class{#t;#s;#e;#i;#o;#r;#a;#l;#n;#u;#A;#f;#P;#h;#B;#U;#b;#k;#g;#m;#E;#c;#v;#w;#d;#_;#y;#S;#F;#x;#I;#p;#T;constructor(e){this.#s=e?.minConcurrency??1,this.#e=e?.maxConcurrency??8,this.#t=e?.initialConcurrency??this.#s,this.#i=e?.errorThreshold??1,this.#o=(e?.cooldownSeconds??10)*1e3,this.#r=e?.plateauThreshold??.05,this.#a=e?.onChange,this.#l=e?.now??Date.now,this.#n=this.#t,this.#u="probing",this.#A=0,this.#f=0,this.#P=0,this.#h=0,this.#B=!1,this.#U=0,this.#b=0,this.#k=1/0,this.#x=0,this.#I=0,this.#p=0,this.#T=0,this.#g=1/0,this.#m=0,this.#E=3e4,this.#c=0,this.#v=0,this.#w=0,this.#d=0,this.#_=0,this.#y=0,this.#S=0,this.#F=0}recordSuccess(e,t){if(e<=0)return;if(this.#U>0){this.#U--;return}this.#h++,this.#x+=e;let s=this.#l();if(this.#I===0){this.#I=s;return}let n=s-this.#I;if(n>=200){let c=this.#x/n*1e3;this.#T===0?this.#p=c:this.#p=.3*c+(1-.3)*this.#p,this.#T++,this.#T>=5&&(this.#w=Math.max(this.#w,this.#p)),this.#x=0,this.#I=s}if(t!==void 0&&t>0&&e>0){let c=t/(e/1048576);this.#_===0?this.#d=c:this.#d=.3*c+(1-.3)*this.#d,this.#_++}if(this.#u==="cooldown"&&s>=this.#A&&(this.#u="probing"),this.#u!=="probing")return;if(this.#f>0){let c;if(this.#T>=25?c=.03:this.#T>=15?c=.04:this.#T>=5&&(c=.07),c!==void 0&&(this.#p-this.#f)/this.#f<=-c){this.#S=0,this.#F=0,this.#L();return}}let r=this.#B?this.#n*2:this.#n;if(this.#h<r)return;if(this.#P=0,this.#T<5){this.#h=0;return}let i=this.#g<1/0&&this.#l()<this.#m?this.#g-1:this.#e,o=Math.min(this.#e,this.#k-1,i);if(this.#n>=o){if(this.#g<1/0&&this.#p>0&&this.#c>0){let c=(this.#p-this.#c)/this.#c;if(c>.2){this.#g=1/0,this.#m=0,this.#E=3e4,this.#c=0,this.#v=0,this.#h=0;return}if(c<=-.07){this.#h=0;return}}if(this.#w>0&&this.#p>0&&this.#T>=5&&(this.#w-this.#p)/this.#w>.07){this.#g=1/0,this.#m=0,this.#E=3e4,this.#c=0,this.#v=0,this.#w=this.#p,this.#h=0;return}if(this.#v>0&&this.#d>0&&this.#_>=2&&(this.#v-this.#d)/this.#v>.2){this.#g=1/0,this.#m=0,this.#E=3e4,this.#c=0,this.#v=0,this.#h=0;return}this.#h=0;return}let l=this.#p;if(this.#f>0){let c=Math.max(.02,this.#r*3/this.#n),d=(l-this.#f)/this.#f;if(d<=-.07){this.#S=0,this.#F=0,this.#L();return}else if(d<c){if(this.#_>=2&&this.#y>0){let h=this.#d/this.#y,p=this.#n/(this.#n-1);if(h<p*.9){this.#h=0;return}}if(this.#S++,this.#S<3){this.#h=0;return}this.#S=0,this.#F=0,this.#L();return}else if(d<c*2){if(this.#F++,this.#F<2){this.#h=0;return}this.#S=0,this.#F=0}else this.#S=0,this.#F=0;this.#g<1/0&&(this.#g=1/0,this.#m=0,this.#E=3e4,this.#c=0,this.#v=0)}this.#y=this.#d,this.#S=0,this.#F=0;let u=this.#n;this.#n++,this.#f=l,this.#h=0,this.#U=Math.max(0,u-1),this.#x=0,this.#I=0,this.#p=0,this.#T=0,this.#d=0,this.#_=0,this.#a?.(this.#n)}#L(){let e=Math.max(this.#s,this.#n-1);e<this.#n&&(this.#g=Math.min(this.#g,this.#n),this.#m=this.#l()+this.#E,this.#E=Math.min(this.#E*2,12e4),this.#c=this.#f,this.#v=this.#y,this.#n=e,this.#f=0,this.#x=0,this.#I=0,this.#p=0,this.#T=0,this.#d=0,this.#_=0,this.#a?.(this.#n)),this.#h=0}recordError(){if(this.#U>0&&this.#U--,this.#P++,this.#P<this.#i)return;this.#n===this.#b&&(this.#k=Math.min(this.#k,this.#n)),this.#b=this.#n;let e=this.#n;this.#n=Math.max(this.#s,Math.floor(this.#n/2)),this.#u="cooldown",this.#A=this.#l()+this.#o,this.#B=!0,this.#P=0,this.#h=0,this.#U=0,this.#f=0,this.#x=0,this.#I=0,this.#p=0,this.#T=0,this.#d=0,this.#_=0,this.#y=0,this.#S=0,this.#F=0,this.#n!==e&&this.#a?.(this.#n)}get concurrency(){return this.#n}get speedBps(){return this.#p}get latencyMsPerMB(){return this.#d}reset(){this.#n=this.#t,this.#u="probing",this.#A=0,this.#f=0,this.#P=0,this.#h=0,this.#B=!1,this.#U=0,this.#b=0,this.#k=1/0,this.#g=1/0,this.#m=0,this.#E=3e4,this.#c=0,this.#v=0,this.#w=0,this.#d=0,this.#_=0,this.#y=0,this.#S=0,this.#F=0,this.#x=0,this.#I=0,this.#p=0,this.#T=0,this.#a?.(this.#n)}};function Ue(a){let e=`[${a}]`;return{info:(...t)=>console.info(e,...t),warn:(...t)=>console.warn(e,...t),error:(...t)=>console.error(e,...t)}}var ae=Ue("transfer");var G=class{uploadAssets={};uploadGroups={};#t=new v;get uploadSpeedState(){return this.#t.serialize()}uploadStats={uploadSpeedMbps:0,timeRemainingSeconds:0,formattedSpeed:"Calculating...",formattedTime:"Calculating..."};dirtyAssetIds=new Set;dirtyGroupIds=new Set;dirtyHistoryGroupIds=new Set;#s(e){e!==0&&this.#t.adjustTotalBytes(-e)}adjustSpeedBaselineForSkippedBytes(e){e!==0&&this.#t.adjustTotalBytes(e)}#e(){Object.values(this.uploadAssets).some(t=>t.status==="queued"||t.status==="in-progress")||this.#t.reset()}#i(e){this.dirtyAssetIds.add(e)}#o(e){this.dirtyGroupIds.add(e)}addUploadAsset({assetId:e,fileReader:t,abortController:s,parentId:n,projectId:r,groupId:i,chunkSize:o,shareContext:l}){this.uploadAssets[e]={assetId:e,totalBytesToUpload:t.size,bytesUploaded:0,status:"queued",fileName:t.name,createdAt:new Date().toISOString(),i:Object.keys(this.uploadAssets).length,parentId:n,projectId:r,groupId:i,retryCount:0,chunkSize:o??0,fileReader:t,abortController:s,shareContext:l},this.#i(e)}updateUploadProgress(e,t){let s=this.uploadAssets[e];if(!s||s.status==="paused"||s.status==="cancelled"||s.status==="success"||s.status==="failed")return;let n=t>0&&s.status==="queued"?"in-progress":s.status,r=Math.min(t,s.totalBytesToUpload);s.bytesUploaded=r,s.status=n,this.#i(e)}markUploadInProgress(e){let t=this.uploadAssets[e];t&&(t.status="in-progress",this.#i(e))}markUploadSuccess(e){let t=this.uploadAssets[e];t&&(t.status="success",t.bytesUploaded=t.totalBytesToUpload,this.#i(e),this.#e(),this.cleanupOnCompletion(e))}markUploadFailed(e,t){let s=this.uploadAssets[e];if(!s)return;let n=s.bytesUploaded;s.status="failed",s.errorMessage=t,s.bytesUploaded=0,this.#i(e),this.#s(n),this.#e(),this.cleanupOnCompletion(e)}removeUploadAsset(e){let s=this.uploadAssets[e]?.bytesUploaded??0;delete this.uploadAssets[e],this.#i(e),this.#s(s),this.#e()}clearAllUploads(){this.uploadAssets={},this.#t.reset()}clearTerminalState(){if(!Object.values(this.uploadAssets).some(t=>t.status==="queued"||t.status==="in-progress"||t.status==="paused")){for(let t of Object.keys(this.uploadAssets))delete this.uploadAssets[t];for(let t of Object.keys(this.uploadGroups))delete this.uploadGroups[t];this.#t.reset()}}resetAssetForRetryQueue(e){let t=this.uploadAssets[e];t&&(t.status="queued",t.bytesUploaded=0,t.errorMessage=void 0,t.retryCount=t.retryCount+1,this.#i(e))}resetAssetForRetrying(e){let t=this.uploadAssets[e];t&&(t.status="queued",t.bytesUploaded=0,t.retryCount=0,this.#i(e))}pauseUpload(e){let t=this.uploadAssets[e];if(!t||t.status!=="queued"&&t.status!=="in-progress")return;let s=0;if(t.chunkedState){let{completedChunkIndices:r,chunkSize:i}=t.chunkedState;for(let o of r){let l=o*i,u=Math.min(l+i,t.totalBytesToUpload);s+=u-l}}let n=t.bytesUploaded-s;t.status="paused",t.bytesUploaded=s,this.#i(e),this.#s(n),this.#e(),t.abortController.abort()}unpauseUpload(e){let t=this.uploadAssets[e];t&&t.status==="paused"&&(t.status="queued",this.#i(e))}pauseUploadGroup(e){let t=this.uploadGroups[e];if(t&&!t.isCreatingFolders){t.isPaused=!0,this.#o(e);for(let s of t.assetIds)this.pauseUpload(s)}}unpauseUploadGroup(e){let t=this.uploadGroups[e];t&&(t.isPaused=!1,this.#o(e))}pauseAllUploads(){for(let e of Object.values(this.uploadGroups))e.isCreatingFolders||this.pauseUploadGroup(e.id)}resumeAllUploads(){for(let e of Object.values(this.uploadGroups))e.isPaused&&this.unpauseUploadGroup(e.id)}markUploadCancelled(e){let t=this.uploadAssets[e];if(!t||t.status==="success"||t.status==="failed"||t.status==="cancelled")return;let s=t.bytesUploaded;t.status="cancelled",this.#i(e),this.#s(s),this.#e(),this.cleanupOnCompletion(e)}setChunkedState(e,t){let s=this.uploadAssets[e];s&&(s.chunkedState=t,this.#i(e))}addCompletedChunkIndex(e,t){let s=this.uploadAssets[e];!s||!s.chunkedState||(s.chunkedState={...s.chunkedState,completedChunkIndices:[...s.chunkedState.completedChunkIndices,t]},this.#i(e))}clearChunkedState(e){let t=this.uploadAssets[e];t&&(t.chunkedState=void 0,this.#i(e))}hasGroupsCreatingFolders(){return Object.values(this.uploadGroups).some(e=>e.isCreatingFolders===!0)}areAllUploadsPaused(){return ie(this.uploadAssets)}hasPausedUploads(){return oe(this.uploadAssets)}getUploadAsset(e){return this.uploadAssets[e]}getSerializableAsset(e){let t=this.uploadAssets[e];if(t)return this.#l(t)}getUploadSummary(){return P(this.#u())}getAllUploadAssetsSortedByRecent(){return Object.values(this.uploadAssets).map(e=>this.#l(e)).sort((e,t)=>t.i-e.i)}getGroupAssets(e){let t=this.uploadGroups[e];return!t||!t.assetIds?[]:t.assetIds.map(s=>this.uploadAssets[s]).filter(Boolean).map(s=>this.#l(s))}getGroupAssetsInternal(e){let t=this.uploadGroups[e];return!t||!t.assetIds?[]:t.assetIds.map(s=>this.uploadAssets[s]).filter(Boolean)}addUploadGroup(e){let t=crypto.randomUUID();return this.uploadGroups[t]={...e,id:t,fileReaders:new Map},this.#o(t),t}updateUploadGroup(e,t){let s=this.uploadGroups[e];s&&(Object.assign(s,t),this.#o(e))}addAssetToGroup(e,t){let s=this.uploadGroups[e];s&&(s.assetIds.push(t),this.#o(e))}removeUploadGroup(e){let t=this.uploadGroups[e];if(t)for(let s of t.assetIds)delete this.uploadAssets[s],this.#i(s);delete this.uploadGroups[e],this.#o(e)}getUploadGroup(e){let t=this.uploadGroups[e];if(t)return this.#n(t)}getAllUploadGroups(){return Object.values(this.uploadGroups).map(e=>this.#n(e)).sort((e,t)=>t.createdAt.localeCompare(e.createdAt))}getUploadGroupProgress(e){let t=this.uploadGroups[e];if(!t)return null;let s=this.getGroupAssets(e);return re(this.#n(t),s)}getUploadGroupStatus(e){let t=this.uploadGroups[e];if(!t)return null;let s=this.getGroupAssets(e);return s.length>0?N(this.#n(t),s):null}getAssetsByParentId(e){return Object.values(this.uploadAssets).filter(t=>t.parentId===e).map(t=>this.#l(t))}getActiveUploadGroups(){return Object.values(this.uploadGroups).filter(e=>{let t=this.getUploadGroupStatus(e.id);return t&&["queued","creating-folders","in-progress"].includes(t)}).map(e=>this.#n(e))}cleanupOnCompletion(e){let t=this.uploadAssets[e];if(!t)return;let s=t.groupId;if(s){let r=this.uploadGroups[s];if(r){let i=this.getGroupAssets(s),o=this.#n(r);this.dirtyHistoryGroupIds.add(s),!i.some(u=>u.status==="queued"||u.status==="in-progress")&&!r.completedAt&&(r.completedAt=new Date().toISOString(),this.#o(s))}}let n=this.getUploadSummary();if(n.cancelledAssetCount+n.failedAssetCount+n.successAssetCount===n.totalAssetCount)for(let r of Object.keys(this.uploadAssets)){let i=this.uploadAssets[r];i&&(i.bytesUploaded=0,this.#i(r))}}updateUploadStats(){let e=this.getUploadSummary(),t=e.queuedAssetCount>0||e.inProgressAssetCount>0;t&&this.#t.sample(e.totalBytesUploaded);let s=this.#t.isWarmedUp?this.#t.speedMbps:0,n=t&&this.#t.isWarmedUp?this.#t.etaSeconds(e.totalBytesToUpload-e.totalBytesUploaded):0;this.uploadStats={uploadSpeedMbps:s,timeRemainingSeconds:n,formattedSpeed:this.#r(s),formattedTime:this.#a(n)}}#r(e){return!Number.isFinite(e)||e<=0?"Calculating...":e<1?`${(e*1e3).toFixed(0)} Kbps`:`${e.toFixed(1)} Mbps`}#a(e){if(!Number.isFinite(e)||e<=0)return"Calculating...";let t=Math.floor(e/3600),s=Math.floor(e%3600/60),n=Math.floor(e%60);return t>0?`${t}h ${s}m left`:s>0?`${s}m ${n}s left`:`${n}s left`}buildHistoryItem(e){let t=this.uploadGroups[e];if(!t)return null;let s=this.getGroupAssets(e),n=this.#n(t);return{groupId:t.id,name:t.name,type:t.type,fileCount:t.fileCount,folderCount:t.folderCount,topLevelFileCount:t.topLevelFileCount,topLevelFolderCount:t.topLevelFolderCount,status:te(n,s),createdAt:t.createdAt,completedAt:t.completedAt,rootDirectoryId:t.rootDirectoryId,projectId:t.projectId,errorMessage:t.errorMessage,conflictResolution:t.conflictResolution}}#l({fileReader:e,abortController:t,shareContext:s,...n}){return n}#n({fileReaders:e,...t}){return t}#u(){let e={};for(let[t,s]of Object.entries(this.uploadAssets))e[t]=this.#l(s);return e}#A(){let e={};for(let[t,s]of Object.entries(this.uploadGroups))e[t]=this.#n(s);return e}getSerializableSnapshot(){return{uploadAssets:this.#u(),uploadGroups:this.#A(),uploadStats:{...this.uploadStats},uploadSpeedState:this.uploadSpeedState}}getDirtyDelta(){let e={};for(let s of this.dirtyAssetIds){let n=this.uploadAssets[s];n&&(e[s]=this.#l(n))}let t={};for(let s of this.dirtyGroupIds){let n=this.uploadGroups[s];n&&(t[s]=this.#n(n))}return{assets:e,groups:t}}clearDirtyFlags(){this.dirtyAssetIds.clear(),this.dirtyGroupIds.clear(),this.dirtyHistoryGroupIds.clear()}};function $e(a,e){let t=new Date(a).getTime(),s=Date.now();return t-s<=e}var z=class{#t;#s=new Map;#e=new Map;constructor(e){this.#t=e}seedToken(e,t,s){this.#s.set(e,{token:t,expiresAt:s})}async getToken(e,t){let s=this.#s.get(e);if(s&&!$e(s.expiresAt,3e5))return s.token;let n=this.#e.get(e);if(n)return n;let r=t?R(t):void 0,i=this.#t.post(`/assets/${e}/token`,{},r).then(o=>{if(!o.token)throw new Error("Token refresh failed: no token in response");return this.#s.set(e,{token:o.token,expiresAt:o.token_expires_at}),this.#e.delete(e),o.token}).catch(o=>{throw this.#e.delete(e),o});return this.#e.set(e,i),i}clearCache(e){e?this.#s.delete(e):this.#s.clear()}};var $=class{#t=[];registerAsset(e){this.#t.push({assetId:e.assetId,totalChunks:e.totalChunks,nextChunkToClaim:0,completedChunkIndices:new Set(e.completedChunkIndices),isPaused:!1})}claimNextChunk(){for(let e of this.#t)if(!e.isPaused)for(;e.nextChunkToClaim<e.totalChunks;){let t=e.nextChunkToClaim;if(e.nextChunkToClaim++,!e.completedChunkIndices.has(t))return{assetId:e.assetId,chunkIndex:t}}return null}markChunkCompleted(e,t){let s=this.#s(e);return s?(s.completedChunkIndices.add(t),s.completedChunkIndices.size>=s.totalChunks):!1}pauseAsset(e){let t=this.#s(e);t&&(t.isPaused=!0)}unpauseAsset(e){let t=this.#s(e);t&&(t.isPaused=!1,this.#e(t))}removeAsset(e){this.#t=this.#t.filter(t=>t.assetId!==e)}hasAsset(e){return this.#t.some(t=>t.assetId===e)}clear(){this.#t=[]}#s(e){return this.#t.find(t=>t.assetId===e)}#e(e){for(let t=0;t<e.totalChunks;t++)if(!e.completedChunkIndices.has(t)){e.nextChunkToClaim=t;return}e.nextChunkToClaim=e.totalChunks}};var He=/[/\\:*?"<>|\x00-\x1F]/g,je=["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"];function le(a){let e=a.replace(He,"_");e=e.replace(/[. ]+$/,""),(!e||e==="."||e==="..")&&(e="unnamed");let t=e.includes(".")?e.substring(0,e.lastIndexOf(".")):e;je.includes(t.toUpperCase())&&(e=`_${e}`);let s=new TextEncoder;if(s.encode(e).length>255){let n=e.lastIndexOf("."),r=n>0?e.substring(n):"",i=n>0?e.substring(0,n):e,l=255-s.encode(r).length;for(;s.encode(i).length>l&&i.length>0;)i=i.slice(0,-1);e=i+r}return e}var A=class extends Error{constructor(){super("Upload cancelled"),this.name="CancelledError"}};function w(a){return a instanceof A||a instanceof Error&&a.name==="CancelledError"||a instanceof Error&&a.name==="CanceledError"||a instanceof Error&&a.name==="AbortError"}function Ve(a){return w(a)||a instanceof Error&&a.name==="NoAuthError"}function Ze(a){return new Promise(e=>setTimeout(e,a))}function Je(a){return a instanceof Blob?a.size:a.byteLength}var x=class{#t;#s;#e;#i;#o;#r;#a;#l;#n;#u;#A;#f;#P;#h=null;#B=0;#U=!1;#b=[];#k=new Set;#g=!1;#m=!1;#E=!1;#c=[];#v=!1;#w=!1;#d=new $;#_=0;#y=new Map;#S=new Map;#F=[];#x=!1;#I=null;#p="keep_both";#T=new Map;#L=null;#C=!1;#D=null;#N=!1;constructor(e){this.#t=e.httpClient,this.#s=new z(e.httpClient),this.#e=new G,this.#i=e.historyStore??null,this.#o=e.settingsStore??null,this.#r=e.analytics??null,this.#a=e.logger??ae,this.#l=e.onChange??null,this.#n=e.onClear??null,this.#u=e.onSummary??null,this.#A=e.onConcurrencyAdjusted??null,this.#P=e.deltaIntervalMs??333;let t=e.maxConcurrency??4;if(e.adaptiveConcurrency!==!1){let s=e.adaptiveConcurrencyConfig;this.#D=new _({initialConcurrency:s?.initialConcurrency??Math.min(2,t),minConcurrency:s?.minConcurrency??1,maxConcurrency:s?.maxConcurrency??t,errorThreshold:s?.errorThreshold,cooldownSeconds:s?.cooldownSeconds??10,plateauThreshold:s?.plateauThreshold,onChange:n=>{let r=this.#f;if(this.setMaxConcurrency(n),r===n)return;let i=this.#N?"reset":n>r?"probe_increase":"error_decrease",o=Math.round(this.#D.speedBps/(1024*1024)*100)/100,l=Math.round(this.#D.latencyMsPerMB*100)/100;this.#a.info(`Adaptive concurrency: ${r} \u2192 ${n} (${i}), speed=${o}MB/s, latency=${l}ms/MB`),this.#A?.({previousConcurrency:r,newConcurrency:n,reason:i,speedMbps:o,latencyMsPerMB:l})},now:s?.now}),this.#f=this.#D.concurrency}else this.#f=t;this.#i&&(this.#b=this.#i.load()),this.#o&&(this.#I=this.#o.getBandwidthLimitBps(),this.#p=this.#o.getConflictResolution()),(this.#l||this.#u)&&(this.#h=setInterval(()=>{this.#le()},this.#P)),this.#r&&(this.#L=setInterval(()=>{this.#ce()},6e4))}startUpload(e){let t=e.assetId||crypto.randomUUID(),s=!!e.assetId,n;if(s){let i=this.#e.getUploadAsset(t);if(!i)throw new Error(`Cannot retry: asset ${t} not found`);n=i.chunkSize}else{if(e.chunkSize===void 0||e.chunkSize===null)throw new Error("chunkSize is required for new uploads");n=e.chunkSize}let r=new AbortController;if(s){if(this.#e.getUploadAsset(t).status==="cancelled")return t;e.resetRetryCount&&this.#e.resetAssetForRetrying(t);let o=this.#e.getUploadAsset(t);o.abortController=r,o.fileReader=e.fileReader,e.shareContext&&(o.shareContext=e.shareContext)}else this.#e.addUploadAsset({assetId:t,fileReader:e.fileReader,abortController:r,parentId:e.directoryId,projectId:e.projectId,groupId:e.groupId,chunkSize:n,shareContext:e.shareContext});return this.#c.push(t),this.#G(),this.#R(),t}startGroupUpload(e){this.#e.clearTerminalState();let t=this.#e.addUploadGroup({type:e.type,name:e.name,assetIds:[],fileCount:e.fileCount,folderCount:e.folderCount,topLevelFileCount:e.topLevelFileCount,topLevelFolderCount:e.topLevelFolderCount,isCreatingFolders:!1,isPaused:!1,createdAt:new Date().toISOString(),rootDirectoryId:e.rootDirectoryId,projectId:e.projectId,retryCount:0,conflictResolution:e.conflictResolutionOverride??this.#p});return this.#T.set(t,Date.now()),this.#r?.("upload:started",{file_count:e.fileCount,total_bytes:e.totalBytes??0,has_folders:e.folderCount>0,project_id:e.projectId}),this.#e.dirtyHistoryGroupIds.add(t),this.#$(),this.#R(),t}pauseUpload(e){let t=this.#e.getUploadAsset(e);this.#e.pauseUpload(e),this.#c=this.#c.filter(s=>s!==e),this.#d.pauseAsset(e),!this.#C&&t&&this.#r&&this.#r("upload:file_paused",{file_name:t.fileName,file_size:t.totalBytesToUpload,bytes_uploaded:t.bytesUploaded,project_id:t.projectId??""}),this.#R()}unpauseUpload(e){this.#e.unpauseUpload(e),this.#R()}cancelUpload(e){let t=this.#e.getUploadAsset(e);if(!t||t.status==="success"||t.status==="failed"||t.status==="cancelled")return;let s=t.fileName,n=t.totalBytesToUpload,r=t.bytesUploaded,i=t.projectId,o=t.chunkedState?.assetId;(t.status==="queued"||t.status==="in-progress")&&t.abortController.abort(),this.#c=this.#c.filter(l=>l!==e),this.#d.removeAsset(e),this.#y.delete(e),this.#S.delete(e),this.#e.markUploadCancelled(e),this.#e.clearChunkedState(e),t.fileReader.close?.(),!this.#C&&this.#r&&this.#r("upload:file_cancelled",{file_name:s,file_size:n,bytes_uploaded:r,project_id:i??""}),this.#R(),this.#M(),this.#G(),this.#q(),o&&(this.#F.push(o),this.#K())}cancelAllUploads(){let e=this.#W();this.#C=!0;try{let s=Object.values(this.#e.uploadAssets).filter(n=>n.status==="queued"||n.status==="in-progress"||n.status==="paused");for(let n of s)this.cancelUpload(n.assetId)}finally{this.#C=!1}this.#r?.("upload:all_cancelled",e)}retryUpload(e){let t=this.#e.getUploadAsset(e);t&&t.status==="failed"&&this.startUpload({fileReader:t.fileReader,directoryId:t.parentId??"",projectId:t.projectId,groupId:t.groupId,assetId:e,resumeChunked:t.chunkedState,resetRetryCount:!0,shareContext:t.shareContext})}resumeUpload(e){let t=this.#e.getUploadAsset(e);if(!t||t.status!=="paused")return;!this.#C&&this.#r&&this.#r("upload:file_resumed",{file_name:t.fileName,file_size:t.totalBytesToUpload,bytes_uploaded:t.bytesUploaded,project_id:t.projectId??""});let s=new AbortController;t.abortController=s,this.#e.unpauseUpload(e),this.#d.hasAsset(e)?(this.#d.unpauseAsset(e),this.#R(),this.#M()):this.startUpload({fileReader:t.fileReader,directoryId:t.parentId??"",projectId:t.projectId,groupId:t.groupId,assetId:e,resumeChunked:t.chunkedState,shareContext:t.shareContext})}pauseUploadGroup(e){let t=this.#e.uploadGroups[e];if(!t)return;let s=this.#C;this.#C=!0,this.#e.pauseUploadGroup(e);for(let n of t.assetIds)this.#c=this.#c.filter(r=>r!==n),this.#d.pauseAsset(n);this.#C=s,this.#C||this.#X(e,t,"upload:group_paused"),this.#R()}unpauseUploadGroup(e){this.#e.unpauseUploadGroup(e),this.#R()}cancelUploadGroup(e){let t=this.#e.uploadGroups[e];if(!t)return;let{totalBytes:s,bytesUploaded:n,filesRemaining:r}=this.#Q(t),i=this.#C;this.#C=!0;for(let o of t.assetIds)this.cancelUpload(o);this.#C=i,this.#C||this.#r?.("upload:group_cancelled",{file_count:t.assetIds.length,files_remaining:r,total_bytes:s,bytes_uploaded:n,project_id:t.projectId}),this.#T.delete(e)}resumeUploadGroup(e){let t=this.#e.uploadGroups[e];if(!t)return;this.#C||this.#X(e,t,"upload:group_resumed");let s=this.#C;this.#C=!0,this.#e.unpauseUploadGroup(e);for(let n of t.assetIds){let r=this.#e.getUploadAsset(n);r&&r.status==="paused"&&this.resumeUpload(n)}this.#C=s}retryUploadGroup(e){let t=this.#e.uploadGroups[e];if(t)for(let s of t.assetIds){let n=this.#e.getUploadAsset(s);n&&n.status==="failed"&&this.retryUpload(s)}}setGroupCreatingFolders(e,t){this.#e.updateUploadGroup(e,{isCreatingFolders:t}),this.#R()}addAssetToGroup(e,t){this.#e.addAssetToGroup(e,t)}pauseAllUploads(){let e=this.#W();this.#C=!0;try{for(let t of Object.values(this.#e.uploadAssets))t.groupId||this.pauseUpload(t.assetId);for(let t of Object.values(this.#e.uploadGroups))t.isCreatingFolders||this.pauseUploadGroup(t.id)}finally{this.#C=!1}this.#r?.("upload:all_paused",e),this.#R()}resumeAllUploads(){let e=this.#W();this.#C=!0;try{for(let t of Object.values(this.#e.uploadAssets))!t.groupId&&t.status==="paused"&&this.resumeUpload(t.assetId);for(let t of Object.values(this.#e.uploadGroups))t.isPaused&&this.resumeUploadGroup(t.id)}finally{this.#C=!1}this.#r?.("upload:all_resumed",e),this.#R()}clearAllUploads(){this.#c=[],this.#d.clear(),this.#y.clear(),this.#S.clear(),this.#e.clearAllUploads(),this.#e.uploadGroups={},this.#D&&(this.#N=!0,this.#D.reset(),this.#N=!1),this.#n?.({all:!0}),this.#u?.(this.#z())}clearHistory(){this.#b=[],this.#i?.clear(),this.#g=!0,this.#k.clear(),this.#E=!0,this.#R()}removeHistoryItem(e){this.#b=this.#b.filter(t=>t.groupId!==e),this.#i?.remove(e),this.#k.add(e),this.#E=!0,this.#R()}removeGroup(e){this.#e.removeUploadGroup(e),this.#b=this.#b.filter(t=>t.groupId!==e),this.#i?.remove(e),this.#k.add(e),this.#E=!0,this.#n?.({groupId:e}),this.#u?.(this.#z()),this.#R()}getHttpClient(){return this.#t}setBandwidthLimit(e){this.#I=e,this.#o?.setBandwidthLimitBps(e),this.#E=!0,this.#R()}getBandwidthLimit(){return this.#I}setConflictResolution(e){this.#p=e,this.#o?.setConflictResolution(e),this.#E=!0,this.#R()}getConflictResolution(){return this.#p}getGroupConflictResolution(e){return this.#e.getUploadGroup(e)?.conflictResolution??this.#p}setMaxConcurrency(e){this.#f=e,this.#M()}#Z(){return this.#I===null?null:Math.floor(this.#I/Math.max(1,this.#f))}getSnapshot(){return this.#e.updateUploadStats(),{...this.#e.getSerializableSnapshot(),uploadHistory:[...this.#b??[]],uploadSummary:this.#z(),bandwidthLimitBps:this.#I,conflictResolution:this.#p}}getUploadAsset(e){return this.#e.getSerializableAsset(e)}getUploadSummary(){return this.#z()}getGroupAssets(e){return this.#e.getGroupAssets(e)}getUploadGroupStatus(e){return this.#e.getUploadGroupStatus(e)}getUploadGroupProgress(e){return this.#e.getUploadGroupProgress(e)}hasGroupsCreatingFolders(){return this.#e.hasGroupsCreatingFolders()}areAllUploadsPaused(){return this.#e.areAllUploadsPaused()}hasPausedUploads(){return this.#e.hasPausedUploads()}hasActiveUploads(){return ne(this.#e.getSerializableSnapshot().uploadAssets)}getServerAssetIdsToCancel(){return se(this.#e.getSerializableSnapshot().uploadAssets)}async cancelActiveServerUploads(){let e=this.getServerAssetIdsToCancel();if(e.length!==0)try{await this.#t.post("/assets/cancel-uploads",{asset_ids:e})}catch{await this.#t.post("/assets/cancel-uploads",{asset_ids:e})}}destroy(){this.#m=!0,this.#h&&(clearInterval(this.#h),this.#h=null),this.#L&&(clearInterval(this.#L),this.#L=null),this.#c=[],this.#d.clear();for(let e of Object.values(this.#e.uploadAssets))e.abortController.abort(),e.fileReader.close?.()}#G(){this.#w||(this.#w=!0,queueMicrotask(()=>{this.#w=!1,this.#J()}))}async#J(){if(!this.#v){for(this.#v=!0;this.#c.length>0&&!(this.#y.size>5);){let e=Math.min(this.#c.length,20-this.#y.size);if(e<=0)break;let t=[],s=[];for(let n=0;n<e&&this.#c.length>0;n++){let r=this.#c[0],i=this.#e.getUploadAsset(r);if(!i||i.status==="cancelled"){this.#c.shift(),n--;continue}if(this.#c.shift(),s.push(r),i.chunkedState)try{this.#ee(r,i),this.#M()}catch(o){w(o)||this.#O(r,o)}else t.push(r)}if(t.length>0){let n=new Map;for(let r of t){let o=this.#e.getUploadAsset(r)?.shareContext?.shareId??"",l=n.get(o);l||(l=[],n.set(o,l)),l.push(r)}for(let r of n.values())try{await this.#te(r)}catch(i){for(let o of r)this.#O(o,i instanceof Error?i:new Error(String(i)))}this.#M()}}this.#v=!1}}#ee(e,t){let s=t.chunkedState,n={serverAssetId:s.assetId,chunkIds:s.chunkIds,chunkSize:s.chunkSize,totalChunks:s.totalChunks,sizeBytes:s.sizeBytes},r=s.completedChunkIndices;this.#y.set(e,n);let i=new Array(n.totalChunks).fill(0);if(r.length>0){for(let l of r){let u=l*n.chunkSize,c=Math.min(u+n.chunkSize,t.totalBytesToUpload);i[l]=c-u}let o=i.reduce((l,u)=>l+u,0);this.#e.updateUploadProgress(e,o)}if(this.#S.set(e,i),n.totalChunks===0||r.length>=n.totalChunks){this.#j(e).catch(o=>{this.#O(e,o)});return}this.#d.registerAsset({assetId:e,totalChunks:n.totalChunks,completedChunkIndices:r})}async#te(e){let t=[],s=[];for(let o of e){let l=this.#e.getUploadAsset(o);if(!l||l.status==="cancelled")continue;let u=le(l.fileName),c=l.groupId?this.#e.getUploadGroup(l.groupId)?.conflictResolution??this.#p:this.#p,d=(()=>{switch(c){case"keep_both":return{auto_rename:"parenthesized"};case"replace":return{conflict_resolution:"replace"};case"skip":return{conflict_resolution:"skip"}}})();t.push({directory_id:l.parentId,id:o,name:u,size_bytes:l.totalBytesToUpload,...d}),s.push(o)}if(t.length===0)return;let n=this.#e.getUploadAsset(s[0]),r=n?.shareContext?R(n.shareContext):void 0,i=await this.#t.post("/assets/chunked/initiate",t,r);for(let o=0;o<s.length;o++){let l=s[o],u=i[o];if(!u||u.error){this.#O(l,new Error(u?.error??"Unknown batch initiation error"));continue}if(u.skipped){let S=this.#e.getUploadAsset(l);S&&(this.#e.adjustSpeedBaselineForSkippedBytes(S.totalBytesToUpload),this.#e.markUploadSuccess(l),this.#e.dirtyHistoryGroupIds.add(S.groupId??""),this.#$(),S.fileReader.close?.());continue}let c=u.result,d=this.#e.getUploadAsset(l);if(!d||d.status==="cancelled"||d.status==="paused"){(!d||d.status==="cancelled")&&(this.#F.push(c.asset_id),this.#K());continue}let h=Math.ceil(d.totalBytesToUpload/c.chunk_size),p=Array.from({length:h},()=>crypto.randomUUID());this.#s.seedToken(c.asset_id,c.token,c.token_expires_at);let f={serverAssetId:c.asset_id,chunkIds:p,chunkSize:c.chunk_size,totalChunks:h,sizeBytes:d.totalBytesToUpload};this.#e.setChunkedState(l,{assetId:f.serverAssetId,chunkIds:f.chunkIds,completedChunkIndices:[],chunkSize:f.chunkSize,totalChunks:f.totalChunks,sizeBytes:f.sizeBytes}),this.#y.set(l,f);let C=new Array(f.totalChunks).fill(0);if(this.#S.set(l,C),f.totalChunks===0){this.#j(l).catch(S=>{this.#O(l,S)});continue}this.#d.registerAsset({assetId:l,totalChunks:f.totalChunks,completedChunkIndices:[]})}}#M(){if(!this.#m)for(;this.#_<this.#f;){let e=this.#d.claimNextChunk();if(!e)break;this.#_++;let{assetId:t,chunkIndex:s}=e;this.#se(t,s).then(n=>{this.#_--,this.#re(t,s,n),this.#M()}).catch(n=>{if(this.#_--,w(n)){this.#M();return}this.#ie(t,s,n),this.#M()})}}async#se(e,t){let s;for(let n=0;n<3;n++)try{let r=this.#S.get(e);return r&&(r[t]=0,this.#H(e)),await this.#ne(e,t)}catch(r){if(w(r))throw r;if(s=r instanceof Error?r:new Error(String(r)),this.#D?.recordError(),this.#oe(e,t,s,n),n===2)throw s;let i=this.#S.get(e);i&&(i[t]=0,this.#H(e));let o=1e3*Math.pow(2,n);await Ze(o)}throw s||new Error("Unknown error during chunk upload")}async#ne(e,t){let s=this.#e.getUploadAsset(e);if(!s)throw new A;if(s.abortController.signal.aborted)throw new A;let n=this.#y.get(e);if(!n)throw new Error(`No chunk metadata for asset ${e}`);let r=t*n.chunkSize,i=Math.min(r+n.chunkSize,s.totalBytesToUpload),o=await s.fileReader.readChunk(r,i),l=Je(o);if(s.abortController.signal.aborted)throw new A;let u=n.chunkIds[t],c=await this.#s.getToken(n.serverAssetId,s.shareContext),d=`/uploads/chunks/${u}`,h=Date.now(),p=await this.#t.putWithToken(d,o,c,{signal:s.abortController.signal,onProgress:S=>{let T=this.#S.get(e);T&&(T[t]=S,this.#H(e))},contentType:"application/octet-stream",getMaxUploadRate:()=>this.#Z()}),f=Date.now()-h;if(!p.status||p.status<200||p.status>=300)throw this.#a.error("Chunk upload returned unexpected status",{chunkIndex:t,chunkId:u,assetId:e,serverAssetId:n.serverAssetId,chunkSizeBytes:l,durationMs:f,httpStatus:p.status,statusText:p.statusText,responseBody:p.data,responseHeaders:p.headers}),new Error(`Chunk ${t} upload failed: unexpected status ${p.status}`);let C=this.#S.get(e);return C&&(C[t]=l,this.#H(e)),f}#H(e){let t=this.#S.get(e);if(!t)return;let s=t.reduce((n,r)=>n+r,0);this.#e.updateUploadProgress(e,s)}#re(e,t,s){if(this.#D){let r=this.#y.get(e);if(r){let i=t*r.chunkSize,l=Math.min(i+r.chunkSize,r.sizeBytes)-i;this.#D.recordSuccess(l,s)}}this.#e.addCompletedChunkIndex(e,t),this.#d.markChunkCompleted(e,t)&&this.#j(e).catch(r=>{this.#O(e,r)})}#ie(e,t,s){this.#O(e,s)}#oe(e,t,s,n){if(!this.#r)return;let r=this.#e.getUploadAsset(e);if(!r)return;let i=this.#y.get(e);if(!i)return;let o=t*i.chunkSize,u=Math.min(o+i.chunkSize,i.sizeBytes)-o,c=s,d=typeof c.code=="string"?c.code:null,h=c.response?.status,p=typeof h=="number"?h:null,f=s.cause?s.cause instanceof Error?s.cause.message:String(s.cause):null;this.#r("upload:chunk_error",{file_name:r.fileName,file_size:r.totalBytesToUpload,chunk_index:t,chunk_size:u,chunks_total:i.totalChunks,attempt:n+1,max_attempts:3,is_final_attempt:n===2,error_type:s.name,error_message:s.message,error_code:d,http_status:p,error_stack:s.stack??null,error_cause:f,project_id:r.projectId??""})}#ae(e,t,s,n){if(!this.#r||w(t))return;let r=t instanceof Error?t.message:"Unknown error",i=t,o=t instanceof Error&&t.cause?t.cause instanceof Error?t.cause.message:String(t.cause):null,l=typeof i.code=="string"?i.code:null,u=i.response?.status,c=typeof u=="number"?u:null,d=this.#y.get(e.assetId);this.#r("upload:file_error",{file_name:e.fileName,file_size:e.totalBytesToUpload,bytes_uploaded:e.bytesUploaded,chunks_completed:e.chunkedState?.completedChunkIndices.length??0,chunks_total:d?.totalChunks??0,attempt:s+1,max_attempts:4,is_final_attempt:n,error_type:t instanceof Error?t.name:"Unknown",error_message:r,error_code:l,http_status:c,error_stack:t instanceof Error?t.stack??null:null,error_cause:o,project_id:e.projectId??""})}async#j(e){let t=this.#e.getUploadAsset(e);if(!t)return;let s=this.#y.get(e);if(!s)return;if(t.abortController.signal.aborted)throw new A;let n=crypto.randomUUID(),r=JSON.stringify(s.chunkIds),i=new TextEncoder().encode(r),o=await this.#s.getToken(s.serverAssetId,t.shareContext),l=`/uploads/manifests/${n}`;try{await this.#t.putWithToken(l,i,o,{signal:t.abortController.signal,contentType:"application/json"})}catch(u){throw this.#a.error("Manifest upload failed",{assetId:e,serverAssetId:s.serverAssetId,manifestId:n},u),u}if(t.abortController.signal.aborted)throw new A;try{let u=t.shareContext?R(t.shareContext):void 0;await this.#t.post(`/assets/${s.serverAssetId}/revisions/commit`,{manifest_id:n,size_bytes:s.sizeBytes,is_initial_upload:!0,client_performed_at:new Date().toISOString()},u)}catch(u){throw this.#a.error("Commit revision failed",{assetId:e,serverAssetId:s.serverAssetId,manifestId:n,sizeBytes:s.sizeBytes},u),u}this.#e.markUploadSuccess(e),this.#e.clearChunkedState(e),this.#$(),this.#R(),this.#y.delete(e),this.#S.delete(e),this.#d.removeAsset(e),await t.fileReader.close?.(),this.#V(e),this.#G(),this.#q()}#O(e,t){let s=this.#e.getUploadAsset(e);if(!s||s.status==="paused"||s.status==="cancelled")return;let n=s.retryCount,r=n>=3||Ve(t);if(this.#ae(s,t,n,r),r){let i=t instanceof Error?t.message:"Unknown error";this.#a.error("Asset upload permanently failed",{assetId:e,fileName:s.fileName,totalRetries:n,bytesUploaded:s.bytesUploaded,totalBytes:s.totalBytesToUpload,chunksCompleted:s.chunkedState?.completedChunkIndices.length??0},t),s.abortController.abort(),this.#e.markUploadFailed(e,i),this.#d.removeAsset(e),this.#y.delete(e),this.#S.delete(e),this.#$(),this.#R(),this.#V(e),this.#G(),this.#q()}else{this.#a.warn("Asset upload failed, scheduling retry",{assetId:e,fileName:s.fileName,retryAttempt:n+1,maxRetries:3},t);let i=s.chunkedState;s.abortController.abort(),this.#d.removeAsset(e),this.#y.delete(e),this.#S.delete(e),this.#e.resetAssetForRetryQueue(e),this.#R(),this.#G();let o=2e3*Math.pow(2,n);setTimeout(()=>{if(this.#m)return;let l=this.#e.getUploadAsset(e);!l||l.status!=="queued"||this.startUpload({fileReader:l.fileReader,directoryId:l.parentId??"",projectId:l.projectId,groupId:l.groupId,assetId:e,resumeChunked:i,shareContext:l.shareContext})},o)}}#q(){this.#D&&(this.hasActiveUploads()||(this.#N=!0,this.#D.reset(),this.#N=!1))}#K(){this.#x||(this.#x=!0,queueMicrotask(()=>{this.#x=!1;let e=this.#F.splice(0);e.length!==0&&this.#t.post("/assets/cancel-uploads",{asset_ids:e}).catch(t=>{this.#a.error("Failed to batch abort uploads",{assetIds:e},t)})}))}#z(){return P(this.#e.getSerializableSnapshot().uploadAssets)}#R(){this.#U||(this.#U=!0,queueMicrotask(()=>{this.#U=!1,this.#ue()}))}#le(){this.#Y(!0)}#ue(){this.#Y(!1)}#Y(e){if(this.#m||!this.#l&&!this.#u)return;e?this.#e.updateUploadStats():(this.#B++,this.#B%3===0&&this.#e.updateUploadStats());let t=this.#e.getDirtyDelta();if(!this.#E&&Object.keys(t.assets).length===0&&Object.keys(t.groups).length===0&&this.#e.dirtyHistoryGroupIds.size===0)return;this.#E=!1,this.#e.dirtyHistoryGroupIds.size>0&&this.#$();let n=this.#e.dirtyHistoryGroupIds.size>0?this.#de():void 0,r=this.#g?!0:void 0,i=this.#k.size>0?[...this.#k]:void 0;this.#g=!1,this.#k.clear();let o=this.#z(),l={assets:t.assets,groups:t.groups,uploadStats:{...this.#e.uploadStats},uploadSpeedState:this.#e.uploadSpeedState,historyItems:n,removedHistoryGroupIds:i,historyCleared:r,uploadSummary:o,bandwidthLimitBps:this.#I,conflictResolution:this.#p};this.#e.clearDirtyFlags(),this.#l?.(l),this.#u?.(o)}#Q(e){let t=0,s=0,n=0;for(let r of e.assetIds){let i=this.#e.getUploadAsset(r);i&&(t+=i.totalBytesToUpload,s+=i.bytesUploaded,i.status!=="success"&&i.status!=="failed"&&i.status!=="cancelled"&&n++)}return{totalBytes:t,bytesUploaded:s,filesRemaining:n}}#W(){let e=0,t=0,s=0,n=0;for(let r of Object.values(this.#e.uploadGroups)){e++;for(let i of r.assetIds){let o=this.#e.getUploadAsset(i);o&&(o.status==="success"||o.status==="failed"||o.status==="cancelled"||(t++,s+=o.totalBytesToUpload,n+=o.bytesUploaded))}}return{group_count:e,file_count:t,total_bytes:s,bytes_uploaded:n}}#ce(){if(!(!this.#r||this.#m))for(let e of Object.values(this.#e.uploadGroups)){let t=!1,s=0,n=0,r=0,i=0,o=0,l=0,u=0,c=0,d=0,h=0;for(let C of e.assetIds){let S=this.#e.getUploadAsset(C);if(!S)continue;let T=S.totalBytesToUpload;switch(s+=T,n+=S.bytesUploaded,S.status){case"success":r++,i+=T;break;case"failed":o++,l+=T;break;case"cancelled":u++,c+=T;break;case"in-progress":d++,t=!0;break;case"queued":h++,t=!0;break;case"paused":t=!0;break}}if(!t)continue;let p=this.#T.get(e.id),f=p?Date.now()-p:0;this.#r("upload:group_progress",{group_id:e.id,file_count:e.assetIds.length,total_bytes:s,bytes_uploaded:n,duration_ms:f,project_id:e.projectId,succeeded_count:r,succeeded_bytes:i,failed_count:o,failed_bytes:l,cancelled_count:u,cancelled_bytes:c,in_progress_count:d,queued_count:h,progress:s>0?n/s:0,concurrency:this.#f,upload_speed_mbps:this.#e.uploadStats.uploadSpeedMbps})}}#X(e,t,s){if(!this.#r)return;let{totalBytes:n,bytesUploaded:r}=this.#Q(t);this.#r(s,{file_count:t.fileCount,total_bytes:n,bytes_uploaded:r,project_id:t.projectId})}#V(e){if(!this.#r)return;let t=this.#e.getUploadAsset(e);if(!t?.groupId)return;let s=this.#e.uploadGroups[t.groupId];if(!s)return;let n=!0,r=0,i=0,o=0,l=0,u=0,c=0,d=0;for(let f of s.assetIds){let C=this.#e.getUploadAsset(f);if(!C)continue;let S=C.totalBytesToUpload;if(d+=S,C.status==="success")r++,i+=S;else if(C.status==="failed")o++,l+=S;else if(C.status==="cancelled")u++,c+=S;else{n=!1;break}}if(!n)return;let h=this.#T.get(s.id),p=h?Date.now()-h:0;this.#r("upload:group_completed",{file_count:s.assetIds.length,total_bytes:d,duration_ms:p,project_id:s.projectId,succeeded_count:r,succeeded_bytes:i,failed_count:o,failed_bytes:l,cancelled_count:u,cancelled_bytes:c}),this.#T.delete(s.id)}#de(){let e={};for(let t of this.#e.dirtyHistoryGroupIds){let s=this.#e.buildHistoryItem(t);s&&(e[t]=s)}return e}#$(){if(!this.#i)return;let e={};for(let t of this.#e.dirtyHistoryGroupIds){let s=this.#e.buildHistoryItem(t);if(s){e[t]=s;let n=this.#b.findIndex(r=>r.groupId===t);n>=0?this.#b[n]=s:this.#b.unshift(s)}}Object.keys(e).length>0&&this.#i.merge(e)}};var B=class{#t=65536;#s=0;async*createIterator(e,t,s){let n=0,r=Math.max(65536,Math.min(e.byteLength,this.#t)),i=this.#s;for(;n<e.byteLength;){if(s?.aborted)return;let o=Math.min(n+r,e.byteLength),l=e.subarray(n,o),u=l.byteLength,c=performance.now();yield l;let d=performance.now()-c;n=o;let h=t();if(h!==null&&h>0){let p=Math.round(h*50/1e3);if(r=Math.max(65536,Math.min(e.byteLength,p)),n<e.byteLength){let C=u/h*1e3-d;C>1&&await new Promise(S=>setTimeout(S,C))}}else{if(d>=.5){let p=u/d*1e3;i=i===0?p:i*(1-.3)+p*.3}else i===0&&(r=Math.min(e.byteLength,r*2));if(i>0){let p=Math.round(i*50/1e3);r=Math.max(65536,Math.min(e.byteLength,p))}}this.#t=r,this.#s=i}}};var y=(a,e=2)=>{if(a===0)return"0 B";let t=1024,s=e<0?0:e,n=["B","KB","MB","GB","TB","PB","EB"],r=Math.floor(Math.log(a)/Math.log(t));return`${(a/Math.pow(t,r)).toFixed(s)} ${n[r]}`},ke=a=>{if(a===null)return null;let t=a*8/(1024*1024);return t<1?`${(t*1024).toFixed(0)} Kbps`:`${t.toFixed(1)} Mbps`},D=a=>{if(a===null)return null;let e=Math.floor(a/3600),t=Math.floor(a%3600/60),s=Math.floor(a%60);return e>0?`${e}h ${t}m`:t>0?`${t}m ${s}s`:`${s}s`};var tt=1e3,ce=class{#t=null;#s=!1;#e=null;#i=null;#o=null;#r=new Map;#a=null;#l=null;#n=null;#u=null;start(){this.#s||(this.#s=!0,this.#r.clear(),this.#a=null,this.#l=null,this.#n=null,this.#u=null,this.#t=setInterval(()=>this.render(),tt))}stop(){this.#s&&(this.#s=!1,this.#t&&(clearInterval(this.#t),this.#t=null),this.render(),Ie.done())}updateSnapshot(e){this.#e=e}updateBatchState(e){this.#i=e}updateDownloadProgress(e){if(!e){this.#o=null;return}let t=e.percentComplete>=100||e.bytesTransferred>=e.totalBytes;!t&&e.speed>0&&(this.#n=e),this.#o=t&&this.#n?{...e,speed:this.#n.speed,eta:this.#n.eta}:e}persistCurrentBatchLine(){!this.#i||this.#i.currentPhase!=="complete"||this.#r.set(this.#i.currentBatch,this.#h(this.#i))}resetUploadSnapshot(){this.#e=null,this.#o=null,this.#n=null,this.#u=null}render(){if(!this.#s)return;let e=this.#i?this.#P(this.#i):this.#f();Ie(e.join(`
|
|
7
|
+
`))}#A(e,t){let s=[`${m.green(y(t.bytesTransferred))} / ${m.gray(y(t.totalBytes))}`,m.cyan(`${t.percentComplete.toFixed(1)}%`),m.magenta(ke(t.speed)??"..."),`${m.yellow("ETA")}: ${m.yellow(D(t.eta)??"...")}`];return`${m.blue(e)}: ${s.join(" | ")}`}#f(){let e=this.#c();return[this.#m(e),this.#E(e)]}#P(e){let t=[];for(let s=1;s<e.currentBatch;s++){let n=this.#r.get(s);n&&t.push(n)}return t.push(this.#h(e)),t.push(this.#U(e)),t}#h(e){let t=`Batch ${e.currentBatch}/${e.totalBatches}`;switch(e.currentPhase){case"downloading":return this.#o?`${t}: ${this.#A("Downloading",this.#o)}`:`${t}: ${m.blue("Downloading...")}`;case"scanning":return`${t}: ${m.yellow(e.extraDetails??"Scanning...")}`;case"syncing_folders":return`${t}: ${m.yellow(e.extraDetails??"Syncing folders...")}`;case"uploading":return this.#B(t,e);case"cleaning":return`${t}: ${m.yellow("Cleaning...")}`;case"complete":return`${t}: ${m.green("Complete")} (${e.batchFilesTotal} files, ${y(e.batchBytesTotal)}${e.extraDetails?`, ${e.extraDetails}`:""})`}}#B(e,t){let s=this.#c(),n=s.successFileCount+s.failedFileCount+s.cancelledFileCount,r=Math.max(t.batchFilesTotal-t.batchFilesSkipped,0),i=Math.max(t.batchBytesTotal-t.batchBytesSkipped,0),o=i>0?(s.uploadedBytes/i*100).toFixed(1):"0.0",l=this.#g(t,s,i),u=l?.formattedSpeed??null,c=l?.formattedTime??null,d=u&&c?`, ${u}, ETA: ${c}`:"",h=t.batchFilesSkipped>0?`, ${m.yellow(`Skipped: ${t.batchFilesSkipped} files / ${y(t.batchBytesSkipped)}`)}`:"";return`${e}: ${m.blue("Uploading")} (${n}/${r} files, ${y(s.uploadedBytes)} / ${y(i)}, ${o}%${d}${h})`}#U(e){let t=this.#c(),s=e.batchFilesSkipped+t.successFileCount+t.failedFileCount+t.cancelledFileCount,n=e.batchBytesSkipped+t.uploadedBytes,r=e.overallFilesCompleted+s,i=e.overallBytesCompleted+n,o=Math.max(e.overallBytesTotal-i,0),l=this.#b(e),u=this.#k(e,t),c=l!==null&&u!==null?l+u:null;return[`Total: ${r}/${e.overallFilesTotal} files`,`${y(i)} / ${y(e.overallBytesTotal)}`,m.yellow(`${y(o)} remaining`),`Download ETA: ${D(l)??"..."}`,`Upload ETA: ${D(u)??"..."}`,`Total ETA: ${D(c)??"..."}`].join(", ")}#b(e){let t=e.overallBytesCompleted+e.batchBytesSkipped+(this.#o?.bytesTransferred??0),s=Math.max(e.overallBytesTotal-t,0);if(s===0)return e.currentPhase==="downloading"&&this.#o?(this.#a=this.#o.eta,this.#o.eta):(this.#a=0,0);if(this.#o&&this.#o.speed>0){let n=Math.ceil(s/this.#o.speed);return this.#a=n,n}return this.#a}#k(e,t){let s=e.overallBytesCompleted+e.batchBytesSkipped+t.uploadedBytes,n=Math.max(e.overallBytesTotal-s,0),r=Math.max(e.batchBytesTotal-e.batchBytesSkipped,0),i=this.#g(e,t,r);if(n===0)return e.currentPhase==="uploading"&&i?(this.#l=i.timeRemainingSeconds,i.timeRemainingSeconds):(this.#l=0,0);if(i&&i.uploadSpeedMbps>0){let o=i.uploadSpeedMbps*1024*1024/8,l=Math.ceil(n/o);return this.#l=l,l}return this.#l}#g(e,t,s){let n=this.#e?.uploadStats,r=s>0&&t.uploadedBytes>=s;return n&&n.uploadSpeedMbps>0&&n.formattedSpeed!=="Calculating..."&&!r?(this.#u={uploadSpeedMbps:n.uploadSpeedMbps,formattedSpeed:n.formattedSpeed,formattedTime:n.formattedTime,timeRemainingSeconds:n.timeRemainingSeconds},this.#u):e.currentPhase==="uploading"&&r&&this.#u?this.#u:n?{uploadSpeedMbps:n.uploadSpeedMbps,formattedSpeed:n.formattedSpeed,formattedTime:n.formattedTime,timeRemainingSeconds:n.timeRemainingSeconds}:null}#m(e){return[`Total: ${e.totalFileCount}`,m.green(`Success: ${e.successFileCount}`),m.red(`Failed: ${e.failedFileCount}`),m.red(`Cancelled: ${e.cancelledFileCount}`),m.blue(`Active: ${e.activeFileCount}`),m.gray(`Queued: ${e.queuedFileCount}`)].join(" | ")}#E(e){let t=Math.max(e.totalBytes-e.uploadedBytes,0),s=this.#e?` | ${m.cyan(`Speed: ${this.#e.uploadStats.formattedSpeed}`)} | ${m.magenta(`Time remaining: ${this.#e.uploadStats.formattedTime}`)}`:"";return[`Total: ${y(e.totalBytes)}`,m.green(`Uploaded: ${y(e.uploadedBytes)}`),m.yellow(`Remaining: ${y(t)}`)].join(" | ")+s}#c(){return this.#e?Object.values(this.#e.uploadAssets).reduce((t,s)=>{switch(t.totalFileCount+=1,t.totalBytes+=s.totalBytesToUpload,t.uploadedBytes+=s.bytesUploaded,s.status){case"success":t.successFileCount+=1;break;case"failed":t.failedFileCount+=1;break;case"cancelled":t.cancelledFileCount+=1;break;case"in-progress":t.activeFileCount+=1;break;default:t.queuedFileCount+=1;break}return t},{totalFileCount:0,successFileCount:0,failedFileCount:0,cancelledFileCount:0,activeFileCount:0,queuedFileCount:0,uploadedBytes:0,totalBytes:0}):{totalFileCount:0,successFileCount:0,failedFileCount:0,cancelledFileCount:0,activeFileCount:0,queuedFileCount:0,uploadedBytes:0,totalBytes:0}}},g=new ce;import{Readable as st}from"node:stream";import Fe from"axios";var I=class{#t;#s;#e;#i;#o=new B;#r=new Set;constructor(e){this.#t=Fe.create({baseURL:e.apiUrl}),this.#s=Fe.create({baseURL:e.edgeWorkerUrl}),this.#e=e.apiKey,this.#i=e.sessionId}get skippedAssetIds(){return this.#r}async post(e,t,s){let n=await this.#t.post(e,t,{headers:{Authorization:`Bearer ${this.#e}`,"X-Aspect-Session-Id":this.#i,"Content-Type":"application/json",...s}});return this.#l(e,t,n.data),n.data}async get(e,t){return(await this.#t.get(e,{headers:{Authorization:`Bearer ${this.#e}`,"X-Aspect-Session-Id":this.#i,...t}})).data}async delete(e,t){await this.#t.delete(e,{headers:{Authorization:`Bearer ${this.#e}`,"X-Aspect-Session-Id":this.#i,...t}})}async putWithToken(e,t,s,n){let r=await this.#a(t),i=this.#o.createIterator(r,n.getMaxUploadRate??(()=>null),n.signal),o=st.from(i,{highWaterMark:1}),l=await this.#s.put(e,o,{timeout:0,signal:n.signal,headers:{"Content-Type":n.contentType??"","Content-Length":r.byteLength.toString(),Authorization:`Bearer ${s}`},transformRequest:[u=>u],maxRedirects:0,onUploadProgress:u=>n.onProgress?.(u.loaded)});return{status:l.status,statusText:l.statusText,headers:Object.fromEntries(Object.entries(l.headers)),data:l.data,raw:l}}async#a(e){if(e instanceof Uint8Array)return e;let t=await e.arrayBuffer();return new Uint8Array(t)}#l(e,t,s){if(e!=="/assets/chunked/initiate"||!Array.isArray(t)||!Array.isArray(s))return;let n=t;s.forEach((i,o)=>{let l=n[o];i.skipped===!0&&typeof l?.id=="string"&&this.#r.add(l.id)})}};import L from"node:path";import de from"node:path";function E(a){let e=a.replace(/\\/g,"/");if(e.split("/").some(r=>r===".."))throw new Error(`Remote path cannot contain parent directory segments: ${a}`);let n=de.posix.normalize(e).replace(/^\/+/,"");if(n===""||n===".")throw new Error(`Remote path must resolve to a file path: ${a}`);return n}function Pe(a,e){let t=new Map,s=[],n=[];for(let r of a){let i=e(r),o=E(i),l=t.get(o);if(l!==void 0){n.push({normalizedPath:o,firstPath:l,duplicatePath:i});continue}t.set(o,i),s.push(r)}return{uniqueItems:s,duplicates:n}}function ve(a,e){let t=E(a.path);return{relativePath:t,absolutePath:de.join(e,t),fileName:de.posix.basename(t),size:a.size}}var q=class{#t;#s;#e=new Map;constructor(e){this.#t=e.rootDirectoryId,this.#s=e.httpClient}async ensureDirectories(e){await this.loadExistingDirectories();let t=this.#i(e);for(let s of t){if(this.#e.has(s))continue;let n=L.posix.dirname(s),r=L.posix.basename(s),i=n==="."?this.#t:this.#e.get(n);if(!i)throw new Error(`Parent directory ID not found for path: ${s}`);let o=await this.#a(i,r);if(o.name!==r)throw new Error(`Server created renamed directory "${o.name}" for planned path "${s}". Refusing to map uploads to an unexpected target path.`);this.#e.set(s,o.id)}return e.map(s=>({file:s,directoryId:this.getDirectoryIdForFile(s.relativePath)}))}async loadExistingDirectories(){let e=await this.#o(this.#t);this.#e.clear(),this.#r(e,"")}getDirectoryIdForFile(e){let t=E(e),s=L.posix.dirname(t);if(s===".")return this.#t;let n=this.#e.get(s);if(!n)throw new Error(`Directory ID not found for path: ${s}`);return n}getExistingDirectoryIdForFile(e){let t=E(e),s=L.posix.dirname(t);return s==="."?this.#t:this.#e.get(s)??null}async checkAssetsExistence(e,t=!0){return e.length===0?[]:(await this.#s.post("/assets/check-exists-and-uploaded",{items:e,delete_if_not_exist:t})).items}#i(e){let t=new Set;for(let s of e){let n=E(s.relativePath),r=L.posix.dirname(n);if(r===".")continue;let i=r.split("/");for(let o=0;o<i.length;o++)t.add(i.slice(0,o+1).join("/"))}return Array.from(t).sort((s,n)=>s.split("/").length-n.split("/").length)}async#o(e){return this.#s.get(`/directories/${e}/tree`)}#r(e,t){let s=t===""?"":t==="."?e.name:`${t}/${e.name}`;s!==""&&this.#e.set(s,e.id);for(let n of e.children){let r=t===""?".":s;this.#r(n,r)}}async#a(e,t){return this.#s.post(`/directories/${e}/directories`,{name:t,auto_rename:"numeric"})}};import*as K from"fs/promises";import*as Y from"path";var nt={".mp4":"video/mp4",".mov":"video/quicktime",".avi":"video/x-msvideo",".mkv":"video/x-matroska",".webm":"video/webm",".wmv":"video/x-ms-wmv",".flv":"video/x-flv",".m4v":"video/x-m4v",".mpg":"video/mpeg",".mpeg":"video/mpeg",".3gp":"video/3gpp",".ts":"video/mp2t",".mts":"video/mp2t",".mxf":"application/mxf",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp",".bmp":"image/bmp",".tiff":"image/tiff",".tif":"image/tiff",".svg":"image/svg+xml",".heic":"image/heic",".heif":"image/heif",".avif":"image/avif",".mp3":"audio/mpeg",".wav":"audio/wav",".aac":"audio/aac",".ogg":"audio/ogg",".flac":"audio/flac",".m4a":"audio/mp4",".wma":"audio/x-ms-wma",".pdf":"application/pdf",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".rtf":"application/rtf",".json":"application/json",".xml":"application/xml"};function rt(a){let e=Y.extname(a).toLowerCase();return nt[e]??""}var W=class a{#t;#s=null;#e=null;#i=!1;#o;#r;#a;constructor(e,t,s,n){this.#t=e,this.#o=t,this.#r=s,this.#a=n}static async create(e){let t=await K.stat(e),s=Y.basename(e),n=rt(s);return new a(e,t.size,s,n)}get name(){return this.#r}get size(){return this.#o}get type(){return this.#a}async readChunk(e,t){if(!this.#s){this.#e||(this.#e=K.open(this.#t,"r"));let r=await this.#e;if(this.#e=null,this.#i)return Buffer.alloc(0);this.#s=r}let s=t-e,n=Buffer.alloc(s);return await this.#s.read(n,0,s,e),n}async close(){this.#i=!0;let e=this.#e;await this.#s?.close(),this.#s=null,e&&await(await e.catch(()=>null))?.close(),this.#e=null}};var it=new Set(["success","failed","cancelled"]),_e=1e3,Q=class{#t;#s;#e;#i;constructor(e){this.#t=e.config,this.#s=e.httpClient??new I({apiUrl:e.config.apiUrl,apiKey:e.config.apiKey,edgeWorkerUrl:e.config.edgeWorkerUrl,sessionId:e.config.sessionName}),this.#e=e.createFileReader??(t=>W.create(t)),this.#i=e.onSnapshot}async uploadFiles(e){let t=e.filter(i=>i.preSkipped===!0),s=e.filter(i=>i.preSkipped!==!0);if(s.length===0)return{totalFileCount:e.length,successFileCount:0,skippedFileCount:t.length,failedFileCount:0,cancelledFileCount:0,failedFiles:[]};let n=new x({httpClient:this.#s,maxConcurrency:this.#t.maxConcurrent,deltaIntervalMs:_e,adaptiveConcurrency:!1}),r=[];try{let i=n.startGroupUpload({name:`${s.length} files`,type:"files",fileCount:s.length,folderCount:0,topLevelFileCount:s.length,topLevelFolderCount:0,rootDirectoryId:this.#t.directoryId,projectId:this.#t.projectId,totalBytes:s.reduce((l,u)=>l+u.file.size,0),conflictResolutionOverride:"skip"});for(let l of s){let u=await this.#e(l.file.absolutePath),c=n.startUpload({fileReader:u,directoryId:l.directoryId,projectId:this.#t.projectId,groupId:i,chunkSize:this.#t.chunkSizeBytes});r.push({assetId:c,file:l.file,fileReader:u})}await this.#o(n);let o=n.getSnapshot();return this.#i?.(o),this.#r({snapshot:o,startedUploads:r,preSkippedCount:t.length})}finally{n.destroy()}}async#o(e){let s=Date.now();for(;Date.now()-s<864e5;){let n=e.getSnapshot();this.#i?.(n);let r=Object.values(n.uploadAssets);if(r.length>0&&r.every(i=>it.has(i.status)))return;await new Promise(i=>setTimeout(i,_e))}throw new Error("Timed out waiting for upload session to finish")}#r(e){let t=[],s=0,n=0,r=0,i=0;for(let o of e.startedUploads){let l=e.snapshot.uploadAssets[o.assetId];if(!l){r+=1,t.push({fileName:o.file.fileName,relativePath:o.file.relativePath,errorMessage:"Upload state missing from upload engine"});continue}if(l.status==="success"){this.#s.skippedAssetIds.has(o.assetId)?n+=1:s+=1;continue}l.status==="cancelled"?i+=1:r+=1,t.push({fileName:o.file.fileName,relativePath:o.file.relativePath,errorMessage:l.errorMessage})}return{totalFileCount:e.startedUploads.length+e.preSkippedCount,successFileCount:s,skippedFileCount:e.preSkippedCount+n,failedFileCount:r,cancelledFileCount:i,failedFiles:t}}};var X=class{#t;#s;#e;#i;constructor(e){this.#t=e,this.#s=new I({apiUrl:e.apiUrl,apiKey:e.apiKey,edgeWorkerUrl:e.edgeWorkerUrl,sessionId:e.sessionName}),this.#e=new q({rootDirectoryId:e.directoryId,httpClient:this.#s}),this.#i=new Q({config:e,httpClient:this.#s,onSnapshot:t=>g.updateSnapshot(t)})}async run(){this.#P("Starting data sync..."),await this.#a(),console.log("Listing all remote files...");let e=await O(this.#t.remote,this.#t.remotePath,this.#t.rcloneOptions.extraRcloneArgs??[]);console.log(`Found ${e.length} files`),console.log(`Total size: ${y(this.#g(e))}`),console.log("");let t=this.#o(e,"remote listing");if(t.length===0){console.log("No files to sync. Exiting."),await this.#l();return}let s={totalFileCount:0,successFileCount:0,skippedFileCount:0,failedFileCount:0,cancelledFileCount:0,completedByteCount:0},n=[],r={batchNumber:1,files:t,totalSize:this.#g(t)};g.start();try{let o=await this.#r({batch:r,totalBatches:1,overallFilesTotal:t.length,overallBytesTotal:r.totalSize,completedTotals:s,batchDir:this.#t.localTempDir});this.#U(s,n,o,r.totalSize)}finally{g.stop()}let i={...s,failedFiles:n};this.#h({summary:i}),await this.#n(i),this.#b(i)}async runBatched(){if(!this.#t.batchSizeBytes)throw new Error("Batch size not configured. Use batchSizeBytes in config.");this.#P("Starting batched data sync...",[`Batch size: ${y(this.#t.batchSizeBytes)}`,`Upload chunk size: ${y(this.#t.chunkSizeBytes)}`,`Max concurrent chunks: ${this.#t.maxConcurrent}`]),await this.#a(),console.log("Listing all remote files...");let e=await O(this.#t.remote,this.#t.remotePath,this.#t.rcloneOptions.extraRcloneArgs??[]);console.log(`Found ${e.length} files`),console.log(`Total size: ${y(this.#g(e))}`),console.log("");let t=this.#o(e,"remote listing");if(t.length===0){console.log("No files to sync. Exiting."),await this.#l();return}let s=Ae(t,this.#t.batchSizeBytes);await this.#f(s);let n={totalFileCount:0,successFileCount:0,skippedFileCount:0,failedFileCount:0,cancelledFileCount:0,completedByteCount:0},r=[];g.start();try{for(let o of s){let l=await this.#r({batch:o,totalBatches:s.length,overallFilesTotal:t.length,overallBytesTotal:this.#g(t),completedTotals:n});this.#U(n,r,l,o.totalSize)}}finally{g.stop()}this.#h({summary:{...n,failedFiles:r},extraLines:[`Batches: ${s.length}`]});let i={...n,failedFiles:r};await this.#n(i),this.#b(i)}async runCheckOnly(){console.log("Running in check-only mode (no downloads/uploads)..."),await ee(),console.log(`Listing remote files from ${this.#t.remote}:${this.#t.remotePath}...`);let e=await O(this.#t.remote,this.#t.remotePath,this.#t.rcloneOptions.extraRcloneArgs??[]);if(console.log(`Found ${e.length} remote files to check`),e.length===0){console.log("No remote files to check. Exiting.");return}console.log("Fetching existing directory structure..."),await this.#e.loadExistingDirectories();let t=[],s=[],n=[],r=new Set;for(let l of e){let u=l.path,c=E(u);if(r.has(c)){n.push(u);continue}r.add(c);let d=this.#e.getExistingDirectoryIdForFile(c);if(!d){s.push(u);continue}t.push({displayPath:u,request:{directory_id:d,name:he.posix.basename(c),size_bytes:l.size}})}let i=await this.#e.checkAssetsExistence(t.map(l=>l.request),!1),o=0;for(let l=0;l<t.length;l++)i[l]?.exists?o+=1:s.push(t[l].displayPath);this.#B({totalRemoteCount:e.length,duplicateFiles:n,existingCount:o,missingFiles:s})}#o(e,t){let s=Pe(e,o=>o.path);if(s.duplicates.length===0)return s.uniqueItems;let n=s.duplicates.slice(0,10).map(o=>`${o.duplicatePath} duplicates ${o.firstPath}`).join(`
|
|
8
|
+
`),r=s.duplicates.length-10,i=r>0?`
|
|
9
|
+
...and ${r} more duplicate paths`:"";return console.warn(`Warning: Duplicate planned target paths found during ${t}. Keeping the first listed file for each path and skipping ${s.duplicates.length} duplicate file(s).
|
|
10
|
+
${n}${i}
|
|
11
|
+
`),console.log(`Syncing ${s.uniqueItems.length} unique target paths after duplicate filtering.`),console.log(""),s.uniqueItems}async#r(e){let t=e.batchDir??he.join(this.#t.localTempDir,`batch${e.batch.batchNumber}`);await M.mkdir(t,{recursive:!0});let s=e.batch.files.map(d=>ve(d,t));g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:0,batchBytesSkipped:0,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal),overallBytesTotal:e.overallBytesTotal,currentPhase:"scanning",extraDetails:`Preparing ${s.length} files...`}),g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:0,batchBytesSkipped:0,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal),overallBytesTotal:e.overallBytesTotal,currentPhase:"syncing_folders",extraDetails:"Ensuring target directories exist..."});let n=await this.#e.ensureDirectories(s);g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:0,batchBytesSkipped:0,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal),overallBytesTotal:e.overallBytesTotal,currentPhase:"scanning",extraDetails:"Checking existing assets..."});let r=await this.#u(n),i=this.#A(r),o=e.batch.files.filter((d,h)=>r[h]?.preSkipped!==!0),l=await Ee(this.#t.localTempDir,e.batch.batchNumber,o),u=async()=>{await Te(l),this.#t.keepLocal||await M.rm(t,{recursive:!0,force:!0}),g.resetUploadSnapshot(),g.updateDownloadProgress(null)};if(o.length===0){let d={totalFileCount:r.length,successFileCount:0,skippedFileCount:r.length,failedFileCount:0,cancelledFileCount:0,failedFiles:[]};return g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:i.skippedFileCount,batchBytesSkipped:i.skippedByteCount,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount+d.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal)+e.batch.totalSize,overallBytesTotal:e.overallBytesTotal,currentPhase:"complete",extraDetails:"everything already synced"}),g.persistCurrentBatchLine(),await u(),d}g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:i.skippedFileCount,batchBytesSkipped:i.skippedByteCount,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal),overallBytesTotal:e.overallBytesTotal,currentPhase:"downloading"}),await Se(this.#t.remote,this.#t.remotePath,t,l,this.#t.rcloneOptions,d=>g.updateDownloadProgress(d)),await ye(t),g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:i.skippedFileCount,batchBytesSkipped:i.skippedByteCount,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal),overallBytesTotal:e.overallBytesTotal,currentPhase:"uploading"});let c=await this.#i.uploadFiles(r);return this.#k(c)?(g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:i.skippedFileCount,batchBytesSkipped:i.skippedByteCount,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount+c.successFileCount+c.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal)+e.batch.totalSize,overallBytesTotal:e.overallBytesTotal,currentPhase:"complete",extraDetails:"failed; local files preserved"}),g.persistCurrentBatchLine(),c):(g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:i.skippedFileCount,batchBytesSkipped:i.skippedByteCount,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount+c.successFileCount+c.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal)+e.batch.totalSize,overallBytesTotal:e.overallBytesTotal,currentPhase:"cleaning"}),await u(),g.updateBatchState({currentBatch:e.batch.batchNumber,totalBatches:e.totalBatches,batchFilesTotal:s.length,batchBytesTotal:e.batch.totalSize,batchFilesSkipped:i.skippedFileCount,batchBytesSkipped:i.skippedByteCount,overallFilesCompleted:e.completedTotals.successFileCount+e.completedTotals.skippedFileCount+c.successFileCount+c.skippedFileCount,overallFilesTotal:e.overallFilesTotal,overallBytesCompleted:this.#m(e.completedTotals,e.overallBytesTotal)+e.batch.totalSize,overallBytesTotal:e.overallBytesTotal,currentPhase:"complete"}),g.persistCurrentBatchLine(),c)}async#a(){console.log("Checking rclone installation..."),await ee(),console.log("rclone is installed"),console.log(""),console.log("Creating session directory..."),await M.mkdir(this.#t.localTempDir,{recursive:!0}),console.log(`Session directory: ${this.#t.localTempDir}`),console.log("")}async#l(){this.#t.keepLocal||(console.log(""),console.log("Cleaning up session directory..."),await M.rm(this.#t.localTempDir,{recursive:!0,force:!0}),console.log(`Deleted session directory: ${this.#t.localTempDir}`))}async#n(e){if(this.#k(e)){console.log(""),console.log(`Preserving session directory for retry/debugging: ${this.#t.localTempDir}`);return}await this.#l()}async#u(e){let t=await this.#e.checkAssetsExistence(e.map(s=>({directory_id:s.directoryId,name:s.file.fileName,size_bytes:s.file.size})));return e.map((s,n)=>({file:s.file,directoryId:s.directoryId,preSkipped:t[n]?.exists===!0}))}#A(e){return e.reduce((t,s)=>(s.preSkipped===!0&&(t.skippedFileCount+=1,t.skippedByteCount+=s.file.size),t),{skippedFileCount:0,skippedByteCount:0})}async#f(e){console.log("Creating batches..."),console.log(`Created ${e.length} batches`);let t=[];for(let s of e){let n=`Batch ${s.batchNumber}: ${s.files.length} files, ${y(s.totalSize)}`;t.push(n),console.log(n)}await M.writeFile(he.join(this.#t.localTempDir,"batches.txt"),`${t.join(`
|
|
12
|
+
`)}
|
|
13
|
+
`,"utf-8"),console.log("")}#P(e,t=[]){console.log(e),console.log(`Session: ${this.#t.sessionName}`),console.log(`Remote: ${this.#t.remote}:${this.#t.remotePath}`),console.log(`Root directory ID: ${this.#t.directoryId}`),console.log(`Project ID: ${this.#t.projectId}`),console.log(`Temp directory: ${this.#t.localTempDir}`),console.log(`Edge worker URL: ${this.#t.edgeWorkerUrl}`);for(let s of t)console.log(s);console.log("")}#h(e){console.log(""),console.log("=".repeat(60)),console.log("Sync Summary"),console.log("=".repeat(60)),console.log(`Total files: ${e.summary.totalFileCount}`),console.log(`Successful: ${e.summary.successFileCount}`),console.log(`Skipped: ${e.summary.skippedFileCount}`),console.log(`Failed: ${e.summary.failedFileCount}`),console.log(`Cancelled: ${e.summary.cancelledFileCount}`);for(let t of e.extraLines??[])console.log(t);if(console.log("=".repeat(60)),e.summary.failedFiles.length>0){console.log(""),console.log("Failed files:");for(let t of e.summary.failedFiles)console.log(` - ${t.relativePath}: ${t.errorMessage??"Unknown error"}`)}}#B(e){if(console.log(""),console.log("=".repeat(60)),console.log("Check Summary"),console.log("=".repeat(60)),console.log(`Total (remote): ${e.totalRemoteCount}`),console.log(`Duplicates skipped: ${e.duplicateFiles.length}`),console.log(`Already exist: ${e.existingCount}`),console.log(`Missing: ${e.missingFiles.length}`),console.log("=".repeat(60)),e.missingFiles.length>0){console.log(""),console.log("Files missing on server:");for(let t of e.missingFiles)console.log(` - ${t}`)}if(e.duplicateFiles.length>0){console.log(""),console.log("Duplicate remote files skipped:");for(let t of e.duplicateFiles)console.log(` - ${t}`)}}#U(e,t,s,n){e.totalFileCount+=s.totalFileCount,e.successFileCount+=s.successFileCount,e.skippedFileCount+=s.skippedFileCount,e.failedFileCount+=s.failedFileCount,e.cancelledFileCount+=s.cancelledFileCount,e.completedByteCount+=n,t.push(...s.failedFiles)}#b(e){let t=e.failedFileCount+e.cancelledFileCount;if(t>0)throw new Error(`Sync completed with ${t} unsuccessful upload(s)`)}#k(e){return e.failedFileCount+e.cancelledFileCount>0}#g(e){return e.reduce((t,s)=>t+s.size,0)}#m(e,t){return Math.min(e.completedByteCount,t)}};var ut=lt(import.meta.url),ct=we.dirname(ut),dt=JSON.parse(at(we.join(ct,"..","package.json"),"utf-8")),pe=new ot;pe.name("aspect-sync").description("Sync files from external services to Aspect via rclone").version(dt.version);pe.requiredOption("--remote <remote>","rclone remote name (e.g., dropbox)").requiredOption("--path <path>","remote path to sync from").requiredOption("--directory-id <id>","Aspect directory ID to upload to").requiredOption("--project-id <id>","Aspect project ID").option("--api-url <url>","Aspect API URL").option("--edge-worker-url <url>","Aspect edge worker URL").option("--api-key <key>","Aspect API key").option("--upload-concurrent <number>","Maximum concurrent chunk uploads to Aspect (default: 16)","16").option("--keep-local","Keep local files after upload (for debugging)",!1).option("--temp-dir <path>","Local temporary directory for synced files").option("--session <name>","Session name for isolation (default: auto-generated timestamp)").option("--check","Check remote files against Aspect without downloading/uploading",!1).option("--batch-size <size>","Enable batched mode with max batch size (e.g., 500GB, 1TB). Helps with large migrations.","").option("--rclone-transfers <number>","Number of parallel file transfers (default: 4)","4").option("--rclone-multi-thread-streams <number>","Streams per large file (default: 4, higher = faster large files)","4").option("--rclone-multi-thread-chunk-size <size>","Chunk size for multi-threading (default: 64MiB)","64MiB").option("--rclone-multi-thread-cutoff <size>","Minimum file size for multi-threading (default: 64MiB)","64MiB").option("--rclone-buffer-size <size>","Buffer size per transfer (default: 64M, memory = transfers \xD7 buffer-size)","64M").option("--rclone-no-mmap","Disable memory-mapped I/O (reduces memory efficiency)",!1).option("--upload-chunk-size <size>","Multipart upload chunk size in MB (default: 64)","64").option("--rclone-extra <arg>","Additional rclone argument to append (repeatable)",(a,e)=>(e??[]).concat(a),[]).action(async a=>{try{let e={ASPECT_API_URL:process.env.ASPECT_API_URL,ASPECT_EDGE_WORKER_URL:process.env.ASPECT_EDGE_WORKER_URL,ASPECT_API_KEY:process.env.ASPECT_API_KEY},t=fe({options:a,environment:e}),s=new X(t);a.check?await s.runCheckOnly():t.batchSizeBytes!==void 0?await s.runBatched():await s.run(),process.exit(0)}catch(e){console.error("Fatal error:",e),process.exit(1)}});pe.parse();
|
package/package.json
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aspect-sync",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "CLI tool to sync files from external services to Aspect via rclone",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"aspect-sync": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist",
|
|
10
|
+
"dist/index.js",
|
|
11
|
+
"dist/index.d.ts",
|
|
11
12
|
"README.md",
|
|
12
13
|
"package.json"
|
|
13
14
|
],
|
|
14
15
|
"type": "module",
|
|
15
16
|
"scripts": {
|
|
16
|
-
"build": "tsc",
|
|
17
|
+
"build": "tsc && esbuild src/index.ts --bundle --platform=node --format=esm --minify --outfile=dist/index.js --external:axios --external:chalk --external:commander --external:log-update",
|
|
17
18
|
"build:watch": "tsc --watch",
|
|
18
19
|
"dev": "tsx src/index.ts",
|
|
20
|
+
"test": "vitest run",
|
|
19
21
|
"clean": "rm -rf dist",
|
|
20
22
|
"prepublishOnly": "npm run clean && npm run build"
|
|
21
23
|
},
|
|
@@ -33,14 +35,16 @@
|
|
|
33
35
|
"axios": "^1.11.0",
|
|
34
36
|
"chalk": "^5.3.0",
|
|
35
37
|
"commander": "^12.0.0",
|
|
36
|
-
"log-update": "^6.0.0"
|
|
37
|
-
"uuid": "^13.0.0"
|
|
38
|
+
"log-update": "^6.0.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
41
|
+
"@aspect/transfer-utils": "file:../packages/transfer-utils",
|
|
42
|
+
"@aspect/upload-core": "file:../packages/upload-core",
|
|
40
43
|
"@types/node": "^22.0.0",
|
|
41
|
-
"
|
|
44
|
+
"esbuild": "^0.28.0",
|
|
42
45
|
"tsx": "^4.7.0",
|
|
43
|
-
"typescript": "^5.8.0"
|
|
46
|
+
"typescript": "^5.8.0",
|
|
47
|
+
"vitest": "^4.0.18"
|
|
44
48
|
},
|
|
45
49
|
"engines": {
|
|
46
50
|
"node": ">=22"
|
package/dist/batchManager.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { RemoteFileListing } from "./types.js";
|
|
2
|
-
export interface BatchInfo {
|
|
3
|
-
batchNumber: number;
|
|
4
|
-
files: RemoteFileListing[];
|
|
5
|
-
totalSize: number;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Groups files into batches, keeping directory contents together where possible.
|
|
9
|
-
* Each batch will be ≤ maxBatchSize bytes (except single files > maxBatchSize).
|
|
10
|
-
*/
|
|
11
|
-
export declare function createBatches(files: RemoteFileListing[], maxBatchSize: number): BatchInfo[];
|
|
12
|
-
/**
|
|
13
|
-
* Writes a batch file containing relative file paths, one per line.
|
|
14
|
-
* Used with rclone's --files-from option.
|
|
15
|
-
*/
|
|
16
|
-
export declare function writeBatchFile(batchDir: string, batchNumber: number, files: RemoteFileListing[]): Promise<string>;
|
|
17
|
-
/**
|
|
18
|
-
* Deletes a batch file after successful completion.
|
|
19
|
-
*/
|
|
20
|
-
export declare function cleanupBatchFile(batchFilePath: string): Promise<void>;
|
|
21
|
-
//# sourceMappingURL=batchManager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.d.ts","sourceRoot":"","sources":["../src/batchManager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,MAAM,WAAW,SAAS;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,iBAAiB,EAAE,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CACzB,KAAK,EAAE,iBAAiB,EAAE,EAC1B,YAAY,EAAE,MAAM,GACrB,SAAS,EAAE,CAmGb;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAChC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,iBAAiB,EAAE,GAC3B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS3E"}
|
package/dist/batchManager.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import fs from "fs/promises";
|
|
2
|
-
import path from "path";
|
|
3
|
-
/**
|
|
4
|
-
* Groups files into batches, keeping directory contents together where possible.
|
|
5
|
-
* Each batch will be ≤ maxBatchSize bytes (except single files > maxBatchSize).
|
|
6
|
-
*/
|
|
7
|
-
export function createBatches(files, maxBatchSize) {
|
|
8
|
-
// Group files by their parent directory
|
|
9
|
-
const dirMap = new Map();
|
|
10
|
-
for (const file of files) {
|
|
11
|
-
const dir = path.dirname(file.path);
|
|
12
|
-
if (!dirMap.has(dir)) {
|
|
13
|
-
dirMap.set(dir, []);
|
|
14
|
-
}
|
|
15
|
-
dirMap.get(dir).push(file);
|
|
16
|
-
}
|
|
17
|
-
// Calculate size per directory
|
|
18
|
-
const dirSizes = new Map();
|
|
19
|
-
for (const [dir, dirFiles] of dirMap.entries()) {
|
|
20
|
-
const totalSize = dirFiles.reduce((sum, f) => sum + f.size, 0);
|
|
21
|
-
dirSizes.set(dir, totalSize);
|
|
22
|
-
}
|
|
23
|
-
// Sort directories by path for consistent ordering
|
|
24
|
-
const sortedDirs = Array.from(dirMap.keys()).sort();
|
|
25
|
-
const batches = [];
|
|
26
|
-
let currentBatch = [];
|
|
27
|
-
let currentBatchSize = 0;
|
|
28
|
-
let batchNumber = 1;
|
|
29
|
-
for (const dir of sortedDirs) {
|
|
30
|
-
const dirFiles = dirMap.get(dir);
|
|
31
|
-
const dirSize = dirSizes.get(dir);
|
|
32
|
-
// Edge case: single file or directory larger than max batch size
|
|
33
|
-
if (dirSize > maxBatchSize) {
|
|
34
|
-
// Flush current batch if it has content
|
|
35
|
-
if (currentBatch.length > 0) {
|
|
36
|
-
batches.push({
|
|
37
|
-
batchNumber: batchNumber++,
|
|
38
|
-
files: currentBatch,
|
|
39
|
-
totalSize: currentBatchSize,
|
|
40
|
-
});
|
|
41
|
-
currentBatch = [];
|
|
42
|
-
currentBatchSize = 0;
|
|
43
|
-
}
|
|
44
|
-
// Put this directory in its own batch (or split into multiple batches)
|
|
45
|
-
if (dirFiles.length === 1) {
|
|
46
|
-
// Single large file - put in its own batch
|
|
47
|
-
batches.push({
|
|
48
|
-
batchNumber: batchNumber++,
|
|
49
|
-
files: dirFiles,
|
|
50
|
-
totalSize: dirSize,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
// Multiple files in directory - split them across batches
|
|
55
|
-
for (const file of dirFiles) {
|
|
56
|
-
if (currentBatchSize + file.size > maxBatchSize && currentBatch.length > 0) {
|
|
57
|
-
// Flush current batch
|
|
58
|
-
batches.push({
|
|
59
|
-
batchNumber: batchNumber++,
|
|
60
|
-
files: currentBatch,
|
|
61
|
-
totalSize: currentBatchSize,
|
|
62
|
-
});
|
|
63
|
-
currentBatch = [];
|
|
64
|
-
currentBatchSize = 0;
|
|
65
|
-
}
|
|
66
|
-
currentBatch.push(file);
|
|
67
|
-
currentBatchSize += file.size;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
// Check if adding this directory would exceed max batch size
|
|
73
|
-
if (currentBatchSize + dirSize > maxBatchSize && currentBatch.length > 0) {
|
|
74
|
-
// Flush current batch
|
|
75
|
-
batches.push({
|
|
76
|
-
batchNumber: batchNumber++,
|
|
77
|
-
files: currentBatch,
|
|
78
|
-
totalSize: currentBatchSize,
|
|
79
|
-
});
|
|
80
|
-
currentBatch = [];
|
|
81
|
-
currentBatchSize = 0;
|
|
82
|
-
}
|
|
83
|
-
// Add directory to current batch
|
|
84
|
-
currentBatch.push(...dirFiles);
|
|
85
|
-
currentBatchSize += dirSize;
|
|
86
|
-
}
|
|
87
|
-
// Flush remaining batch
|
|
88
|
-
if (currentBatch.length > 0) {
|
|
89
|
-
batches.push({
|
|
90
|
-
batchNumber: batchNumber++,
|
|
91
|
-
files: currentBatch,
|
|
92
|
-
totalSize: currentBatchSize,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
return batches;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Writes a batch file containing relative file paths, one per line.
|
|
99
|
-
* Used with rclone's --files-from option.
|
|
100
|
-
*/
|
|
101
|
-
export async function writeBatchFile(batchDir, batchNumber, files) {
|
|
102
|
-
const batchFilePath = path.join(batchDir, `batch${batchNumber}.txt`);
|
|
103
|
-
const content = `${files.map((f) => f.path).join("\n")}\n`;
|
|
104
|
-
await fs.writeFile(batchFilePath, content, "utf-8");
|
|
105
|
-
return batchFilePath;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Deletes a batch file after successful completion.
|
|
109
|
-
*/
|
|
110
|
-
export async function cleanupBatchFile(batchFilePath) {
|
|
111
|
-
try {
|
|
112
|
-
await fs.unlink(batchFilePath);
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
// Ignore errors if file doesn't exist
|
|
116
|
-
if (error.code !== "ENOENT") {
|
|
117
|
-
throw error;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
//# sourceMappingURL=batchManager.js.map
|