@arke-institute/sdk 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -4
- package/dist/{index-BrXke2kI.d.ts → crypto-CQnwqWQn.d.ts} +175 -18
- package/dist/{index-FHcLPBSV.d.cts → crypto-iYgzUi77.d.cts} +175 -18
- package/dist/generated/index.d.cts +381 -28
- package/dist/generated/index.d.ts +381 -28
- package/dist/index.cjs +553 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +553 -10
- package/dist/index.js.map +1 -1
- package/dist/operations/index.cjs +703 -10
- package/dist/operations/index.cjs.map +1 -1
- package/dist/operations/index.d.cts +155 -1
- package/dist/operations/index.d.ts +155 -1
- package/dist/operations/index.js +684 -9
- package/dist/operations/index.js.map +1 -1
- package/openapi/spec.json +8648 -0
- package/openapi/version.json +7 -0
- package/package.json +12 -4
package/dist/index.cjs
CHANGED
|
@@ -57,9 +57,9 @@ var DEFAULT_CONFIG = {
|
|
|
57
57
|
|
|
58
58
|
// src/client/errors.ts
|
|
59
59
|
var ArkeError = class extends Error {
|
|
60
|
-
constructor(message,
|
|
60
|
+
constructor(message, code2, status, details) {
|
|
61
61
|
super(message);
|
|
62
|
-
this.code =
|
|
62
|
+
this.code = code2;
|
|
63
63
|
this.status = status;
|
|
64
64
|
this.details = details;
|
|
65
65
|
this.name = "ArkeError";
|
|
@@ -198,6 +198,532 @@ function createArkeClient(config) {
|
|
|
198
198
|
return new ArkeClient(config);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// src/operations/upload/cid.ts
|
|
202
|
+
var import_cid = require("multiformats/cid");
|
|
203
|
+
var import_sha2 = require("multiformats/hashes/sha2");
|
|
204
|
+
var raw = __toESM(require("multiformats/codecs/raw"), 1);
|
|
205
|
+
async function computeCid(data) {
|
|
206
|
+
let bytes;
|
|
207
|
+
if (data instanceof Blob) {
|
|
208
|
+
const buffer = await data.arrayBuffer();
|
|
209
|
+
bytes = new Uint8Array(buffer);
|
|
210
|
+
} else if (data instanceof ArrayBuffer) {
|
|
211
|
+
bytes = new Uint8Array(data);
|
|
212
|
+
} else {
|
|
213
|
+
bytes = data;
|
|
214
|
+
}
|
|
215
|
+
const hash = await import_sha2.sha256.digest(bytes);
|
|
216
|
+
const cid = import_cid.CID.create(1, raw.code, hash);
|
|
217
|
+
return cid.toString();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/operations/upload/engine.ts
|
|
221
|
+
async function parallelLimit(items, concurrency, fn) {
|
|
222
|
+
const results = [];
|
|
223
|
+
let index = 0;
|
|
224
|
+
async function worker() {
|
|
225
|
+
while (index < items.length) {
|
|
226
|
+
const currentIndex = index++;
|
|
227
|
+
const item = items[currentIndex];
|
|
228
|
+
results[currentIndex] = await fn(item, currentIndex);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());
|
|
232
|
+
await Promise.all(workers);
|
|
233
|
+
return results;
|
|
234
|
+
}
|
|
235
|
+
function getParentPath(relativePath) {
|
|
236
|
+
const lastSlash = relativePath.lastIndexOf("/");
|
|
237
|
+
if (lastSlash === -1) return null;
|
|
238
|
+
return relativePath.slice(0, lastSlash);
|
|
239
|
+
}
|
|
240
|
+
async function uploadTree(client, tree, options) {
|
|
241
|
+
const { target, onProgress, concurrency = 5, continueOnError = false, note } = options;
|
|
242
|
+
const errors = [];
|
|
243
|
+
const createdFolders = [];
|
|
244
|
+
const createdFiles = [];
|
|
245
|
+
const reportProgress = (progress) => {
|
|
246
|
+
if (onProgress) {
|
|
247
|
+
onProgress({
|
|
248
|
+
phase: "scanning",
|
|
249
|
+
totalFiles: tree.files.length,
|
|
250
|
+
completedFiles: 0,
|
|
251
|
+
totalFolders: tree.folders.length,
|
|
252
|
+
completedFolders: 0,
|
|
253
|
+
...progress
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
try {
|
|
258
|
+
let collectionId;
|
|
259
|
+
let collectionCid;
|
|
260
|
+
let collectionCreated = false;
|
|
261
|
+
if (target.createCollection) {
|
|
262
|
+
reportProgress({ phase: "scanning", currentFolder: "Creating collection..." });
|
|
263
|
+
const collectionBody = {
|
|
264
|
+
label: target.createCollection.label,
|
|
265
|
+
description: target.createCollection.description,
|
|
266
|
+
roles: target.createCollection.roles,
|
|
267
|
+
note
|
|
268
|
+
};
|
|
269
|
+
const { data, error } = await client.api.POST("/collections", {
|
|
270
|
+
body: collectionBody
|
|
271
|
+
});
|
|
272
|
+
if (error || !data) {
|
|
273
|
+
throw new Error(`Failed to create collection: ${JSON.stringify(error)}`);
|
|
274
|
+
}
|
|
275
|
+
collectionId = data.id;
|
|
276
|
+
collectionCid = data.cid;
|
|
277
|
+
collectionCreated = true;
|
|
278
|
+
} else if (target.collectionId) {
|
|
279
|
+
collectionId = target.collectionId;
|
|
280
|
+
const { data, error } = await client.api.GET("/collections/{id}", {
|
|
281
|
+
params: { path: { id: collectionId } }
|
|
282
|
+
});
|
|
283
|
+
if (error || !data) {
|
|
284
|
+
throw new Error(`Failed to fetch collection: ${JSON.stringify(error)}`);
|
|
285
|
+
}
|
|
286
|
+
collectionCid = data.cid;
|
|
287
|
+
} else {
|
|
288
|
+
throw new Error("Must provide either collectionId or createCollection in target");
|
|
289
|
+
}
|
|
290
|
+
const rootParentId = target.parentId ?? collectionId;
|
|
291
|
+
reportProgress({
|
|
292
|
+
phase: "computing-cids",
|
|
293
|
+
totalFiles: tree.files.length,
|
|
294
|
+
completedFiles: 0
|
|
295
|
+
});
|
|
296
|
+
const preparedFiles = [];
|
|
297
|
+
let cidProgress = 0;
|
|
298
|
+
await parallelLimit(tree.files, concurrency, async (file) => {
|
|
299
|
+
try {
|
|
300
|
+
const data = await file.getData();
|
|
301
|
+
const cid = await computeCid(data);
|
|
302
|
+
preparedFiles.push({
|
|
303
|
+
...file,
|
|
304
|
+
cid
|
|
305
|
+
});
|
|
306
|
+
cidProgress++;
|
|
307
|
+
reportProgress({
|
|
308
|
+
phase: "computing-cids",
|
|
309
|
+
completedFiles: cidProgress,
|
|
310
|
+
currentFile: file.relativePath
|
|
311
|
+
});
|
|
312
|
+
} catch (err) {
|
|
313
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
314
|
+
if (continueOnError) {
|
|
315
|
+
errors.push({ path: file.relativePath, error: `CID computation failed: ${errorMsg}` });
|
|
316
|
+
} else {
|
|
317
|
+
throw new Error(`Failed to compute CID for ${file.relativePath}: ${errorMsg}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
reportProgress({
|
|
322
|
+
phase: "creating-folders",
|
|
323
|
+
totalFolders: tree.folders.length,
|
|
324
|
+
completedFolders: 0
|
|
325
|
+
});
|
|
326
|
+
const sortedFolders = [...tree.folders].sort(
|
|
327
|
+
(a, b) => a.relativePath.split("/").length - b.relativePath.split("/").length
|
|
328
|
+
);
|
|
329
|
+
for (let i = 0; i < sortedFolders.length; i++) {
|
|
330
|
+
const folder = sortedFolders[i];
|
|
331
|
+
try {
|
|
332
|
+
const folderBody = {
|
|
333
|
+
label: folder.name,
|
|
334
|
+
collection: collectionId,
|
|
335
|
+
note
|
|
336
|
+
};
|
|
337
|
+
const { data, error } = await client.api.POST("/folders", {
|
|
338
|
+
body: folderBody
|
|
339
|
+
});
|
|
340
|
+
if (error || !data) {
|
|
341
|
+
throw new Error(JSON.stringify(error));
|
|
342
|
+
}
|
|
343
|
+
createdFolders.push({
|
|
344
|
+
name: folder.name,
|
|
345
|
+
relativePath: folder.relativePath,
|
|
346
|
+
id: data.id,
|
|
347
|
+
entityCid: data.cid
|
|
348
|
+
});
|
|
349
|
+
reportProgress({
|
|
350
|
+
phase: "creating-folders",
|
|
351
|
+
completedFolders: i + 1,
|
|
352
|
+
currentFolder: folder.relativePath
|
|
353
|
+
});
|
|
354
|
+
} catch (err) {
|
|
355
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
356
|
+
if (continueOnError) {
|
|
357
|
+
errors.push({ path: folder.relativePath, error: `Folder creation failed: ${errorMsg}` });
|
|
358
|
+
} else {
|
|
359
|
+
throw new Error(`Failed to create folder ${folder.relativePath}: ${errorMsg}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const folderPathToEntity = /* @__PURE__ */ new Map();
|
|
364
|
+
for (const folder of createdFolders) {
|
|
365
|
+
folderPathToEntity.set(folder.relativePath, folder);
|
|
366
|
+
}
|
|
367
|
+
reportProgress({
|
|
368
|
+
phase: "creating-files",
|
|
369
|
+
totalFiles: preparedFiles.length,
|
|
370
|
+
completedFiles: 0
|
|
371
|
+
});
|
|
372
|
+
let fileCreateProgress = 0;
|
|
373
|
+
await parallelLimit(preparedFiles, concurrency, async (file) => {
|
|
374
|
+
try {
|
|
375
|
+
const fileBody = {
|
|
376
|
+
key: file.cid,
|
|
377
|
+
// Use CID as storage key (best practice)
|
|
378
|
+
filename: file.name,
|
|
379
|
+
content_type: file.mimeType,
|
|
380
|
+
size: file.size,
|
|
381
|
+
cid: file.cid,
|
|
382
|
+
collection: collectionId
|
|
383
|
+
};
|
|
384
|
+
const { data, error } = await client.api.POST("/files", {
|
|
385
|
+
body: fileBody
|
|
386
|
+
});
|
|
387
|
+
if (error || !data) {
|
|
388
|
+
throw new Error(JSON.stringify(error));
|
|
389
|
+
}
|
|
390
|
+
createdFiles.push({
|
|
391
|
+
...file,
|
|
392
|
+
id: data.id,
|
|
393
|
+
entityCid: data.cid,
|
|
394
|
+
uploadUrl: data.upload_url,
|
|
395
|
+
uploadExpiresAt: data.upload_expires_at
|
|
396
|
+
});
|
|
397
|
+
fileCreateProgress++;
|
|
398
|
+
reportProgress({
|
|
399
|
+
phase: "creating-files",
|
|
400
|
+
completedFiles: fileCreateProgress,
|
|
401
|
+
currentFile: file.relativePath
|
|
402
|
+
});
|
|
403
|
+
} catch (err) {
|
|
404
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
405
|
+
if (continueOnError) {
|
|
406
|
+
errors.push({ path: file.relativePath, error: `File creation failed: ${errorMsg}` });
|
|
407
|
+
} else {
|
|
408
|
+
throw new Error(`Failed to create file ${file.relativePath}: ${errorMsg}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
const totalBytes = createdFiles.reduce((sum, f) => sum + f.size, 0);
|
|
413
|
+
let bytesUploaded = 0;
|
|
414
|
+
reportProgress({
|
|
415
|
+
phase: "uploading-content",
|
|
416
|
+
totalFiles: createdFiles.length,
|
|
417
|
+
completedFiles: 0,
|
|
418
|
+
totalBytes,
|
|
419
|
+
bytesUploaded: 0
|
|
420
|
+
});
|
|
421
|
+
let uploadProgress = 0;
|
|
422
|
+
await parallelLimit(createdFiles, concurrency, async (file) => {
|
|
423
|
+
try {
|
|
424
|
+
const data = await file.getData();
|
|
425
|
+
let body;
|
|
426
|
+
if (data instanceof Blob) {
|
|
427
|
+
body = data;
|
|
428
|
+
} else if (data instanceof Uint8Array) {
|
|
429
|
+
const arrayBuffer = new ArrayBuffer(data.byteLength);
|
|
430
|
+
new Uint8Array(arrayBuffer).set(data);
|
|
431
|
+
body = new Blob([arrayBuffer], { type: file.mimeType });
|
|
432
|
+
} else {
|
|
433
|
+
body = new Blob([data], { type: file.mimeType });
|
|
434
|
+
}
|
|
435
|
+
const response = await fetch(file.uploadUrl, {
|
|
436
|
+
method: "PUT",
|
|
437
|
+
body,
|
|
438
|
+
headers: {
|
|
439
|
+
"Content-Type": file.mimeType
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
if (!response.ok) {
|
|
443
|
+
throw new Error(`Upload failed with status ${response.status}`);
|
|
444
|
+
}
|
|
445
|
+
bytesUploaded += file.size;
|
|
446
|
+
uploadProgress++;
|
|
447
|
+
reportProgress({
|
|
448
|
+
phase: "uploading-content",
|
|
449
|
+
completedFiles: uploadProgress,
|
|
450
|
+
currentFile: file.relativePath,
|
|
451
|
+
bytesUploaded,
|
|
452
|
+
totalBytes
|
|
453
|
+
});
|
|
454
|
+
} catch (err) {
|
|
455
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
456
|
+
if (continueOnError) {
|
|
457
|
+
errors.push({ path: file.relativePath, error: `Upload failed: ${errorMsg}` });
|
|
458
|
+
} else {
|
|
459
|
+
throw new Error(`Failed to upload ${file.relativePath}: ${errorMsg}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
reportProgress({ phase: "linking" });
|
|
464
|
+
const filePathToEntity = /* @__PURE__ */ new Map();
|
|
465
|
+
for (const file of createdFiles) {
|
|
466
|
+
filePathToEntity.set(file.relativePath, file);
|
|
467
|
+
}
|
|
468
|
+
const parentGroups = /* @__PURE__ */ new Map();
|
|
469
|
+
for (const folder of createdFolders) {
|
|
470
|
+
const parentPath = getParentPath(folder.relativePath);
|
|
471
|
+
let parentId;
|
|
472
|
+
if (parentPath === null) {
|
|
473
|
+
parentId = rootParentId;
|
|
474
|
+
} else {
|
|
475
|
+
const parentFolder = folderPathToEntity.get(parentPath);
|
|
476
|
+
if (!parentFolder) {
|
|
477
|
+
errors.push({
|
|
478
|
+
path: folder.relativePath,
|
|
479
|
+
error: `Parent folder not found: ${parentPath}`
|
|
480
|
+
});
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
parentId = parentFolder.id;
|
|
484
|
+
}
|
|
485
|
+
if (!parentGroups.has(parentId)) {
|
|
486
|
+
parentGroups.set(parentId, { folderId: parentId, children: [] });
|
|
487
|
+
}
|
|
488
|
+
parentGroups.get(parentId).children.push({ id: folder.id });
|
|
489
|
+
}
|
|
490
|
+
for (const file of createdFiles) {
|
|
491
|
+
const parentPath = getParentPath(file.relativePath);
|
|
492
|
+
let parentId;
|
|
493
|
+
if (parentPath === null) {
|
|
494
|
+
parentId = rootParentId;
|
|
495
|
+
} else {
|
|
496
|
+
const parentFolder = folderPathToEntity.get(parentPath);
|
|
497
|
+
if (!parentFolder) {
|
|
498
|
+
errors.push({
|
|
499
|
+
path: file.relativePath,
|
|
500
|
+
error: `Parent folder not found: ${parentPath}`
|
|
501
|
+
});
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
parentId = parentFolder.id;
|
|
505
|
+
}
|
|
506
|
+
if (!parentGroups.has(parentId)) {
|
|
507
|
+
parentGroups.set(parentId, { folderId: parentId, children: [] });
|
|
508
|
+
}
|
|
509
|
+
parentGroups.get(parentId).children.push({ id: file.id });
|
|
510
|
+
}
|
|
511
|
+
for (const [parentId, group] of parentGroups) {
|
|
512
|
+
if (group.children.length === 0) continue;
|
|
513
|
+
try {
|
|
514
|
+
let expectTip;
|
|
515
|
+
if (parentId === collectionId) {
|
|
516
|
+
const { data, error: error2 } = await client.api.GET("/collections/{id}", {
|
|
517
|
+
params: { path: { id: collectionId } }
|
|
518
|
+
});
|
|
519
|
+
if (error2 || !data) {
|
|
520
|
+
throw new Error(`Failed to fetch collection CID: ${JSON.stringify(error2)}`);
|
|
521
|
+
}
|
|
522
|
+
expectTip = data.cid;
|
|
523
|
+
} else {
|
|
524
|
+
const { data, error: error2 } = await client.api.GET("/folders/{id}", {
|
|
525
|
+
params: { path: { id: parentId } }
|
|
526
|
+
});
|
|
527
|
+
if (error2 || !data) {
|
|
528
|
+
throw new Error(`Failed to fetch folder CID: ${JSON.stringify(error2)}`);
|
|
529
|
+
}
|
|
530
|
+
expectTip = data.cid;
|
|
531
|
+
}
|
|
532
|
+
const bulkBody = {
|
|
533
|
+
expect_tip: expectTip,
|
|
534
|
+
children: group.children,
|
|
535
|
+
note
|
|
536
|
+
};
|
|
537
|
+
const { error } = await client.api.POST("/folders/{id}/children/bulk", {
|
|
538
|
+
params: { path: { id: parentId } },
|
|
539
|
+
body: bulkBody
|
|
540
|
+
});
|
|
541
|
+
if (error) {
|
|
542
|
+
throw new Error(JSON.stringify(error));
|
|
543
|
+
}
|
|
544
|
+
} catch (err) {
|
|
545
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
546
|
+
if (continueOnError) {
|
|
547
|
+
errors.push({
|
|
548
|
+
path: `parent:${parentId}`,
|
|
549
|
+
error: `Bulk linking failed: ${errorMsg}`
|
|
550
|
+
});
|
|
551
|
+
} else {
|
|
552
|
+
throw new Error(`Failed to link children to ${parentId}: ${errorMsg}`);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
reportProgress({ phase: "complete" });
|
|
557
|
+
const resultFolders = createdFolders.map((f) => ({
|
|
558
|
+
id: f.id,
|
|
559
|
+
cid: f.entityCid,
|
|
560
|
+
type: "folder",
|
|
561
|
+
relativePath: f.relativePath
|
|
562
|
+
}));
|
|
563
|
+
const resultFiles = createdFiles.map((f) => ({
|
|
564
|
+
id: f.id,
|
|
565
|
+
cid: f.entityCid,
|
|
566
|
+
type: "file",
|
|
567
|
+
relativePath: f.relativePath
|
|
568
|
+
}));
|
|
569
|
+
return {
|
|
570
|
+
success: errors.length === 0,
|
|
571
|
+
collection: {
|
|
572
|
+
id: collectionId,
|
|
573
|
+
cid: collectionCid,
|
|
574
|
+
created: collectionCreated
|
|
575
|
+
},
|
|
576
|
+
folders: resultFolders,
|
|
577
|
+
files: resultFiles,
|
|
578
|
+
errors
|
|
579
|
+
};
|
|
580
|
+
} catch (err) {
|
|
581
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
582
|
+
reportProgress({
|
|
583
|
+
phase: "error",
|
|
584
|
+
error: errorMsg
|
|
585
|
+
});
|
|
586
|
+
return {
|
|
587
|
+
success: false,
|
|
588
|
+
collection: {
|
|
589
|
+
id: target.collectionId ?? "",
|
|
590
|
+
cid: "",
|
|
591
|
+
created: false
|
|
592
|
+
},
|
|
593
|
+
folders: createdFolders.map((f) => ({
|
|
594
|
+
id: f.id,
|
|
595
|
+
cid: f.entityCid,
|
|
596
|
+
type: "folder",
|
|
597
|
+
relativePath: f.relativePath
|
|
598
|
+
})),
|
|
599
|
+
files: createdFiles.map((f) => ({
|
|
600
|
+
id: f.id,
|
|
601
|
+
cid: f.entityCid,
|
|
602
|
+
type: "file",
|
|
603
|
+
relativePath: f.relativePath
|
|
604
|
+
})),
|
|
605
|
+
errors: [...errors, { path: "", error: errorMsg }]
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/operations/upload/scanners.ts
|
|
611
|
+
function getMimeType(filename) {
|
|
612
|
+
const ext = filename.toLowerCase().split(".").pop() || "";
|
|
613
|
+
const mimeTypes = {
|
|
614
|
+
// Images
|
|
615
|
+
jpg: "image/jpeg",
|
|
616
|
+
jpeg: "image/jpeg",
|
|
617
|
+
png: "image/png",
|
|
618
|
+
gif: "image/gif",
|
|
619
|
+
webp: "image/webp",
|
|
620
|
+
svg: "image/svg+xml",
|
|
621
|
+
ico: "image/x-icon",
|
|
622
|
+
bmp: "image/bmp",
|
|
623
|
+
tiff: "image/tiff",
|
|
624
|
+
tif: "image/tiff",
|
|
625
|
+
// Documents
|
|
626
|
+
pdf: "application/pdf",
|
|
627
|
+
doc: "application/msword",
|
|
628
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
629
|
+
xls: "application/vnd.ms-excel",
|
|
630
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
631
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
632
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
633
|
+
odt: "application/vnd.oasis.opendocument.text",
|
|
634
|
+
ods: "application/vnd.oasis.opendocument.spreadsheet",
|
|
635
|
+
odp: "application/vnd.oasis.opendocument.presentation",
|
|
636
|
+
// Text
|
|
637
|
+
txt: "text/plain",
|
|
638
|
+
md: "text/markdown",
|
|
639
|
+
csv: "text/csv",
|
|
640
|
+
html: "text/html",
|
|
641
|
+
htm: "text/html",
|
|
642
|
+
css: "text/css",
|
|
643
|
+
xml: "text/xml",
|
|
644
|
+
rtf: "application/rtf",
|
|
645
|
+
// Code
|
|
646
|
+
js: "text/javascript",
|
|
647
|
+
mjs: "text/javascript",
|
|
648
|
+
ts: "text/typescript",
|
|
649
|
+
jsx: "text/javascript",
|
|
650
|
+
tsx: "text/typescript",
|
|
651
|
+
json: "application/json",
|
|
652
|
+
yaml: "text/yaml",
|
|
653
|
+
yml: "text/yaml",
|
|
654
|
+
// Archives
|
|
655
|
+
zip: "application/zip",
|
|
656
|
+
tar: "application/x-tar",
|
|
657
|
+
gz: "application/gzip",
|
|
658
|
+
rar: "application/vnd.rar",
|
|
659
|
+
"7z": "application/x-7z-compressed",
|
|
660
|
+
// Audio
|
|
661
|
+
mp3: "audio/mpeg",
|
|
662
|
+
wav: "audio/wav",
|
|
663
|
+
ogg: "audio/ogg",
|
|
664
|
+
m4a: "audio/mp4",
|
|
665
|
+
flac: "audio/flac",
|
|
666
|
+
// Video
|
|
667
|
+
mp4: "video/mp4",
|
|
668
|
+
webm: "video/webm",
|
|
669
|
+
avi: "video/x-msvideo",
|
|
670
|
+
mov: "video/quicktime",
|
|
671
|
+
mkv: "video/x-matroska",
|
|
672
|
+
// Fonts
|
|
673
|
+
woff: "font/woff",
|
|
674
|
+
woff2: "font/woff2",
|
|
675
|
+
ttf: "font/ttf",
|
|
676
|
+
otf: "font/otf",
|
|
677
|
+
// Other
|
|
678
|
+
wasm: "application/wasm"
|
|
679
|
+
};
|
|
680
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
681
|
+
}
|
|
682
|
+
async function scanDirectory(directoryPath, options = {}) {
|
|
683
|
+
const fs = await import("fs/promises");
|
|
684
|
+
const path = await import("path");
|
|
685
|
+
const { ignore = ["node_modules", ".git", ".DS_Store"], includeHidden = false } = options;
|
|
686
|
+
const files = [];
|
|
687
|
+
const folders = [];
|
|
688
|
+
const rootName = path.basename(directoryPath);
|
|
689
|
+
async function scanDir(dirPath, relativePath) {
|
|
690
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
691
|
+
for (const entry of entries) {
|
|
692
|
+
const name = entry.name;
|
|
693
|
+
if (!includeHidden && name.startsWith(".")) {
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
if (ignore.some((pattern) => name === pattern || name.match(pattern))) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
const fullPath = path.join(dirPath, name);
|
|
700
|
+
const entryRelativePath = relativePath ? `${relativePath}/${name}` : name;
|
|
701
|
+
if (entry.isDirectory()) {
|
|
702
|
+
folders.push({
|
|
703
|
+
name,
|
|
704
|
+
relativePath: entryRelativePath
|
|
705
|
+
});
|
|
706
|
+
await scanDir(fullPath, entryRelativePath);
|
|
707
|
+
} else if (entry.isFile()) {
|
|
708
|
+
const stat = await fs.stat(fullPath);
|
|
709
|
+
files.push({
|
|
710
|
+
name,
|
|
711
|
+
relativePath: entryRelativePath,
|
|
712
|
+
size: stat.size,
|
|
713
|
+
mimeType: getMimeType(name),
|
|
714
|
+
getData: async () => {
|
|
715
|
+
const buffer = await fs.readFile(fullPath);
|
|
716
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
await scanDir(directoryPath, "");
|
|
723
|
+
folders.sort((a, b) => a.relativePath.split("/").length - b.relativePath.split("/").length);
|
|
724
|
+
return { files, folders };
|
|
725
|
+
}
|
|
726
|
+
|
|
201
727
|
// src/operations/folders.ts
|
|
202
728
|
var FolderOperations = class {
|
|
203
729
|
constructor(client) {
|
|
@@ -206,15 +732,32 @@ var FolderOperations = class {
|
|
|
206
732
|
/**
|
|
207
733
|
* Upload a local directory to Arke
|
|
208
734
|
*
|
|
209
|
-
*
|
|
210
|
-
* Steps:
|
|
211
|
-
* 1. Scan directory structure
|
|
212
|
-
* 2. Create folder hierarchy (depth-first)
|
|
213
|
-
* 3. Upload files in parallel (with concurrency limit)
|
|
214
|
-
* 4. Create bidirectional relationships (folder contains file)
|
|
735
|
+
* @deprecated Use uploadTree and scanDirectory instead
|
|
215
736
|
*/
|
|
216
|
-
async uploadDirectory(
|
|
217
|
-
|
|
737
|
+
async uploadDirectory(localPath, options) {
|
|
738
|
+
const tree = await scanDirectory(localPath);
|
|
739
|
+
const result = await uploadTree(this.client, tree, {
|
|
740
|
+
target: {
|
|
741
|
+
collectionId: options.collectionId,
|
|
742
|
+
parentId: options.parentFolderId
|
|
743
|
+
},
|
|
744
|
+
concurrency: options.concurrency,
|
|
745
|
+
onProgress: options.onProgress ? (p) => {
|
|
746
|
+
options.onProgress({
|
|
747
|
+
phase: p.phase === "computing-cids" || p.phase === "creating-folders" ? "creating-folders" : p.phase === "creating-files" || p.phase === "uploading-content" ? "uploading-files" : p.phase === "linking" ? "linking" : p.phase === "complete" ? "complete" : "scanning",
|
|
748
|
+
totalFiles: p.totalFiles,
|
|
749
|
+
completedFiles: p.completedFiles,
|
|
750
|
+
totalFolders: p.totalFolders,
|
|
751
|
+
completedFolders: p.completedFolders,
|
|
752
|
+
currentFile: p.currentFile
|
|
753
|
+
});
|
|
754
|
+
} : void 0
|
|
755
|
+
});
|
|
756
|
+
return {
|
|
757
|
+
rootFolder: result.folders[0] || null,
|
|
758
|
+
folders: result.folders,
|
|
759
|
+
files: result.files
|
|
760
|
+
};
|
|
218
761
|
}
|
|
219
762
|
};
|
|
220
763
|
|